Nushell plugin for interacting with D-Bus
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add `dbus introspect` command, for access to introspection data in a more comfortable format than XML

+187
+35
README.md
··· 19 19 dbus call - Call a method and get its response 20 20 dbus get - Get a D-Bus property 21 21 dbus get-all - Get all D-Bus property for the given objects 22 + dbus introspect - Introspect a D-Bus object 22 23 dbus set - Get all D-Bus property for the given objects 23 24 24 25 Flags: 25 26 -h, --help - Display the help message for this command 27 + 28 + ## `dbus introspect` 29 + 30 + Introspect a D-Bus object 31 + 32 + Returns information about available nodes, interfaces, methods, signals, and properties on the given object path 33 + 34 + Search terms: dbus 35 + 36 + Usage: 37 + > dbus introspect {flags} <object> 38 + 39 + Flags: 40 + -h, --help - Display the help message for this command 41 + --session - Send to the session message bus (default) 42 + --system - Send to the system message bus 43 + --started - Send to the bus that started this process, if applicable 44 + --bus <String> - Send to the bus server at the given address 45 + --peer <String> - Send to a non-bus D-Bus server at the given address. Will not call the Hello method on initialization. 46 + --timeout <Duration> - How long to wait for a response 47 + --dest (required parameter) <String> - The name of the connection that owns the object 48 + 49 + Parameters: 50 + object <string>: The path to the object to introspect 51 + 52 + Examples: 53 + Look at the MPRIS2 interfaces exposed by Spotify 54 + > dbus introspect --dest=org.mpris.MediaPlayer2.spotify /org/mpris/MediaPlayer2 | explore 55 + 56 + Get methods exposed by KDE Plasma's on-screen display service 57 + > dbus introspect --dest=org.kde.plasmashell /org/kde/osdService | get interfaces | where name == org.kde.osdService | get 0.methods 58 + 59 + List objects exposed by KWin 60 + > dbus introspect --dest=org.kde.KWin / | get children | select name 26 61 27 62 ## `dbus call` 28 63
+107
src/introspection.rs
··· 1 + use nu_protocol::{Value, record, Span}; 1 2 use serde::Deserialize; 3 + 4 + macro_rules! list_to_value { 5 + ($list:expr, $span:expr) => ( 6 + Value::list($list.iter().map(|i| i.to_value($span)).collect(), $span) 7 + ) 8 + } 2 9 3 10 #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] 4 11 #[serde(rename_all = "kebab-case")] ··· 41 48 pub fn get_property_signature(&self, interface: &str, property: &str) -> Option<&str> { 42 49 Some(&self.get_interface(interface)?.get_property(property)?.r#type) 43 50 } 51 + 52 + /// Represent the node as a nushell [Value] 53 + pub fn to_value(&self, span: Span) -> Value { 54 + Value::record(record!{ 55 + "name" => self.name.as_ref().map(|s| Value::string(s, span)).unwrap_or_default(), 56 + "interfaces" => list_to_value!(self.interfaces, span), 57 + "children" => list_to_value!(self.children, span), 58 + }, span) 59 + } 44 60 } 45 61 46 62 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] ··· 69 85 70 86 pub fn get_property(&self, name: &str) -> Option<&Property> { 71 87 self.properties.iter().find(|p| p.name == name) 88 + } 89 + 90 + /// Represent the interface as a nushell [Value] 91 + pub fn to_value(&self, span: Span) -> Value { 92 + Value::record(record!{ 93 + "name" => Value::string(&self.name, span), 94 + "methods" => list_to_value!(self.methods, span), 95 + "signals" => list_to_value!(self.signals, span), 96 + "properties" => list_to_value!(self.properties, span), 97 + "signals" => list_to_value!(self.signals, span), 98 + }, span) 72 99 } 73 100 } 74 101 ··· 99 126 .map(|arg| &arg.r#type[..]) 100 127 .collect() 101 128 } 129 + 130 + /// Represent the method as a nushell [Value] 131 + pub fn to_value(&self, span: Span) -> Value { 132 + Value::record(record!{ 133 + "name" => Value::string(&self.name, span), 134 + "args" => list_to_value!(self.args, span), 135 + "annotations" => list_to_value!(self.annotations, span), 136 + }, span) 137 + } 102 138 } 103 139 104 140 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] ··· 124 160 direction, 125 161 } 126 162 } 163 + 164 + /// Represent the method as a nushell [Value] 165 + pub fn to_value(&self, span: Span) -> Value { 166 + Value::record(record!{ 167 + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 168 + "type" => Value::string(&self.r#type, span), 169 + "direction" => self.direction.to_value(span), 170 + }, span) 171 + } 127 172 } 128 173 129 174 #[derive(Debug, Clone, Copy, Deserialize, Default, PartialEq, Eq)] ··· 134 179 Out, 135 180 } 136 181 182 + impl Direction { 183 + /// Represent the direction as a nushell [Value] 184 + pub fn to_value(&self, span: Span) -> Value { 185 + match self { 186 + Direction::In => Value::string("in", span), 187 + Direction::Out => Value::string("out", span), 188 + } 189 + } 190 + } 191 + 137 192 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 138 193 #[serde(rename_all = "kebab-case")] 139 194 pub struct Signal { ··· 144 199 pub annotations: Vec<Annotation>, 145 200 } 146 201 202 + impl Signal { 203 + /// Represent the signal as a nushell [Value] 204 + pub fn to_value(&self, span: Span) -> Value { 205 + Value::record(record!{ 206 + "name" => Value::string(&self.name, span), 207 + "args" => list_to_value!(self.args, span), 208 + "annotations" => list_to_value!(self.annotations, span), 209 + }, span) 210 + } 211 + } 212 + 147 213 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 148 214 #[serde(rename_all = "kebab-case")] 149 215 pub struct SignalArg { ··· 152 218 pub r#type: String, 153 219 } 154 220 221 + impl SignalArg { 222 + /// Represent the argument as a nushell [Value] 223 + pub fn to_value(&self, span: Span) -> Value { 224 + Value::record(record!{ 225 + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 226 + "type" => Value::string(&self.r#type, span), 227 + }, span) 228 + } 229 + } 230 + 155 231 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 156 232 #[serde(rename_all = "kebab-case")] 157 233 pub struct Property { ··· 162 238 pub annotations: Vec<Annotation>, 163 239 } 164 240 241 + impl Property { 242 + /// Represent the property as a nushell [Value] 243 + pub fn to_value(&self, span: Span) -> Value { 244 + Value::record(record!{ 245 + "name" => Value::string(&self.name, span), 246 + "type" => Value::string(&self.r#type, span), 247 + "args" => self.access.to_value(span), 248 + "annotations" => list_to_value!(self.annotations, span), 249 + }, span) 250 + } 251 + } 252 + 165 253 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 166 254 #[serde(rename_all = "lowercase")] 167 255 pub enum Access { ··· 170 258 ReadWrite, 171 259 } 172 260 261 + impl Access { 262 + /// Represent the access as a nushell [Value] 263 + pub fn to_value(&self, span: Span) -> Value { 264 + match self { 265 + Access::Read => Value::string("read", span), 266 + Access::Write => Value::string("write", span), 267 + Access::ReadWrite => Value::string("readwrite", span), 268 + } 269 + } 270 + } 271 + 173 272 #[derive(Debug, Clone, Deserialize, PartialEq, Eq)] 174 273 #[serde(rename_all = "kebab-case")] 175 274 pub struct Annotation { ··· 181 280 #[cfg(test)] 182 281 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Annotation { 183 282 Annotation { name: name.into(), value: value.into() } 283 + } 284 + 285 + /// Represent the annotation as a nushell [Value] 286 + pub fn to_value(&self, span: Span) -> Value { 287 + Value::record(record!{ 288 + "name" => Value::string(&self.name, span), 289 + "value" => Value::string(&self.value, span), 290 + }, span) 184 291 } 185 292 } 186 293
+45
src/main.rs
··· 26 26 PluginSignature::build("dbus") 27 27 .is_dbus_command() 28 28 .usage("Commands for interacting with D-Bus"), 29 + PluginSignature::build("dbus introspect") 30 + .is_dbus_command() 31 + .accepts_dbus_client_options() 32 + .usage("Introspect a D-Bus object") 33 + .extra_usage("Returns information about available nodes, interfaces, methods, \ 34 + signals, and properties on the given object path") 35 + .named("timeout", SyntaxShape::Duration, "How long to wait for a response", None) 36 + .required_named("dest", SyntaxShape::String, 37 + "The name of the connection that owns the object", 38 + None) 39 + .required("object", SyntaxShape::String, 40 + "The path to the object to introspect") 41 + .plugin_examples(vec![ 42 + PluginExample { 43 + example: "dbus introspect --dest=org.mpris.MediaPlayer2.spotify \ 44 + /org/mpris/MediaPlayer2 | explore".into(), 45 + description: "Look at the MPRIS2 interfaces exposed by Spotify".into(), 46 + result: None, 47 + }, 48 + PluginExample { 49 + example: "dbus introspect --dest=org.kde.plasmashell \ 50 + /org/kde/osdService | get interfaces | \ 51 + where name == org.kde.osdService | get 0.methods".into(), 52 + description: "Get methods exposed by KDE Plasma's on-screen display \ 53 + service".into(), 54 + result: None, 55 + }, 56 + PluginExample { 57 + example: "dbus introspect --dest=org.kde.KWin / | get children | \ 58 + select name".into(), 59 + description: "List objects exposed by KWin".into(), 60 + result: None, 61 + }, 62 + ]), 29 63 PluginSignature::build("dbus call") 30 64 .is_dbus_command() 31 65 .accepts_dbus_client_options() ··· 169 203 span: Some(call.head) 170 204 }), 171 205 206 + "dbus introspect" => self.introspect(call), 172 207 "dbus call" => self.call(call), 173 208 "dbus get" => self.get(call), 174 209 "dbus get-all" => self.get_all(call), ··· 208 243 } 209 244 210 245 impl NuPluginDbus { 246 + fn introspect(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 247 + let config = DbusClientConfig::try_from(call)?; 248 + let dbus = DbusClient::new(config)?; 249 + let node = dbus.introspect( 250 + &call.get_flag("dest")?.unwrap(), 251 + &call.req(0)?, 252 + )?; 253 + Ok(node.to_value(call.head)) 254 + } 255 + 211 256 fn call(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 212 257 let config = DbusClientConfig::try_from(call)?; 213 258 let dbus = DbusClient::new(config)?;