Skip to content

Commit bc3d152

Browse files
committed
Add dbus introspect command, for access to introspection data in a more comfortable format than XML
1 parent 3f6feab commit bc3d152

3 files changed

Lines changed: 187 additions & 0 deletions

File tree

README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,46 @@ Then add `register ~/.cargo/bin/nu_plugin_dbus` to your `~/.config/nushell/confi
1919
dbus call - Call a method and get its response
2020
dbus get - Get a D-Bus property
2121
dbus get-all - Get all D-Bus property for the given objects
22+
dbus introspect - Introspect a D-Bus object
2223
dbus set - Get all D-Bus property for the given objects
2324

2425
Flags:
2526
-h, --help - Display the help message for this command
2627

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
61+
2762
## `dbus call`
2863

2964
Call a method and get its response

src/introspection.rs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1+
use nu_protocol::{Value, record, Span};
12
use serde::Deserialize;
23

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+
}
9+
310
#[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)]
411
#[serde(rename_all = "kebab-case")]
512
pub struct Node {
@@ -41,6 +48,15 @@ impl Node {
4148
pub fn get_property_signature(&self, interface: &str, property: &str) -> Option<&str> {
4249
Some(&self.get_interface(interface)?.get_property(property)?.r#type)
4350
}
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+
}
4460
}
4561

4662
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
@@ -70,6 +86,17 @@ impl Interface {
7086
pub fn get_property(&self, name: &str) -> Option<&Property> {
7187
self.properties.iter().find(|p| p.name == name)
7288
}
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)
99+
}
73100
}
74101

75102
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
@@ -99,6 +126,15 @@ impl Method {
99126
.map(|arg| &arg.r#type[..])
100127
.collect()
101128
}
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+
}
102138
}
103139

104140
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
@@ -124,6 +160,15 @@ impl MethodArg {
124160
direction,
125161
}
126162
}
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+
}
127172
}
128173

129174
#[derive(Debug, Clone, Copy, Deserialize, Default, PartialEq, Eq)]
@@ -134,6 +179,16 @@ pub enum Direction {
134179
Out,
135180
}
136181

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+
137192
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
138193
#[serde(rename_all = "kebab-case")]
139194
pub struct Signal {
@@ -144,6 +199,17 @@ pub struct Signal {
144199
pub annotations: Vec<Annotation>,
145200
}
146201

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+
147213
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
148214
#[serde(rename_all = "kebab-case")]
149215
pub struct SignalArg {
@@ -152,6 +218,16 @@ pub struct SignalArg {
152218
pub r#type: String,
153219
}
154220

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+
155231
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
156232
#[serde(rename_all = "kebab-case")]
157233
pub struct Property {
@@ -162,6 +238,18 @@ pub struct Property {
162238
pub annotations: Vec<Annotation>,
163239
}
164240

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+
165253
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
166254
#[serde(rename_all = "lowercase")]
167255
pub enum Access {
@@ -170,6 +258,17 @@ pub enum Access {
170258
ReadWrite,
171259
}
172260

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+
173272
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
174273
#[serde(rename_all = "kebab-case")]
175274
pub struct Annotation {
@@ -182,6 +281,14 @@ impl Annotation {
182281
pub fn new(name: impl Into<String>, value: impl Into<String>) -> Annotation {
183282
Annotation { name: name.into(), value: value.into() }
184283
}
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)
291+
}
185292
}
186293

187294
#[cfg(test)]

src/main.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,40 @@ impl Plugin for NuPluginDbus {
2626
PluginSignature::build("dbus")
2727
.is_dbus_command()
2828
.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+
]),
2963
PluginSignature::build("dbus call")
3064
.is_dbus_command()
3165
.accepts_dbus_client_options()
@@ -169,6 +203,7 @@ impl Plugin for NuPluginDbus {
169203
span: Some(call.head)
170204
}),
171205

206+
"dbus introspect" => self.introspect(call),
172207
"dbus call" => self.call(call),
173208
"dbus get" => self.get(call),
174209
"dbus get-all" => self.get_all(call),
@@ -208,6 +243,16 @@ impl DbusSignatureUtilExt for PluginSignature {
208243
}
209244

210245
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+
211256
fn call(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> {
212257
let config = DbusClientConfig::try_from(call)?;
213258
let dbus = DbusClient::new(config)?;

0 commit comments

Comments
 (0)