Nushell plugin for interacting with D-Bus
0
fork

Configure Feed

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

make dbus call take arguments

+558 -16
+26 -4
src/client.rs
··· 2 2 use nu_plugin::LabeledError; 3 3 use nu_protocol::{Spanned, Value}; 4 4 5 - use crate::config::{DbusClientConfig, DbusBusChoice}; 5 + use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item}; 6 6 7 7 /// Executes D-Bus actions on a connection, handling nushell types 8 8 pub struct DbusClient { ··· 42 42 object: &Spanned<String>, 43 43 interface: &Spanned<String>, 44 44 method: &Spanned<String>, 45 - ) -> Result<Value, LabeledError> { 46 - // TODO accept arguments 45 + signature: Option<&Spanned<String>>, 46 + args: &[Value], 47 + ) -> Result<Vec<Value>, LabeledError> { 47 48 macro_rules! error { 48 49 ($label:expr) => (LabeledError { 49 50 label: $label, ··· 67 68 let valid_interface = validate_with!(dbus::strings::Interface, interface)?; 68 69 let valid_method = validate_with!(dbus::strings::Member, method)?; 69 70 71 + // Parse the signature 72 + let valid_signature = signature.map(|s| DbusType::parse_all(&s.item).map_err(|err| { 73 + LabeledError { 74 + label: err, 75 + msg: "in signature specified here".into(), 76 + span: Some(s.span), 77 + } 78 + })).transpose()?; 79 + 80 + if let Some(sig) = &valid_signature { 81 + if sig.len() != args.len() { 82 + error!(format!("expected {} arguments, got {}", sig.len(), args.len())); 83 + } 84 + } 85 + 70 86 // Construct the method call message 71 - let message = Message::new_method_call( 87 + let mut message = Message::new_method_call( 72 88 valid_dest, 73 89 valid_object, 74 90 valid_interface, 75 91 valid_method, 76 92 ).map_err(|err| error!(err))?; 93 + 94 + // Convert the args to message items 95 + let sigs_iter = valid_signature.iter().flatten().map(Some).chain(std::iter::repeat(None)); 96 + for (val, sig) in args.iter().zip(sigs_iter) { 97 + message = message.append1(to_message_item(val, sig)?); 98 + } 77 99 78 100 // Send it on the channel and get the response 79 101 let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item)
+167 -9
src/convert.rs
··· 1 - use dbus::{Message, arg::{ArgType, RefArg}}; 1 + use dbus::{Message, arg::{ArgType, RefArg, messageitem::{MessageItemArray, MessageItem, MessageItemDict}}, Signature}; 2 + use nu_plugin::LabeledError; 2 3 use nu_protocol::{Value, Span, Record}; 4 + use std::str::FromStr; 5 + 6 + use crate::dbus_type::DbusType; 3 7 4 - pub fn from_message(message: &Message) -> Result<Value, String> { 8 + /// Get the arguments of a message as nushell Values 9 + pub fn from_message(message: &Message) -> Result<Vec<Value>, String> { 5 10 let mut out = vec![]; 6 11 for refarg in message.iter_init() { 7 12 out.push(from_refarg(&refarg)?); 8 13 } 9 - Ok(Value::list(out, Span::unknown())) 14 + Ok(out) 10 15 } 11 16 12 17 pub fn from_refarg(refarg: &dyn RefArg) -> Result<Value, String> { ··· 15 20 if refarg.signature().starts_with("a{") { 16 21 // This is a dictionary 17 22 let mut record = Record::new(); 18 - for entry in refarg.as_iter().unwrap() { 19 - let mut entry_iter = entry.as_iter().unwrap(); 20 - let key = entry_iter.next().unwrap(); 21 - let val = entry_iter.next().unwrap(); 22 - if let Some(key) = key.as_str() { 23 - record.insert(key, from_refarg(val)?); 23 + let mut iter = refarg.as_iter().unwrap(); 24 + while let Some(key) = iter.next() { 25 + if let Some(val) = iter.next() { 26 + if let Some(key_str) = key.as_str() { 27 + record.insert(key_str, from_refarg(val)?); 28 + } 24 29 } 25 30 } 26 31 Value::record(record, Span::unknown()) ··· 68 73 return Err("Encountered invalid D-Bus value".into()), 69 74 }) 70 75 } 76 + 77 + pub fn to_message_item(value: &Value, expected_type: Option<&DbusType>) 78 + -> Result<MessageItem, LabeledError> 79 + { 80 + // Report errors from conversion. Error must support Display 81 + macro_rules! try_convert { 82 + ($result_expr:expr) => ($result_expr.map_err(|err| LabeledError { 83 + label: format!("Failed to convert value to the D-Bus `{:?}` type", 84 + expected_type.unwrap()), 85 + msg: err.to_string(), 86 + span: Some(value.span()), 87 + })?) 88 + } 89 + 90 + // Try to match values to expected types 91 + match (value, expected_type) { 92 + // Boolean 93 + (Value::Bool { val, .. }, Some(DbusType::Boolean)) => 94 + Ok(MessageItem::Bool(*val)), 95 + 96 + // Strings and specialized strings 97 + (Value::String { val, .. }, Some(DbusType::String)) => 98 + Ok(MessageItem::Str(val.to_owned())), 99 + (Value::String { val, .. }, Some(DbusType::ObjectPath)) => 100 + Ok(MessageItem::ObjectPath(try_convert!(dbus::strings::Path::new(val)))), 101 + (Value::String { val, .. }, Some(DbusType::Signature)) => 102 + Ok(MessageItem::Signature(try_convert!(dbus::strings::Signature::new(val)))), 103 + 104 + // Signed ints 105 + (Value::Int { val, .. }, Some(DbusType::Int64)) => 106 + Ok(MessageItem::Int64(*val)), 107 + (Value::Int { val, .. }, Some(DbusType::Int32)) => 108 + Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))), 109 + (Value::Int { val, .. }, Some(DbusType::Int16)) => 110 + Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))), 111 + 112 + // Unsigned ints 113 + (Value::Int { val, .. }, Some(DbusType::UInt64)) => 114 + Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))), 115 + (Value::Int { val, .. }, Some(DbusType::UInt32)) => 116 + Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))), 117 + (Value::Int { val, .. }, Some(DbusType::UInt16)) => 118 + Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))), 119 + (Value::Int { val, .. }, Some(DbusType::Byte)) => 120 + Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))), 121 + 122 + // Ints from string 123 + (Value::String { val, .. }, Some(DbusType::Int64)) => 124 + Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))), 125 + (Value::String { val, .. }, Some(DbusType::Int32)) => 126 + Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))), 127 + (Value::String { val, .. }, Some(DbusType::Int16)) => 128 + Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))), 129 + (Value::String { val, .. }, Some(DbusType::UInt64)) => 130 + Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))), 131 + (Value::String { val, .. }, Some(DbusType::UInt32)) => 132 + Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))), 133 + (Value::String { val, .. }, Some(DbusType::UInt16)) => 134 + Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))), 135 + (Value::String { val, .. }, Some(DbusType::Byte)) => 136 + Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))), 137 + 138 + // Float 139 + (Value::Float { val, .. }, Some(DbusType::Double)) => 140 + Ok(MessageItem::Double(*val)), 141 + (Value::String { val, .. }, Some(DbusType::Double)) => 142 + Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))), 143 + 144 + // List/array 145 + (Value::List { vals, .. }, Some(DbusType::Array(content_type))) => { 146 + let content_sig = Signature::from(content_type.stringify()); 147 + let items = vals.iter() 148 + .map(|content| to_message_item(content, Some(content_type))) 149 + .collect::<Result<Vec<MessageItem>, _>>()?; 150 + Ok(MessageItem::Array(MessageItemArray::new(items, content_sig).unwrap())) 151 + }, 152 + 153 + // Struct 154 + (Value::List { vals, .. }, Some(DbusType::Struct(types))) => { 155 + if vals.len() != types.len() { 156 + return Err(LabeledError { 157 + label: format!("expected struct with {} element(s) ({:?})", types.len(), types), 158 + msg: format!("this list has {} element(s) instead", vals.len()), 159 + span: Some(value.span()) 160 + }); 161 + } 162 + let items = vals.iter().zip(types) 163 + .map(|(content, r#type)| to_message_item(content, Some(r#type))) 164 + .collect::<Result<Vec<MessageItem>, _>>()?; 165 + Ok(MessageItem::Struct(items)) 166 + }, 167 + 168 + // Record/dict 169 + (Value::Record { val, .. }, Some(DbusType::Array(content_type))) 170 + if matches!(**content_type, DbusType::DictEntry(_, _)) => 171 + { 172 + if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type { 173 + let key_sig = Signature::from(key_type.stringify()); 174 + let val_sig = Signature::from(val_type.stringify()); 175 + let pairs = val.iter() 176 + .map(|(key, val)| { 177 + let key_as_value = Value::string(key, value.span()); 178 + let key_message_item = to_message_item(&key_as_value, Some(key_type))?; 179 + let val_message_item = to_message_item(val, Some(val_type))?; 180 + Ok((key_message_item, val_message_item)) 181 + }) 182 + .collect::<Result<Vec<_>, LabeledError>>()?; 183 + Ok(MessageItem::Dict(MessageItemDict::new(pairs, key_sig, val_sig).unwrap())) 184 + } else { 185 + unreachable!() 186 + } 187 + }, 188 + 189 + // Variant - use automatic type 190 + (other_value, Some(DbusType::Variant)) => 191 + Ok(MessageItem::Variant(Box::new(to_message_item(other_value, None)?))), 192 + 193 + // Value not compatible with expected type 194 + (other_value, Some(expectation)) => 195 + Err(LabeledError { 196 + label: format!("`{}` can not be converted to the D-Bus `{:?}` type", 197 + other_value.get_type(), expectation), 198 + msg: format!("expected a `{:?}` here", expectation), 199 + span: Some(other_value.span()), 200 + }), 201 + 202 + // Automatic types (with no type expectation) 203 + (Value::String { .. }, None) => 204 + to_message_item(value, Some(&DbusType::String)), 205 + (Value::Int { .. }, None) => 206 + to_message_item(value, Some(&DbusType::Int64)), 207 + (Value::Float { .. }, None) => 208 + to_message_item(value, Some(&DbusType::Double)), 209 + (Value::Bool { .. }, None) => 210 + to_message_item(value, Some(&DbusType::Boolean)), 211 + (Value::List { .. }, None) => 212 + to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))), 213 + (Value::Record { .. }, None) => 214 + to_message_item(value, Some(&DbusType::Array( 215 + DbusType::DictEntry( 216 + DbusType::String.into(), 217 + DbusType::Variant.into() 218 + ).into()))), 219 + 220 + // No expected type, but can't handle this type 221 + _ => 222 + Err(LabeledError { 223 + label: format!("can not use values of type `{}` in D-Bus calls", value.get_type()), 224 + msg: "use a supported type here instead".into(), 225 + span: Some(value.span()), 226 + }) 227 + } 228 + }
+342
src/dbus_type.rs
··· 1 + /// Representation of fully specified D-Bus types 2 + /// 3 + /// [dbus::arg::ArgType] does not sufficiently specify the types inside of a container type, 4 + /// and also doesn't provide any parse/dump capability 5 + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 6 + pub enum DbusType { 7 + Byte, 8 + Boolean, 9 + Int16, 10 + UInt16, 11 + Int32, 12 + UInt32, 13 + Int64, 14 + UInt64, 15 + Double, 16 + String, 17 + ObjectPath, 18 + Signature, 19 + Array(Box<DbusType>), 20 + Struct(Vec<DbusType>), 21 + Variant, 22 + DictEntry(Box<DbusType>, Box<DbusType>), 23 + } 24 + 25 + impl DbusType { 26 + /// Parse one type from a D-Bus signature, and return the remainder 27 + pub fn parse(input: &str) -> Result<(DbusType, &str), String> { 28 + use self::DbusType::*; 29 + 30 + if input.is_empty() { 31 + return Err("unexpected end of D-Bus type string".into()); 32 + } 33 + 34 + match input.chars().nth(0).unwrap() { 35 + 'y' => Ok((Byte, &input[1..])), 36 + 'b' => Ok((Boolean, &input[1..])), 37 + 'n' => Ok((Int16, &input[1..])), 38 + 'q' => Ok((UInt16, &input[1..])), 39 + 'i' => Ok((Int32, &input[1..])), 40 + 'u' => Ok((UInt32, &input[1..])), 41 + 'x' => Ok((Int64, &input[1..])), 42 + 't' => Ok((UInt64, &input[1..])), 43 + 'd' => Ok((Double, &input[1..])), 44 + 's' => Ok((String, &input[1..])), 45 + 'o' => Ok((ObjectPath, &input[1..])), 46 + 'g' => Ok((Signature, &input[1..])), 47 + 'a' => { 48 + // The next type is the content type of the array 49 + let (content_type, remainder) = Self::parse(&input[1..])?; 50 + Ok((Array(content_type.into()), remainder)) 51 + }, 52 + '(' => { 53 + // Parse the struct content until we get to the end ) char 54 + let mut remainder = &input[1..]; 55 + let mut types = vec![]; 56 + loop { 57 + if remainder.is_empty() { 58 + break Err("unexpected end of D-Bus type string \ 59 + before end of array".into()); 60 + } else if remainder.starts_with(')') { 61 + break Ok((DbusType::Struct(types), &remainder[1..])); 62 + } else { 63 + let (r#type, new_remainder) = Self::parse(remainder)?; 64 + types.push(r#type); 65 + remainder = new_remainder; 66 + } 67 + } 68 + }, 69 + 'v' => Ok((Variant, &input[1..])), 70 + '{' => { 71 + // Expect two types 72 + let (key_type, key_remainder) = Self::parse(&input[1..])?; 73 + let (val_type, val_remainder) = Self::parse(key_remainder)?; 74 + // Must end with } 75 + if val_remainder.starts_with('}') { 76 + Ok((DbusType::DictEntry(key_type.into(), val_type.into()), &val_remainder[1..])) 77 + } else { 78 + Err(format!("expected `}}` char to end dictionary in D-Bus type \ 79 + but remainder is {:?}", val_remainder)) 80 + } 81 + }, 82 + other => Err(format!("unexpected char {other:?} in D-Bus type representation")) 83 + } 84 + } 85 + 86 + /// Parse multiple types from a D-Bus signature 87 + pub fn parse_all(mut input: &str) -> Result<Vec<DbusType>, String> { 88 + let mut out = vec![]; 89 + while !input.is_empty() { 90 + let (parsed, remainder) = Self::parse(input)?; 91 + out.push(parsed); 92 + input = remainder; 93 + } 94 + Ok(out) 95 + } 96 + 97 + /// Convert the D-Bus type into a string suitable for the wire format 98 + pub fn stringify(&self) -> String { 99 + use self::DbusType::*; 100 + 101 + match self { 102 + Byte => 'y'.into(), 103 + Boolean => 'b'.into(), 104 + Int16 => 'n'.into(), 105 + UInt16 => 'q'.into(), 106 + Int32 => 'i'.into(), 107 + UInt32 => 'u'.into(), 108 + Int64 => 'x'.into(), 109 + UInt64 => 't'.into(), 110 + Double => 'd'.into(), 111 + String => 's'.into(), 112 + ObjectPath => 'o'.into(), 113 + Signature => 'g'.into(), 114 + 115 + // a<type> 116 + Array(content) => format!("a{}", content.stringify()), 117 + 118 + // (<type1><type2><typeN..>) 119 + Struct(types) => std::iter::once("(".to_owned()) 120 + .chain(types.iter().map(|t| t.stringify())) 121 + .chain(std::iter::once(")".to_owned())) 122 + .collect(), 123 + 124 + Variant => 'v'.into(), 125 + 126 + // {<key><val>} 127 + DictEntry(key, val) => format!("{{{}{}}}", key.stringify(), val.stringify()), 128 + } 129 + } 130 + } 131 + 132 + #[cfg(test)] 133 + macro_rules! should_parse_to { 134 + ($str:expr, $result:expr) => ( 135 + assert_eq!(DbusType::parse($str), Ok(($result, ""))) 136 + ) 137 + } 138 + 139 + #[test] 140 + fn test_parse_simple_types() { 141 + use self::DbusType::*; 142 + should_parse_to!("y", Byte); 143 + should_parse_to!("b", Boolean); 144 + should_parse_to!("n", Int16); 145 + should_parse_to!("q", UInt16); 146 + should_parse_to!("i", Int32); 147 + should_parse_to!("u", UInt32); 148 + should_parse_to!("x", Int64); 149 + should_parse_to!("t", UInt64); 150 + should_parse_to!("d", Double); 151 + should_parse_to!("s", String); 152 + should_parse_to!("o", ObjectPath); 153 + should_parse_to!("g", Signature); 154 + should_parse_to!("v", Variant); 155 + } 156 + 157 + #[test] 158 + fn test_parse_simple_type_remainder() -> Result<(), String> { 159 + let (_, remainder) = DbusType::parse("gyn")?; 160 + assert_eq!(remainder, "yn"); 161 + Ok(()) 162 + } 163 + 164 + #[test] 165 + fn test_parse_simple_invalid() { 166 + assert!(DbusType::parse("*").is_err()); 167 + } 168 + 169 + #[test] 170 + fn test_parse_simple_array() { 171 + use self::DbusType::*; 172 + should_parse_to!("ay", Array(Byte.into())); 173 + } 174 + 175 + #[test] 176 + fn test_parse_nested_array() { 177 + use self::DbusType::*; 178 + should_parse_to!("aai", Array(Array(Int32.into()).into())); 179 + } 180 + 181 + #[test] 182 + fn test_parse_array_remainder() -> Result<(), String> { 183 + let (_, remainder) = DbusType::parse("ay(oi)")?; 184 + assert_eq!(remainder, "(oi)"); 185 + Ok(()) 186 + } 187 + 188 + #[test] 189 + fn test_parse_array_unclosed() { 190 + assert!(DbusType::parse("a").is_err()); 191 + } 192 + 193 + #[test] 194 + fn test_parse_simple_struct() { 195 + use self::DbusType::*; 196 + should_parse_to!("()", Struct(vec![])); 197 + should_parse_to!("(y)", Struct(vec![Byte])); 198 + should_parse_to!("(sy)", Struct(vec![String, Byte])); 199 + should_parse_to!("(xto)", Struct(vec![Int64, UInt64, ObjectPath])); 200 + } 201 + 202 + #[test] 203 + fn test_parse_nested_struct() { 204 + use self::DbusType::*; 205 + should_parse_to!("((xx))", Struct(vec![Struct(vec![Int64, Int64])])); 206 + should_parse_to!("(y(xx))", Struct(vec![Byte, Struct(vec![Int64, Int64])])); 207 + should_parse_to!("(y(ss)o)", Struct(vec![Byte, Struct(vec![String, String]), ObjectPath])); 208 + should_parse_to!("((yy)s)", Struct(vec![Struct(vec![Byte, Byte]), String])); 209 + } 210 + 211 + #[test] 212 + fn test_parse_struct_remainder() -> Result<(), String> { 213 + let (_, remainder) = DbusType::parse("(oi)ay")?; 214 + assert_eq!(remainder, "ay"); 215 + Ok(()) 216 + } 217 + 218 + #[test] 219 + fn test_parse_struct_unclosed() { 220 + assert!(DbusType::parse("(ss").is_err()); 221 + } 222 + 223 + #[test] 224 + fn test_parse_dict_entry() { 225 + use self::DbusType::*; 226 + should_parse_to!("{ss}", DictEntry(String.into(), String.into())); 227 + should_parse_to!("{s(bd)}", DictEntry(String.into(), Struct(vec![Boolean, Double]).into())); 228 + } 229 + 230 + #[test] 231 + fn test_parse_array_dict() { 232 + use self::DbusType::*; 233 + should_parse_to!("a{sd}", Array(DictEntry(String.into(), Double.into()).into())); 234 + } 235 + 236 + #[test] 237 + fn test_parse_dict_entry_remainder() -> Result<(), String> { 238 + let (_, remainder) = DbusType::parse("{sd}{sai}")?; 239 + assert_eq!(remainder, "{sai}"); 240 + Ok(()) 241 + } 242 + 243 + #[test] 244 + fn test_parse_dict_entry_unclosed() { 245 + assert!(DbusType::parse("{ss").is_err()); 246 + } 247 + 248 + #[test] 249 + fn test_parse_all() { 250 + use self::DbusType::*; 251 + assert_eq!(DbusType::parse_all(""), Ok(vec![])); 252 + assert_eq!( 253 + DbusType::parse_all("s"), 254 + Ok(vec![ 255 + String, 256 + ]) 257 + ); 258 + assert_eq!( 259 + DbusType::parse_all("isbb"), 260 + Ok(vec![ 261 + Int32, 262 + String, 263 + Boolean, 264 + Boolean, 265 + ]) 266 + ); 267 + assert_eq!( 268 + DbusType::parse_all("ia{s(bi)}s"), 269 + Ok(vec![ 270 + Int32, 271 + Array(DictEntry(String.into(), Struct(vec![Boolean, Int32]).into()).into()), 272 + String, 273 + ]) 274 + ); 275 + } 276 + 277 + #[cfg(test)] 278 + macro_rules! should_stringify_to { 279 + ($type:expr, $result:expr) => ( 280 + assert_eq!(DbusType::stringify(&$type), $result) 281 + ) 282 + } 283 + 284 + #[test] 285 + fn test_stringify_simple_types() { 286 + use self::DbusType::*; 287 + should_stringify_to!(Byte, "y"); 288 + should_stringify_to!(Boolean, "b"); 289 + should_stringify_to!(Int16, "n"); 290 + should_stringify_to!(UInt16, "q"); 291 + should_stringify_to!(Int32, "i"); 292 + should_stringify_to!(UInt32, "u"); 293 + should_stringify_to!(Int64, "x"); 294 + should_stringify_to!(UInt64, "t"); 295 + should_stringify_to!(Double, "d"); 296 + should_stringify_to!(String, "s"); 297 + should_stringify_to!(ObjectPath, "o"); 298 + should_stringify_to!(Signature, "g"); 299 + should_stringify_to!(Variant, "v"); 300 + } 301 + 302 + #[test] 303 + fn test_stringify_array() { 304 + use self::DbusType::*; 305 + should_stringify_to!(Array(Variant.into()), "av"); 306 + should_stringify_to!(Array(Array(String.into()).into()), "aas"); 307 + } 308 + 309 + #[test] 310 + fn test_stringify_struct() { 311 + use self::DbusType::*; 312 + should_stringify_to!(Struct(vec![]), "()"); 313 + should_stringify_to!(Struct(vec![Int32]), "(i)"); 314 + should_stringify_to!(Struct(vec![Int32, String]), "(is)"); 315 + should_stringify_to!(Struct(vec![Byte, Int32, String]), "(yis)"); 316 + should_stringify_to!(Struct(vec![Byte, Struct(vec![String, Boolean]), String]), "(y(sb)s)"); 317 + } 318 + 319 + #[test] 320 + fn test_stringify_dict_entry() { 321 + use self::DbusType::*; 322 + should_stringify_to!(DictEntry(String.into(), Int32.into()), "{si}"); 323 + should_stringify_to!(DictEntry(Int32.into(), String.into()), "{is}"); 324 + } 325 + 326 + #[test] 327 + fn test_stringify_nested() { 328 + use self::DbusType::*; 329 + should_stringify_to!(Array(DictEntry(String.into(), Int32.into()).into()), "a{si}"); 330 + should_stringify_to!( 331 + Array( 332 + DictEntry( 333 + String.into(), 334 + Struct(vec![ 335 + Byte, 336 + Array(Int32.into()) 337 + ]).into() 338 + ).into() 339 + ), 340 + "a{s(yai)}" 341 + ); 342 + }
+23 -3
src/main.rs
··· 1 1 use nu_plugin::{serve_plugin, MsgPackSerializer, Plugin, EvaluatedCall, LabeledError}; 2 - use nu_protocol::{PluginSignature, Value, SyntaxShape, PluginExample}; 2 + use nu_protocol::{PluginSignature, Value, SyntaxShape, PluginExample, Span}; 3 3 4 4 mod config; 5 5 mod client; 6 6 mod convert; 7 + mod dbus_type; 7 8 8 9 use config::*; 9 10 use client::*; ··· 25 26 .is_dbus_command() 26 27 .accepts_dbus_client_options() 27 28 .usage("Call a method and get its response") 29 + .extra_usage("Returns an array if the method call returns more than one value.") 28 30 .named("timeout", SyntaxShape::Duration, "How long to wait for a response", None) 31 + .named("signature", SyntaxShape::String, 32 + "Signature of the arguments to send, in D-Bus format\n\ 33 + If not provided, they will be guessed automatically (but poorly)", None) 34 + .switch("no-flatten", "Always return a list of all return values", None) 29 35 .required_named("dest", SyntaxShape::String, 30 36 "The name of the connection to send the method to", 31 37 None) ··· 35 41 "The name of the interface the method belongs to") 36 42 .required("method", SyntaxShape::String, 37 43 "The name of the method to send") 44 + .rest("args", SyntaxShape::Any, 45 + "Arguments to send with the method call") 38 46 .plugin_examples(vec![ 39 47 PluginExample { 40 48 example: "dbus call --dest=org.freedesktop.DBus \ ··· 98 106 fn call(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 99 107 let config = DbusClientConfig::try_from(call)?; 100 108 let dbus = DbusClient::new(config)?; 101 - dbus.call( 109 + let values = dbus.call( 102 110 &call.get_flag("dest")?.unwrap(), 103 111 &call.req(0)?, 104 112 &call.req(1)?, 105 113 &call.req(2)?, 106 - ) 114 + call.get_flag("signature")?.as_ref(), 115 + &call.positional[3..] 116 + )?; 117 + 118 + let flatten = !call.get_flag::<bool>("no-flatten")?.unwrap_or(false); 119 + 120 + // Make the output easier to deal with by returning a list only if there are multiple return 121 + // values (not so common) 122 + match values.len() { 123 + 0 if flatten => Ok(Value::nothing(Span::unknown())), 124 + 1 if flatten => Ok(values.into_iter().nth(0).unwrap()), 125 + _ => Ok(Value::list(values, Span::unknown())) 126 + } 107 127 } 108 128 }