diff --git a/src/client.rs b/src/client.rs index 4f9140f..210a677 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1,8 +1,18 @@ -use dbus::{channel::{Channel, BusType}, Message, arg::messageitem::MessageItem}; +use dbus::{ + arg::messageitem::MessageItem, + channel::{BusType, Channel}, + Message, +}; use nu_plugin::LabeledError; use nu_protocol::{Spanned, Value}; -use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item, introspection::Node, pattern::Pattern}; +use crate::{ + config::{DbusBusChoice, DbusClientConfig}, + convert::to_message_item, + dbus_type::DbusType, + introspection::Node, + pattern::Pattern, +}; /// Executes D-Bus actions on a connection, handling nushell types pub struct DbusClient { @@ -12,13 +22,13 @@ pub struct DbusClient { // Convenience macros for error handling macro_rules! validate_with { - ($type:ty, $spanned:expr) => (<$type>::new(&$spanned.item).map_err(|msg| { - LabeledError { + ($type:ty, $spanned:expr) => { + <$type>::new(&$spanned.item).map_err(|msg| LabeledError { label: msg, msg: "this argument is incorrect".into(), span: Some($spanned.span), - } - })) + }) + }; } impl DbusClient { @@ -33,16 +43,15 @@ impl DbusClient { ch.register()?; 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 { + label: err.to_string(), + msg: "while connecting to D-Bus as specified here".into(), + span: Some(config.bus_choice.span), })?; Ok(DbusClient { config, - conn: channel + conn: channel, }) } @@ -50,7 +59,7 @@ impl DbusClient { LabeledError { label: err.to_string(), msg: msg.to_string(), - span: Some(self.config.span) + span: Some(self.config.span), } } @@ -69,15 +78,19 @@ impl DbusClient { valid_dest, valid_object, "org.freedesktop.DBus.Introspectable", - "Introspect" - ).map_err(|err| self.error(err, context))?; + "Introspect", + ) + .map_err(|err| self.error(err, context))?; // Send and get the response - let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item) + let resp = self + .conn + .send_with_reply_and_block(message, self.config.timeout.item) .map_err(|err| self.error(err, context))?; // Parse it to a Node - let xml: &str = resp.get1() + let xml: &str = resp + .get1() .ok_or_else(|| self.error("Introspect method returned the wrong type", context))?; Node::from_xml(xml).map_err(|err| self.error(err, context)) @@ -95,10 +108,10 @@ impl DbusClient { if let Some(sig) = node.get_method_args_signature(&interface.item, &method.item) { DbusType::parse_all(&sig).map_err(|err| LabeledError { - label: format!("while getting interface {:?} method {:?} signature: {}", - interface.item, - method.item, - err), + label: 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), }) @@ -122,17 +135,20 @@ 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!("while getting interface {:?} property {:?} signature: {}", - interface.item, - property.item, - err), + DbusType::parse_all(sig).map_err(|err| LabeledError { + label: 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), }) } else { Err(LabeledError { - label: format!("Property {:?} not found on {:?}", property.item, interface.item), + label: format!( + "Property {:?} not found on {:?}", + property.item, interface.item + ), msg: "check that this property/interface is correct".into(), span: Some(property.span), }) @@ -158,52 +174,61 @@ impl DbusClient { let valid_method = validate_with!(dbus::strings::Member, method)?; // 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), - } - })).transpose()?; + 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), + }) + }) + .transpose()?; // If not provided, try introspection (unless disabled) if valid_signature.is_none() && self.config.introspect { match self.get_method_signature_by_introspection(dest, object, interface, method) { Ok(sig) => { valid_signature = Some(sig); - }, + } Err(err) => { - eprintln!("Warning: D-Bus introspection failed on {:?}. \ + eprintln!( + "Warning: D-Bus introspection failed on {:?}. \ Use `--no-introspect` or pass `--signature` to silence this warning. \ Cause: {}", - object.item, - err.label); + object.item, err.label + ); } } } if let Some(sig) = &valid_signature { if sig.len() != args.len() { - self.error(format!("expected {} arguments, got {}", sig.len(), args.len()), context); + self.error( + format!("expected {} arguments, got {}", sig.len(), args.len()), + context, + ); } } // Construct the method call message - let mut message = Message::new_method_call( - valid_dest, - valid_object, - valid_interface, - valid_method, - ).map_err(|err| self.error(err, context))?; + let mut message = + Message::new_method_call(valid_dest, valid_object, valid_interface, valid_method) + .map_err(|err| self.error(err, context))?; // Convert the args to message items - let sigs_iter = valid_signature.iter().flatten().map(Some).chain(std::iter::repeat(None)); + let sigs_iter = valid_signature + .iter() + .flatten() + .map(Some) + .chain(std::iter::repeat(None)); for (val, sig) in args.iter().zip(sigs_iter) { message = message.append1(to_message_item(val, sig)?); } // Send it on the channel and get the response - let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item) + let resp = self + .conn + .send_with_reply_and_block(message, self.config.timeout.item) .map_err(|err| self.error(err, context))?; crate::convert::from_message(&resp, self.config.span) @@ -224,11 +249,21 @@ impl DbusClient { self.call( dest, object, - &Spanned { item: "org.freedesktop.DBus.Properties".into(), span: self.config.span }, - &Spanned { item: "Get".into(), span: self.config.span }, - Some(&Spanned { item: "ss".into(), span: self.config.span }), - &[interface_val, property_val] - ).map(|val| val.into_iter().nth(0).unwrap_or_default()) + &Spanned { + item: "org.freedesktop.DBus.Properties".into(), + span: self.config.span, + }, + &Spanned { + item: "Get".into(), + span: self.config.span, + }, + Some(&Spanned { + item: "ss".into(), + span: self.config.span, + }), + &[interface_val, property_val], + ) + .map(|val| val.into_iter().nth(0).unwrap_or_default()) } /// Get all D-Bus properties from the given object @@ -243,11 +278,21 @@ impl DbusClient { self.call( dest, object, - &Spanned { item: "org.freedesktop.DBus.Properties".into(), span: self.config.span }, - &Spanned { item: "GetAll".into(), span: self.config.span }, - Some(&Spanned { item: "s".into(), span: self.config.span }), - &[interface_val] - ).map(|val| val.into_iter().nth(0).unwrap_or_default()) + &Spanned { + item: "org.freedesktop.DBus.Properties".into(), + span: self.config.span, + }, + &Spanned { + item: "GetAll".into(), + span: self.config.span, + }, + Some(&Spanned { + item: "s".into(), + span: self.config.span, + }), + &[interface_val], + ) + .map(|val| val.into_iter().nth(0).unwrap_or_default()) } /// Set a D-Bus property on the given object @@ -267,34 +312,42 @@ impl DbusClient { let valid_object = validate_with!(dbus::strings::Path, object)?; // 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), - } - })).transpose()?; + 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), + }) + }) + .transpose()?; // If not provided, try introspection (unless disabled) if valid_signature.is_none() && self.config.introspect { match self.get_property_signature_by_introspection(dest, object, interface, property) { Ok(sig) => { valid_signature = Some(sig); - }, + } Err(err) => { - eprintln!("Warning: D-Bus introspection failed on {:?}. \ + eprintln!( + "Warning: D-Bus introspection failed on {:?}. \ Use `--no-introspect` or pass `--signature` to silence this warning. \ Cause: {}", - object.item, - err.label); + object.item, err.label + ); } } } if let Some(sig) = &valid_signature { if sig.len() != 1 { - self.error(format!( - "expected single object signature, but there are {}", sig.len()), context); + self.error( + format!( + "expected single object signature, but there are {}", + sig.len() + ), + context, + ); } } @@ -304,40 +357,47 @@ impl DbusClient { valid_object, "org.freedesktop.DBus.Properties", "Set", - ).map_err(|err| self.error(err, context))? - .append2(&interface.item, &property.item) - .append1( - // Box it in a variant as required for property setting - MessageItem::Variant(Box::new( - to_message_item(value, valid_signature.as_ref().map(|s| &s[0]))?)) - ); + ) + .map_err(|err| self.error(err, context))? + .append2(&interface.item, &property.item) + .append1( + // Box it in a variant as required for property setting + MessageItem::Variant(Box::new(to_message_item( + value, + valid_signature.as_ref().map(|s| &s[0]), + )?)), + ); // Send it on the channel and get the response - self.conn.send_with_reply_and_block(message, self.config.timeout.item) + self.conn + .send_with_reply_and_block(message, self.config.timeout.item) .map_err(|err| self.error(err, context))?; Ok(()) } - pub fn list(&self, pattern: Option<&Pattern>) - -> Result, LabeledError> - { + pub fn list(&self, pattern: Option<&Pattern>) -> Result, LabeledError> { let context = "while listing D-Bus connection names"; let message = Message::new_method_call( "org.freedesktop.DBus", "/org/freedesktop/DBus", "org.freedesktop.DBus", - "ListNames" - ).map_err(|err| self.error(err, context))?; + "ListNames", + ) + .map_err(|err| self.error(err, context))?; - self.conn.send_with_reply_and_block(message, self.config.timeout.item) + self.conn + .send_with_reply_and_block(message, self.config.timeout.item) .map_err(|err| self.error(err, context)) .and_then(|reply| reply.read1().map_err(|err| self.error(err, context))) .map(|names: Vec| { // Filter the names by the pattern if let Some(pattern) = pattern { - names.into_iter().filter(|name| pattern.is_match(name)).collect() + names + .into_iter() + .filter(|name| pattern.is_match(name)) + .collect() } else { names } diff --git a/src/config.rs b/src/config.rs index f48dbb1..2471ada 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,7 +1,7 @@ use std::time::Duration; use nu_plugin::{EvaluatedCall, LabeledError}; -use nu_protocol::{Spanned, Span}; +use nu_protocol::{Span, Spanned}; /// General configuration related to the D-Bus client connection #[derive(Debug, Clone)] @@ -37,8 +37,14 @@ impl TryFrom<&EvaluatedCall> for DbusClientConfig { fn try_from(call: &EvaluatedCall) -> Result { let mut config = DbusClientConfig { span: call.head, - bus_choice: Spanned { item: DbusBusChoice::default(), span: call.head }, - timeout: Spanned { item: Duration::from_secs(2), span: call.head }, + bus_choice: Spanned { + item: DbusBusChoice::default(), + span: call.head, + }, + timeout: Spanned { + item: Duration::from_secs(2), + span: call.head, + }, introspect: true, }; @@ -51,41 +57,50 @@ impl TryFrom<&EvaluatedCall> for DbusClientConfig { "session" => DbusBusChoice::Session, "system" => DbusBusChoice::System, "started" => DbusBusChoice::Started, - _ => unreachable!() + _ => unreachable!(), + }; + config.bus_choice = Spanned { + item: dest, + span: name.span, }; - config.bus_choice = Spanned { item: dest, span: name.span }; } - }, + } r#type @ ("bus" | "peer") => { if let Some(value) = value { let address = value.as_str()?; let dest = match r#type { "bus" => DbusBusChoice::Bus(address.to_owned()), "peer" => DbusBusChoice::Peer(address.to_owned()), - _ => unreachable!() + _ => unreachable!(), + }; + config.bus_choice = Spanned { + item: dest, + span: value.span(), }; - config.bus_choice = Spanned { item: dest, span: value.span() }; } - }, + } "timeout" => { if let Some(value) = value { - let nanos: u64 = value.as_duration()?.try_into().map_err(|_| { - LabeledError { + let nanos: u64 = + value.as_duration()?.try_into().map_err(|_| LabeledError { label: "Timeout must be a positive duration".into(), msg: "invalid timeout specified here".into(), span: Some(value.span()), - } - })?; + })?; let item = Duration::from_nanos(nanos); - config.timeout = Spanned { item, span: value.span() }; + config.timeout = Spanned { + item, + span: value.span(), + }; } - }, + } "no-introspect" => { - config.introspect = !value.as_ref() + config.introspect = !value + .as_ref() .and_then(|v| v.as_bool().ok()) .unwrap_or(false); - }, - _ => () + } + _ => (), } } diff --git a/src/convert.rs b/src/convert.rs index fb078f2..6002171 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -1,6 +1,12 @@ -use dbus::{Message, arg::{ArgType, RefArg, messageitem::{MessageItemArray, MessageItem, MessageItemDict}}, Signature}; +use dbus::{ + arg::{ + messageitem::{MessageItem, MessageItemArray, MessageItemDict}, + ArgType, RefArg, + }, + Message, Signature, +}; use nu_plugin::LabeledError; -use nu_protocol::{Value, Span, Record}; +use nu_protocol::{Record, Span, Value}; use std::str::FromStr; use crate::dbus_type::DbusType; @@ -31,115 +37,146 @@ pub fn from_refarg(refarg: &dyn RefArg, span: Span) -> Result { Value::record(record, span) } else if &*refarg.signature() == "ay" { // Byte array - better to return as binary - let bytes = dbus::arg::cast::>(&refarg.box_clone()).unwrap().to_owned(); + let bytes = dbus::arg::cast::>(&refarg.box_clone()) + .unwrap() + .to_owned(); Value::binary(bytes, span) } else { // It's an array Value::list( - refarg.as_iter().unwrap().map(|v| from_refarg(v, span)).flatten().collect(), - span) + refarg + .as_iter() + .unwrap() + .flat_map(|v| from_refarg(v, span)) + .collect(), + span, + ) } - }, + } ArgType::Variant => { - let inner = refarg.as_iter().unwrap().nth(0).unwrap(); + let inner = refarg.as_iter().unwrap().next().unwrap(); return from_refarg(inner, span); - }, - ArgType::Boolean => - Value::bool(refarg.as_i64().unwrap() != 0, span), + } + ArgType::Boolean => Value::bool(refarg.as_i64().unwrap() != 0, span), // Strings - ArgType::String | ArgType::ObjectPath | ArgType::Signature => - Value::string(refarg.as_str().unwrap(), span), + ArgType::String | ArgType::ObjectPath | ArgType::Signature => { + Value::string(refarg.as_str().unwrap(), span) + } // Ints - ArgType::Byte | ArgType::Int16 | ArgType::UInt16 | ArgType::Int32 | - ArgType::UInt32 | ArgType::Int64 | ArgType::UnixFd => - Value::int(refarg.as_i64().unwrap(), span), + ArgType::Byte + | ArgType::Int16 + | ArgType::UInt16 + | ArgType::Int32 + | ArgType::UInt32 + | ArgType::Int64 + | ArgType::UnixFd => Value::int(refarg.as_i64().unwrap(), span), // Nushell doesn't support u64, so present it as a string ArgType::UInt64 => Value::string(refarg.as_u64().unwrap().to_string(), span), // Floats - ArgType::Double => - Value::float(refarg.as_f64().unwrap(), span), - - ArgType::Struct => - Value::list( - refarg.as_iter().unwrap().map(|v| from_refarg(v, span)).flatten().collect(), - span), - - ArgType::DictEntry => - return Err("Encountered dictionary entry outside of dictionary".into()), - ArgType::Invalid => - return Err("Encountered invalid D-Bus value".into()), + ArgType::Double => Value::float(refarg.as_f64().unwrap(), span), + + ArgType::Struct => Value::list( + refarg + .as_iter() + .unwrap() + .flat_map(|v| from_refarg(v, span)) + .collect(), + span, + ), + + ArgType::DictEntry => { + return Err("Encountered dictionary entry outside of dictionary".into()) + } + ArgType::Invalid => return Err("Encountered invalid D-Bus value".into()), }) } -pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>) - -> Result -{ +pub fn to_message_item( + value: &Value, + expected_type: Option<&DbusType>, +) -> Result { // Report errors from conversion. Error must support Display macro_rules! try_convert { - ($result_expr:expr) => ($result_expr.map_err(|err| LabeledError { - label: format!("Failed to convert value to the D-Bus `{:?}` type", - expected_type.unwrap()), - msg: err.to_string(), - span: Some(value.span()), - })?) + ($result_expr:expr) => { + $result_expr.map_err(|err| LabeledError { + label: format!( + "Failed to convert value to the D-Bus `{:?}` type", + expected_type.unwrap() + ), + msg: err.to_string(), + span: Some(value.span()), + })? + }; } // Try to match values to expected types match (value, expected_type) { // Boolean - (Value::Bool { val, .. }, Some(DbusType::Boolean)) => - Ok(MessageItem::Bool(*val)), + (Value::Bool { val, .. }, Some(DbusType::Boolean)) => Ok(MessageItem::Bool(*val)), // Strings and specialized strings - (Value::String { val, .. }, Some(DbusType::String)) => - Ok(MessageItem::Str(val.to_owned())), - (Value::String { val, .. }, Some(DbusType::ObjectPath)) => - Ok(MessageItem::ObjectPath(try_convert!(dbus::strings::Path::new(val)))), - (Value::String { val, .. }, Some(DbusType::Signature)) => - Ok(MessageItem::Signature(try_convert!(dbus::strings::Signature::new(val)))), + (Value::String { val, .. }, Some(DbusType::String)) => Ok(MessageItem::Str(val.to_owned())), + (Value::String { val, .. }, Some(DbusType::ObjectPath)) => Ok(MessageItem::ObjectPath( + try_convert!(dbus::strings::Path::new(val)), + )), + (Value::String { val, .. }, Some(DbusType::Signature)) => Ok(MessageItem::Signature( + try_convert!(dbus::strings::Signature::new(val)), + )), // Signed ints - (Value::Int { val, .. }, Some(DbusType::Int64)) => - Ok(MessageItem::Int64(*val)), - (Value::Int { val, .. }, Some(DbusType::Int32)) => - Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))), - (Value::Int { val, .. }, Some(DbusType::Int16)) => - Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))), + (Value::Int { val, .. }, Some(DbusType::Int64)) => Ok(MessageItem::Int64(*val)), + (Value::Int { val, .. }, Some(DbusType::Int32)) => { + Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))) + } + (Value::Int { val, .. }, Some(DbusType::Int16)) => { + Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))) + } // Unsigned ints - (Value::Int { val, .. }, Some(DbusType::UInt64)) => - Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))), - (Value::Int { val, .. }, Some(DbusType::UInt32)) => - Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))), - (Value::Int { val, .. }, Some(DbusType::UInt16)) => - Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))), - (Value::Int { val, .. }, Some(DbusType::Byte)) => - Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))), + (Value::Int { val, .. }, Some(DbusType::UInt64)) => { + Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))) + } + (Value::Int { val, .. }, Some(DbusType::UInt32)) => { + Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))) + } + (Value::Int { val, .. }, Some(DbusType::UInt16)) => { + Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))) + } + (Value::Int { val, .. }, Some(DbusType::Byte)) => { + Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))) + } // Ints from string - (Value::String { val, .. }, Some(DbusType::Int64)) => - Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::Int32)) => - Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::Int16)) => - Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::UInt64)) => - Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::UInt32)) => - Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::UInt16)) => - Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))), - (Value::String { val, .. }, Some(DbusType::Byte)) => - Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))), + (Value::String { val, .. }, Some(DbusType::Int64)) => { + Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::Int32)) => { + Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::Int16)) => { + Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::UInt64)) => { + Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::UInt32)) => { + Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::UInt16)) => { + Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))) + } + (Value::String { val, .. }, Some(DbusType::Byte)) => { + Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))) + } // Float - (Value::Float { val, .. }, Some(DbusType::Double)) => - Ok(MessageItem::Double(*val)), - (Value::String { val, .. }, Some(DbusType::Double)) => - Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))), + (Value::Float { val, .. }, Some(DbusType::Double)) => Ok(MessageItem::Double(*val)), + (Value::String { val, .. }, Some(DbusType::Double)) => { + Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))) + } // Binary (Value::Binary { val, .. }, Some(r#type @ DbusType::Array(content_type))) @@ -147,33 +184,48 @@ pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>) { // FIXME: this is likely pretty inefficient for a bunch of bytes let sig = Signature::from(r#type.stringify()); - let items = val.iter().cloned().map(MessageItem::Byte).collect::>(); - Ok(MessageItem::Array(MessageItemArray::new(items, sig).unwrap())) - }, + let items = val + .iter() + .cloned() + .map(MessageItem::Byte) + .collect::>(); + Ok(MessageItem::Array( + MessageItemArray::new(items, sig).unwrap(), + )) + } // List/array (Value::List { vals, .. }, Some(r#type @ DbusType::Array(content_type))) => { let sig = Signature::from(r#type.stringify()); - let items = vals.iter() + let items = vals + .iter() .map(|content| to_message_item(content, Some(content_type))) .collect::, _>>()?; - Ok(MessageItem::Array(MessageItemArray::new(items, sig).unwrap())) - }, + Ok(MessageItem::Array( + MessageItemArray::new(items, sig).unwrap(), + )) + } // Struct (Value::List { vals, .. }, Some(DbusType::Struct(types))) => { if vals.len() != types.len() { return Err(LabeledError { - label: format!("expected struct with {} element(s) ({:?})", types.len(), types), + label: format!( + "expected struct with {} element(s) ({:?})", + types.len(), + types + ), msg: format!("this list has {} element(s) instead", vals.len()), - span: Some(value.span()) + span: Some(value.span()), }); } - let items = vals.iter().zip(types) + let items = vals + .iter() + .zip(types) .map(|(content, r#type)| to_message_item(content, Some(r#type))) .collect::, _>>()?; Ok(MessageItem::Struct(items)) - }, + } // Record/dict (Value::Record { val, .. }, Some(DbusType::Array(content_type))) @@ -182,7 +234,8 @@ pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>) if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type { let key_sig = Signature::from(key_type.stringify()); let val_sig = Signature::from(val_type.stringify()); - let pairs = val.iter() + let pairs = val + .iter() .map(|(key, val)| { let key_as_value = Value::string(key, value.span()); let key_message_item = to_message_item(&key_as_value, Some(key_type))?; @@ -190,49 +243,53 @@ pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>) Ok((key_message_item, val_message_item)) }) .collect::, LabeledError>>()?; - Ok(MessageItem::Dict(MessageItemDict::new(pairs, key_sig, val_sig).unwrap())) + Ok(MessageItem::Dict( + MessageItemDict::new(pairs, key_sig, val_sig).unwrap(), + )) } else { unreachable!() } - }, + } // Variant - use automatic type - (other_value, Some(DbusType::Variant)) => - Ok(MessageItem::Variant(Box::new(to_message_item(other_value, None)?))), + (other_value, Some(DbusType::Variant)) => Ok(MessageItem::Variant(Box::new( + to_message_item(other_value, None)?, + ))), // Value not compatible with expected type - (other_value, Some(expectation)) => - Err(LabeledError { - label: format!("`{}` can not be converted to the D-Bus `{:?}` type", - other_value.get_type(), expectation), - msg: format!("expected a `{:?}` here", expectation), - span: Some(other_value.span()), - }), + (other_value, Some(expectation)) => Err(LabeledError { + label: format!( + "`{}` can not be converted to the D-Bus `{:?}` type", + other_value.get_type(), + expectation + ), + msg: format!("expected a `{:?}` here", expectation), + span: Some(other_value.span()), + }), // Automatic types (with no type expectation) - (Value::String { .. }, None) => - to_message_item(value, Some(&DbusType::String)), - (Value::Int { .. }, None) => - to_message_item(value, Some(&DbusType::Int64)), - (Value::Float { .. }, None) => - to_message_item(value, Some(&DbusType::Double)), - (Value::Bool { .. }, None) => - to_message_item(value, Some(&DbusType::Boolean)), - (Value::List { .. }, None) => - to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))), - (Value::Record { .. }, None) => - to_message_item(value, Some(&DbusType::Array( - DbusType::DictEntry( - DbusType::String.into(), - DbusType::Variant.into() - ).into()))), + (Value::String { .. }, None) => to_message_item(value, Some(&DbusType::String)), + (Value::Int { .. }, None) => to_message_item(value, Some(&DbusType::Int64)), + (Value::Float { .. }, None) => to_message_item(value, Some(&DbusType::Double)), + (Value::Bool { .. }, None) => to_message_item(value, Some(&DbusType::Boolean)), + (Value::List { .. }, None) => { + to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))) + } + (Value::Record { .. }, None) => to_message_item( + value, + Some(&DbusType::Array( + DbusType::DictEntry(DbusType::String.into(), DbusType::Variant.into()).into(), + )), + ), // No expected type, but can't handle this type - _ => - Err(LabeledError { - label: format!("can not use values of type `{}` in D-Bus calls", value.get_type()), - msg: "use a supported type here instead".into(), - span: Some(value.span()), - }) + _ => Err(LabeledError { + label: format!( + "can not use values of type `{}` in D-Bus calls", + value.get_type() + ), + msg: "use a supported type here instead".into(), + span: Some(value.span()), + }), } } diff --git a/src/dbus_type.rs b/src/dbus_type.rs index 5f087c6..6779eb1 100644 --- a/src/dbus_type.rs +++ b/src/dbus_type.rs @@ -48,7 +48,7 @@ impl DbusType { // The next type is the content type of the array let (content_type, remainder) = Self::parse(&input[1..])?; Ok((Array(content_type.into()), remainder)) - }, + } '(' => { // Parse the struct content until we get to the end ) char let mut remainder = &input[1..]; @@ -56,30 +56,39 @@ impl DbusType { loop { if remainder.is_empty() { break Err("unexpected end of D-Bus type string \ - before end of array".into()); - } else if remainder.starts_with(')') { - break Ok((DbusType::Struct(types), &remainder[1..])); + before end of array" + .into()); + } else if let Some(new_remainder) = remainder.strip_prefix(')') { + break Ok((DbusType::Struct(types), new_remainder)); } else { let (r#type, new_remainder) = Self::parse(remainder)?; types.push(r#type); remainder = new_remainder; } } - }, + } 'v' => Ok((Variant, &input[1..])), '{' => { // Expect two types let (key_type, key_remainder) = Self::parse(&input[1..])?; let (val_type, val_remainder) = Self::parse(key_remainder)?; // Must end with } - if val_remainder.starts_with('}') { - Ok((DbusType::DictEntry(key_type.into(), val_type.into()), &val_remainder[1..])) + if let Some(new_remainder) = val_remainder.strip_prefix('}') { + Ok(( + DbusType::DictEntry(key_type.into(), val_type.into()), + new_remainder, + )) } else { - Err(format!("expected `}}` char to end dictionary in D-Bus type \ - but remainder is {:?}", val_remainder)) + Err(format!( + "expected `}}` char to end dictionary in D-Bus type \ + but remainder is {:?}", + val_remainder + )) } - }, - other => Err(format!("unexpected char {other:?} in D-Bus type representation")) + } + other => Err(format!( + "unexpected char {other:?} in D-Bus type representation" + )), } } @@ -99,18 +108,18 @@ impl DbusType { use self::DbusType::*; match self { - Byte => 'y'.into(), - Boolean => 'b'.into(), - Int16 => 'n'.into(), - UInt16 => 'q'.into(), - Int32 => 'i'.into(), - UInt32 => 'u'.into(), - Int64 => 'x'.into(), - UInt64 => 't'.into(), - Double => 'd'.into(), - String => 's'.into(), + Byte => 'y'.into(), + Boolean => 'b'.into(), + Int16 => 'n'.into(), + UInt16 => 'q'.into(), + Int32 => 'i'.into(), + UInt32 => 'u'.into(), + Int64 => 'x'.into(), + UInt64 => 't'.into(), + Double => 'd'.into(), + String => 's'.into(), ObjectPath => 'o'.into(), - Signature => 'g'.into(), + Signature => 'g'.into(), // a Array(content) => format!("a{}", content.stringify()), @@ -131,9 +140,9 @@ impl DbusType { #[cfg(test)] macro_rules! should_parse_to { - ($str:expr, $result:expr) => ( + ($str:expr, $result:expr) => { assert_eq!(DbusType::parse($str), Ok(($result, ""))) - ) + }; } #[test] @@ -204,7 +213,10 @@ fn test_parse_nested_struct() { use self::DbusType::*; should_parse_to!("((xx))", Struct(vec![Struct(vec![Int64, Int64])])); should_parse_to!("(y(xx))", Struct(vec![Byte, Struct(vec![Int64, Int64])])); - should_parse_to!("(y(ss)o)", Struct(vec![Byte, Struct(vec![String, String]), ObjectPath])); + should_parse_to!( + "(y(ss)o)", + Struct(vec![Byte, Struct(vec![String, String]), ObjectPath]) + ); should_parse_to!("((yy)s)", Struct(vec![Struct(vec![Byte, Byte]), String])); } @@ -224,13 +236,19 @@ fn test_parse_struct_unclosed() { fn test_parse_dict_entry() { use self::DbusType::*; should_parse_to!("{ss}", DictEntry(String.into(), String.into())); - should_parse_to!("{s(bd)}", DictEntry(String.into(), Struct(vec![Boolean, Double]).into())); + should_parse_to!( + "{s(bd)}", + DictEntry(String.into(), Struct(vec![Boolean, Double]).into()) + ); } #[test] fn test_parse_array_dict() { use self::DbusType::*; - should_parse_to!("a{sd}", Array(DictEntry(String.into(), Double.into()).into())); + should_parse_to!( + "a{sd}", + Array(DictEntry(String.into(), Double.into()).into()) + ); } #[test] @@ -249,20 +267,10 @@ fn test_parse_dict_entry_unclosed() { fn test_parse_all() { use self::DbusType::*; assert_eq!(DbusType::parse_all(""), Ok(vec![])); - assert_eq!( - DbusType::parse_all("s"), - Ok(vec![ - String, - ]) - ); + assert_eq!(DbusType::parse_all("s"), Ok(vec![String,])); assert_eq!( DbusType::parse_all("isbb"), - Ok(vec![ - Int32, - String, - Boolean, - Boolean, - ]) + Ok(vec![Int32, String, Boolean, Boolean,]) ); assert_eq!( DbusType::parse_all("ia{s(bi)}s"), @@ -276,27 +284,27 @@ fn test_parse_all() { #[cfg(test)] macro_rules! should_stringify_to { - ($type:expr, $result:expr) => ( + ($type:expr, $result:expr) => { assert_eq!(DbusType::stringify(&$type), $result) - ) + }; } #[test] fn test_stringify_simple_types() { use self::DbusType::*; - should_stringify_to!(Byte, "y"); - should_stringify_to!(Boolean, "b"); - should_stringify_to!(Int16, "n"); - should_stringify_to!(UInt16, "q"); - should_stringify_to!(Int32, "i"); - should_stringify_to!(UInt32, "u"); - should_stringify_to!(Int64, "x"); - should_stringify_to!(UInt64, "t"); - should_stringify_to!(Double, "d"); - should_stringify_to!(String, "s"); + should_stringify_to!(Byte, "y"); + should_stringify_to!(Boolean, "b"); + should_stringify_to!(Int16, "n"); + should_stringify_to!(UInt16, "q"); + should_stringify_to!(Int32, "i"); + should_stringify_to!(UInt32, "u"); + should_stringify_to!(Int64, "x"); + should_stringify_to!(UInt64, "t"); + should_stringify_to!(Double, "d"); + should_stringify_to!(String, "s"); should_stringify_to!(ObjectPath, "o"); - should_stringify_to!(Signature, "g"); - should_stringify_to!(Variant, "v"); + should_stringify_to!(Signature, "g"); + should_stringify_to!(Variant, "v"); } #[test] @@ -313,7 +321,10 @@ fn test_stringify_struct() { should_stringify_to!(Struct(vec![Int32]), "(i)"); should_stringify_to!(Struct(vec![Int32, String]), "(is)"); should_stringify_to!(Struct(vec![Byte, Int32, String]), "(yis)"); - should_stringify_to!(Struct(vec![Byte, Struct(vec![String, Boolean]), String]), "(y(sb)s)"); + should_stringify_to!( + Struct(vec![Byte, Struct(vec![String, Boolean]), String]), + "(y(sb)s)" + ); } #[test] @@ -326,16 +337,17 @@ fn test_stringify_dict_entry() { #[test] fn test_stringify_nested() { use self::DbusType::*; - should_stringify_to!(Array(DictEntry(String.into(), Int32.into()).into()), "a{si}"); + should_stringify_to!( + Array(DictEntry(String.into(), Int32.into()).into()), + "a{si}" + ); should_stringify_to!( Array( DictEntry( String.into(), - Struct(vec![ - Byte, - Array(Int32.into()) - ]).into() - ).into() + Struct(vec![Byte, Array(Int32.into())]).into() + ) + .into() ), "a{s(yai)}" ); diff --git a/src/introspection.rs b/src/introspection.rs index e2fa549..10d8a51 100644 --- a/src/introspection.rs +++ b/src/introspection.rs @@ -1,10 +1,10 @@ -use nu_protocol::{Value, record, Span}; +use nu_protocol::{record, Span, Value}; use serde::Deserialize; macro_rules! list_to_value { - ($list:expr, $span:expr) => ( + ($list:expr, $span:expr) => { Value::list($list.iter().map(|i| i.to_value($span)).collect(), $span) - ) + }; } #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] @@ -41,21 +41,33 @@ impl Node { /// Find a method on an interface on this node, and then generate the signature of the method /// args pub fn get_method_args_signature(&self, interface: &str, method: &str) -> Option { - Some(self.get_interface(interface)?.get_method(method)?.in_signature()) + Some( + self.get_interface(interface)? + .get_method(method)? + .in_signature(), + ) } /// Find the signature of a property on an interface on this node pub fn get_property_signature(&self, interface: &str, property: &str) -> Option<&str> { - Some(&self.get_interface(interface)?.get_property(property)?.r#type) + Some( + &self + .get_interface(interface)? + .get_property(property)? + .r#type, + ) } /// Represent the node as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => self.name.as_ref().map(|s| Value::string(s, span)).unwrap_or_default(), - "interfaces" => list_to_value!(self.interfaces, span), - "children" => list_to_value!(self.children, span), - }, span) + Value::record( + record! { + "name" => self.name.as_ref().map(|s| Value::string(s, span)).unwrap_or_default(), + "interfaces" => list_to_value!(self.interfaces, span), + "children" => list_to_value!(self.children, span), + }, + span, + ) } } @@ -89,13 +101,16 @@ impl Interface { /// Represent the interface as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => Value::string(&self.name, span), - "methods" => list_to_value!(self.methods, span), - "signals" => list_to_value!(self.signals, span), - "properties" => list_to_value!(self.properties, span), - "signals" => list_to_value!(self.signals, span), - }, span) + Value::record( + record! { + "name" => Value::string(&self.name, span), + "methods" => list_to_value!(self.methods, span), + "signals" => list_to_value!(self.signals, span), + "properties" => list_to_value!(self.properties, span), + "signals" => list_to_value!(self.signals, span), + }, + span, + ) } } @@ -112,7 +127,8 @@ pub struct Method { impl Method { /// Get the signature of the method args pub fn in_signature(&self) -> String { - self.args.iter() + self.args + .iter() .filter(|arg| arg.direction == Direction::In) .map(|arg| &arg.r#type[..]) .collect() @@ -121,7 +137,8 @@ impl Method { #[allow(dead_code)] /// Get the signature of the method result pub fn out_signature(&self) -> String { - self.args.iter() + self.args + .iter() .filter(|arg| arg.direction == Direction::Out) .map(|arg| &arg.r#type[..]) .collect() @@ -129,11 +146,14 @@ impl Method { /// Represent the method as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => Value::string(&self.name, span), - "args" => list_to_value!(self.args, span), - "annotations" => list_to_value!(self.annotations, span), - }, span) + Value::record( + record! { + "name" => Value::string(&self.name, span), + "args" => list_to_value!(self.args, span), + "annotations" => list_to_value!(self.annotations, span), + }, + span, + ) } } @@ -152,7 +172,7 @@ impl MethodArg { pub fn new( name: impl Into, r#type: impl Into, - direction: Direction + direction: Direction, ) -> MethodArg { MethodArg { name: Some(name.into()), @@ -163,11 +183,14 @@ impl MethodArg { /// Represent the method as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), - "type" => Value::string(&self.r#type, span), - "direction" => self.direction.to_value(span), - }, span) + Value::record( + record! { + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), + "type" => Value::string(&self.r#type, span), + "direction" => self.direction.to_value(span), + }, + span, + ) } } @@ -181,7 +204,7 @@ pub enum Direction { impl Direction { /// Represent the direction as a nushell [Value] - pub fn to_value(&self, span: Span) -> Value { + pub fn to_value(self, span: Span) -> Value { match self { Direction::In => Value::string("in", span), Direction::Out => Value::string("out", span), @@ -202,11 +225,14 @@ pub struct Signal { impl Signal { /// Represent the signal as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => Value::string(&self.name, span), - "args" => list_to_value!(self.args, span), - "annotations" => list_to_value!(self.annotations, span), - }, span) + Value::record( + record! { + "name" => Value::string(&self.name, span), + "args" => list_to_value!(self.args, span), + "annotations" => list_to_value!(self.annotations, span), + }, + span, + ) } } @@ -221,10 +247,13 @@ pub struct SignalArg { impl SignalArg { /// Represent the argument as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), - "type" => Value::string(&self.r#type, span), - }, span) + Value::record( + record! { + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), + "type" => Value::string(&self.r#type, span), + }, + span, + ) } } @@ -241,12 +270,15 @@ pub struct Property { impl Property { /// Represent the property as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => Value::string(&self.name, span), - "type" => Value::string(&self.r#type, span), - "args" => self.access.to_value(span), - "annotations" => list_to_value!(self.annotations, span), - }, span) + Value::record( + record! { + "name" => Value::string(&self.name, span), + "type" => Value::string(&self.r#type, span), + "args" => self.access.to_value(span), + "annotations" => list_to_value!(self.annotations, span), + }, + span, + ) } } @@ -279,15 +311,21 @@ pub struct Annotation { impl Annotation { #[cfg(test)] pub fn new(name: impl Into, value: impl Into) -> Annotation { - Annotation { name: name.into(), value: value.into() } + Annotation { + name: name.into(), + value: value.into(), + } } /// Represent the annotation as a nushell [Value] pub fn to_value(&self, span: Span) -> Value { - Value::record(record!{ - "name" => Value::string(&self.name, span), - "value" => Value::string(&self.value, span), - }, span) + Value::record( + record! { + "name" => Value::string(&self.name, span), + "value" => Value::string(&self.value, span), + }, + span, + ) } } @@ -305,9 +343,7 @@ pub fn test_introspection_doc_rs() -> Node { MethodArg::new("bar", "as", Direction::In), MethodArg::new("baz", "a{us}", Direction::Out), ], - annotations: vec![ - Annotation::new("org.freedesktop.DBus.Deprecated", "true"), - ], + annotations: vec![Annotation::new("org.freedesktop.DBus.Deprecated", "true")], }, Method { name: "Bazify".into(), @@ -320,35 +356,30 @@ pub fn test_introspection_doc_rs() -> Node { }, Method { name: "Mogrify".into(), - args: vec![ - MethodArg::new("bar", "(iiav)", Direction::In), - ], - annotations: vec![] - }, - ], - signals: vec![ - Signal { - name: "Changed".into(), - args: vec![ - SignalArg { name: Some("new_value".into()), r#type: "b".into() }, - ], - annotations: vec![] - }, - ], - properties: vec![ - Property { - name: "Bar".into(), - r#type: "y".into(), - access: Access::ReadWrite, + args: vec![MethodArg::new("bar", "(iiav)", Direction::In)], annotations: vec![], - } + }, ], - annotations: vec![] + signals: vec![Signal { + name: "Changed".into(), + args: vec![SignalArg { + name: Some("new_value".into()), + r#type: "b".into(), + }], + annotations: vec![], + }], + properties: vec![Property { + name: "Bar".into(), + r#type: "y".into(), + access: Access::ReadWrite, + annotations: vec![], + }], + annotations: vec![], }], children: vec![ Node::with_name("child_of_sample_object"), Node::with_name("another_child_of_sample_object"), - ] + ], } } diff --git a/src/main.rs b/src/main.rs index cc3b3a2..fbc8b51 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,15 @@ -use nu_plugin::{serve_plugin, MsgPackSerializer, Plugin, EvaluatedCall, LabeledError}; -use nu_protocol::{PluginSignature, Value, SyntaxShape, PluginExample, Span, Type}; +use nu_plugin::{serve_plugin, EvaluatedCall, LabeledError, MsgPackSerializer, Plugin}; +use nu_protocol::{PluginExample, PluginSignature, Span, SyntaxShape, Type, Value}; -mod config; mod client; +mod config; mod convert; mod dbus_type; mod introspection; mod pattern; -use config::*; use client::*; +use config::*; use crate::pattern::Pattern; @@ -23,14 +23,16 @@ struct NuPluginDbus; impl Plugin for NuPluginDbus { fn signature(&self) -> Vec { macro_rules! str { - ($s:expr) => (Value::string($s, Span::unknown())) + ($s:expr) => { + Value::string($s, Span::unknown()) + }; } vec![ PluginSignature::build("dbus") - .is_dbus_command() + .dbus_command() .usage("Commands for interacting with D-Bus"), PluginSignature::build("dbus introspect") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("Introspect a D-Bus object") @@ -65,7 +67,7 @@ impl Plugin for NuPluginDbus { }, ]), PluginSignature::build("dbus call") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("Call a method and get its response") @@ -108,7 +110,7 @@ impl Plugin for NuPluginDbus { }, ]), PluginSignature::build("dbus get") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("Get a D-Bus property") @@ -139,7 +141,7 @@ impl Plugin for NuPluginDbus { }, ]), PluginSignature::build("dbus get-all") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("Get all D-Bus properties for the given object") @@ -165,7 +167,7 @@ impl Plugin for NuPluginDbus { }, ]), PluginSignature::build("dbus set") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("Set a D-Bus property") @@ -196,7 +198,7 @@ impl Plugin for NuPluginDbus { }, ]), PluginSignature::build("dbus list") - .is_dbus_command() + .dbus_command() .accepts_dbus_client_options() .accepts_timeout() .usage("List all available connection names on the bus") @@ -244,7 +246,7 @@ impl Plugin for NuPluginDbus { "dbus" => Err(LabeledError { label: "The `dbus` command requires a subcommand".into(), msg: "add --help to see subcommands".into(), - span: Some(call.head) + span: Some(call.head), }), "dbus introspect" => self.introspect(call), @@ -257,21 +259,21 @@ impl Plugin for NuPluginDbus { _ => Err(LabeledError { label: "Plugin invoked with unknown command name".into(), msg: "unknown command".into(), - span: Some(call.head) - }) + span: Some(call.head), + }), } } } /// For conveniently adding the base options to a dbus command trait DbusSignatureUtilExt { - fn is_dbus_command(self) -> Self; + fn dbus_command(self) -> Self; fn accepts_dbus_client_options(self) -> Self; fn accepts_timeout(self) -> Self; } impl DbusSignatureUtilExt for PluginSignature { - fn is_dbus_command(self) -> Self { + fn dbus_command(self) -> Self { self.search_terms(vec!["dbus".into()]) .category(nu_protocol::Category::Platform) } @@ -279,16 +281,33 @@ impl DbusSignatureUtilExt for PluginSignature { fn accepts_dbus_client_options(self) -> Self { self.switch("session", "Send to the session message bus (default)", None) .switch("system", "Send to the system message bus", None) - .switch("started", "Send to the bus that started this process, if applicable", None) - .named("bus", SyntaxShape::String, "Send to the bus server at the given address", None) - .named("peer", SyntaxShape::String, + .switch( + "started", + "Send to the bus that started this process, if applicable", + None, + ) + .named( + "bus", + SyntaxShape::String, + "Send to the bus server at the given address", + None, + ) + .named( + "peer", + SyntaxShape::String, "Send to a non-bus D-Bus server at the given address. \ Will not call the Hello method on initialization.", - None) + None, + ) } fn accepts_timeout(self) -> Self { - self.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None) + self.named( + "timeout", + SyntaxShape::Duration, + "How long to wait for a response", + None, + ) } } @@ -296,10 +315,7 @@ impl NuPluginDbus { fn introspect(&self, call: &EvaluatedCall) -> Result { let config = DbusClientConfig::try_from(call)?; let dbus = DbusClient::new(config)?; - let node = dbus.introspect( - &call.get_flag("dest")?.unwrap(), - &call.req(0)?, - )?; + let node = dbus.introspect(&call.get_flag("dest")?.unwrap(), &call.req(0)?)?; Ok(node.to_value(call.head)) } @@ -312,7 +328,7 @@ impl NuPluginDbus { &call.req(1)?, &call.req(2)?, call.get_flag("signature")?.as_ref(), - &call.positional[3..] + &call.positional[3..], )?; let flatten = !call.get_flag::("no-flatten")?.unwrap_or(false); @@ -322,7 +338,7 @@ impl NuPluginDbus { 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)) + _ => Ok(Value::list(values, call.head)), } } @@ -364,10 +380,16 @@ impl NuPluginDbus { fn list(&self, call: &EvaluatedCall) -> Result { let config = DbusClientConfig::try_from(call)?; let dbus = DbusClient::new(config)?; - let pattern = call.opt::(0)?.map(|pat| Pattern::new(&pat, Some('.'))); + let pattern = call + .opt::(0)? + .map(|pat| Pattern::new(&pat, Some('.'))); let result = dbus.list(pattern.as_ref())?; Ok(Value::list( - result.into_iter().map(|s| Value::string(s, call.head)).collect(), - call.head)) + result + .into_iter() + .map(|s| Value::string(s, call.head)) + .collect(), + call.head, + )) } } diff --git a/src/pattern.rs b/src/pattern.rs index ec66c77..da7f62b 100644 --- a/src/pattern.rs +++ b/src/pattern.rs @@ -17,19 +17,18 @@ impl Pattern { let mut tokens = vec![]; for ch in pattern.chars() { match ch { - '*' => + '*' => { if tokens.last() == Some(&PatternToken::OneWildcard) { *tokens.last_mut().unwrap() = PatternToken::ManyWildcard; } else { tokens.push(PatternToken::OneWildcard); - }, - '?' => - tokens.push(PatternToken::AnyChar), - _ => - match tokens.last_mut() { - Some(PatternToken::Exact(ref mut s)) => s.push(ch), - _ => tokens.push(PatternToken::Exact(ch.into())), - }, + } + } + '?' => tokens.push(PatternToken::AnyChar), + _ => match tokens.last_mut() { + Some(PatternToken::Exact(ref mut s)) => s.push(ch), + _ => tokens.push(PatternToken::Exact(ch.into())), + }, } } Pattern { separator, tokens } @@ -57,14 +56,17 @@ impl Pattern { MatchState::Precise => { // Can't possibly match return false; - }, + } MatchState::ScanAhead { stop_at_separator } => { if search_str.is_empty() { // End of input, can't match return false; } - if stop_at_separator && - self.separator.is_some_and(|sep| search_str.starts_with(sep)) { + if stop_at_separator + && self + .separator + .is_some_and(|sep| search_str.starts_with(sep)) + { // Found the separator. Consume a char and revert to precise // mode search_str = &search_str[1..]; @@ -76,17 +78,21 @@ impl Pattern { } } } - }, + } PatternToken::OneWildcard => { // Set the mode to ScanAhead, stopping at separator - state = MatchState::ScanAhead { stop_at_separator: true }; + state = MatchState::ScanAhead { + stop_at_separator: true, + }; tokens = &tokens[1..]; - }, + } PatternToken::ManyWildcard => { // Set the mode to ScanAhead, ignoring separator - state = MatchState::ScanAhead { stop_at_separator: false }; + state = MatchState::ScanAhead { + stop_at_separator: false, + }; tokens = &tokens[1..]; - }, + } PatternToken::AnyChar => { if !search_str.is_empty() { // Take a char from the search str and continue @@ -96,11 +102,15 @@ impl Pattern { // End of input return false; } - }, + } } } - #[cfg(test)] { - println!("end, state={:?}, search_str={:?}, tokens={:?}", state, search_str, tokens); + #[cfg(test)] + { + println!( + "end, state={:?}, search_str={:?}, tokens={:?}", + state, search_str, tokens + ); } if !search_str.is_empty() { // If the search str is not empty at the end @@ -108,15 +118,20 @@ impl Pattern { // We didn't end with a wildcard, so this is a fail MatchState::Precise => false, // This could be a match as long as the separator isn't contained in the remainder - MatchState::ScanAhead { stop_at_separator: true } => + MatchState::ScanAhead { + stop_at_separator: true, + } => { if let Some(separator) = self.separator { !search_str.contains(separator) } else { // No separator specified, so this is a success true - }, + } + } // Always a success, no matter what remains - MatchState::ScanAhead { stop_at_separator: false } => true, + MatchState::ScanAhead { + stop_at_separator: false, + } => true, } } else { // The match has succeeded - there is nothing more to match @@ -129,119 +144,162 @@ impl Pattern { fn test_pattern_new() { assert_eq!( Pattern::new("", Some('/')), - Pattern { separator: Some('/'), tokens: vec![] } + Pattern { + separator: Some('/'), + tokens: vec![] + } ); assert_eq!( Pattern::new("", None), - Pattern { separator: None, tokens: vec![] } + Pattern { + separator: None, + tokens: vec![] + } ); assert_eq!( Pattern::new("org.freedesktop.DBus", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.freedesktop.DBus".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![PatternToken::Exact("org.freedesktop.DBus".into()),] + } ); assert_eq!( Pattern::new("*", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::OneWildcard, - ] } + Pattern { + separator: Some('.'), + tokens: vec![PatternToken::OneWildcard,] + } ); assert_eq!( Pattern::new("**", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::ManyWildcard, - ] } + Pattern { + separator: Some('.'), + tokens: vec![PatternToken::ManyWildcard,] + } ); assert_eq!( Pattern::new("?", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::AnyChar, - ] } + Pattern { + separator: Some('.'), + tokens: vec![PatternToken::AnyChar,] + } ); assert_eq!( Pattern::new("org.freedesktop.*", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.freedesktop.".into()), - PatternToken::OneWildcard, - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.freedesktop.".into()), + PatternToken::OneWildcard, + ] + } ); assert_eq!( Pattern::new("org.freedesktop.**", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.freedesktop.".into()), - PatternToken::ManyWildcard, - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.freedesktop.".into()), + PatternToken::ManyWildcard, + ] + } ); assert_eq!( Pattern::new("org.*.DBus", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.".into()), - PatternToken::OneWildcard, - PatternToken::Exact(".DBus".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.".into()), + PatternToken::OneWildcard, + PatternToken::Exact(".DBus".into()), + ] + } ); assert_eq!( Pattern::new("org.**.DBus", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.".into()), - PatternToken::ManyWildcard, - PatternToken::Exact(".DBus".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.".into()), + PatternToken::ManyWildcard, + PatternToken::Exact(".DBus".into()), + ] + } ); assert_eq!( Pattern::new("org.**.?Bus", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.".into()), - PatternToken::ManyWildcard, - PatternToken::Exact(".".into()), - PatternToken::AnyChar, - PatternToken::Exact("Bus".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.".into()), + PatternToken::ManyWildcard, + PatternToken::Exact(".".into()), + PatternToken::AnyChar, + PatternToken::Exact("Bus".into()), + ] + } ); assert_eq!( Pattern::new("org.free*top", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.free".into()), - PatternToken::OneWildcard, - PatternToken::Exact("top".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.free".into()), + PatternToken::OneWildcard, + PatternToken::Exact("top".into()), + ] + } ); assert_eq!( Pattern::new("org.free**top", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.free".into()), - PatternToken::ManyWildcard, - PatternToken::Exact("top".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.free".into()), + PatternToken::ManyWildcard, + PatternToken::Exact("top".into()), + ] + } ); assert_eq!( Pattern::new("org.**top", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.".into()), - PatternToken::ManyWildcard, - PatternToken::Exact("top".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.".into()), + PatternToken::ManyWildcard, + PatternToken::Exact("top".into()), + ] + } ); assert_eq!( Pattern::new("**top", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::ManyWildcard, - PatternToken::Exact("top".into()), - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::ManyWildcard, + PatternToken::Exact("top".into()), + ] + } ); assert_eq!( Pattern::new("org.free**", Some('.')), - Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("org.free".into()), - PatternToken::ManyWildcard, - ] } + Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("org.free".into()), + PatternToken::ManyWildcard, + ] + } ); } #[test] fn test_pattern_is_match_empty() { - let pat = Pattern { separator: Some('.'), tokens: vec![] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![], + }; assert!(pat.is_match("")); assert!(!pat.is_match("anystring")); assert!(!pat.is_match("anystring.anyotherstring")); @@ -249,9 +307,10 @@ fn test_pattern_is_match_empty() { #[test] fn test_pattern_is_match_exact() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("specific".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![PatternToken::Exact("specific".into())], + }; assert!(pat.is_match("specific")); assert!(!pat.is_match("")); assert!(!pat.is_match("specifi")); @@ -260,11 +319,14 @@ fn test_pattern_is_match_exact() { #[test] fn test_pattern_is_match_one_wildcard() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("foo.".into()), - PatternToken::OneWildcard, - PatternToken::Exact(".baz".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("foo.".into()), + PatternToken::OneWildcard, + PatternToken::Exact(".baz".into()), + ], + }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); @@ -277,10 +339,13 @@ fn test_pattern_is_match_one_wildcard() { #[test] fn test_pattern_is_match_one_wildcard_at_end() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("foo.".into()), - PatternToken::OneWildcard, - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("foo.".into()), + PatternToken::OneWildcard, + ], + }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("foo.grok")); assert!(pat.is_match("foo.")); @@ -292,10 +357,13 @@ fn test_pattern_is_match_one_wildcard_at_end() { #[test] fn test_pattern_is_match_one_wildcard_at_start() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::OneWildcard, - PatternToken::Exact(".bar".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::OneWildcard, + PatternToken::Exact(".bar".into()), + ], + }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("grok.bar")); assert!(pat.is_match(".bar")); @@ -307,11 +375,14 @@ fn test_pattern_is_match_one_wildcard_at_start() { #[test] fn test_pattern_is_match_one_wildcard_no_separator() { - let pat = Pattern { separator: None, tokens: vec![ - PatternToken::Exact("foo.".into()), - PatternToken::OneWildcard, - PatternToken::Exact(".baz".into()), - ] }; + let pat = Pattern { + separator: None, + tokens: vec![ + PatternToken::Exact("foo.".into()), + PatternToken::OneWildcard, + PatternToken::Exact(".baz".into()), + ], + }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); @@ -325,11 +396,14 @@ fn test_pattern_is_match_one_wildcard_no_separator() { #[test] fn test_pattern_is_match_many_wildcard() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("foo.".into()), - PatternToken::ManyWildcard, - PatternToken::Exact(".baz".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("foo.".into()), + PatternToken::ManyWildcard, + PatternToken::Exact(".baz".into()), + ], + }; assert!(pat.is_match("foo.bar.baz")); assert!(pat.is_match("foo.grok.baz")); assert!(pat.is_match("foo..baz")); @@ -343,10 +417,13 @@ fn test_pattern_is_match_many_wildcard() { #[test] fn test_pattern_is_match_many_wildcard_at_end() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("foo.".into()), - PatternToken::ManyWildcard, - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("foo.".into()), + PatternToken::ManyWildcard, + ], + }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("foo.grok")); assert!(pat.is_match("foo.")); @@ -358,10 +435,13 @@ fn test_pattern_is_match_many_wildcard_at_end() { #[test] fn test_pattern_is_match_many_wildcard_at_start() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::ManyWildcard, - PatternToken::Exact(".bar".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::ManyWildcard, + PatternToken::Exact(".bar".into()), + ], + }; assert!(pat.is_match("foo.bar")); assert!(pat.is_match("grok.bar")); assert!(pat.is_match("should.match.bar")); @@ -373,11 +453,14 @@ fn test_pattern_is_match_many_wildcard_at_start() { #[test] fn test_pattern_is_match_any_char() { - let pat = Pattern { separator: Some('.'), tokens: vec![ - PatternToken::Exact("fo".into()), - PatternToken::AnyChar, - PatternToken::Exact(".baz".into()), - ] }; + let pat = Pattern { + separator: Some('.'), + tokens: vec![ + PatternToken::Exact("fo".into()), + PatternToken::AnyChar, + PatternToken::Exact(".baz".into()), + ], + }; assert!(pat.is_match("foo.baz")); assert!(pat.is_match("foe.baz")); assert!(pat.is_match("foi.baz"));