Skip to content

Commit d7385a2

Browse files
committed
implement latest nushell api updates
1 parent 8f042e5 commit d7385a2

12 files changed

Lines changed: 698 additions & 425 deletions

File tree

src/client.rs

Lines changed: 42 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use dbus::{
33
channel::{BusType, Channel},
44
Message,
55
};
6-
use nu_plugin::LabeledError;
7-
use nu_protocol::{Spanned, Value};
6+
use nu_protocol::{LabeledError, Spanned, Value};
87

98
use crate::{
109
config::{DbusBusChoice, DbusClientConfig},
@@ -23,11 +22,8 @@ pub struct DbusClient {
2322
// Convenience macros for error handling
2423
macro_rules! validate_with {
2524
($type:ty, $spanned:expr) => {
26-
<$type>::new(&$spanned.item).map_err(|msg| LabeledError {
27-
label: msg,
28-
msg: "this argument is incorrect".into(),
29-
span: Some($spanned.span),
30-
})
25+
<$type>::new(&$spanned.item)
26+
.map_err(|msg| LabeledError::new("Invalid argument").with_label(msg, $spanned.span))
3127
};
3228
}
3329

@@ -44,10 +40,11 @@ impl DbusClient {
4440
Ok(ch)
4541
}),
4642
}
47-
.map_err(|err| LabeledError {
48-
label: err.to_string(),
49-
msg: "while connecting to D-Bus as specified here".into(),
50-
span: Some(config.bus_choice.span),
43+
.map_err(|err| {
44+
LabeledError::new(err.to_string()).with_label(
45+
"while connecting to D-Bus as specified here",
46+
config.bus_choice.span,
47+
)
5148
})?;
5249
Ok(DbusClient {
5350
config,
@@ -56,11 +53,7 @@ impl DbusClient {
5653
}
5754

5855
fn error(&self, err: impl std::fmt::Display, msg: impl std::fmt::Display) -> LabeledError {
59-
LabeledError {
60-
label: err.to_string(),
61-
msg: msg.to_string(),
62-
span: Some(self.config.span),
63-
}
56+
LabeledError::new(err.to_string()).with_label(msg.to_string(), self.config.span)
6457
}
6558

6659
/// Introspect a D-Bus object
@@ -107,20 +100,22 @@ impl DbusClient {
107100
let node = self.introspect(dest, object)?;
108101

109102
if let Some(sig) = node.get_method_args_signature(&interface.item, &method.item) {
110-
DbusType::parse_all(&sig).map_err(|err| LabeledError {
111-
label: format!(
103+
DbusType::parse_all(&sig).map_err(|err| {
104+
LabeledError::new(format!(
112105
"while getting interface {:?} method {:?} signature: {}",
113106
interface.item, method.item, err
114-
),
115-
msg: "try running with --no-introspect or --signature".into(),
116-
span: Some(self.config.span),
107+
))
108+
.with_label(
109+
"try running with --no-introspect or --signature",
110+
self.config.span,
111+
)
117112
})
118113
} else {
119-
Err(LabeledError {
120-
label: format!("Method {:?} not found on {:?}", method.item, interface.item),
121-
msg: "check that this method/interface is correct".into(),
122-
span: Some(method.span),
123-
})
114+
Err(LabeledError::new(format!(
115+
"Method {:?} not found on {:?}",
116+
method.item, interface.item
117+
))
118+
.with_label("check that this method/interface is correct", method.span))
124119
}
125120
}
126121

@@ -135,23 +130,25 @@ impl DbusClient {
135130
let node = self.introspect(dest, object)?;
136131

137132
if let Some(sig) = node.get_property_signature(&interface.item, &property.item) {
138-
DbusType::parse_all(sig).map_err(|err| LabeledError {
139-
label: format!(
133+
DbusType::parse_all(sig).map_err(|err| {
134+
LabeledError::new(format!(
140135
"while getting interface {:?} property {:?} signature: {}",
141136
interface.item, property.item, err
142-
),
143-
msg: "try running with --no-introspect or --signature".into(),
144-
span: Some(self.config.span),
137+
))
138+
.with_label(
139+
"try running with --no-introspect or --signature",
140+
self.config.span,
141+
)
145142
})
146143
} else {
147-
Err(LabeledError {
148-
label: format!(
149-
"Property {:?} not found on {:?}",
150-
property.item, interface.item
151-
),
152-
msg: "check that this property/interface is correct".into(),
153-
span: Some(property.span),
154-
})
144+
Err(LabeledError::new(format!(
145+
"Property {:?} not found on {:?}",
146+
property.item, interface.item
147+
))
148+
.with_label(
149+
"check that this property or interface is correct",
150+
property.span,
151+
))
155152
}
156153
}
157154

@@ -176,10 +173,8 @@ impl DbusClient {
176173
// Parse the signature
177174
let mut valid_signature = signature
178175
.map(|s| {
179-
DbusType::parse_all(&s.item).map_err(|err| LabeledError {
180-
label: err,
181-
msg: "in signature specified here".into(),
182-
span: Some(s.span),
176+
DbusType::parse_all(&s.item).map_err(|err| {
177+
LabeledError::new(err).with_label("in signature specified here", s.span)
183178
})
184179
})
185180
.transpose()?;
@@ -195,7 +190,7 @@ impl DbusClient {
195190
"Warning: D-Bus introspection failed on {:?}. \
196191
Use `--no-introspect` or pass `--signature` to silence this warning. \
197192
Cause: {}",
198-
object.item, err.label
193+
object.item, err
199194
);
200195
}
201196
}
@@ -314,10 +309,8 @@ impl DbusClient {
314309
// Parse the signature
315310
let mut valid_signature = signature
316311
.map(|s| {
317-
DbusType::parse_all(&s.item).map_err(|err| LabeledError {
318-
label: err,
319-
msg: "in signature specified here".into(),
320-
span: Some(s.span),
312+
DbusType::parse_all(&s.item).map_err(|err| {
313+
LabeledError::new(err).with_label("in signature specified here", s.span)
321314
})
322315
})
323316
.transpose()?;
@@ -333,7 +326,7 @@ impl DbusClient {
333326
"Warning: D-Bus introspection failed on {:?}. \
334327
Use `--no-introspect` or pass `--signature` to silence this warning. \
335328
Cause: {}",
336-
object.item, err.label
329+
object.item, err
337330
);
338331
}
339332
}

src/commands/call.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
2+
use nu_protocol::{Example, LabeledError, Signature, SyntaxShape, Type, Value};
3+
4+
use crate::{client::DbusClient, config::DbusClientConfig, DbusSignatureUtilExt};
5+
6+
pub struct Call;
7+
8+
impl 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 usage(&self) -> &str {
69+
"Call a method and get its response"
70+
}
71+
72+
fn extra_usage(&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+
}

src/commands/get.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
2+
use nu_protocol::{Example, LabeledError, Signature, SyntaxShape, Type, Value};
3+
4+
use crate::{client::DbusClient, config::DbusClientConfig, DbusSignatureUtilExt};
5+
6+
pub struct Get;
7+
8+
impl SimplePluginCommand for Get {
9+
type Plugin = crate::NuPluginDbus;
10+
11+
fn name(&self) -> &str {
12+
"dbus get"
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+
.required_named(
22+
"dest",
23+
SyntaxShape::String,
24+
"The name of the connection to read the property from",
25+
None,
26+
)
27+
.required(
28+
"object",
29+
SyntaxShape::String,
30+
"The path to the object to read the property from",
31+
)
32+
.required(
33+
"interface",
34+
SyntaxShape::String,
35+
"The name of the interface the property belongs to",
36+
)
37+
.required(
38+
"property",
39+
SyntaxShape::String,
40+
"The name of the property to read",
41+
)
42+
}
43+
44+
fn usage(&self) -> &str {
45+
"Get a D-Bus property"
46+
}
47+
48+
fn search_terms(&self) -> Vec<&str> {
49+
vec!["dbus", "property", "read"]
50+
}
51+
52+
fn examples(&self) -> Vec<Example> {
53+
vec![Example {
54+
example: "dbus get --dest=org.mpris.MediaPlayer2.spotify \
55+
/org/mpris/MediaPlayer2 \
56+
org.mpris.MediaPlayer2.Player Metadata",
57+
description: "Get the currently playing song in Spotify",
58+
result: Some(Value::test_record(nu_protocol::record!(
59+
"xesam:title" => Value::test_string("Birdie"),
60+
"xesam:artist" => Value::test_list(vec![
61+
Value::test_string("LOVE PSYCHEDELICO")
62+
]),
63+
"xesam:album" => Value::test_string("Love Your Love"),
64+
"xesam:url" => Value::test_string("https://open.spotify.com/track/51748BvzeeMs4PIdPuyZmv"),
65+
))),
66+
}]
67+
}
68+
69+
fn run(
70+
&self,
71+
_plugin: &Self::Plugin,
72+
_engine: &EngineInterface,
73+
call: &EvaluatedCall,
74+
_input: &Value,
75+
) -> Result<Value, LabeledError> {
76+
let config = DbusClientConfig::try_from(call)?;
77+
let dbus = DbusClient::new(config)?;
78+
dbus.get(
79+
&call.get_flag("dest")?.unwrap(),
80+
&call.req(0)?,
81+
&call.req(1)?,
82+
&call.req(2)?,
83+
)
84+
}
85+
}

0 commit comments

Comments
 (0)