Nushell plugin for interacting with D-Bus
0
fork

Configure Feed

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

at 7068e7598ea7bc117bb332430f81bcb05b482e53 238 lines 11 kB view raw
1use dbus::{Message, arg::{ArgType, RefArg, messageitem::{MessageItemArray, MessageItem, MessageItemDict}}, Signature}; 2use nu_plugin::LabeledError; 3use nu_protocol::{Value, Span, Record}; 4use std::str::FromStr; 5 6use crate::dbus_type::DbusType; 7 8/// Get the arguments of a message as nushell Values 9pub fn from_message(message: &Message, span: Span) -> Result<Vec<Value>, String> { 10 let mut out = vec![]; 11 for refarg in message.iter_init() { 12 out.push(from_refarg(&refarg, span)?); 13 } 14 Ok(out) 15} 16 17pub fn from_refarg(refarg: &dyn RefArg, span: Span) -> Result<Value, String> { 18 Ok(match refarg.arg_type() { 19 ArgType::Array => { 20 if refarg.signature().starts_with("a{") { 21 // This is a dictionary 22 let mut record = Record::new(); 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, span)?); 28 } 29 } 30 } 31 Value::record(record, span) 32 } else if &*refarg.signature() == "ay" { 33 // Byte array - better to return as binary 34 let bytes = dbus::arg::cast::<Vec<u8>>(&refarg.box_clone()).unwrap().to_owned(); 35 Value::binary(bytes, span) 36 } else { 37 // It's an array 38 Value::list( 39 refarg.as_iter().unwrap().map(|v| from_refarg(v, span)).flatten().collect(), 40 span) 41 } 42 }, 43 ArgType::Variant => { 44 let inner = refarg.as_iter().unwrap().nth(0).unwrap(); 45 return from_refarg(inner, span); 46 }, 47 ArgType::Boolean => 48 Value::bool(refarg.as_i64().unwrap() != 0, span), 49 50 // Strings 51 ArgType::String | ArgType::ObjectPath | ArgType::Signature => 52 Value::string(refarg.as_str().unwrap(), span), 53 // Ints 54 ArgType::Byte | ArgType::Int16 | ArgType::UInt16 | ArgType::Int32 | 55 ArgType::UInt32 | ArgType::Int64 | ArgType::UnixFd => 56 Value::int(refarg.as_i64().unwrap(), span), 57 58 // Nushell doesn't support u64, so present it as a string 59 ArgType::UInt64 => Value::string(refarg.as_u64().unwrap().to_string(), span), 60 61 // Floats 62 ArgType::Double => 63 Value::float(refarg.as_f64().unwrap(), span), 64 65 ArgType::Struct => 66 Value::list( 67 refarg.as_iter().unwrap().map(|v| from_refarg(v, span)).flatten().collect(), 68 span), 69 70 ArgType::DictEntry => 71 return Err("Encountered dictionary entry outside of dictionary".into()), 72 ArgType::Invalid => 73 return Err("Encountered invalid D-Bus value".into()), 74 }) 75} 76 77pub 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 // Binary 145 (Value::Binary { val, .. }, Some(r#type @ DbusType::Array(content_type))) 146 if matches!(**content_type, DbusType::Byte) => 147 { 148 // FIXME: this is likely pretty inefficient for a bunch of bytes 149 let sig = Signature::from(r#type.stringify()); 150 let items = val.iter().cloned().map(MessageItem::Byte).collect::<Vec<_>>(); 151 Ok(MessageItem::Array(MessageItemArray::new(items, sig).unwrap())) 152 }, 153 154 // List/array 155 (Value::List { vals, .. }, Some(r#type @ DbusType::Array(content_type))) => { 156 let sig = Signature::from(r#type.stringify()); 157 let items = vals.iter() 158 .map(|content| to_message_item(content, Some(content_type))) 159 .collect::<Result<Vec<MessageItem>, _>>()?; 160 Ok(MessageItem::Array(MessageItemArray::new(items, sig).unwrap())) 161 }, 162 163 // Struct 164 (Value::List { vals, .. }, Some(DbusType::Struct(types))) => { 165 if vals.len() != types.len() { 166 return Err(LabeledError { 167 label: format!("expected struct with {} element(s) ({:?})", types.len(), types), 168 msg: format!("this list has {} element(s) instead", vals.len()), 169 span: Some(value.span()) 170 }); 171 } 172 let items = vals.iter().zip(types) 173 .map(|(content, r#type)| to_message_item(content, Some(r#type))) 174 .collect::<Result<Vec<MessageItem>, _>>()?; 175 Ok(MessageItem::Struct(items)) 176 }, 177 178 // Record/dict 179 (Value::Record { val, .. }, Some(DbusType::Array(content_type))) 180 if matches!(**content_type, DbusType::DictEntry(_, _)) => 181 { 182 if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type { 183 let key_sig = Signature::from(key_type.stringify()); 184 let val_sig = Signature::from(val_type.stringify()); 185 let pairs = val.iter() 186 .map(|(key, val)| { 187 let key_as_value = Value::string(key, value.span()); 188 let key_message_item = to_message_item(&key_as_value, Some(key_type))?; 189 let val_message_item = to_message_item(val, Some(val_type))?; 190 Ok((key_message_item, val_message_item)) 191 }) 192 .collect::<Result<Vec<_>, LabeledError>>()?; 193 Ok(MessageItem::Dict(MessageItemDict::new(pairs, key_sig, val_sig).unwrap())) 194 } else { 195 unreachable!() 196 } 197 }, 198 199 // Variant - use automatic type 200 (other_value, Some(DbusType::Variant)) => 201 Ok(MessageItem::Variant(Box::new(to_message_item(other_value, None)?))), 202 203 // Value not compatible with expected type 204 (other_value, Some(expectation)) => 205 Err(LabeledError { 206 label: format!("`{}` can not be converted to the D-Bus `{:?}` type", 207 other_value.get_type(), expectation), 208 msg: format!("expected a `{:?}` here", expectation), 209 span: Some(other_value.span()), 210 }), 211 212 // Automatic types (with no type expectation) 213 (Value::String { .. }, None) => 214 to_message_item(value, Some(&DbusType::String)), 215 (Value::Int { .. }, None) => 216 to_message_item(value, Some(&DbusType::Int64)), 217 (Value::Float { .. }, None) => 218 to_message_item(value, Some(&DbusType::Double)), 219 (Value::Bool { .. }, None) => 220 to_message_item(value, Some(&DbusType::Boolean)), 221 (Value::List { .. }, None) => 222 to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))), 223 (Value::Record { .. }, None) => 224 to_message_item(value, Some(&DbusType::Array( 225 DbusType::DictEntry( 226 DbusType::String.into(), 227 DbusType::Variant.into() 228 ).into()))), 229 230 // No expected type, but can't handle this type 231 _ => 232 Err(LabeledError { 233 label: format!("can not use values of type `{}` in D-Bus calls", value.get_type()), 234 msg: "use a supported type here instead".into(), 235 span: Some(value.span()), 236 }) 237 } 238}