Nushell plugin for interacting with D-Bus
1use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
2use nu_protocol::{Example, LabeledError, Signature, SyntaxShape, Type, Value};
3
4use crate::{DbusSignatureUtilExt, client::DbusClient, config::DbusClientConfig};
5
6pub struct Call;
7
8impl SimplePluginCommand for Call {
9 type Plugin = crate::NuPluginDbus;
10
11 fn name(&self) -> &str {
12 "dbus call"
13 }
14
15 fn signature(&self) -> Signature {
16 Signature::build(self.name())
17 .dbus_command()
18 .accepts_dbus_client_options()
19 .accepts_timeout()
20 .input_output_type(Type::Nothing, Type::Any)
21 .named(
22 "signature",
23 SyntaxShape::String,
24 "Signature of the arguments to send, in D-Bus format.\n \
25 If not provided, they will be determined from introspection.\n \
26 If --no-introspect is specified and this is not provided, they will \
27 be guessed (poorly)",
28 None,
29 )
30 .switch(
31 "no-flatten",
32 "Always return a list of all return values",
33 None,
34 )
35 .switch(
36 "no-introspect",
37 "Don't use introspection to determine the correct argument signature",
38 None,
39 )
40 .required_named(
41 "dest",
42 SyntaxShape::String,
43 "The name of the connection to send the method to",
44 None,
45 )
46 .required(
47 "object",
48 SyntaxShape::String,
49 "The path to the object to call the method on",
50 )
51 .required(
52 "interface",
53 SyntaxShape::String,
54 "The name of the interface the method belongs to",
55 )
56 .required(
57 "method",
58 SyntaxShape::String,
59 "The name of the method to send",
60 )
61 .rest(
62 "args",
63 SyntaxShape::Any,
64 "Arguments to send with the method call",
65 )
66 }
67
68 fn description(&self) -> &str {
69 "Call a method and get its response"
70 }
71
72 fn extra_description(&self) -> &str {
73 "Returns an array if the method call returns more than one value."
74 }
75
76 fn search_terms(&self) -> Vec<&str> {
77 vec!["dbus"]
78 }
79
80 fn examples(&self) -> Vec<Example<'_>> {
81 vec![
82 Example {
83 example: "dbus call --dest=org.freedesktop.DBus \
84 /org/freedesktop/DBus org.freedesktop.DBus.Peer Ping",
85 description: "Ping the D-Bus server itself",
86 result: None,
87 },
88 Example {
89 example: "dbus call --dest=org.freedesktop.Notifications \
90 /org/freedesktop/Notifications org.freedesktop.Notifications \
91 Notify \"Floppy disks\" 0 \"media-floppy\" \"Rarely seen\" \
92 \"But sometimes still used\" [] {} 5000",
93 description: "Show a notification on the desktop for 5 seconds",
94 result: None,
95 },
96 ]
97 }
98
99 fn run(
100 &self,
101 _plugin: &Self::Plugin,
102 _engine: &EngineInterface,
103 call: &EvaluatedCall,
104 _input: &Value,
105 ) -> Result<Value, LabeledError> {
106 let config = DbusClientConfig::try_from(call)?;
107 let dbus = DbusClient::new(config)?;
108 let values = dbus.call(
109 &call.get_flag("dest")?.unwrap(),
110 &call.req(0)?,
111 &call.req(1)?,
112 &call.req(2)?,
113 call.get_flag("signature")?.as_ref(),
114 &call.positional[3..],
115 )?;
116
117 let flatten = !call.get_flag::<bool>("no-flatten")?.unwrap_or(false);
118
119 // Make the output easier to deal with by returning a list only if there are multiple return
120 // values (not so common)
121 match values.len() {
122 0 if flatten => Ok(Value::nothing(call.head)),
123 1 if flatten => Ok(values.into_iter().nth(0).unwrap()),
124 _ => Ok(Value::list(values, call.head)),
125 }
126 }
127}