Nushell plugin for interacting with D-Bus
at main 290 lines 11 kB view raw
1use dbus::{ 2 Message, Signature, 3 arg::{ 4 ArgType, RefArg, 5 messageitem::{MessageItem, MessageItemArray, MessageItemDict}, 6 }, 7}; 8use nu_protocol::{LabeledError, Record, Span, Value}; 9use std::str::FromStr; 10 11use crate::dbus_type::DbusType; 12 13/// Get the arguments of a message as nushell Values 14pub fn from_message(message: &Message, span: Span) -> Result<Vec<Value>, String> { 15 let mut out = vec![]; 16 for refarg in message.iter_init() { 17 out.push(from_refarg(&refarg, span)?); 18 } 19 Ok(out) 20} 21 22pub fn from_refarg(refarg: &dyn RefArg, span: Span) -> Result<Value, String> { 23 Ok(match refarg.arg_type() { 24 ArgType::Array => { 25 if refarg.signature().starts_with("a{") { 26 // This is a dictionary 27 let mut record = Record::new(); 28 let mut iter = refarg.as_iter().unwrap(); 29 while let Some(key) = iter.next() { 30 if let Some(val) = iter.next() { 31 if let Some(key_str) = key.as_str() { 32 record.insert(key_str, from_refarg(val, span)?); 33 } 34 } 35 } 36 Value::record(record, span) 37 } else if &*refarg.signature() == "ay" { 38 // Byte array - better to return as binary 39 let bytes = dbus::arg::cast::<Vec<u8>>(&refarg.box_clone()) 40 .unwrap() 41 .to_owned(); 42 Value::binary(bytes, span) 43 } else { 44 // It's an array 45 Value::list( 46 refarg 47 .as_iter() 48 .unwrap() 49 .flat_map(|v| from_refarg(v, span)) 50 .collect(), 51 span, 52 ) 53 } 54 } 55 ArgType::Variant => { 56 let inner = refarg.as_iter().unwrap().next().unwrap(); 57 return from_refarg(inner, span); 58 } 59 ArgType::Boolean => Value::bool(refarg.as_i64().unwrap() != 0, span), 60 61 // Strings 62 ArgType::String | ArgType::ObjectPath | ArgType::Signature => { 63 Value::string(refarg.as_str().unwrap(), span) 64 } 65 // Ints 66 ArgType::Byte 67 | ArgType::Int16 68 | ArgType::UInt16 69 | ArgType::Int32 70 | ArgType::UInt32 71 | ArgType::Int64 72 | ArgType::UnixFd => Value::int(refarg.as_i64().unwrap(), span), 73 74 // Nushell doesn't support u64, so present it as a string 75 ArgType::UInt64 => Value::string(refarg.as_u64().unwrap().to_string(), span), 76 77 // Floats 78 ArgType::Double => Value::float(refarg.as_f64().unwrap(), span), 79 80 ArgType::Struct => Value::list( 81 refarg 82 .as_iter() 83 .unwrap() 84 .flat_map(|v| from_refarg(v, span)) 85 .collect(), 86 span, 87 ), 88 89 ArgType::DictEntry => { 90 return Err("Encountered dictionary entry outside of dictionary".into()); 91 } 92 ArgType::Invalid => return Err("Encountered invalid D-Bus value".into()), 93 }) 94} 95 96pub fn to_message_item( 97 value: &Value, 98 expected_type: Option<&DbusType>, 99) -> Result<MessageItem, LabeledError> { 100 // Report errors from conversion. Error must support Display 101 macro_rules! try_convert { 102 ($result_expr:expr) => { 103 $result_expr.map_err(|err| { 104 LabeledError::new(format!( 105 "Failed to convert value to the D-Bus `{:?}` type", 106 expected_type.unwrap() 107 )) 108 .with_label(err.to_string(), value.span()) 109 })? 110 }; 111 } 112 113 // Try to match values to expected types 114 match (value, expected_type) { 115 // Boolean 116 (Value::Bool { val, .. }, Some(DbusType::Boolean)) => Ok(MessageItem::Bool(*val)), 117 118 // Strings and specialized strings 119 (Value::String { val, .. }, Some(DbusType::String)) => Ok(MessageItem::Str(val.to_owned())), 120 (Value::String { val, .. }, Some(DbusType::ObjectPath)) => Ok(MessageItem::ObjectPath( 121 try_convert!(dbus::strings::Path::new(val)), 122 )), 123 (Value::String { val, .. }, Some(DbusType::Signature)) => Ok(MessageItem::Signature( 124 try_convert!(dbus::strings::Signature::new(val)), 125 )), 126 127 // Signed ints 128 (Value::Int { val, .. }, Some(DbusType::Int64)) => Ok(MessageItem::Int64(*val)), 129 (Value::Int { val, .. }, Some(DbusType::Int32)) => { 130 Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))) 131 } 132 (Value::Int { val, .. }, Some(DbusType::Int16)) => { 133 Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))) 134 } 135 136 // Unsigned ints 137 (Value::Int { val, .. }, Some(DbusType::UInt64)) => { 138 Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))) 139 } 140 (Value::Int { val, .. }, Some(DbusType::UInt32)) => { 141 Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))) 142 } 143 (Value::Int { val, .. }, Some(DbusType::UInt16)) => { 144 Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))) 145 } 146 (Value::Int { val, .. }, Some(DbusType::Byte)) => { 147 Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))) 148 } 149 150 // Ints from string 151 (Value::String { val, .. }, Some(DbusType::Int64)) => { 152 Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))) 153 } 154 (Value::String { val, .. }, Some(DbusType::Int32)) => { 155 Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))) 156 } 157 (Value::String { val, .. }, Some(DbusType::Int16)) => { 158 Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))) 159 } 160 (Value::String { val, .. }, Some(DbusType::UInt64)) => { 161 Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))) 162 } 163 (Value::String { val, .. }, Some(DbusType::UInt32)) => { 164 Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))) 165 } 166 (Value::String { val, .. }, Some(DbusType::UInt16)) => { 167 Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))) 168 } 169 (Value::String { val, .. }, Some(DbusType::Byte)) => { 170 Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))) 171 } 172 173 // Float 174 (Value::Float { val, .. }, Some(DbusType::Double)) => Ok(MessageItem::Double(*val)), 175 (Value::String { val, .. }, Some(DbusType::Double)) => { 176 Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))) 177 } 178 179 // Binary 180 (Value::Binary { val, .. }, Some(r#type @ DbusType::Array(content_type))) 181 if matches!(**content_type, DbusType::Byte) => 182 { 183 // FIXME: this is likely pretty inefficient for a bunch of bytes 184 let sig = Signature::from(r#type.stringify()); 185 let items = val 186 .iter() 187 .cloned() 188 .map(MessageItem::Byte) 189 .collect::<Vec<_>>(); 190 Ok(MessageItem::Array( 191 MessageItemArray::new(items, sig).unwrap(), 192 )) 193 } 194 195 // List/array 196 (Value::List { vals, .. }, Some(r#type @ DbusType::Array(content_type))) => { 197 let sig = Signature::from(r#type.stringify()); 198 let items = vals 199 .iter() 200 .map(|content| to_message_item(content, Some(content_type))) 201 .collect::<Result<Vec<MessageItem>, _>>()?; 202 Ok(MessageItem::Array( 203 MessageItemArray::new(items, sig).unwrap(), 204 )) 205 } 206 207 // Struct 208 (Value::List { vals, .. }, Some(DbusType::Struct(types))) => { 209 if vals.len() != types.len() { 210 return Err(LabeledError::new(format!( 211 "expected struct with {} element(s) ({:?})", 212 types.len(), 213 types 214 )) 215 .with_label( 216 format!("this list has {} element(s) instead", vals.len()), 217 value.span(), 218 )); 219 } 220 let items = vals 221 .iter() 222 .zip(types) 223 .map(|(content, r#type)| to_message_item(content, Some(r#type))) 224 .collect::<Result<Vec<MessageItem>, _>>()?; 225 Ok(MessageItem::Struct(items)) 226 } 227 228 // Record/dict 229 (Value::Record { val, .. }, Some(DbusType::Array(content_type))) 230 if matches!(**content_type, DbusType::DictEntry(_, _)) => 231 { 232 if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type { 233 let key_sig = Signature::from(key_type.stringify()); 234 let val_sig = Signature::from(val_type.stringify()); 235 let pairs = val 236 .iter() 237 .map(|(key, val)| { 238 let key_as_value = Value::string(key, value.span()); 239 let key_message_item = to_message_item(&key_as_value, Some(key_type))?; 240 let val_message_item = to_message_item(val, Some(val_type))?; 241 Ok((key_message_item, val_message_item)) 242 }) 243 .collect::<Result<Vec<_>, LabeledError>>()?; 244 Ok(MessageItem::Dict( 245 MessageItemDict::new(pairs, key_sig, val_sig).unwrap(), 246 )) 247 } else { 248 unreachable!() 249 } 250 } 251 252 // Variant - use automatic type 253 (other_value, Some(DbusType::Variant)) => Ok(MessageItem::Variant(Box::new( 254 to_message_item(other_value, None)?, 255 ))), 256 257 // Value not compatible with expected type 258 (other_value, Some(expectation)) => Err(LabeledError::new(format!( 259 "`{}` can not be converted to the D-Bus `{:?}` type", 260 other_value.get_type(), 261 expectation 262 )) 263 .with_label( 264 format!("expected a `{:?}` here", expectation), 265 other_value.span(), 266 )), 267 268 // Automatic types (with no type expectation) 269 (Value::String { .. }, None) => to_message_item(value, Some(&DbusType::String)), 270 (Value::Int { .. }, None) => to_message_item(value, Some(&DbusType::Int64)), 271 (Value::Float { .. }, None) => to_message_item(value, Some(&DbusType::Double)), 272 (Value::Bool { .. }, None) => to_message_item(value, Some(&DbusType::Boolean)), 273 (Value::List { .. }, None) => { 274 to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))) 275 } 276 (Value::Record { .. }, None) => to_message_item( 277 value, 278 Some(&DbusType::Array( 279 DbusType::DictEntry(DbusType::String.into(), DbusType::Variant.into()).into(), 280 )), 281 ), 282 283 // No expected type, but can't handle this type 284 _ => Err(LabeledError::new(format!( 285 "can not use values of type `{}` in D-Bus calls", 286 value.get_type() 287 )) 288 .with_label("use a supported type here instead", value.span())), 289 } 290}