Nushell plugin for interacting with D-Bus

implement clippy, formatting suggestions

+795 -515
+146 -86
src/client.rs
··· 1 - use dbus::{channel::{Channel, BusType}, Message, arg::messageitem::MessageItem}; 2 use nu_plugin::LabeledError; 3 use nu_protocol::{Spanned, Value}; 4 5 - use crate::{config::{DbusClientConfig, DbusBusChoice}, dbus_type::DbusType, convert::to_message_item, introspection::Node, pattern::Pattern}; 6 7 /// Executes D-Bus actions on a connection, handling nushell types 8 pub struct DbusClient { ··· 12 13 // Convenience macros for error handling 14 macro_rules! validate_with { 15 - ($type:ty, $spanned:expr) => (<$type>::new(&$spanned.item).map_err(|msg| { 16 - LabeledError { 17 label: msg, 18 msg: "this argument is incorrect".into(), 19 span: Some($spanned.span), 20 - } 21 - })) 22 } 23 24 impl DbusClient { ··· 33 ch.register()?; 34 Ok(ch) 35 }), 36 - }.map_err(|err| { 37 - LabeledError { 38 - label: err.to_string(), 39 - msg: "while connecting to D-Bus as specified here".into(), 40 - span: Some(config.bus_choice.span), 41 - } 42 })?; 43 Ok(DbusClient { 44 config, 45 - conn: channel 46 }) 47 } 48 ··· 50 LabeledError { 51 label: err.to_string(), 52 msg: msg.to_string(), 53 - span: Some(self.config.span) 54 } 55 } 56 ··· 69 valid_dest, 70 valid_object, 71 "org.freedesktop.DBus.Introspectable", 72 - "Introspect" 73 - ).map_err(|err| self.error(err, context))?; 74 75 // Send and get the response 76 - let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item) 77 .map_err(|err| self.error(err, context))?; 78 79 // Parse it to a Node 80 - let xml: &str = resp.get1() 81 .ok_or_else(|| self.error("Introspect method returned the wrong type", context))?; 82 83 Node::from_xml(xml).map_err(|err| self.error(err, context)) ··· 95 96 if let Some(sig) = node.get_method_args_signature(&interface.item, &method.item) { 97 DbusType::parse_all(&sig).map_err(|err| LabeledError { 98 - label: format!("while getting interface {:?} method {:?} signature: {}", 99 - interface.item, 100 - method.item, 101 - err), 102 msg: "try running with --no-introspect or --signature".into(), 103 span: Some(self.config.span), 104 }) ··· 122 let node = self.introspect(dest, object)?; 123 124 if let Some(sig) = node.get_property_signature(&interface.item, &property.item) { 125 - DbusType::parse_all(&sig).map_err(|err| LabeledError { 126 - label: format!("while getting interface {:?} property {:?} signature: {}", 127 - interface.item, 128 - property.item, 129 - err), 130 msg: "try running with --no-introspect or --signature".into(), 131 span: Some(self.config.span), 132 }) 133 } else { 134 Err(LabeledError { 135 - label: format!("Property {:?} not found on {:?}", property.item, interface.item), 136 msg: "check that this property/interface is correct".into(), 137 span: Some(property.span), 138 }) ··· 158 let valid_method = validate_with!(dbus::strings::Member, method)?; 159 160 // Parse the signature 161 - let mut valid_signature = signature.map(|s| DbusType::parse_all(&s.item).map_err(|err| { 162 - LabeledError { 163 - label: err, 164 - msg: "in signature specified here".into(), 165 - span: Some(s.span), 166 - } 167 - })).transpose()?; 168 169 // If not provided, try introspection (unless disabled) 170 if valid_signature.is_none() && self.config.introspect { 171 match self.get_method_signature_by_introspection(dest, object, interface, method) { 172 Ok(sig) => { 173 valid_signature = Some(sig); 174 - }, 175 Err(err) => { 176 - eprintln!("Warning: D-Bus introspection failed on {:?}. \ 177 Use `--no-introspect` or pass `--signature` to silence this warning. \ 178 Cause: {}", 179 - object.item, 180 - err.label); 181 } 182 } 183 } 184 185 if let Some(sig) = &valid_signature { 186 if sig.len() != args.len() { 187 - self.error(format!("expected {} arguments, got {}", sig.len(), args.len()), context); 188 } 189 } 190 191 // Construct the method call message 192 - let mut message = Message::new_method_call( 193 - valid_dest, 194 - valid_object, 195 - valid_interface, 196 - valid_method, 197 - ).map_err(|err| self.error(err, context))?; 198 199 // Convert the args to message items 200 - let sigs_iter = valid_signature.iter().flatten().map(Some).chain(std::iter::repeat(None)); 201 for (val, sig) in args.iter().zip(sigs_iter) { 202 message = message.append1(to_message_item(val, sig)?); 203 } 204 205 // Send it on the channel and get the response 206 - let resp = self.conn.send_with_reply_and_block(message, self.config.timeout.item) 207 .map_err(|err| self.error(err, context))?; 208 209 crate::convert::from_message(&resp, self.config.span) ··· 224 self.call( 225 dest, 226 object, 227 - &Spanned { item: "org.freedesktop.DBus.Properties".into(), span: self.config.span }, 228 - &Spanned { item: "Get".into(), span: self.config.span }, 229 - Some(&Spanned { item: "ss".into(), span: self.config.span }), 230 - &[interface_val, property_val] 231 - ).map(|val| val.into_iter().nth(0).unwrap_or_default()) 232 } 233 234 /// Get all D-Bus properties from the given object ··· 243 self.call( 244 dest, 245 object, 246 - &Spanned { item: "org.freedesktop.DBus.Properties".into(), span: self.config.span }, 247 - &Spanned { item: "GetAll".into(), span: self.config.span }, 248 - Some(&Spanned { item: "s".into(), span: self.config.span }), 249 - &[interface_val] 250 - ).map(|val| val.into_iter().nth(0).unwrap_or_default()) 251 } 252 253 /// Set a D-Bus property on the given object ··· 267 let valid_object = validate_with!(dbus::strings::Path, object)?; 268 269 // Parse the signature 270 - let mut valid_signature = signature.map(|s| DbusType::parse_all(&s.item).map_err(|err| { 271 - LabeledError { 272 - label: err, 273 - msg: "in signature specified here".into(), 274 - span: Some(s.span), 275 - } 276 - })).transpose()?; 277 278 // If not provided, try introspection (unless disabled) 279 if valid_signature.is_none() && self.config.introspect { 280 match self.get_property_signature_by_introspection(dest, object, interface, property) { 281 Ok(sig) => { 282 valid_signature = Some(sig); 283 - }, 284 Err(err) => { 285 - eprintln!("Warning: D-Bus introspection failed on {:?}. \ 286 Use `--no-introspect` or pass `--signature` to silence this warning. \ 287 Cause: {}", 288 - object.item, 289 - err.label); 290 } 291 } 292 } 293 294 if let Some(sig) = &valid_signature { 295 if sig.len() != 1 { 296 - self.error(format!( 297 - "expected single object signature, but there are {}", sig.len()), context); 298 } 299 } 300 ··· 304 valid_object, 305 "org.freedesktop.DBus.Properties", 306 "Set", 307 - ).map_err(|err| self.error(err, context))? 308 - .append2(&interface.item, &property.item) 309 - .append1( 310 - // Box it in a variant as required for property setting 311 - MessageItem::Variant(Box::new( 312 - to_message_item(value, valid_signature.as_ref().map(|s| &s[0]))?)) 313 - ); 314 315 // Send it on the channel and get the response 316 - self.conn.send_with_reply_and_block(message, self.config.timeout.item) 317 .map_err(|err| self.error(err, context))?; 318 319 Ok(()) 320 } 321 322 - pub fn list(&self, pattern: Option<&Pattern>) 323 - -> Result<Vec<String>, LabeledError> 324 - { 325 let context = "while listing D-Bus connection names"; 326 327 let message = Message::new_method_call( 328 "org.freedesktop.DBus", 329 "/org/freedesktop/DBus", 330 "org.freedesktop.DBus", 331 - "ListNames" 332 - ).map_err(|err| self.error(err, context))?; 333 334 - self.conn.send_with_reply_and_block(message, self.config.timeout.item) 335 .map_err(|err| self.error(err, context)) 336 .and_then(|reply| reply.read1().map_err(|err| self.error(err, context))) 337 .map(|names: Vec<String>| { 338 // Filter the names by the pattern 339 if let Some(pattern) = pattern { 340 - names.into_iter().filter(|name| pattern.is_match(name)).collect() 341 } else { 342 names 343 }
··· 1 + use dbus::{ 2 + arg::messageitem::MessageItem, 3 + channel::{BusType, Channel}, 4 + Message, 5 + }; 6 use nu_plugin::LabeledError; 7 use nu_protocol::{Spanned, Value}; 8 9 + use crate::{ 10 + config::{DbusBusChoice, DbusClientConfig}, 11 + convert::to_message_item, 12 + dbus_type::DbusType, 13 + introspection::Node, 14 + pattern::Pattern, 15 + }; 16 17 /// Executes D-Bus actions on a connection, handling nushell types 18 pub struct DbusClient { ··· 22 23 // Convenience macros for error handling 24 macro_rules! validate_with { 25 + ($type:ty, $spanned:expr) => { 26 + <$type>::new(&$spanned.item).map_err(|msg| LabeledError { 27 label: msg, 28 msg: "this argument is incorrect".into(), 29 span: Some($spanned.span), 30 + }) 31 + }; 32 } 33 34 impl DbusClient { ··· 43 ch.register()?; 44 Ok(ch) 45 }), 46 + } 47 + .map_err(|err| LabeledError { 48 + label: err.to_string(), 49 + msg: "while connecting to D-Bus as specified here".into(), 50 + span: Some(config.bus_choice.span), 51 })?; 52 Ok(DbusClient { 53 config, 54 + conn: channel, 55 }) 56 } 57 ··· 59 LabeledError { 60 label: err.to_string(), 61 msg: msg.to_string(), 62 + span: Some(self.config.span), 63 } 64 } 65 ··· 78 valid_dest, 79 valid_object, 80 "org.freedesktop.DBus.Introspectable", 81 + "Introspect", 82 + ) 83 + .map_err(|err| self.error(err, context))?; 84 85 // Send and get the response 86 + let resp = self 87 + .conn 88 + .send_with_reply_and_block(message, self.config.timeout.item) 89 .map_err(|err| self.error(err, context))?; 90 91 // Parse it to a Node 92 + let xml: &str = resp 93 + .get1() 94 .ok_or_else(|| self.error("Introspect method returned the wrong type", context))?; 95 96 Node::from_xml(xml).map_err(|err| self.error(err, context)) ··· 108 109 if let Some(sig) = node.get_method_args_signature(&interface.item, &method.item) { 110 DbusType::parse_all(&sig).map_err(|err| LabeledError { 111 + label: format!( 112 + "while getting interface {:?} method {:?} signature: {}", 113 + interface.item, method.item, err 114 + ), 115 msg: "try running with --no-introspect or --signature".into(), 116 span: Some(self.config.span), 117 }) ··· 135 let node = self.introspect(dest, object)?; 136 137 if let Some(sig) = node.get_property_signature(&interface.item, &property.item) { 138 + DbusType::parse_all(sig).map_err(|err| LabeledError { 139 + label: format!( 140 + "while getting interface {:?} property {:?} signature: {}", 141 + interface.item, property.item, err 142 + ), 143 msg: "try running with --no-introspect or --signature".into(), 144 span: Some(self.config.span), 145 }) 146 } else { 147 Err(LabeledError { 148 + label: format!( 149 + "Property {:?} not found on {:?}", 150 + property.item, interface.item 151 + ), 152 msg: "check that this property/interface is correct".into(), 153 span: Some(property.span), 154 }) ··· 174 let valid_method = validate_with!(dbus::strings::Member, method)?; 175 176 // Parse the signature 177 + let mut valid_signature = signature 178 + .map(|s| { 179 + DbusType::parse_all(&s.item).map_err(|err| LabeledError { 180 + label: err, 181 + msg: "in signature specified here".into(), 182 + span: Some(s.span), 183 + }) 184 + }) 185 + .transpose()?; 186 187 // If not provided, try introspection (unless disabled) 188 if valid_signature.is_none() && self.config.introspect { 189 match self.get_method_signature_by_introspection(dest, object, interface, method) { 190 Ok(sig) => { 191 valid_signature = Some(sig); 192 + } 193 Err(err) => { 194 + eprintln!( 195 + "Warning: D-Bus introspection failed on {:?}. \ 196 Use `--no-introspect` or pass `--signature` to silence this warning. \ 197 Cause: {}", 198 + object.item, err.label 199 + ); 200 } 201 } 202 } 203 204 if let Some(sig) = &valid_signature { 205 if sig.len() != args.len() { 206 + self.error( 207 + format!("expected {} arguments, got {}", sig.len(), args.len()), 208 + context, 209 + ); 210 } 211 } 212 213 // Construct the method call message 214 + let mut message = 215 + Message::new_method_call(valid_dest, valid_object, valid_interface, valid_method) 216 + .map_err(|err| self.error(err, context))?; 217 218 // Convert the args to message items 219 + let sigs_iter = valid_signature 220 + .iter() 221 + .flatten() 222 + .map(Some) 223 + .chain(std::iter::repeat(None)); 224 for (val, sig) in args.iter().zip(sigs_iter) { 225 message = message.append1(to_message_item(val, sig)?); 226 } 227 228 // Send it on the channel and get the response 229 + let resp = self 230 + .conn 231 + .send_with_reply_and_block(message, self.config.timeout.item) 232 .map_err(|err| self.error(err, context))?; 233 234 crate::convert::from_message(&resp, self.config.span) ··· 249 self.call( 250 dest, 251 object, 252 + &Spanned { 253 + item: "org.freedesktop.DBus.Properties".into(), 254 + span: self.config.span, 255 + }, 256 + &Spanned { 257 + item: "Get".into(), 258 + span: self.config.span, 259 + }, 260 + Some(&Spanned { 261 + item: "ss".into(), 262 + span: self.config.span, 263 + }), 264 + &[interface_val, property_val], 265 + ) 266 + .map(|val| val.into_iter().nth(0).unwrap_or_default()) 267 } 268 269 /// Get all D-Bus properties from the given object ··· 278 self.call( 279 dest, 280 object, 281 + &Spanned { 282 + item: "org.freedesktop.DBus.Properties".into(), 283 + span: self.config.span, 284 + }, 285 + &Spanned { 286 + item: "GetAll".into(), 287 + span: self.config.span, 288 + }, 289 + Some(&Spanned { 290 + item: "s".into(), 291 + span: self.config.span, 292 + }), 293 + &[interface_val], 294 + ) 295 + .map(|val| val.into_iter().nth(0).unwrap_or_default()) 296 } 297 298 /// Set a D-Bus property on the given object ··· 312 let valid_object = validate_with!(dbus::strings::Path, object)?; 313 314 // Parse the signature 315 + let mut valid_signature = signature 316 + .map(|s| { 317 + DbusType::parse_all(&s.item).map_err(|err| LabeledError { 318 + label: err, 319 + msg: "in signature specified here".into(), 320 + span: Some(s.span), 321 + }) 322 + }) 323 + .transpose()?; 324 325 // If not provided, try introspection (unless disabled) 326 if valid_signature.is_none() && self.config.introspect { 327 match self.get_property_signature_by_introspection(dest, object, interface, property) { 328 Ok(sig) => { 329 valid_signature = Some(sig); 330 + } 331 Err(err) => { 332 + eprintln!( 333 + "Warning: D-Bus introspection failed on {:?}. \ 334 Use `--no-introspect` or pass `--signature` to silence this warning. \ 335 Cause: {}", 336 + object.item, err.label 337 + ); 338 } 339 } 340 } 341 342 if let Some(sig) = &valid_signature { 343 if sig.len() != 1 { 344 + self.error( 345 + format!( 346 + "expected single object signature, but there are {}", 347 + sig.len() 348 + ), 349 + context, 350 + ); 351 } 352 } 353 ··· 357 valid_object, 358 "org.freedesktop.DBus.Properties", 359 "Set", 360 + ) 361 + .map_err(|err| self.error(err, context))? 362 + .append2(&interface.item, &property.item) 363 + .append1( 364 + // Box it in a variant as required for property setting 365 + MessageItem::Variant(Box::new(to_message_item( 366 + value, 367 + valid_signature.as_ref().map(|s| &s[0]), 368 + )?)), 369 + ); 370 371 // Send it on the channel and get the response 372 + self.conn 373 + .send_with_reply_and_block(message, self.config.timeout.item) 374 .map_err(|err| self.error(err, context))?; 375 376 Ok(()) 377 } 378 379 + pub fn list(&self, pattern: Option<&Pattern>) -> Result<Vec<String>, LabeledError> { 380 let context = "while listing D-Bus connection names"; 381 382 let message = Message::new_method_call( 383 "org.freedesktop.DBus", 384 "/org/freedesktop/DBus", 385 "org.freedesktop.DBus", 386 + "ListNames", 387 + ) 388 + .map_err(|err| self.error(err, context))?; 389 390 + self.conn 391 + .send_with_reply_and_block(message, self.config.timeout.item) 392 .map_err(|err| self.error(err, context)) 393 .and_then(|reply| reply.read1().map_err(|err| self.error(err, context))) 394 .map(|names: Vec<String>| { 395 // Filter the names by the pattern 396 if let Some(pattern) = pattern { 397 + names 398 + .into_iter() 399 + .filter(|name| pattern.is_match(name)) 400 + .collect() 401 } else { 402 names 403 }
+33 -18
src/config.rs
··· 1 use std::time::Duration; 2 3 use nu_plugin::{EvaluatedCall, LabeledError}; 4 - use nu_protocol::{Spanned, Span}; 5 6 /// General configuration related to the D-Bus client connection 7 #[derive(Debug, Clone)] ··· 37 fn try_from(call: &EvaluatedCall) -> Result<Self, Self::Error> { 38 let mut config = DbusClientConfig { 39 span: call.head, 40 - bus_choice: Spanned { item: DbusBusChoice::default(), span: call.head }, 41 - timeout: Spanned { item: Duration::from_secs(2), span: call.head }, 42 introspect: true, 43 }; 44 ··· 51 "session" => DbusBusChoice::Session, 52 "system" => DbusBusChoice::System, 53 "started" => DbusBusChoice::Started, 54 - _ => unreachable!() 55 }; 56 - config.bus_choice = Spanned { item: dest, span: name.span }; 57 } 58 - }, 59 r#type @ ("bus" | "peer") => { 60 if let Some(value) = value { 61 let address = value.as_str()?; 62 let dest = match r#type { 63 "bus" => DbusBusChoice::Bus(address.to_owned()), 64 "peer" => DbusBusChoice::Peer(address.to_owned()), 65 - _ => unreachable!() 66 }; 67 - config.bus_choice = Spanned { item: dest, span: value.span() }; 68 } 69 - }, 70 "timeout" => { 71 if let Some(value) = value { 72 - let nanos: u64 = value.as_duration()?.try_into().map_err(|_| { 73 - LabeledError { 74 label: "Timeout must be a positive duration".into(), 75 msg: "invalid timeout specified here".into(), 76 span: Some(value.span()), 77 - } 78 - })?; 79 let item = Duration::from_nanos(nanos); 80 - config.timeout = Spanned { item, span: value.span() }; 81 } 82 - }, 83 "no-introspect" => { 84 - config.introspect = !value.as_ref() 85 .and_then(|v| v.as_bool().ok()) 86 .unwrap_or(false); 87 - }, 88 - _ => () 89 } 90 } 91
··· 1 use std::time::Duration; 2 3 use nu_plugin::{EvaluatedCall, LabeledError}; 4 + use nu_protocol::{Span, Spanned}; 5 6 /// General configuration related to the D-Bus client connection 7 #[derive(Debug, Clone)] ··· 37 fn try_from(call: &EvaluatedCall) -> Result<Self, Self::Error> { 38 let mut config = DbusClientConfig { 39 span: call.head, 40 + bus_choice: Spanned { 41 + item: DbusBusChoice::default(), 42 + span: call.head, 43 + }, 44 + timeout: Spanned { 45 + item: Duration::from_secs(2), 46 + span: call.head, 47 + }, 48 introspect: true, 49 }; 50 ··· 57 "session" => DbusBusChoice::Session, 58 "system" => DbusBusChoice::System, 59 "started" => DbusBusChoice::Started, 60 + _ => unreachable!(), 61 }; 62 + config.bus_choice = Spanned { 63 + item: dest, 64 + span: name.span, 65 + }; 66 } 67 + } 68 r#type @ ("bus" | "peer") => { 69 if let Some(value) = value { 70 let address = value.as_str()?; 71 let dest = match r#type { 72 "bus" => DbusBusChoice::Bus(address.to_owned()), 73 "peer" => DbusBusChoice::Peer(address.to_owned()), 74 + _ => unreachable!(), 75 + }; 76 + config.bus_choice = Spanned { 77 + item: dest, 78 + span: value.span(), 79 }; 80 } 81 + } 82 "timeout" => { 83 if let Some(value) = value { 84 + let nanos: u64 = 85 + value.as_duration()?.try_into().map_err(|_| LabeledError { 86 label: "Timeout must be a positive duration".into(), 87 msg: "invalid timeout specified here".into(), 88 span: Some(value.span()), 89 + })?; 90 let item = Duration::from_nanos(nanos); 91 + config.timeout = Spanned { 92 + item, 93 + span: value.span(), 94 + }; 95 } 96 + } 97 "no-introspect" => { 98 + config.introspect = !value 99 + .as_ref() 100 .and_then(|v| v.as_bool().ok()) 101 .unwrap_or(false); 102 + } 103 + _ => (), 104 } 105 } 106
+175 -118
src/convert.rs
··· 1 - use dbus::{Message, arg::{ArgType, RefArg, messageitem::{MessageItemArray, MessageItem, MessageItemDict}}, Signature}; 2 use nu_plugin::LabeledError; 3 - use nu_protocol::{Value, Span, Record}; 4 use std::str::FromStr; 5 6 use crate::dbus_type::DbusType; ··· 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 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 // Binary 145 (Value::Binary { val, .. }, Some(r#type @ DbusType::Array(content_type))) ··· 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))) ··· 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))?; ··· 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 }
··· 1 + use dbus::{ 2 + arg::{ 3 + messageitem::{MessageItem, MessageItemArray, MessageItemDict}, 4 + ArgType, RefArg, 5 + }, 6 + Message, Signature, 7 + }; 8 use nu_plugin::LabeledError; 9 + use nu_protocol::{Record, Span, Value}; 10 use std::str::FromStr; 11 12 use crate::dbus_type::DbusType; ··· 37 Value::record(record, span) 38 } else if &*refarg.signature() == "ay" { 39 // Byte array - better to return as binary 40 + let bytes = dbus::arg::cast::<Vec<u8>>(&refarg.box_clone()) 41 + .unwrap() 42 + .to_owned(); 43 Value::binary(bytes, span) 44 } else { 45 // It's an array 46 Value::list( 47 + refarg 48 + .as_iter() 49 + .unwrap() 50 + .flat_map(|v| from_refarg(v, span)) 51 + .collect(), 52 + span, 53 + ) 54 } 55 + } 56 ArgType::Variant => { 57 + let inner = refarg.as_iter().unwrap().next().unwrap(); 58 return from_refarg(inner, span); 59 + } 60 + ArgType::Boolean => Value::bool(refarg.as_i64().unwrap() != 0, span), 61 62 // Strings 63 + ArgType::String | ArgType::ObjectPath | ArgType::Signature => { 64 + Value::string(refarg.as_str().unwrap(), span) 65 + } 66 // Ints 67 + ArgType::Byte 68 + | ArgType::Int16 69 + | ArgType::UInt16 70 + | ArgType::Int32 71 + | ArgType::UInt32 72 + | ArgType::Int64 73 + | ArgType::UnixFd => Value::int(refarg.as_i64().unwrap(), span), 74 75 // Nushell doesn't support u64, so present it as a string 76 ArgType::UInt64 => Value::string(refarg.as_u64().unwrap().to_string(), span), 77 78 // Floats 79 + ArgType::Double => Value::float(refarg.as_f64().unwrap(), span), 80 81 + ArgType::Struct => Value::list( 82 + refarg 83 + .as_iter() 84 + .unwrap() 85 + .flat_map(|v| from_refarg(v, span)) 86 + .collect(), 87 + span, 88 + ), 89 90 + ArgType::DictEntry => { 91 + return Err("Encountered dictionary entry outside of dictionary".into()) 92 + } 93 + ArgType::Invalid => return Err("Encountered invalid D-Bus value".into()), 94 }) 95 } 96 97 + pub fn to_message_item( 98 + value: &Value, 99 + expected_type: Option<&DbusType>, 100 + ) -> Result<MessageItem, LabeledError> { 101 // Report errors from conversion. Error must support Display 102 macro_rules! try_convert { 103 + ($result_expr:expr) => { 104 + $result_expr.map_err(|err| LabeledError { 105 + label: format!( 106 + "Failed to convert value to the D-Bus `{:?}` type", 107 + expected_type.unwrap() 108 + ), 109 + msg: err.to_string(), 110 + span: Some(value.span()), 111 + })? 112 + }; 113 } 114 115 // Try to match values to expected types 116 match (value, expected_type) { 117 // Boolean 118 + (Value::Bool { val, .. }, Some(DbusType::Boolean)) => Ok(MessageItem::Bool(*val)), 119 120 // Strings and specialized strings 121 + (Value::String { val, .. }, Some(DbusType::String)) => Ok(MessageItem::Str(val.to_owned())), 122 + (Value::String { val, .. }, Some(DbusType::ObjectPath)) => Ok(MessageItem::ObjectPath( 123 + try_convert!(dbus::strings::Path::new(val)), 124 + )), 125 + (Value::String { val, .. }, Some(DbusType::Signature)) => Ok(MessageItem::Signature( 126 + try_convert!(dbus::strings::Signature::new(val)), 127 + )), 128 129 // Signed ints 130 + (Value::Int { val, .. }, Some(DbusType::Int64)) => Ok(MessageItem::Int64(*val)), 131 + (Value::Int { val, .. }, Some(DbusType::Int32)) => { 132 + Ok(MessageItem::Int32(try_convert!(i32::try_from(*val)))) 133 + } 134 + (Value::Int { val, .. }, Some(DbusType::Int16)) => { 135 + Ok(MessageItem::Int16(try_convert!(i16::try_from(*val)))) 136 + } 137 138 // Unsigned ints 139 + (Value::Int { val, .. }, Some(DbusType::UInt64)) => { 140 + Ok(MessageItem::UInt64(try_convert!(u64::try_from(*val)))) 141 + } 142 + (Value::Int { val, .. }, Some(DbusType::UInt32)) => { 143 + Ok(MessageItem::UInt32(try_convert!(u32::try_from(*val)))) 144 + } 145 + (Value::Int { val, .. }, Some(DbusType::UInt16)) => { 146 + Ok(MessageItem::UInt16(try_convert!(u16::try_from(*val)))) 147 + } 148 + (Value::Int { val, .. }, Some(DbusType::Byte)) => { 149 + Ok(MessageItem::Byte(try_convert!(u8::try_from(*val)))) 150 + } 151 152 // Ints from string 153 + (Value::String { val, .. }, Some(DbusType::Int64)) => { 154 + Ok(MessageItem::Int64(try_convert!(i64::from_str(&val[..])))) 155 + } 156 + (Value::String { val, .. }, Some(DbusType::Int32)) => { 157 + Ok(MessageItem::Int32(try_convert!(i32::from_str(&val[..])))) 158 + } 159 + (Value::String { val, .. }, Some(DbusType::Int16)) => { 160 + Ok(MessageItem::Int16(try_convert!(i16::from_str(&val[..])))) 161 + } 162 + (Value::String { val, .. }, Some(DbusType::UInt64)) => { 163 + Ok(MessageItem::UInt64(try_convert!(u64::from_str(&val[..])))) 164 + } 165 + (Value::String { val, .. }, Some(DbusType::UInt32)) => { 166 + Ok(MessageItem::UInt32(try_convert!(u32::from_str(&val[..])))) 167 + } 168 + (Value::String { val, .. }, Some(DbusType::UInt16)) => { 169 + Ok(MessageItem::UInt16(try_convert!(u16::from_str(&val[..])))) 170 + } 171 + (Value::String { val, .. }, Some(DbusType::Byte)) => { 172 + Ok(MessageItem::Byte(try_convert!(u8::from_str(&val[..])))) 173 + } 174 175 // Float 176 + (Value::Float { val, .. }, Some(DbusType::Double)) => Ok(MessageItem::Double(*val)), 177 + (Value::String { val, .. }, Some(DbusType::Double)) => { 178 + Ok(MessageItem::Double(try_convert!(f64::from_str(&val[..])))) 179 + } 180 181 // Binary 182 (Value::Binary { val, .. }, Some(r#type @ DbusType::Array(content_type))) ··· 184 { 185 // FIXME: this is likely pretty inefficient for a bunch of bytes 186 let sig = Signature::from(r#type.stringify()); 187 + let items = val 188 + .iter() 189 + .cloned() 190 + .map(MessageItem::Byte) 191 + .collect::<Vec<_>>(); 192 + Ok(MessageItem::Array( 193 + MessageItemArray::new(items, sig).unwrap(), 194 + )) 195 + } 196 197 // List/array 198 (Value::List { vals, .. }, Some(r#type @ DbusType::Array(content_type))) => { 199 let sig = Signature::from(r#type.stringify()); 200 + let items = vals 201 + .iter() 202 .map(|content| to_message_item(content, Some(content_type))) 203 .collect::<Result<Vec<MessageItem>, _>>()?; 204 + Ok(MessageItem::Array( 205 + MessageItemArray::new(items, sig).unwrap(), 206 + )) 207 + } 208 209 // Struct 210 (Value::List { vals, .. }, Some(DbusType::Struct(types))) => { 211 if vals.len() != types.len() { 212 return Err(LabeledError { 213 + label: format!( 214 + "expected struct with {} element(s) ({:?})", 215 + types.len(), 216 + types 217 + ), 218 msg: format!("this list has {} element(s) instead", vals.len()), 219 + span: Some(value.span()), 220 }); 221 } 222 + let items = vals 223 + .iter() 224 + .zip(types) 225 .map(|(content, r#type)| to_message_item(content, Some(r#type))) 226 .collect::<Result<Vec<MessageItem>, _>>()?; 227 Ok(MessageItem::Struct(items)) 228 + } 229 230 // Record/dict 231 (Value::Record { val, .. }, Some(DbusType::Array(content_type))) ··· 234 if let DbusType::DictEntry(ref key_type, ref val_type) = **content_type { 235 let key_sig = Signature::from(key_type.stringify()); 236 let val_sig = Signature::from(val_type.stringify()); 237 + let pairs = val 238 + .iter() 239 .map(|(key, val)| { 240 let key_as_value = Value::string(key, value.span()); 241 let key_message_item = to_message_item(&key_as_value, Some(key_type))?; ··· 243 Ok((key_message_item, val_message_item)) 244 }) 245 .collect::<Result<Vec<_>, LabeledError>>()?; 246 + Ok(MessageItem::Dict( 247 + MessageItemDict::new(pairs, key_sig, val_sig).unwrap(), 248 + )) 249 } else { 250 unreachable!() 251 } 252 + } 253 254 // Variant - use automatic type 255 + (other_value, Some(DbusType::Variant)) => Ok(MessageItem::Variant(Box::new( 256 + to_message_item(other_value, None)?, 257 + ))), 258 259 // Value not compatible with expected type 260 + (other_value, Some(expectation)) => Err(LabeledError { 261 + label: format!( 262 + "`{}` can not be converted to the D-Bus `{:?}` type", 263 + other_value.get_type(), 264 + expectation 265 + ), 266 + msg: format!("expected a `{:?}` here", expectation), 267 + span: Some(other_value.span()), 268 + }), 269 270 // Automatic types (with no type expectation) 271 + (Value::String { .. }, None) => to_message_item(value, Some(&DbusType::String)), 272 + (Value::Int { .. }, None) => to_message_item(value, Some(&DbusType::Int64)), 273 + (Value::Float { .. }, None) => to_message_item(value, Some(&DbusType::Double)), 274 + (Value::Bool { .. }, None) => to_message_item(value, Some(&DbusType::Boolean)), 275 + (Value::List { .. }, None) => { 276 + to_message_item(value, Some(&DbusType::Array(DbusType::Variant.into()))) 277 + } 278 + (Value::Record { .. }, None) => to_message_item( 279 + value, 280 + Some(&DbusType::Array( 281 + DbusType::DictEntry(DbusType::String.into(), DbusType::Variant.into()).into(), 282 + )), 283 + ), 284 285 // No expected type, but can't handle this type 286 + _ => Err(LabeledError { 287 + label: format!( 288 + "can not use values of type `{}` in D-Bus calls", 289 + value.get_type() 290 + ), 291 + msg: "use a supported type here instead".into(), 292 + span: Some(value.span()), 293 + }), 294 } 295 }
+72 -60
src/dbus_type.rs
··· 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..]; ··· 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 ··· 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()), ··· 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] ··· 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 ··· 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] ··· 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"), ··· 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] ··· 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] ··· 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 );
··· 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..]; ··· 56 loop { 57 if remainder.is_empty() { 58 break Err("unexpected end of D-Bus type string \ 59 + before end of array" 60 + .into()); 61 + } else if let Some(new_remainder) = remainder.strip_prefix(')') { 62 + break Ok((DbusType::Struct(types), new_remainder)); 63 } else { 64 let (r#type, new_remainder) = Self::parse(remainder)?; 65 types.push(r#type); 66 remainder = new_remainder; 67 } 68 } 69 + } 70 'v' => Ok((Variant, &input[1..])), 71 '{' => { 72 // Expect two types 73 let (key_type, key_remainder) = Self::parse(&input[1..])?; 74 let (val_type, val_remainder) = Self::parse(key_remainder)?; 75 // Must end with } 76 + if let Some(new_remainder) = val_remainder.strip_prefix('}') { 77 + Ok(( 78 + DbusType::DictEntry(key_type.into(), val_type.into()), 79 + new_remainder, 80 + )) 81 } else { 82 + Err(format!( 83 + "expected `}}` char to end dictionary in D-Bus type \ 84 + but remainder is {:?}", 85 + val_remainder 86 + )) 87 } 88 + } 89 + other => Err(format!( 90 + "unexpected char {other:?} in D-Bus type representation" 91 + )), 92 } 93 } 94 ··· 108 use self::DbusType::*; 109 110 match self { 111 + Byte => 'y'.into(), 112 + Boolean => 'b'.into(), 113 + Int16 => 'n'.into(), 114 + UInt16 => 'q'.into(), 115 + Int32 => 'i'.into(), 116 + UInt32 => 'u'.into(), 117 + Int64 => 'x'.into(), 118 + UInt64 => 't'.into(), 119 + Double => 'd'.into(), 120 + String => 's'.into(), 121 ObjectPath => 'o'.into(), 122 + Signature => 'g'.into(), 123 124 // a<type> 125 Array(content) => format!("a{}", content.stringify()), ··· 140 141 #[cfg(test)] 142 macro_rules! should_parse_to { 143 + ($str:expr, $result:expr) => { 144 assert_eq!(DbusType::parse($str), Ok(($result, ""))) 145 + }; 146 } 147 148 #[test] ··· 213 use self::DbusType::*; 214 should_parse_to!("((xx))", Struct(vec![Struct(vec![Int64, Int64])])); 215 should_parse_to!("(y(xx))", Struct(vec![Byte, Struct(vec![Int64, Int64])])); 216 + should_parse_to!( 217 + "(y(ss)o)", 218 + Struct(vec![Byte, Struct(vec![String, String]), ObjectPath]) 219 + ); 220 should_parse_to!("((yy)s)", Struct(vec![Struct(vec![Byte, Byte]), String])); 221 } 222 ··· 236 fn test_parse_dict_entry() { 237 use self::DbusType::*; 238 should_parse_to!("{ss}", DictEntry(String.into(), String.into())); 239 + should_parse_to!( 240 + "{s(bd)}", 241 + DictEntry(String.into(), Struct(vec![Boolean, Double]).into()) 242 + ); 243 } 244 245 #[test] 246 fn test_parse_array_dict() { 247 use self::DbusType::*; 248 + should_parse_to!( 249 + "a{sd}", 250 + Array(DictEntry(String.into(), Double.into()).into()) 251 + ); 252 } 253 254 #[test] ··· 267 fn test_parse_all() { 268 use self::DbusType::*; 269 assert_eq!(DbusType::parse_all(""), Ok(vec![])); 270 + assert_eq!(DbusType::parse_all("s"), Ok(vec![String,])); 271 assert_eq!( 272 DbusType::parse_all("isbb"), 273 + Ok(vec![Int32, String, Boolean, Boolean,]) 274 ); 275 assert_eq!( 276 DbusType::parse_all("ia{s(bi)}s"), ··· 284 285 #[cfg(test)] 286 macro_rules! should_stringify_to { 287 + ($type:expr, $result:expr) => { 288 assert_eq!(DbusType::stringify(&$type), $result) 289 + }; 290 } 291 292 #[test] 293 fn test_stringify_simple_types() { 294 use self::DbusType::*; 295 + should_stringify_to!(Byte, "y"); 296 + should_stringify_to!(Boolean, "b"); 297 + should_stringify_to!(Int16, "n"); 298 + should_stringify_to!(UInt16, "q"); 299 + should_stringify_to!(Int32, "i"); 300 + should_stringify_to!(UInt32, "u"); 301 + should_stringify_to!(Int64, "x"); 302 + should_stringify_to!(UInt64, "t"); 303 + should_stringify_to!(Double, "d"); 304 + should_stringify_to!(String, "s"); 305 should_stringify_to!(ObjectPath, "o"); 306 + should_stringify_to!(Signature, "g"); 307 + should_stringify_to!(Variant, "v"); 308 } 309 310 #[test] ··· 321 should_stringify_to!(Struct(vec![Int32]), "(i)"); 322 should_stringify_to!(Struct(vec![Int32, String]), "(is)"); 323 should_stringify_to!(Struct(vec![Byte, Int32, String]), "(yis)"); 324 + should_stringify_to!( 325 + Struct(vec![Byte, Struct(vec![String, Boolean]), String]), 326 + "(y(sb)s)" 327 + ); 328 } 329 330 #[test] ··· 337 #[test] 338 fn test_stringify_nested() { 339 use self::DbusType::*; 340 + should_stringify_to!( 341 + Array(DictEntry(String.into(), Int32.into()).into()), 342 + "a{si}" 343 + ); 344 should_stringify_to!( 345 Array( 346 DictEntry( 347 String.into(), 348 + Struct(vec![Byte, Array(Int32.into())]).into() 349 + ) 350 + .into() 351 ), 352 "a{s(yai)}" 353 );
+108 -77
src/introspection.rs
··· 1 - use nu_protocol::{Value, record, Span}; 2 use serde::Deserialize; 3 4 macro_rules! list_to_value { 5 - ($list:expr, $span:expr) => ( 6 Value::list($list.iter().map(|i| i.to_value($span)).collect(), $span) 7 - ) 8 } 9 10 #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] ··· 41 /// Find a method on an interface on this node, and then generate the signature of the method 42 /// args 43 pub fn get_method_args_signature(&self, interface: &str, method: &str) -> Option<String> { 44 - Some(self.get_interface(interface)?.get_method(method)?.in_signature()) 45 } 46 47 /// Find the signature of a property on an interface on this node 48 pub fn get_property_signature(&self, interface: &str, property: &str) -> Option<&str> { 49 - Some(&self.get_interface(interface)?.get_property(property)?.r#type) 50 } 51 52 /// Represent the node as a nushell [Value] 53 pub fn to_value(&self, span: Span) -> Value { 54 - Value::record(record!{ 55 - "name" => self.name.as_ref().map(|s| Value::string(s, span)).unwrap_or_default(), 56 - "interfaces" => list_to_value!(self.interfaces, span), 57 - "children" => list_to_value!(self.children, span), 58 - }, span) 59 } 60 } 61 ··· 89 90 /// Represent the interface as a nushell [Value] 91 pub fn to_value(&self, span: Span) -> Value { 92 - Value::record(record!{ 93 - "name" => Value::string(&self.name, span), 94 - "methods" => list_to_value!(self.methods, span), 95 - "signals" => list_to_value!(self.signals, span), 96 - "properties" => list_to_value!(self.properties, span), 97 - "signals" => list_to_value!(self.signals, span), 98 - }, span) 99 } 100 } 101 ··· 112 impl Method { 113 /// Get the signature of the method args 114 pub fn in_signature(&self) -> String { 115 - self.args.iter() 116 .filter(|arg| arg.direction == Direction::In) 117 .map(|arg| &arg.r#type[..]) 118 .collect() ··· 121 #[allow(dead_code)] 122 /// Get the signature of the method result 123 pub fn out_signature(&self) -> String { 124 - self.args.iter() 125 .filter(|arg| arg.direction == Direction::Out) 126 .map(|arg| &arg.r#type[..]) 127 .collect() ··· 129 130 /// Represent the method as a nushell [Value] 131 pub fn to_value(&self, span: Span) -> Value { 132 - Value::record(record!{ 133 - "name" => Value::string(&self.name, span), 134 - "args" => list_to_value!(self.args, span), 135 - "annotations" => list_to_value!(self.annotations, span), 136 - }, span) 137 } 138 } 139 ··· 152 pub fn new( 153 name: impl Into<String>, 154 r#type: impl Into<String>, 155 - direction: Direction 156 ) -> MethodArg { 157 MethodArg { 158 name: Some(name.into()), ··· 163 164 /// Represent the method as a nushell [Value] 165 pub fn to_value(&self, span: Span) -> Value { 166 - Value::record(record!{ 167 - "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 168 - "type" => Value::string(&self.r#type, span), 169 - "direction" => self.direction.to_value(span), 170 - }, span) 171 } 172 } 173 ··· 181 182 impl Direction { 183 /// Represent the direction as a nushell [Value] 184 - pub fn to_value(&self, span: Span) -> Value { 185 match self { 186 Direction::In => Value::string("in", span), 187 Direction::Out => Value::string("out", span), ··· 202 impl Signal { 203 /// Represent the signal as a nushell [Value] 204 pub fn to_value(&self, span: Span) -> Value { 205 - Value::record(record!{ 206 - "name" => Value::string(&self.name, span), 207 - "args" => list_to_value!(self.args, span), 208 - "annotations" => list_to_value!(self.annotations, span), 209 - }, span) 210 } 211 } 212 ··· 221 impl SignalArg { 222 /// Represent the argument as a nushell [Value] 223 pub fn to_value(&self, span: Span) -> Value { 224 - Value::record(record!{ 225 - "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 226 - "type" => Value::string(&self.r#type, span), 227 - }, span) 228 } 229 } 230 ··· 241 impl Property { 242 /// Represent the property as a nushell [Value] 243 pub fn to_value(&self, span: Span) -> Value { 244 - Value::record(record!{ 245 - "name" => Value::string(&self.name, span), 246 - "type" => Value::string(&self.r#type, span), 247 - "args" => self.access.to_value(span), 248 - "annotations" => list_to_value!(self.annotations, span), 249 - }, span) 250 } 251 } 252 ··· 279 impl Annotation { 280 #[cfg(test)] 281 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Annotation { 282 - Annotation { name: name.into(), value: value.into() } 283 } 284 285 /// Represent the annotation as a nushell [Value] 286 pub fn to_value(&self, span: Span) -> Value { 287 - Value::record(record!{ 288 - "name" => Value::string(&self.name, span), 289 - "value" => Value::string(&self.value, span), 290 - }, span) 291 } 292 } 293 ··· 305 MethodArg::new("bar", "as", Direction::In), 306 MethodArg::new("baz", "a{us}", Direction::Out), 307 ], 308 - annotations: vec![ 309 - Annotation::new("org.freedesktop.DBus.Deprecated", "true"), 310 - ], 311 }, 312 Method { 313 name: "Bazify".into(), ··· 320 }, 321 Method { 322 name: "Mogrify".into(), 323 - args: vec![ 324 - MethodArg::new("bar", "(iiav)", Direction::In), 325 - ], 326 - annotations: vec![] 327 - }, 328 - ], 329 - signals: vec![ 330 - Signal { 331 - name: "Changed".into(), 332 - args: vec![ 333 - SignalArg { name: Some("new_value".into()), r#type: "b".into() }, 334 - ], 335 - annotations: vec![] 336 - }, 337 - ], 338 - properties: vec![ 339 - Property { 340 - name: "Bar".into(), 341 - r#type: "y".into(), 342 - access: Access::ReadWrite, 343 annotations: vec![], 344 - } 345 ], 346 - annotations: vec![] 347 }], 348 children: vec![ 349 Node::with_name("child_of_sample_object"), 350 Node::with_name("another_child_of_sample_object"), 351 - ] 352 } 353 } 354
··· 1 + use nu_protocol::{record, Span, Value}; 2 use serde::Deserialize; 3 4 macro_rules! list_to_value { 5 + ($list:expr, $span:expr) => { 6 Value::list($list.iter().map(|i| i.to_value($span)).collect(), $span) 7 + }; 8 } 9 10 #[derive(Debug, Clone, Deserialize, PartialEq, Eq, Default)] ··· 41 /// Find a method on an interface on this node, and then generate the signature of the method 42 /// args 43 pub fn get_method_args_signature(&self, interface: &str, method: &str) -> Option<String> { 44 + Some( 45 + self.get_interface(interface)? 46 + .get_method(method)? 47 + .in_signature(), 48 + ) 49 } 50 51 /// Find the signature of a property on an interface on this node 52 pub fn get_property_signature(&self, interface: &str, property: &str) -> Option<&str> { 53 + Some( 54 + &self 55 + .get_interface(interface)? 56 + .get_property(property)? 57 + .r#type, 58 + ) 59 } 60 61 /// Represent the node as a nushell [Value] 62 pub fn to_value(&self, span: Span) -> Value { 63 + Value::record( 64 + record! { 65 + "name" => self.name.as_ref().map(|s| Value::string(s, span)).unwrap_or_default(), 66 + "interfaces" => list_to_value!(self.interfaces, span), 67 + "children" => list_to_value!(self.children, span), 68 + }, 69 + span, 70 + ) 71 } 72 } 73 ··· 101 102 /// Represent the interface as a nushell [Value] 103 pub fn to_value(&self, span: Span) -> Value { 104 + Value::record( 105 + record! { 106 + "name" => Value::string(&self.name, span), 107 + "methods" => list_to_value!(self.methods, span), 108 + "signals" => list_to_value!(self.signals, span), 109 + "properties" => list_to_value!(self.properties, span), 110 + "signals" => list_to_value!(self.signals, span), 111 + }, 112 + span, 113 + ) 114 } 115 } 116 ··· 127 impl Method { 128 /// Get the signature of the method args 129 pub fn in_signature(&self) -> String { 130 + self.args 131 + .iter() 132 .filter(|arg| arg.direction == Direction::In) 133 .map(|arg| &arg.r#type[..]) 134 .collect() ··· 137 #[allow(dead_code)] 138 /// Get the signature of the method result 139 pub fn out_signature(&self) -> String { 140 + self.args 141 + .iter() 142 .filter(|arg| arg.direction == Direction::Out) 143 .map(|arg| &arg.r#type[..]) 144 .collect() ··· 146 147 /// Represent the method as a nushell [Value] 148 pub fn to_value(&self, span: Span) -> Value { 149 + Value::record( 150 + record! { 151 + "name" => Value::string(&self.name, span), 152 + "args" => list_to_value!(self.args, span), 153 + "annotations" => list_to_value!(self.annotations, span), 154 + }, 155 + span, 156 + ) 157 } 158 } 159 ··· 172 pub fn new( 173 name: impl Into<String>, 174 r#type: impl Into<String>, 175 + direction: Direction, 176 ) -> MethodArg { 177 MethodArg { 178 name: Some(name.into()), ··· 183 184 /// Represent the method as a nushell [Value] 185 pub fn to_value(&self, span: Span) -> Value { 186 + Value::record( 187 + record! { 188 + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 189 + "type" => Value::string(&self.r#type, span), 190 + "direction" => self.direction.to_value(span), 191 + }, 192 + span, 193 + ) 194 } 195 } 196 ··· 204 205 impl Direction { 206 /// Represent the direction as a nushell [Value] 207 + pub fn to_value(self, span: Span) -> Value { 208 match self { 209 Direction::In => Value::string("in", span), 210 Direction::Out => Value::string("out", span), ··· 225 impl Signal { 226 /// Represent the signal as a nushell [Value] 227 pub fn to_value(&self, span: Span) -> Value { 228 + Value::record( 229 + record! { 230 + "name" => Value::string(&self.name, span), 231 + "args" => list_to_value!(self.args, span), 232 + "annotations" => list_to_value!(self.annotations, span), 233 + }, 234 + span, 235 + ) 236 } 237 } 238 ··· 247 impl SignalArg { 248 /// Represent the argument as a nushell [Value] 249 pub fn to_value(&self, span: Span) -> Value { 250 + Value::record( 251 + record! { 252 + "name" => self.name.as_ref().map(|n| Value::string(n, span)).unwrap_or_default(), 253 + "type" => Value::string(&self.r#type, span), 254 + }, 255 + span, 256 + ) 257 } 258 } 259 ··· 270 impl Property { 271 /// Represent the property as a nushell [Value] 272 pub fn to_value(&self, span: Span) -> Value { 273 + Value::record( 274 + record! { 275 + "name" => Value::string(&self.name, span), 276 + "type" => Value::string(&self.r#type, span), 277 + "args" => self.access.to_value(span), 278 + "annotations" => list_to_value!(self.annotations, span), 279 + }, 280 + span, 281 + ) 282 } 283 } 284 ··· 311 impl Annotation { 312 #[cfg(test)] 313 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Annotation { 314 + Annotation { 315 + name: name.into(), 316 + value: value.into(), 317 + } 318 } 319 320 /// Represent the annotation as a nushell [Value] 321 pub fn to_value(&self, span: Span) -> Value { 322 + Value::record( 323 + record! { 324 + "name" => Value::string(&self.name, span), 325 + "value" => Value::string(&self.value, span), 326 + }, 327 + span, 328 + ) 329 } 330 } 331 ··· 343 MethodArg::new("bar", "as", Direction::In), 344 MethodArg::new("baz", "a{us}", Direction::Out), 345 ], 346 + annotations: vec![Annotation::new("org.freedesktop.DBus.Deprecated", "true")], 347 }, 348 Method { 349 name: "Bazify".into(), ··· 356 }, 357 Method { 358 name: "Mogrify".into(), 359 + args: vec![MethodArg::new("bar", "(iiav)", Direction::In)], 360 annotations: vec![], 361 + }, 362 ], 363 + signals: vec![Signal { 364 + name: "Changed".into(), 365 + args: vec![SignalArg { 366 + name: Some("new_value".into()), 367 + r#type: "b".into(), 368 + }], 369 + annotations: vec![], 370 + }], 371 + properties: vec![Property { 372 + name: "Bar".into(), 373 + r#type: "y".into(), 374 + access: Access::ReadWrite, 375 + annotations: vec![], 376 + }], 377 + annotations: vec![], 378 }], 379 children: vec![ 380 Node::with_name("child_of_sample_object"), 381 Node::with_name("another_child_of_sample_object"), 382 + ], 383 } 384 } 385
+53 -31
src/main.rs
··· 1 - use nu_plugin::{serve_plugin, MsgPackSerializer, Plugin, EvaluatedCall, LabeledError}; 2 - use nu_protocol::{PluginSignature, Value, SyntaxShape, PluginExample, Span, Type}; 3 4 mod config; 5 - mod client; 6 mod convert; 7 mod dbus_type; 8 mod introspection; 9 mod pattern; 10 11 - use config::*; 12 use client::*; 13 14 use crate::pattern::Pattern; 15 ··· 23 impl Plugin for NuPluginDbus { 24 fn signature(&self) -> Vec<PluginSignature> { 25 macro_rules! str { 26 - ($s:expr) => (Value::string($s, Span::unknown())) 27 } 28 vec![ 29 PluginSignature::build("dbus") 30 - .is_dbus_command() 31 .usage("Commands for interacting with D-Bus"), 32 PluginSignature::build("dbus introspect") 33 - .is_dbus_command() 34 .accepts_dbus_client_options() 35 .accepts_timeout() 36 .usage("Introspect a D-Bus object") ··· 65 }, 66 ]), 67 PluginSignature::build("dbus call") 68 - .is_dbus_command() 69 .accepts_dbus_client_options() 70 .accepts_timeout() 71 .usage("Call a method and get its response") ··· 108 }, 109 ]), 110 PluginSignature::build("dbus get") 111 - .is_dbus_command() 112 .accepts_dbus_client_options() 113 .accepts_timeout() 114 .usage("Get a D-Bus property") ··· 139 }, 140 ]), 141 PluginSignature::build("dbus get-all") 142 - .is_dbus_command() 143 .accepts_dbus_client_options() 144 .accepts_timeout() 145 .usage("Get all D-Bus properties for the given object") ··· 165 }, 166 ]), 167 PluginSignature::build("dbus set") 168 - .is_dbus_command() 169 .accepts_dbus_client_options() 170 .accepts_timeout() 171 .usage("Set a D-Bus property") ··· 196 }, 197 ]), 198 PluginSignature::build("dbus list") 199 - .is_dbus_command() 200 .accepts_dbus_client_options() 201 .accepts_timeout() 202 .usage("List all available connection names on the bus") ··· 244 "dbus" => Err(LabeledError { 245 label: "The `dbus` command requires a subcommand".into(), 246 msg: "add --help to see subcommands".into(), 247 - span: Some(call.head) 248 }), 249 250 "dbus introspect" => self.introspect(call), ··· 257 _ => Err(LabeledError { 258 label: "Plugin invoked with unknown command name".into(), 259 msg: "unknown command".into(), 260 - span: Some(call.head) 261 - }) 262 } 263 } 264 } 265 266 /// For conveniently adding the base options to a dbus command 267 trait DbusSignatureUtilExt { 268 - fn is_dbus_command(self) -> Self; 269 fn accepts_dbus_client_options(self) -> Self; 270 fn accepts_timeout(self) -> Self; 271 } 272 273 impl DbusSignatureUtilExt for PluginSignature { 274 - fn is_dbus_command(self) -> Self { 275 self.search_terms(vec!["dbus".into()]) 276 .category(nu_protocol::Category::Platform) 277 } ··· 279 fn accepts_dbus_client_options(self) -> Self { 280 self.switch("session", "Send to the session message bus (default)", None) 281 .switch("system", "Send to the system message bus", None) 282 - .switch("started", "Send to the bus that started this process, if applicable", None) 283 - .named("bus", SyntaxShape::String, "Send to the bus server at the given address", None) 284 - .named("peer", SyntaxShape::String, 285 "Send to a non-bus D-Bus server at the given address. \ 286 Will not call the Hello method on initialization.", 287 - None) 288 } 289 290 fn accepts_timeout(self) -> Self { 291 - self.named("timeout", SyntaxShape::Duration, "How long to wait for a response", None) 292 } 293 } 294 ··· 296 fn introspect(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 297 let config = DbusClientConfig::try_from(call)?; 298 let dbus = DbusClient::new(config)?; 299 - let node = dbus.introspect( 300 - &call.get_flag("dest")?.unwrap(), 301 - &call.req(0)?, 302 - )?; 303 Ok(node.to_value(call.head)) 304 } 305 ··· 312 &call.req(1)?, 313 &call.req(2)?, 314 call.get_flag("signature")?.as_ref(), 315 - &call.positional[3..] 316 )?; 317 318 let flatten = !call.get_flag::<bool>("no-flatten")?.unwrap_or(false); ··· 322 match values.len() { 323 0 if flatten => Ok(Value::nothing(call.head)), 324 1 if flatten => Ok(values.into_iter().nth(0).unwrap()), 325 - _ => Ok(Value::list(values, call.head)) 326 } 327 } 328 ··· 364 fn list(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 365 let config = DbusClientConfig::try_from(call)?; 366 let dbus = DbusClient::new(config)?; 367 - let pattern = call.opt::<String>(0)?.map(|pat| Pattern::new(&pat, Some('.'))); 368 let result = dbus.list(pattern.as_ref())?; 369 Ok(Value::list( 370 - result.into_iter().map(|s| Value::string(s, call.head)).collect(), 371 - call.head)) 372 } 373 }
··· 1 + use nu_plugin::{serve_plugin, EvaluatedCall, LabeledError, MsgPackSerializer, Plugin}; 2 + use nu_protocol::{PluginExample, PluginSignature, Span, SyntaxShape, Type, Value}; 3 4 + mod client; 5 mod config; 6 mod convert; 7 mod dbus_type; 8 mod introspection; 9 mod pattern; 10 11 use client::*; 12 + use config::*; 13 14 use crate::pattern::Pattern; 15 ··· 23 impl Plugin for NuPluginDbus { 24 fn signature(&self) -> Vec<PluginSignature> { 25 macro_rules! str { 26 + ($s:expr) => { 27 + Value::string($s, Span::unknown()) 28 + }; 29 } 30 vec![ 31 PluginSignature::build("dbus") 32 + .dbus_command() 33 .usage("Commands for interacting with D-Bus"), 34 PluginSignature::build("dbus introspect") 35 + .dbus_command() 36 .accepts_dbus_client_options() 37 .accepts_timeout() 38 .usage("Introspect a D-Bus object") ··· 67 }, 68 ]), 69 PluginSignature::build("dbus call") 70 + .dbus_command() 71 .accepts_dbus_client_options() 72 .accepts_timeout() 73 .usage("Call a method and get its response") ··· 110 }, 111 ]), 112 PluginSignature::build("dbus get") 113 + .dbus_command() 114 .accepts_dbus_client_options() 115 .accepts_timeout() 116 .usage("Get a D-Bus property") ··· 141 }, 142 ]), 143 PluginSignature::build("dbus get-all") 144 + .dbus_command() 145 .accepts_dbus_client_options() 146 .accepts_timeout() 147 .usage("Get all D-Bus properties for the given object") ··· 167 }, 168 ]), 169 PluginSignature::build("dbus set") 170 + .dbus_command() 171 .accepts_dbus_client_options() 172 .accepts_timeout() 173 .usage("Set a D-Bus property") ··· 198 }, 199 ]), 200 PluginSignature::build("dbus list") 201 + .dbus_command() 202 .accepts_dbus_client_options() 203 .accepts_timeout() 204 .usage("List all available connection names on the bus") ··· 246 "dbus" => Err(LabeledError { 247 label: "The `dbus` command requires a subcommand".into(), 248 msg: "add --help to see subcommands".into(), 249 + span: Some(call.head), 250 }), 251 252 "dbus introspect" => self.introspect(call), ··· 259 _ => Err(LabeledError { 260 label: "Plugin invoked with unknown command name".into(), 261 msg: "unknown command".into(), 262 + span: Some(call.head), 263 + }), 264 } 265 } 266 } 267 268 /// For conveniently adding the base options to a dbus command 269 trait DbusSignatureUtilExt { 270 + fn dbus_command(self) -> Self; 271 fn accepts_dbus_client_options(self) -> Self; 272 fn accepts_timeout(self) -> Self; 273 } 274 275 impl DbusSignatureUtilExt for PluginSignature { 276 + fn dbus_command(self) -> Self { 277 self.search_terms(vec!["dbus".into()]) 278 .category(nu_protocol::Category::Platform) 279 } ··· 281 fn accepts_dbus_client_options(self) -> Self { 282 self.switch("session", "Send to the session message bus (default)", None) 283 .switch("system", "Send to the system message bus", None) 284 + .switch( 285 + "started", 286 + "Send to the bus that started this process, if applicable", 287 + None, 288 + ) 289 + .named( 290 + "bus", 291 + SyntaxShape::String, 292 + "Send to the bus server at the given address", 293 + None, 294 + ) 295 + .named( 296 + "peer", 297 + SyntaxShape::String, 298 "Send to a non-bus D-Bus server at the given address. \ 299 Will not call the Hello method on initialization.", 300 + None, 301 + ) 302 } 303 304 fn accepts_timeout(self) -> Self { 305 + self.named( 306 + "timeout", 307 + SyntaxShape::Duration, 308 + "How long to wait for a response", 309 + None, 310 + ) 311 } 312 } 313 ··· 315 fn introspect(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 316 let config = DbusClientConfig::try_from(call)?; 317 let dbus = DbusClient::new(config)?; 318 + let node = dbus.introspect(&call.get_flag("dest")?.unwrap(), &call.req(0)?)?; 319 Ok(node.to_value(call.head)) 320 } 321 ··· 328 &call.req(1)?, 329 &call.req(2)?, 330 call.get_flag("signature")?.as_ref(), 331 + &call.positional[3..], 332 )?; 333 334 let flatten = !call.get_flag::<bool>("no-flatten")?.unwrap_or(false); ··· 338 match values.len() { 339 0 if flatten => Ok(Value::nothing(call.head)), 340 1 if flatten => Ok(values.into_iter().nth(0).unwrap()), 341 + _ => Ok(Value::list(values, call.head)), 342 } 343 } 344 ··· 380 fn list(&self, call: &EvaluatedCall) -> Result<Value, LabeledError> { 381 let config = DbusClientConfig::try_from(call)?; 382 let dbus = DbusClient::new(config)?; 383 + let pattern = call 384 + .opt::<String>(0)? 385 + .map(|pat| Pattern::new(&pat, Some('.'))); 386 let result = dbus.list(pattern.as_ref())?; 387 Ok(Value::list( 388 + result 389 + .into_iter() 390 + .map(|s| Value::string(s, call.head)) 391 + .collect(), 392 + call.head, 393 + )) 394 } 395 }
+208 -125
src/pattern.rs
··· 17 let mut tokens = vec![]; 18 for ch in pattern.chars() { 19 match ch { 20 - '*' => 21 if tokens.last() == Some(&PatternToken::OneWildcard) { 22 *tokens.last_mut().unwrap() = PatternToken::ManyWildcard; 23 } else { 24 tokens.push(PatternToken::OneWildcard); 25 - }, 26 - '?' => 27 - tokens.push(PatternToken::AnyChar), 28 - _ => 29 - match tokens.last_mut() { 30 - Some(PatternToken::Exact(ref mut s)) => s.push(ch), 31 - _ => tokens.push(PatternToken::Exact(ch.into())), 32 - }, 33 } 34 } 35 Pattern { separator, tokens } ··· 57 MatchState::Precise => { 58 // Can't possibly match 59 return false; 60 - }, 61 MatchState::ScanAhead { stop_at_separator } => { 62 if search_str.is_empty() { 63 // End of input, can't match 64 return false; 65 } 66 - if stop_at_separator && 67 - self.separator.is_some_and(|sep| search_str.starts_with(sep)) { 68 // Found the separator. Consume a char and revert to precise 69 // mode 70 search_str = &search_str[1..]; ··· 76 } 77 } 78 } 79 - }, 80 PatternToken::OneWildcard => { 81 // Set the mode to ScanAhead, stopping at separator 82 - state = MatchState::ScanAhead { stop_at_separator: true }; 83 tokens = &tokens[1..]; 84 - }, 85 PatternToken::ManyWildcard => { 86 // Set the mode to ScanAhead, ignoring separator 87 - state = MatchState::ScanAhead { stop_at_separator: false }; 88 tokens = &tokens[1..]; 89 - }, 90 PatternToken::AnyChar => { 91 if !search_str.is_empty() { 92 // Take a char from the search str and continue ··· 96 // End of input 97 return false; 98 } 99 - }, 100 } 101 } 102 - #[cfg(test)] { 103 - println!("end, state={:?}, search_str={:?}, tokens={:?}", state, search_str, tokens); 104 } 105 if !search_str.is_empty() { 106 // If the search str is not empty at the end ··· 108 // We didn't end with a wildcard, so this is a fail 109 MatchState::Precise => false, 110 // This could be a match as long as the separator isn't contained in the remainder 111 - MatchState::ScanAhead { stop_at_separator: true } => 112 if let Some(separator) = self.separator { 113 !search_str.contains(separator) 114 } else { 115 // No separator specified, so this is a success 116 true 117 - }, 118 // Always a success, no matter what remains 119 - MatchState::ScanAhead { stop_at_separator: false } => true, 120 } 121 } else { 122 // The match has succeeded - there is nothing more to match ··· 129 fn test_pattern_new() { 130 assert_eq!( 131 Pattern::new("", Some('/')), 132 - Pattern { separator: Some('/'), tokens: vec![] } 133 ); 134 assert_eq!( 135 Pattern::new("", None), 136 - Pattern { separator: None, tokens: vec![] } 137 ); 138 assert_eq!( 139 Pattern::new("org.freedesktop.DBus", Some('.')), 140 - Pattern { separator: Some('.'), tokens: vec![ 141 - PatternToken::Exact("org.freedesktop.DBus".into()), 142 - ] } 143 ); 144 assert_eq!( 145 Pattern::new("*", Some('.')), 146 - Pattern { separator: Some('.'), tokens: vec![ 147 - PatternToken::OneWildcard, 148 - ] } 149 ); 150 assert_eq!( 151 Pattern::new("**", Some('.')), 152 - Pattern { separator: Some('.'), tokens: vec![ 153 - PatternToken::ManyWildcard, 154 - ] } 155 ); 156 assert_eq!( 157 Pattern::new("?", Some('.')), 158 - Pattern { separator: Some('.'), tokens: vec![ 159 - PatternToken::AnyChar, 160 - ] } 161 ); 162 assert_eq!( 163 Pattern::new("org.freedesktop.*", Some('.')), 164 - Pattern { separator: Some('.'), tokens: vec![ 165 - PatternToken::Exact("org.freedesktop.".into()), 166 - PatternToken::OneWildcard, 167 - ] } 168 ); 169 assert_eq!( 170 Pattern::new("org.freedesktop.**", Some('.')), 171 - Pattern { separator: Some('.'), tokens: vec![ 172 - PatternToken::Exact("org.freedesktop.".into()), 173 - PatternToken::ManyWildcard, 174 - ] } 175 ); 176 assert_eq!( 177 Pattern::new("org.*.DBus", Some('.')), 178 - Pattern { separator: Some('.'), tokens: vec![ 179 - PatternToken::Exact("org.".into()), 180 - PatternToken::OneWildcard, 181 - PatternToken::Exact(".DBus".into()), 182 - ] } 183 ); 184 assert_eq!( 185 Pattern::new("org.**.DBus", Some('.')), 186 - Pattern { separator: Some('.'), tokens: vec![ 187 - PatternToken::Exact("org.".into()), 188 - PatternToken::ManyWildcard, 189 - PatternToken::Exact(".DBus".into()), 190 - ] } 191 ); 192 assert_eq!( 193 Pattern::new("org.**.?Bus", Some('.')), 194 - Pattern { separator: Some('.'), tokens: vec![ 195 - PatternToken::Exact("org.".into()), 196 - PatternToken::ManyWildcard, 197 - PatternToken::Exact(".".into()), 198 - PatternToken::AnyChar, 199 - PatternToken::Exact("Bus".into()), 200 - ] } 201 ); 202 assert_eq!( 203 Pattern::new("org.free*top", Some('.')), 204 - Pattern { separator: Some('.'), tokens: vec![ 205 - PatternToken::Exact("org.free".into()), 206 - PatternToken::OneWildcard, 207 - PatternToken::Exact("top".into()), 208 - ] } 209 ); 210 assert_eq!( 211 Pattern::new("org.free**top", Some('.')), 212 - Pattern { separator: Some('.'), tokens: vec![ 213 - PatternToken::Exact("org.free".into()), 214 - PatternToken::ManyWildcard, 215 - PatternToken::Exact("top".into()), 216 - ] } 217 ); 218 assert_eq!( 219 Pattern::new("org.**top", Some('.')), 220 - Pattern { separator: Some('.'), tokens: vec![ 221 - PatternToken::Exact("org.".into()), 222 - PatternToken::ManyWildcard, 223 - PatternToken::Exact("top".into()), 224 - ] } 225 ); 226 assert_eq!( 227 Pattern::new("**top", Some('.')), 228 - Pattern { separator: Some('.'), tokens: vec![ 229 - PatternToken::ManyWildcard, 230 - PatternToken::Exact("top".into()), 231 - ] } 232 ); 233 assert_eq!( 234 Pattern::new("org.free**", Some('.')), 235 - Pattern { separator: Some('.'), tokens: vec![ 236 - PatternToken::Exact("org.free".into()), 237 - PatternToken::ManyWildcard, 238 - ] } 239 ); 240 } 241 242 #[test] 243 fn test_pattern_is_match_empty() { 244 - let pat = Pattern { separator: Some('.'), tokens: vec![] }; 245 assert!(pat.is_match("")); 246 assert!(!pat.is_match("anystring")); 247 assert!(!pat.is_match("anystring.anyotherstring")); ··· 249 250 #[test] 251 fn test_pattern_is_match_exact() { 252 - let pat = Pattern { separator: Some('.'), tokens: vec![ 253 - PatternToken::Exact("specific".into()), 254 - ] }; 255 assert!(pat.is_match("specific")); 256 assert!(!pat.is_match("")); 257 assert!(!pat.is_match("specifi")); ··· 260 261 #[test] 262 fn test_pattern_is_match_one_wildcard() { 263 - let pat = Pattern { separator: Some('.'), tokens: vec![ 264 - PatternToken::Exact("foo.".into()), 265 - PatternToken::OneWildcard, 266 - PatternToken::Exact(".baz".into()), 267 - ] }; 268 assert!(pat.is_match("foo.bar.baz")); 269 assert!(pat.is_match("foo.grok.baz")); 270 assert!(pat.is_match("foo..baz")); ··· 277 278 #[test] 279 fn test_pattern_is_match_one_wildcard_at_end() { 280 - let pat = Pattern { separator: Some('.'), tokens: vec![ 281 - PatternToken::Exact("foo.".into()), 282 - PatternToken::OneWildcard, 283 - ] }; 284 assert!(pat.is_match("foo.bar")); 285 assert!(pat.is_match("foo.grok")); 286 assert!(pat.is_match("foo.")); ··· 292 293 #[test] 294 fn test_pattern_is_match_one_wildcard_at_start() { 295 - let pat = Pattern { separator: Some('.'), tokens: vec![ 296 - PatternToken::OneWildcard, 297 - PatternToken::Exact(".bar".into()), 298 - ] }; 299 assert!(pat.is_match("foo.bar")); 300 assert!(pat.is_match("grok.bar")); 301 assert!(pat.is_match(".bar")); ··· 307 308 #[test] 309 fn test_pattern_is_match_one_wildcard_no_separator() { 310 - let pat = Pattern { separator: None, tokens: vec![ 311 - PatternToken::Exact("foo.".into()), 312 - PatternToken::OneWildcard, 313 - PatternToken::Exact(".baz".into()), 314 - ] }; 315 assert!(pat.is_match("foo.bar.baz")); 316 assert!(pat.is_match("foo.grok.baz")); 317 assert!(pat.is_match("foo..baz")); ··· 325 326 #[test] 327 fn test_pattern_is_match_many_wildcard() { 328 - let pat = Pattern { separator: Some('.'), tokens: vec![ 329 - PatternToken::Exact("foo.".into()), 330 - PatternToken::ManyWildcard, 331 - PatternToken::Exact(".baz".into()), 332 - ] }; 333 assert!(pat.is_match("foo.bar.baz")); 334 assert!(pat.is_match("foo.grok.baz")); 335 assert!(pat.is_match("foo..baz")); ··· 343 344 #[test] 345 fn test_pattern_is_match_many_wildcard_at_end() { 346 - let pat = Pattern { separator: Some('.'), tokens: vec![ 347 - PatternToken::Exact("foo.".into()), 348 - PatternToken::ManyWildcard, 349 - ] }; 350 assert!(pat.is_match("foo.bar")); 351 assert!(pat.is_match("foo.grok")); 352 assert!(pat.is_match("foo.")); ··· 358 359 #[test] 360 fn test_pattern_is_match_many_wildcard_at_start() { 361 - let pat = Pattern { separator: Some('.'), tokens: vec![ 362 - PatternToken::ManyWildcard, 363 - PatternToken::Exact(".bar".into()), 364 - ] }; 365 assert!(pat.is_match("foo.bar")); 366 assert!(pat.is_match("grok.bar")); 367 assert!(pat.is_match("should.match.bar")); ··· 373 374 #[test] 375 fn test_pattern_is_match_any_char() { 376 - let pat = Pattern { separator: Some('.'), tokens: vec![ 377 - PatternToken::Exact("fo".into()), 378 - PatternToken::AnyChar, 379 - PatternToken::Exact(".baz".into()), 380 - ] }; 381 assert!(pat.is_match("foo.baz")); 382 assert!(pat.is_match("foe.baz")); 383 assert!(pat.is_match("foi.baz"));
··· 17 let mut tokens = vec![]; 18 for ch in pattern.chars() { 19 match ch { 20 + '*' => { 21 if tokens.last() == Some(&PatternToken::OneWildcard) { 22 *tokens.last_mut().unwrap() = PatternToken::ManyWildcard; 23 } else { 24 tokens.push(PatternToken::OneWildcard); 25 + } 26 + } 27 + '?' => tokens.push(PatternToken::AnyChar), 28 + _ => match tokens.last_mut() { 29 + Some(PatternToken::Exact(ref mut s)) => s.push(ch), 30 + _ => tokens.push(PatternToken::Exact(ch.into())), 31 + }, 32 } 33 } 34 Pattern { separator, tokens } ··· 56 MatchState::Precise => { 57 // Can't possibly match 58 return false; 59 + } 60 MatchState::ScanAhead { stop_at_separator } => { 61 if search_str.is_empty() { 62 // End of input, can't match 63 return false; 64 } 65 + if stop_at_separator 66 + && self 67 + .separator 68 + .is_some_and(|sep| search_str.starts_with(sep)) 69 + { 70 // Found the separator. Consume a char and revert to precise 71 // mode 72 search_str = &search_str[1..]; ··· 78 } 79 } 80 } 81 + } 82 PatternToken::OneWildcard => { 83 // Set the mode to ScanAhead, stopping at separator 84 + state = MatchState::ScanAhead { 85 + stop_at_separator: true, 86 + }; 87 tokens = &tokens[1..]; 88 + } 89 PatternToken::ManyWildcard => { 90 // Set the mode to ScanAhead, ignoring separator 91 + state = MatchState::ScanAhead { 92 + stop_at_separator: false, 93 + }; 94 tokens = &tokens[1..]; 95 + } 96 PatternToken::AnyChar => { 97 if !search_str.is_empty() { 98 // Take a char from the search str and continue ··· 102 // End of input 103 return false; 104 } 105 + } 106 } 107 } 108 + #[cfg(test)] 109 + { 110 + println!( 111 + "end, state={:?}, search_str={:?}, tokens={:?}", 112 + state, search_str, tokens 113 + ); 114 } 115 if !search_str.is_empty() { 116 // If the search str is not empty at the end ··· 118 // We didn't end with a wildcard, so this is a fail 119 MatchState::Precise => false, 120 // This could be a match as long as the separator isn't contained in the remainder 121 + MatchState::ScanAhead { 122 + stop_at_separator: true, 123 + } => { 124 if let Some(separator) = self.separator { 125 !search_str.contains(separator) 126 } else { 127 // No separator specified, so this is a success 128 true 129 + } 130 + } 131 // Always a success, no matter what remains 132 + MatchState::ScanAhead { 133 + stop_at_separator: false, 134 + } => true, 135 } 136 } else { 137 // The match has succeeded - there is nothing more to match ··· 144 fn test_pattern_new() { 145 assert_eq!( 146 Pattern::new("", Some('/')), 147 + Pattern { 148 + separator: Some('/'), 149 + tokens: vec![] 150 + } 151 ); 152 assert_eq!( 153 Pattern::new("", None), 154 + Pattern { 155 + separator: None, 156 + tokens: vec![] 157 + } 158 ); 159 assert_eq!( 160 Pattern::new("org.freedesktop.DBus", Some('.')), 161 + Pattern { 162 + separator: Some('.'), 163 + tokens: vec![PatternToken::Exact("org.freedesktop.DBus".into()),] 164 + } 165 ); 166 assert_eq!( 167 Pattern::new("*", Some('.')), 168 + Pattern { 169 + separator: Some('.'), 170 + tokens: vec![PatternToken::OneWildcard,] 171 + } 172 ); 173 assert_eq!( 174 Pattern::new("**", Some('.')), 175 + Pattern { 176 + separator: Some('.'), 177 + tokens: vec![PatternToken::ManyWildcard,] 178 + } 179 ); 180 assert_eq!( 181 Pattern::new("?", Some('.')), 182 + Pattern { 183 + separator: Some('.'), 184 + tokens: vec![PatternToken::AnyChar,] 185 + } 186 ); 187 assert_eq!( 188 Pattern::new("org.freedesktop.*", Some('.')), 189 + Pattern { 190 + separator: Some('.'), 191 + tokens: vec![ 192 + PatternToken::Exact("org.freedesktop.".into()), 193 + PatternToken::OneWildcard, 194 + ] 195 + } 196 ); 197 assert_eq!( 198 Pattern::new("org.freedesktop.**", Some('.')), 199 + Pattern { 200 + separator: Some('.'), 201 + tokens: vec![ 202 + PatternToken::Exact("org.freedesktop.".into()), 203 + PatternToken::ManyWildcard, 204 + ] 205 + } 206 ); 207 assert_eq!( 208 Pattern::new("org.*.DBus", Some('.')), 209 + Pattern { 210 + separator: Some('.'), 211 + tokens: vec![ 212 + PatternToken::Exact("org.".into()), 213 + PatternToken::OneWildcard, 214 + PatternToken::Exact(".DBus".into()), 215 + ] 216 + } 217 ); 218 assert_eq!( 219 Pattern::new("org.**.DBus", Some('.')), 220 + Pattern { 221 + separator: Some('.'), 222 + tokens: vec![ 223 + PatternToken::Exact("org.".into()), 224 + PatternToken::ManyWildcard, 225 + PatternToken::Exact(".DBus".into()), 226 + ] 227 + } 228 ); 229 assert_eq!( 230 Pattern::new("org.**.?Bus", Some('.')), 231 + Pattern { 232 + separator: Some('.'), 233 + tokens: vec![ 234 + PatternToken::Exact("org.".into()), 235 + PatternToken::ManyWildcard, 236 + PatternToken::Exact(".".into()), 237 + PatternToken::AnyChar, 238 + PatternToken::Exact("Bus".into()), 239 + ] 240 + } 241 ); 242 assert_eq!( 243 Pattern::new("org.free*top", Some('.')), 244 + Pattern { 245 + separator: Some('.'), 246 + tokens: vec![ 247 + PatternToken::Exact("org.free".into()), 248 + PatternToken::OneWildcard, 249 + PatternToken::Exact("top".into()), 250 + ] 251 + } 252 ); 253 assert_eq!( 254 Pattern::new("org.free**top", Some('.')), 255 + Pattern { 256 + separator: Some('.'), 257 + tokens: vec![ 258 + PatternToken::Exact("org.free".into()), 259 + PatternToken::ManyWildcard, 260 + PatternToken::Exact("top".into()), 261 + ] 262 + } 263 ); 264 assert_eq!( 265 Pattern::new("org.**top", Some('.')), 266 + Pattern { 267 + separator: Some('.'), 268 + tokens: vec![ 269 + PatternToken::Exact("org.".into()), 270 + PatternToken::ManyWildcard, 271 + PatternToken::Exact("top".into()), 272 + ] 273 + } 274 ); 275 assert_eq!( 276 Pattern::new("**top", Some('.')), 277 + Pattern { 278 + separator: Some('.'), 279 + tokens: vec![ 280 + PatternToken::ManyWildcard, 281 + PatternToken::Exact("top".into()), 282 + ] 283 + } 284 ); 285 assert_eq!( 286 Pattern::new("org.free**", Some('.')), 287 + Pattern { 288 + separator: Some('.'), 289 + tokens: vec![ 290 + PatternToken::Exact("org.free".into()), 291 + PatternToken::ManyWildcard, 292 + ] 293 + } 294 ); 295 } 296 297 #[test] 298 fn test_pattern_is_match_empty() { 299 + let pat = Pattern { 300 + separator: Some('.'), 301 + tokens: vec![], 302 + }; 303 assert!(pat.is_match("")); 304 assert!(!pat.is_match("anystring")); 305 assert!(!pat.is_match("anystring.anyotherstring")); ··· 307 308 #[test] 309 fn test_pattern_is_match_exact() { 310 + let pat = Pattern { 311 + separator: Some('.'), 312 + tokens: vec![PatternToken::Exact("specific".into())], 313 + }; 314 assert!(pat.is_match("specific")); 315 assert!(!pat.is_match("")); 316 assert!(!pat.is_match("specifi")); ··· 319 320 #[test] 321 fn test_pattern_is_match_one_wildcard() { 322 + let pat = Pattern { 323 + separator: Some('.'), 324 + tokens: vec![ 325 + PatternToken::Exact("foo.".into()), 326 + PatternToken::OneWildcard, 327 + PatternToken::Exact(".baz".into()), 328 + ], 329 + }; 330 assert!(pat.is_match("foo.bar.baz")); 331 assert!(pat.is_match("foo.grok.baz")); 332 assert!(pat.is_match("foo..baz")); ··· 339 340 #[test] 341 fn test_pattern_is_match_one_wildcard_at_end() { 342 + let pat = Pattern { 343 + separator: Some('.'), 344 + tokens: vec![ 345 + PatternToken::Exact("foo.".into()), 346 + PatternToken::OneWildcard, 347 + ], 348 + }; 349 assert!(pat.is_match("foo.bar")); 350 assert!(pat.is_match("foo.grok")); 351 assert!(pat.is_match("foo.")); ··· 357 358 #[test] 359 fn test_pattern_is_match_one_wildcard_at_start() { 360 + let pat = Pattern { 361 + separator: Some('.'), 362 + tokens: vec![ 363 + PatternToken::OneWildcard, 364 + PatternToken::Exact(".bar".into()), 365 + ], 366 + }; 367 assert!(pat.is_match("foo.bar")); 368 assert!(pat.is_match("grok.bar")); 369 assert!(pat.is_match(".bar")); ··· 375 376 #[test] 377 fn test_pattern_is_match_one_wildcard_no_separator() { 378 + let pat = Pattern { 379 + separator: None, 380 + tokens: vec![ 381 + PatternToken::Exact("foo.".into()), 382 + PatternToken::OneWildcard, 383 + PatternToken::Exact(".baz".into()), 384 + ], 385 + }; 386 assert!(pat.is_match("foo.bar.baz")); 387 assert!(pat.is_match("foo.grok.baz")); 388 assert!(pat.is_match("foo..baz")); ··· 396 397 #[test] 398 fn test_pattern_is_match_many_wildcard() { 399 + let pat = Pattern { 400 + separator: Some('.'), 401 + tokens: vec![ 402 + PatternToken::Exact("foo.".into()), 403 + PatternToken::ManyWildcard, 404 + PatternToken::Exact(".baz".into()), 405 + ], 406 + }; 407 assert!(pat.is_match("foo.bar.baz")); 408 assert!(pat.is_match("foo.grok.baz")); 409 assert!(pat.is_match("foo..baz")); ··· 417 418 #[test] 419 fn test_pattern_is_match_many_wildcard_at_end() { 420 + let pat = Pattern { 421 + separator: Some('.'), 422 + tokens: vec![ 423 + PatternToken::Exact("foo.".into()), 424 + PatternToken::ManyWildcard, 425 + ], 426 + }; 427 assert!(pat.is_match("foo.bar")); 428 assert!(pat.is_match("foo.grok")); 429 assert!(pat.is_match("foo.")); ··· 435 436 #[test] 437 fn test_pattern_is_match_many_wildcard_at_start() { 438 + let pat = Pattern { 439 + separator: Some('.'), 440 + tokens: vec![ 441 + PatternToken::ManyWildcard, 442 + PatternToken::Exact(".bar".into()), 443 + ], 444 + }; 445 assert!(pat.is_match("foo.bar")); 446 assert!(pat.is_match("grok.bar")); 447 assert!(pat.is_match("should.match.bar")); ··· 453 454 #[test] 455 fn test_pattern_is_match_any_char() { 456 + let pat = Pattern { 457 + separator: Some('.'), 458 + tokens: vec![ 459 + PatternToken::Exact("fo".into()), 460 + PatternToken::AnyChar, 461 + PatternToken::Exact(".baz".into()), 462 + ], 463 + }; 464 assert!(pat.is_match("foo.baz")); 465 assert!(pat.is_match("foe.baz")); 466 assert!(pat.is_match("foi.baz"));