Nushell plugin for interacting with D-Bus
1use std::time::Duration;
2
3use nu_plugin::EvaluatedCall;
4use nu_protocol::{LabeledError, Span, Spanned};
5
6/// General configuration related to the D-Bus client connection
7#[derive(Debug, Clone)]
8pub struct DbusClientConfig {
9 pub span: Span,
10 /// Which bus should we connect to?
11 pub bus_choice: Spanned<DbusBusChoice>,
12 /// How long to wait for a method call to return
13 pub timeout: Spanned<Duration>,
14 /// Enable introspection if signature unknown (default true)
15 pub introspect: bool,
16}
17
18/// Where to connect to the D-Bus server
19#[derive(Debug, Clone, Default)]
20pub enum DbusBusChoice {
21 /// Connect to the session bus
22 #[default]
23 Session,
24 /// Connect to the system bus
25 System,
26 /// Connect to the bus that started this process
27 Started,
28 /// Connect to a bus instance at the given address
29 Bus(String),
30 /// Connect to a non-bus D-Bus server at the given address (will not send Hello)
31 Peer(String),
32}
33
34impl TryFrom<&EvaluatedCall> for DbusClientConfig {
35 type Error = LabeledError;
36
37 fn try_from(call: &EvaluatedCall) -> Result<Self, Self::Error> {
38 let mut config = DbusClientConfig {
39 span: call.head,
40 bus_choice: Spanned {
41 item: DbusBusChoice::default(),
42 span: call.head,
43 },
44 timeout: Spanned {
45 item: Duration::from_secs(2),
46 span: call.head,
47 },
48 introspect: true,
49 };
50
51 // Handle recognized config args
52 for (name, value) in &call.named {
53 match &name.item[..] {
54 r#type @ ("session" | "system" | "started") => {
55 if value.is_none() || value.as_ref().is_some_and(|v| v.is_true()) {
56 let dest = match r#type {
57 "session" => DbusBusChoice::Session,
58 "system" => DbusBusChoice::System,
59 "started" => DbusBusChoice::Started,
60 _ => unreachable!(),
61 };
62 config.bus_choice = Spanned {
63 item: dest,
64 span: name.span,
65 };
66 }
67 }
68 r#type @ ("bus" | "peer") => {
69 if let Some(value) = value {
70 let address = value.as_str()?;
71 let dest = match r#type {
72 "bus" => DbusBusChoice::Bus(address.to_owned()),
73 "peer" => DbusBusChoice::Peer(address.to_owned()),
74 _ => unreachable!(),
75 };
76 config.bus_choice = Spanned {
77 item: dest,
78 span: value.span(),
79 };
80 }
81 }
82 "timeout" => {
83 if let Some(value) = value {
84 let nanos: u64 = value.as_duration()?.try_into().map_err(|_| {
85 LabeledError::new("Timeout must be a positive duration")
86 .with_label("invalid timeout specified here", value.span())
87 })?;
88 let item = Duration::from_nanos(nanos);
89 config.timeout = Spanned {
90 item,
91 span: value.span(),
92 };
93 }
94 }
95 "no-introspect" => {
96 config.introspect = !value
97 .as_ref()
98 .and_then(|v| v.as_bool().ok())
99 .unwrap_or(false);
100 }
101 _ => (),
102 }
103 }
104
105 Ok(config)
106 }
107}