Skip to content

Commit

Permalink
implement latest nushell api updates
Browse files Browse the repository at this point in the history
  • Loading branch information
devyn committed Apr 3, 2024
1 parent 8f042e5 commit d7385a2
Show file tree
Hide file tree
Showing 12 changed files with 698 additions and 425 deletions.
91 changes: 42 additions & 49 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ use dbus::{
channel::{BusType, Channel},
Message,
};
use nu_plugin::LabeledError;
use nu_protocol::{Spanned, Value};
use nu_protocol::{LabeledError, Spanned, Value};

use crate::{
config::{DbusBusChoice, DbusClientConfig},
Expand All @@ -23,11 +22,8 @@ pub struct DbusClient {
// Convenience macros for error handling
macro_rules! validate_with {
($type:ty, $spanned:expr) => {
<$type>::new(&$spanned.item).map_err(|msg| LabeledError {
label: msg,
msg: "this argument is incorrect".into(),
span: Some($spanned.span),
})
<$type>::new(&$spanned.item)
.map_err(|msg| LabeledError::new("Invalid argument").with_label(msg, $spanned.span))
};
}

Expand All @@ -44,10 +40,11 @@ impl DbusClient {
Ok(ch)
}),
}
.map_err(|err| LabeledError {
label: err.to_string(),
msg: "while connecting to D-Bus as specified here".into(),
span: Some(config.bus_choice.span),
.map_err(|err| {
LabeledError::new(err.to_string()).with_label(
"while connecting to D-Bus as specified here",
config.bus_choice.span,
)
})?;
Ok(DbusClient {
config,
Expand All @@ -56,11 +53,7 @@ impl DbusClient {
}

fn error(&self, err: impl std::fmt::Display, msg: impl std::fmt::Display) -> LabeledError {
LabeledError {
label: err.to_string(),
msg: msg.to_string(),
span: Some(self.config.span),
}
LabeledError::new(err.to_string()).with_label(msg.to_string(), self.config.span)
}

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

if let Some(sig) = node.get_method_args_signature(&interface.item, &method.item) {
DbusType::parse_all(&sig).map_err(|err| LabeledError {
label: format!(
DbusType::parse_all(&sig).map_err(|err| {
LabeledError::new(format!(
"while getting interface {:?} method {:?} signature: {}",
interface.item, method.item, err
),
msg: "try running with --no-introspect or --signature".into(),
span: Some(self.config.span),
))
.with_label(
"try running with --no-introspect or --signature",
self.config.span,
)
})
} else {
Err(LabeledError {
label: format!("Method {:?} not found on {:?}", method.item, interface.item),
msg: "check that this method/interface is correct".into(),
span: Some(method.span),
})
Err(LabeledError::new(format!(
"Method {:?} not found on {:?}",
method.item, interface.item
))
.with_label("check that this method/interface is correct", method.span))
}
}

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

if let Some(sig) = node.get_property_signature(&interface.item, &property.item) {
DbusType::parse_all(sig).map_err(|err| LabeledError {
label: format!(
DbusType::parse_all(sig).map_err(|err| {
LabeledError::new(format!(
"while getting interface {:?} property {:?} signature: {}",
interface.item, property.item, err
),
msg: "try running with --no-introspect or --signature".into(),
span: Some(self.config.span),
))
.with_label(
"try running with --no-introspect or --signature",
self.config.span,
)
})
} else {
Err(LabeledError {
label: format!(
"Property {:?} not found on {:?}",
property.item, interface.item
),
msg: "check that this property/interface is correct".into(),
span: Some(property.span),
})
Err(LabeledError::new(format!(
"Property {:?} not found on {:?}",
property.item, interface.item
))
.with_label(
"check that this property or interface is correct",
property.span,
))
}
}

Expand All @@ -176,10 +173,8 @@ impl DbusClient {
// Parse the signature
let mut valid_signature = signature
.map(|s| {
DbusType::parse_all(&s.item).map_err(|err| LabeledError {
label: err,
msg: "in signature specified here".into(),
span: Some(s.span),
DbusType::parse_all(&s.item).map_err(|err| {
LabeledError::new(err).with_label("in signature specified here", s.span)
})
})
.transpose()?;
Expand All @@ -195,7 +190,7 @@ impl DbusClient {
"Warning: D-Bus introspection failed on {:?}. \
Use `--no-introspect` or pass `--signature` to silence this warning. \
Cause: {}",
object.item, err.label
object.item, err
);
}
}
Expand Down Expand Up @@ -314,10 +309,8 @@ impl DbusClient {
// Parse the signature
let mut valid_signature = signature
.map(|s| {
DbusType::parse_all(&s.item).map_err(|err| LabeledError {
label: err,
msg: "in signature specified here".into(),
span: Some(s.span),
DbusType::parse_all(&s.item).map_err(|err| {
LabeledError::new(err).with_label("in signature specified here", s.span)
})
})
.transpose()?;
Expand All @@ -333,7 +326,7 @@ impl DbusClient {
"Warning: D-Bus introspection failed on {:?}. \
Use `--no-introspect` or pass `--signature` to silence this warning. \
Cause: {}",
object.item, err.label
object.item, err
);
}
}
Expand Down
127 changes: 127 additions & 0 deletions src/commands/call.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Example, LabeledError, Signature, SyntaxShape, Type, Value};

use crate::{client::DbusClient, config::DbusClientConfig, DbusSignatureUtilExt};

pub struct Call;

impl SimplePluginCommand for Call {
type Plugin = crate::NuPluginDbus;

fn name(&self) -> &str {
"dbus call"
}

fn signature(&self) -> Signature {
Signature::build(self.name())
.dbus_command()
.accepts_dbus_client_options()
.accepts_timeout()
.input_output_type(Type::Nothing, Type::Any)
.named(
"signature",
SyntaxShape::String,
"Signature of the arguments to send, in D-Bus format.\n \
If not provided, they will be determined from introspection.\n \
If --no-introspect is specified and this is not provided, they will \
be guessed (poorly)",
None,
)
.switch(
"no-flatten",
"Always return a list of all return values",
None,
)
.switch(
"no-introspect",
"Don't use introspection to determine the correct argument signature",
None,
)
.required_named(
"dest",
SyntaxShape::String,
"The name of the connection to send the method to",
None,
)
.required(
"object",
SyntaxShape::String,
"The path to the object to call the method on",
)
.required(
"interface",
SyntaxShape::String,
"The name of the interface the method belongs to",
)
.required(
"method",
SyntaxShape::String,
"The name of the method to send",
)
.rest(
"args",
SyntaxShape::Any,
"Arguments to send with the method call",
)
}

fn usage(&self) -> &str {
"Call a method and get its response"
}

fn extra_usage(&self) -> &str {
"Returns an array if the method call returns more than one value."
}

fn search_terms(&self) -> Vec<&str> {
vec!["dbus"]
}

fn examples(&self) -> Vec<Example> {
vec![
Example {
example: "dbus call --dest=org.freedesktop.DBus \
/org/freedesktop/DBus org.freedesktop.DBus.Peer Ping",
description: "Ping the D-Bus server itself",
result: None,
},
Example {
example: "dbus call --dest=org.freedesktop.Notifications \
/org/freedesktop/Notifications org.freedesktop.Notifications \
Notify \"Floppy disks\" 0 \"media-floppy\" \"Rarely seen\" \
\"But sometimes still used\" [] {} 5000",
description: "Show a notification on the desktop for 5 seconds",
result: None,
},
]
}

fn run(
&self,
_plugin: &Self::Plugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let config = DbusClientConfig::try_from(call)?;
let dbus = DbusClient::new(config)?;
let values = dbus.call(
&call.get_flag("dest")?.unwrap(),
&call.req(0)?,
&call.req(1)?,
&call.req(2)?,
call.get_flag("signature")?.as_ref(),
&call.positional[3..],
)?;

let flatten = !call.get_flag::<bool>("no-flatten")?.unwrap_or(false);

// Make the output easier to deal with by returning a list only if there are multiple return
// values (not so common)
match values.len() {
0 if flatten => Ok(Value::nothing(call.head)),
1 if flatten => Ok(values.into_iter().nth(0).unwrap()),
_ => Ok(Value::list(values, call.head)),
}
}
}
85 changes: 85 additions & 0 deletions src/commands/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use nu_plugin::{EngineInterface, EvaluatedCall, SimplePluginCommand};
use nu_protocol::{Example, LabeledError, Signature, SyntaxShape, Type, Value};

use crate::{client::DbusClient, config::DbusClientConfig, DbusSignatureUtilExt};

pub struct Get;

impl SimplePluginCommand for Get {
type Plugin = crate::NuPluginDbus;

fn name(&self) -> &str {
"dbus get"
}

fn signature(&self) -> Signature {
Signature::build(self.name())
.dbus_command()
.accepts_dbus_client_options()
.accepts_timeout()
.input_output_type(Type::Nothing, Type::Any)
.required_named(
"dest",
SyntaxShape::String,
"The name of the connection to read the property from",
None,
)
.required(
"object",
SyntaxShape::String,
"The path to the object to read the property from",
)
.required(
"interface",
SyntaxShape::String,
"The name of the interface the property belongs to",
)
.required(
"property",
SyntaxShape::String,
"The name of the property to read",
)
}

fn usage(&self) -> &str {
"Get a D-Bus property"
}

fn search_terms(&self) -> Vec<&str> {
vec!["dbus", "property", "read"]
}

fn examples(&self) -> Vec<Example> {
vec![Example {
example: "dbus get --dest=org.mpris.MediaPlayer2.spotify \
/org/mpris/MediaPlayer2 \
org.mpris.MediaPlayer2.Player Metadata",
description: "Get the currently playing song in Spotify",
result: Some(Value::test_record(nu_protocol::record!(
"xesam:title" => Value::test_string("Birdie"),
"xesam:artist" => Value::test_list(vec![
Value::test_string("LOVE PSYCHEDELICO")
]),
"xesam:album" => Value::test_string("Love Your Love"),
"xesam:url" => Value::test_string("https://open.spotify.com/track/51748BvzeeMs4PIdPuyZmv"),
))),
}]
}

fn run(
&self,
_plugin: &Self::Plugin,
_engine: &EngineInterface,
call: &EvaluatedCall,
_input: &Value,
) -> Result<Value, LabeledError> {
let config = DbusClientConfig::try_from(call)?;
let dbus = DbusClient::new(config)?;
dbus.get(
&call.get_flag("dest")?.unwrap(),
&call.req(0)?,
&call.req(1)?,
&call.req(2)?,
)
}
}
Loading

0 comments on commit d7385a2

Please sign in to comment.