| 1 | #![allow (unused_macros)] |
| 2 | #![allow (dead_code)] |
| 3 | #![allow (unused_imports)] |
| 4 | |
| 5 | use std::{fs, path::PathBuf, str::FromStr}; |
| 6 | |
| 7 | use crate::Result; |
| 8 | |
| 9 | /// Resolve XML path from either: |
| 10 | /// |
| 11 | /// - provided argument, |
| 12 | /// - default location (`xml/`, `XML/`, `../xml` or `../XML`) or |
| 13 | /// - env_variable (`LOCKSTEP_XML_PATH`) |
| 14 | /// |
| 15 | /// If no XML path is provided, it tries to find the default XML path. |
| 16 | /// If the environment variable is set, it overrides the default, or |
| 17 | /// argument path. |
| 18 | /// |
| 19 | /// # Example |
| 20 | /// |
| 21 | /// ```rust |
| 22 | /// # use zbus_lockstep::resolve_xml_path; |
| 23 | /// # use std::path::PathBuf; |
| 24 | /// # fn main() { |
| 25 | /// // path to XML files |
| 26 | /// std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 27 | /// |
| 28 | /// let xml_path = resolve_xml_path(None).unwrap(); |
| 29 | /// assert_eq!(xml_path, PathBuf::from("../xml" ).canonicalize().unwrap()); |
| 30 | /// # } |
| 31 | /// ``` |
| 32 | /// # Panics |
| 33 | /// |
| 34 | /// Panics if no XML path is provided and the default XML path is not found. |
| 35 | pub fn resolve_xml_path(xml: Option<&str>) -> Result<PathBuf> { |
| 36 | let mut xml = xml; |
| 37 | let current_dir: PathBuf = std::env::current_dir()?; |
| 38 | |
| 39 | // We want to know the name of the crate we are expanded in. |
| 40 | let crate_name = std::env::var("CARGO_PKG_NAME" ).unwrap_or_else(|_| String::from("unknown" )); |
| 41 | |
| 42 | let current_dir_lower_case = current_dir.join("xml" ); |
| 43 | let current_dir_upper_case = current_dir.join("XML" ); |
| 44 | |
| 45 | let parent_dir_lower_case = current_dir.join("../xml" ); |
| 46 | let parent_dir_upper_case = current_dir.join("../XML" ); |
| 47 | |
| 48 | let crate_dir_lower_case = current_dir.join(&crate_name).join("xml" ); |
| 49 | let crate_dir_upper_case = current_dir.join(&crate_name).join("XML" ); |
| 50 | |
| 51 | // If no XML path is provided, try to find the default XML path. |
| 52 | if xml.is_none() { |
| 53 | if current_dir_lower_case.exists() { |
| 54 | xml = Some( |
| 55 | current_dir_lower_case |
| 56 | .to_str() |
| 57 | .expect("current_dir_lower_case is valid UTF-8" ), |
| 58 | ); |
| 59 | } |
| 60 | |
| 61 | if current_dir_upper_case.exists() { |
| 62 | xml = Some( |
| 63 | current_dir_upper_case |
| 64 | .to_str() |
| 65 | .expect("current_dir_upper_case is valid UTF-8" ), |
| 66 | ); |
| 67 | } |
| 68 | |
| 69 | if parent_dir_lower_case.exists() { |
| 70 | xml = Some( |
| 71 | parent_dir_lower_case |
| 72 | .to_str() |
| 73 | .expect("parent_dir_lower_case is valid UTF-8" ), |
| 74 | ); |
| 75 | } |
| 76 | |
| 77 | if parent_dir_upper_case.exists() { |
| 78 | xml = Some( |
| 79 | parent_dir_upper_case |
| 80 | .to_str() |
| 81 | .expect("parent_dir_upper_case is valid UTF-8" ), |
| 82 | ); |
| 83 | } |
| 84 | |
| 85 | if crate_dir_lower_case.exists() { |
| 86 | xml = Some( |
| 87 | crate_dir_lower_case |
| 88 | .to_str() |
| 89 | .expect("crate_dir_lower_case is valid UTF-8" ), |
| 90 | ); |
| 91 | } |
| 92 | |
| 93 | if crate_dir_upper_case.exists() { |
| 94 | xml = Some( |
| 95 | crate_dir_upper_case |
| 96 | .to_str() |
| 97 | .expect("crate_dir_upper_case is valid UTF-8" ), |
| 98 | ); |
| 99 | } |
| 100 | } |
| 101 | |
| 102 | let env_xml_path = std::env::var("LOCKSTEP_XML_PATH" ); |
| 103 | if env_xml_path.is_ok() { |
| 104 | // Override the default, or argument path if the environment variable is set. |
| 105 | xml = env_xml_path.as_ref().map(|s| s.as_str()).ok(); |
| 106 | } |
| 107 | |
| 108 | // If no XML path is provided and the default XML path is not found, panic. |
| 109 | if xml.is_none() { |
| 110 | panic!( |
| 111 | "No XML path provided and default XML path not found. Current dir: \"{}\" " , |
| 112 | current_dir.to_str().expect("current_dir is valid UTF-8" ) |
| 113 | ); |
| 114 | } |
| 115 | |
| 116 | // Convert, canonicalize and return the XML path. |
| 117 | let xml = PathBuf::from_str(xml.unwrap())?; |
| 118 | Ok(xml.canonicalize()?) |
| 119 | } |
| 120 | |
| 121 | /// A generic helper to find the file path and interface name of a member. |
| 122 | #[doc (hidden)] |
| 123 | #[macro_export ] |
| 124 | macro_rules! find_definition_in_dbus_xml { |
| 125 | ($xml_path_buf:expr, $member:expr, $iface:expr, $msg_type:expr) => {{ |
| 126 | use $crate::MsgType; |
| 127 | |
| 128 | let xml_path_buf: std::path::PathBuf = $xml_path_buf; |
| 129 | let member: &str = $member; |
| 130 | let iface: Option<String> = $iface; |
| 131 | let msg_type: MsgType = $msg_type; |
| 132 | |
| 133 | let mut xml_file_path = None; |
| 134 | let mut interface_name = None; |
| 135 | |
| 136 | let read_dir = std::fs::read_dir(&xml_path_buf).expect("Failed to read XML directory" ); |
| 137 | |
| 138 | // Walk the XML files in the directory. |
| 139 | for entry in read_dir { |
| 140 | let entry = entry.expect("Failed to read entry" ); |
| 141 | |
| 142 | // Skip directories and non-XML files. |
| 143 | if entry.path().is_dir() || entry.path().extension().unwrap() != "xml" { |
| 144 | continue; |
| 145 | } |
| 146 | |
| 147 | let entry_path = entry.path().clone(); |
| 148 | let file = std::fs::File::open(entry.path()).expect("Failed to open file" ); |
| 149 | let node = $crate::zbus_xml::Node::from_reader(file).expect("Failed to parse XML file" ); |
| 150 | |
| 151 | for interface in node.interfaces() { |
| 152 | // If called with an `iface` arg, skip he interfaces that do not match. |
| 153 | if iface.is_some() && interface.name().as_str() != iface.clone().unwrap() { |
| 154 | continue; |
| 155 | } |
| 156 | |
| 157 | match msg_type { |
| 158 | MsgType::Method => { |
| 159 | for dbus_item in interface.methods() { |
| 160 | if dbus_item.name() == member { |
| 161 | if interface_name.is_some() { |
| 162 | panic!( |
| 163 | "Multiple interfaces offer the same {:?} member: {}, please specify the interface name." , |
| 164 | msg_type, member |
| 165 | ); |
| 166 | } |
| 167 | interface_name = Some(interface.name().to_string()); |
| 168 | xml_file_path = Some(entry_path.clone()); |
| 169 | continue; |
| 170 | } |
| 171 | } |
| 172 | } |
| 173 | MsgType::Signal => { |
| 174 | for dbus_item in interface.signals() { |
| 175 | if dbus_item.name() == member { |
| 176 | if interface_name.is_some() { |
| 177 | panic!( |
| 178 | "Multiple interfaces offer the same {:?} member: {}, please specify the interface name." , |
| 179 | msg_type, member |
| 180 | ); |
| 181 | } |
| 182 | interface_name = Some(interface.name().to_string()); |
| 183 | xml_file_path = Some(entry_path.clone()); |
| 184 | continue; |
| 185 | } |
| 186 | } |
| 187 | } |
| 188 | MsgType::Property => { |
| 189 | for dbus_item in interface.properties() { |
| 190 | if dbus_item.name() == member { |
| 191 | if interface_name.is_some() { |
| 192 | panic!( |
| 193 | "Multiple interfaces offer the same {:?} member: {}, please specify the interface name." , |
| 194 | msg_type, member |
| 195 | ); |
| 196 | } |
| 197 | interface_name = Some(interface.name().to_string()); |
| 198 | xml_file_path = Some(entry_path.clone()); |
| 199 | continue; |
| 200 | } |
| 201 | } |
| 202 | } |
| 203 | }; |
| 204 | } |
| 205 | } |
| 206 | |
| 207 | // If the interface member was not found, return an error. |
| 208 | if xml_file_path.is_none() { |
| 209 | panic!("Member not found in XML files." ); |
| 210 | } |
| 211 | |
| 212 | (xml_file_path.unwrap(), interface_name.unwrap()) |
| 213 | }}; |
| 214 | } |
| 215 | |
| 216 | /// Retrieve the signature of a method's return type. |
| 217 | /// |
| 218 | /// This macro will take a method member name and return the signature of the |
| 219 | /// return type. |
| 220 | /// |
| 221 | /// Essentially a wrapper around [`zbus_lockstep::get_method_return_type`], |
| 222 | /// but this macro tries to do its job with less arguments. |
| 223 | /// |
| 224 | /// It will search in the XML specification of the method for the return type |
| 225 | /// and return the signature of that type. |
| 226 | /// |
| 227 | /// If multiple interfaces offer the same method, you will need to specify the |
| 228 | /// interface name as well. |
| 229 | /// |
| 230 | /// This macro can be called with or without the interface name. |
| 231 | /// |
| 232 | /// # Examples |
| 233 | /// |
| 234 | /// Basic usage: |
| 235 | /// |
| 236 | /// ```rust |
| 237 | /// use zbus_lockstep::method_return_signature; |
| 238 | /// use zvariant::Signature; |
| 239 | /// |
| 240 | /// std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 241 | /// |
| 242 | /// let sig = method_return_signature!("RequestName" ); |
| 243 | /// assert_eq!(&sig, &Signature::from_str_unchecked("u" )); |
| 244 | /// ``` |
| 245 | /// The macro supports colling arguments with identifiers as well as without. |
| 246 | /// The macro may also be called with an interface name or interface and argument name: |
| 247 | /// |
| 248 | /// ```rust |
| 249 | /// # use zbus_lockstep::{method_return_signature}; |
| 250 | /// # std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 251 | /// let _sig = method_return_signature!("RequestName" , "org.example.Node" , "grape" ); |
| 252 | /// |
| 253 | /// // or alternatively |
| 254 | /// |
| 255 | /// let _sig = method_return_signature!(member: "RequestName" , interface: "org.example.Node" , argument: "grape" ); |
| 256 | /// ``` |
| 257 | #[macro_export ] |
| 258 | macro_rules! method_return_signature { |
| 259 | ($member:expr) => {{ |
| 260 | use $crate::MsgType; |
| 261 | let member = $member; |
| 262 | |
| 263 | // Looking for default path or path specified by environment variable. |
| 264 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 265 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 266 | "Failed to resolve XML path, current dir: {}" , |
| 267 | current_dir.to_str().unwrap() |
| 268 | )); |
| 269 | |
| 270 | // Find the definition of the method in the XML specification. |
| 271 | let (file_path, interface_name) = |
| 272 | $crate::find_definition_in_dbus_xml!(xml_path, member, None, MsgType::Method); |
| 273 | |
| 274 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 275 | $crate::get_method_return_type(file, &interface_name, member, None) |
| 276 | .expect("Failed to get method arguments type signature" ) |
| 277 | }}; |
| 278 | |
| 279 | (member: $member:expr) => { |
| 280 | $crate::method_return_signature!($member) |
| 281 | }; |
| 282 | |
| 283 | ($member:expr, $interface:expr) => {{ |
| 284 | let member = $member; |
| 285 | use $crate::MsgType; |
| 286 | |
| 287 | let interface = Some($interface.to_string()); |
| 288 | |
| 289 | // Looking for default path or path specified by environment variable. |
| 290 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 291 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 292 | "Failed to resolve XML path, current dir: {}" , |
| 293 | current_dir.to_str().unwrap() |
| 294 | )); |
| 295 | |
| 296 | // Find the definition of the method in the XML specification. |
| 297 | let (file_path, interface_name) = |
| 298 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Method); |
| 299 | |
| 300 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 301 | $crate::get_method_return_type(file, &interface_name, member, None) |
| 302 | .expect("Failed to get method arguments type signature" ) |
| 303 | }}; |
| 304 | |
| 305 | (member: $member:expr, interface: $interface:expr) => { |
| 306 | $crate::method_return_signature!($member, $interface) |
| 307 | }; |
| 308 | |
| 309 | ($member:expr, $interface:expr, $argument:expr) => {{ |
| 310 | let member = $member; |
| 311 | use $crate::MsgType; |
| 312 | |
| 313 | let interface = Some($interface.to_string()); |
| 314 | let argument = Some($argument); |
| 315 | |
| 316 | // Looking for default path or path specified by environment variable. |
| 317 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 318 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 319 | "Failed to resolve XML path, current dir: {}" , |
| 320 | current_dir.to_str().unwrap() |
| 321 | )); |
| 322 | |
| 323 | // Find the definition of the method in the XML specification. |
| 324 | let (file_path, interface_name) = |
| 325 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Method); |
| 326 | |
| 327 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 328 | $crate::get_method_return_type(file, &interface_name, member, argument) |
| 329 | .expect("Failed to get method argument(s) type signature" ) |
| 330 | }}; |
| 331 | |
| 332 | (member: $member:expr, interface: $interface:expr, argument: $argument:expr) => { |
| 333 | $crate::method_return_signature!($member, $interface, $argument) |
| 334 | }; |
| 335 | } |
| 336 | |
| 337 | /// Retrieve the signature of a method's arguments. |
| 338 | /// |
| 339 | /// Essentially a wrapper around [`zbus_lockstep::get_method_args_type`], |
| 340 | /// but this macro tries to do its job with less arguments. |
| 341 | /// |
| 342 | /// This macro will take a method member name and return the signature of the |
| 343 | /// arguments type. |
| 344 | /// |
| 345 | /// It will search in the XML specification of the method for the arguments type |
| 346 | /// and return the signature of that type. |
| 347 | /// |
| 348 | /// If multiple interfaces offer the same member, you will need to |
| 349 | /// specify the interface name as well. |
| 350 | /// |
| 351 | /// This macro can be called with or without the interface name. |
| 352 | /// |
| 353 | /// # Examples |
| 354 | /// |
| 355 | /// ```rust |
| 356 | /// use zbus_lockstep::method_args_signature; |
| 357 | /// use zvariant::Signature; |
| 358 | /// |
| 359 | /// std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 360 | /// |
| 361 | /// let sig = method_args_signature!("RequestName" ); |
| 362 | /// assert_eq!(&sig, &Signature::from_str_unchecked("(su)" )); |
| 363 | /// ``` |
| 364 | /// The macro supports colling arguments with identifiers as well as without. |
| 365 | /// The macro may also be called with an interface name or interface and argument name: |
| 366 | /// |
| 367 | /// ```rust |
| 368 | /// # use zbus_lockstep::{method_args_signature}; |
| 369 | /// # std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 370 | /// let _sig = method_args_signature!("RequestName" , "org.example.Node" , "apple" ); |
| 371 | /// |
| 372 | /// // or alternatively |
| 373 | /// |
| 374 | /// let _sig = method_args_signature!(member: "RequestName" , interface: "org.example.Node" , argument: "apple" ); |
| 375 | /// ``` |
| 376 | #[macro_export ] |
| 377 | macro_rules! method_args_signature { |
| 378 | ($member:expr) => {{ |
| 379 | use $crate::MsgType; |
| 380 | let member = $member; |
| 381 | |
| 382 | // Looking for default path or path specified by environment variable. |
| 383 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 384 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 385 | "Failed to resolve XML path, current dir: {}" , |
| 386 | current_dir.to_str().unwrap() |
| 387 | )); |
| 388 | |
| 389 | // Find the definition of the method in the XML specification. |
| 390 | let (file_path, interface_name) = |
| 391 | $crate::find_definition_in_dbus_xml!(xml_path, member, None, MsgType::Method); |
| 392 | |
| 393 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 394 | $crate::get_method_args_type(file, &interface_name, member, None) |
| 395 | .expect("Failed to get method arguments type signature" ) |
| 396 | }}; |
| 397 | |
| 398 | (member: $member:expr) => { |
| 399 | $crate::method_args_signature!($member) |
| 400 | }; |
| 401 | |
| 402 | ($member:expr, $interface:expr) => {{ |
| 403 | use $crate::MsgType; |
| 404 | let member = $member; |
| 405 | |
| 406 | let interface = Some($interface.to_string()); |
| 407 | |
| 408 | // Looking for default path or path specified by environment variable. |
| 409 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 410 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 411 | "Failed to resolve XML path, current dir: {}" , |
| 412 | current_dir.to_str().unwrap() |
| 413 | )); |
| 414 | |
| 415 | // Find the definition of the method in the XML specification. |
| 416 | let (file_path, interface_name) = |
| 417 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Method); |
| 418 | |
| 419 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 420 | $crate::get_method_args_type(file, &interface_name, member, None) |
| 421 | .expect("Failed to get method arguments type signature" ) |
| 422 | }}; |
| 423 | |
| 424 | (member: $member:expr, interface: $interface:expr) => { |
| 425 | $crate::method_args_signature!($member, $interface) |
| 426 | }; |
| 427 | |
| 428 | ($member:expr, $interface:expr, $argument:expr) => {{ |
| 429 | use $crate::MsgType; |
| 430 | let member = $member; |
| 431 | let interface = Some($interface.to_string()); |
| 432 | |
| 433 | let argument = Some($argument); |
| 434 | |
| 435 | // Looking for default path or path specified by environment variable. |
| 436 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 437 | |
| 438 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 439 | "Failed to resolve XML path, current dir: {}" , |
| 440 | current_dir.to_str().unwrap() |
| 441 | )); |
| 442 | // Find the definition of the method in the XML specification. |
| 443 | let (file_path, interface_name) = |
| 444 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Method); |
| 445 | |
| 446 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 447 | $crate::get_method_args_type(file, &interface_name, member, argument) |
| 448 | .expect("Failed to get method argument(s) type signature" ) |
| 449 | }}; |
| 450 | |
| 451 | (member: $member:expr, interface: $interface:expr, argument: $argument:expr) => { |
| 452 | $crate::method_args_signature!($member, $interface, $argument) |
| 453 | }; |
| 454 | } |
| 455 | |
| 456 | /// Retrieve the signature of a signal's body type. |
| 457 | /// |
| 458 | /// Essentially a wrapper around [`zbus_lockstep::get_signal_body_type`], |
| 459 | /// but this macro tries to find it with less arguments. |
| 460 | /// |
| 461 | /// This macro will take a signal member name and return the signature of the |
| 462 | /// signal body type. |
| 463 | /// |
| 464 | /// If multiple interfaces offer the same member, you will need to |
| 465 | /// specify the interface name as well. |
| 466 | /// |
| 467 | /// This macro can be called with or without the interface name. |
| 468 | /// |
| 469 | /// # Examples |
| 470 | /// |
| 471 | /// ```rust |
| 472 | /// use zbus_lockstep::signal_body_type_signature; |
| 473 | /// use zvariant::Signature; |
| 474 | /// |
| 475 | /// std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 476 | /// |
| 477 | /// let sig = signal_body_type_signature!("AddNode" ); |
| 478 | /// assert_eq!(&sig, &Signature::from_str_unchecked("(so)" )); |
| 479 | /// ``` |
| 480 | /// The macro supports colling arguments with identifiers as well as without. |
| 481 | /// The macro may also be called with an interface name or interface and argument name: |
| 482 | /// |
| 483 | /// ```rust |
| 484 | /// # use zbus_lockstep::{signal_body_type_signature}; |
| 485 | /// # std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 486 | /// let _sig = signal_body_type_signature!("Alert" , "org.example.Node" , "color" ); |
| 487 | /// |
| 488 | /// // or alternatively |
| 489 | /// |
| 490 | /// let _sig = signal_body_type_signature!(member: "Alert" , interface: "org.example.Node" , argument: "color" ); |
| 491 | /// ``` |
| 492 | #[macro_export ] |
| 493 | macro_rules! signal_body_type_signature { |
| 494 | ($member:expr) => {{ |
| 495 | use $crate::MsgType; |
| 496 | let member = $member; |
| 497 | |
| 498 | // Looking for default path or path specified by environment variable. |
| 499 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 500 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 501 | "Failed to resolve XML path, current dir: {}" , |
| 502 | current_dir.to_str().unwrap() |
| 503 | )); |
| 504 | |
| 505 | // Find the definition of the method in the XML specification. |
| 506 | let (file_path, interface_name) = |
| 507 | $crate::find_definition_in_dbus_xml!(xml_path, member, None, MsgType::Signal); |
| 508 | |
| 509 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 510 | |
| 511 | $crate::get_signal_body_type(file, &interface_name, member, None) |
| 512 | .expect("Failed to get method arguments type signature" ) |
| 513 | }}; |
| 514 | |
| 515 | (member: $member:expr) => { |
| 516 | $crate::signal_body_type_signature!($member) |
| 517 | }; |
| 518 | |
| 519 | ($member:expr, $interface:expr) => {{ |
| 520 | use $crate::MsgType; |
| 521 | let member = $member; |
| 522 | let interface = Some($interface.to_string()); |
| 523 | |
| 524 | // Looking for default path or path specified by environment variable. |
| 525 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 526 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 527 | "Failed to resolve XML path, current dir: {}" , |
| 528 | current_dir.to_str().unwrap() |
| 529 | )); |
| 530 | |
| 531 | // Find the definition of the method in the XML specification. |
| 532 | let (file_path, interface_name) = |
| 533 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Signal); |
| 534 | |
| 535 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 536 | $crate::get_signal_body_type(file, &interface_name, member, None) |
| 537 | .expect("Failed to get method arguments type signature" ) |
| 538 | }}; |
| 539 | |
| 540 | (member: $member:expr, interface: $interface:expr) => { |
| 541 | $crate::signal_body_type_signature!($member, $interface) |
| 542 | }; |
| 543 | |
| 544 | ($member:expr, $interface:expr, $argument:expr) => {{ |
| 545 | use $crate::MsgType; |
| 546 | let member = $member; |
| 547 | let interface = Some($interface.to_string()); |
| 548 | |
| 549 | let argument = Some($argument); |
| 550 | |
| 551 | // Looking for default path or path specified by environment variable. |
| 552 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 553 | |
| 554 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 555 | "Failed to resolve XML path, current dir: {}" , |
| 556 | current_dir.to_str().unwrap() |
| 557 | )); |
| 558 | |
| 559 | // Find the definition of the method in the XML specification. |
| 560 | let (file_path, interface_name) = |
| 561 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Signal); |
| 562 | |
| 563 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 564 | $crate::get_signal_body_type(file, &interface_name, member, argument) |
| 565 | .expect("Failed to get method argument(s) type signature" ) |
| 566 | }}; |
| 567 | |
| 568 | (member: $member:expr, interface: $interface:expr, argument: $argument:expr) => { |
| 569 | $crate::signal_body_type_signature!($member, $interface, $argument) |
| 570 | }; |
| 571 | } |
| 572 | |
| 573 | /// Retrieve the signature of a property's type. |
| 574 | /// |
| 575 | /// Essentially a wrapper around [`zbus_lockstep::get_property_type`], |
| 576 | /// but this macro tries to do with less arguments. |
| 577 | /// |
| 578 | /// This macro will take a property name and return the signature of the |
| 579 | /// property's type. |
| 580 | /// |
| 581 | /// If multiple interfaces offer the same member, you will need to |
| 582 | /// specify the interface name as well. |
| 583 | /// |
| 584 | /// This macro can be called with or without the interface name. |
| 585 | /// |
| 586 | /// # Examples |
| 587 | /// |
| 588 | /// ```rust |
| 589 | /// use zbus_lockstep::property_type_signature; |
| 590 | /// use zvariant::Signature; |
| 591 | /// |
| 592 | /// std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 593 | /// |
| 594 | /// let sig = property_type_signature!("Features" ); |
| 595 | /// assert_eq!(&sig, &Signature::from_str_unchecked("as" )); |
| 596 | /// ``` |
| 597 | /// The member name and/or interface name can be used tp identify the arguments: |
| 598 | /// |
| 599 | /// ```rust |
| 600 | /// # use zbus_lockstep::{property_type_signature}; |
| 601 | /// # std::env::set_var("LOCKSTEP_XML_PATH" , "../xml" ); |
| 602 | /// let _sig = property_type_signature!(member: "Features" , interface: "org.example.Node" ); |
| 603 | /// ``` |
| 604 | #[macro_export ] |
| 605 | macro_rules! property_type_signature { |
| 606 | ($member:expr) => {{ |
| 607 | use $crate::MsgType; |
| 608 | let member = $member; |
| 609 | |
| 610 | // Looking for default path or path specified by environment variable. |
| 611 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 612 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 613 | "Failed to resolve XML path, current dir: {}" , |
| 614 | current_dir.to_str().unwrap() |
| 615 | )); |
| 616 | |
| 617 | // Find the definition of the method in the XML specification. |
| 618 | let (file_path, interface_name) = |
| 619 | $crate::find_definition_in_dbus_xml!(xml_path, member, None, MsgType::Property); |
| 620 | |
| 621 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 622 | |
| 623 | $crate::get_property_type(file, &interface_name, member) |
| 624 | .expect("Failed to get property type signature" ) |
| 625 | }}; |
| 626 | |
| 627 | (member: $member:expr) => { |
| 628 | $crate::property_type_signature!($member) |
| 629 | }; |
| 630 | |
| 631 | ($member:expr, $interface:expr) => {{ |
| 632 | use $crate::MsgType; |
| 633 | let member = $member; |
| 634 | let interface = Some($interface.to_string()); |
| 635 | |
| 636 | // Looking for default path or path specified by environment variable. |
| 637 | let current_dir: std::path::PathBuf = std::env::current_dir().unwrap(); |
| 638 | let xml_path = $crate::resolve_xml_path(None).expect(&format!( |
| 639 | "Failed to resolve XML path, current dir: {}" , |
| 640 | current_dir.to_str().unwrap() |
| 641 | )); |
| 642 | |
| 643 | // Find the definition of the method in the XML specification. |
| 644 | let (file_path, interface_name) = |
| 645 | $crate::find_definition_in_dbus_xml!(xml_path, member, interface, MsgType::Property); |
| 646 | |
| 647 | let file = std::fs::File::open(file_path).expect("Failed to open file" ); |
| 648 | $crate::get_property_type(file, &interface_name, member) |
| 649 | .expect("Failed to get property type signature" ) |
| 650 | }}; |
| 651 | |
| 652 | (member: $member:expr, interface: $interface:expr) => { |
| 653 | $crate::property_type_signature!($member, $interface) |
| 654 | }; |
| 655 | } |
| 656 | |
| 657 | #[cfg (test)] |
| 658 | mod test { |
| 659 | use zvariant::Signature; |
| 660 | |
| 661 | use crate::signal_body_type_signature; |
| 662 | |
| 663 | #[test ] |
| 664 | fn test_signal_body_signature_macro() { |
| 665 | // path to XML files can be set by environment variable |
| 666 | // std::env::set_var("LOCKSTEP_XML_PATH", "../xml"); |
| 667 | // But `resolve_xml_path` can find the `xml` in parent by itself. |
| 668 | |
| 669 | let sig = crate::signal_body_type_signature!("AddNode" ); |
| 670 | assert_eq!(&sig, &zvariant::Signature::from_str_unchecked("(so)" )); |
| 671 | } |
| 672 | |
| 673 | #[test ] |
| 674 | fn test_signal_body_signature_macro_with_identifier() { |
| 675 | let sig = crate::signal_body_type_signature!(member: "AddNode" ); |
| 676 | assert_eq!(sig, Signature::from_str_unchecked("(so)" )); |
| 677 | } |
| 678 | |
| 679 | #[test ] |
| 680 | fn test_signal_body_signature_macro_with_interface() { |
| 681 | let sig = crate::signal_body_type_signature!("AddNode" , "org.example.Node" ); |
| 682 | assert_eq!(sig, Signature::from_str_unchecked("(so)" )); |
| 683 | } |
| 684 | |
| 685 | #[test ] |
| 686 | fn test_signal_body_signature_macro_with_interface_and_identifiers() { |
| 687 | let sig = |
| 688 | crate::signal_body_type_signature!(member: "AddNode" , interface: "org.example.Node" ); |
| 689 | assert_eq!(sig, Signature::from_str_unchecked("(so)" )); |
| 690 | } |
| 691 | |
| 692 | #[test ] |
| 693 | fn test_signal_body_signature_macro_with_argument_and_interface() { |
| 694 | let sig = crate::signal_body_type_signature!("Alert" , "org.example.Node" , "volume" ); |
| 695 | assert_eq!(sig, Signature::from_str_unchecked("d" )); |
| 696 | } |
| 697 | |
| 698 | #[test ] |
| 699 | fn test_signal_body_signature_macro_with_argument_and_identifiers_and_interface() { |
| 700 | let sig = crate::signal_body_type_signature!( |
| 701 | member: "Alert" , |
| 702 | interface: "org.example.Node" , |
| 703 | argument: "urgent" |
| 704 | ); |
| 705 | assert_eq!(sig, Signature::from_str_unchecked("b" )); |
| 706 | } |
| 707 | |
| 708 | #[test ] |
| 709 | fn test_method_args_signature_macro() { |
| 710 | let sig = crate::method_args_signature!("RequestName" ); |
| 711 | assert_eq!(sig, Signature::from_str_unchecked("(su)" )); |
| 712 | } |
| 713 | |
| 714 | #[test ] |
| 715 | fn test_method_args_signature_macro_with_identifier() { |
| 716 | let sig = crate::method_args_signature!(member: "RequestName" ); |
| 717 | assert_eq!(sig, Signature::from_str_unchecked("(su)" )); |
| 718 | } |
| 719 | |
| 720 | #[test ] |
| 721 | fn test_method_args_signature_macro_with_interface() { |
| 722 | let sig = crate::method_args_signature!("RequestName" , "org.example.Node" ); |
| 723 | assert_eq!(sig, Signature::from_str_unchecked("(su)" )); |
| 724 | } |
| 725 | |
| 726 | #[test ] |
| 727 | fn test_method_args_signature_macro_with_interface_and_identifiers() { |
| 728 | let sig = |
| 729 | crate::method_args_signature!(member: "RequestName" , interface: "org.example.Node" ); |
| 730 | assert_eq!(sig, Signature::from_str_unchecked("(su)" )); |
| 731 | } |
| 732 | |
| 733 | #[test ] |
| 734 | fn test_method_args_signature_macro_with_argument_and_interface() { |
| 735 | let sig = crate::method_args_signature!("RequestName" , "org.example.Node" , "apple" ); |
| 736 | assert_eq!(sig, Signature::from_str_unchecked("s" )); |
| 737 | } |
| 738 | |
| 739 | #[test ] |
| 740 | fn test_method_args_signature_macro_with_argument_and_identifiers_and_interface() { |
| 741 | let sig = crate::method_args_signature!( |
| 742 | member: "RequestName" , |
| 743 | interface: "org.example.Node" , |
| 744 | argument: "orange" |
| 745 | ); |
| 746 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 747 | } |
| 748 | |
| 749 | #[test ] |
| 750 | fn test_method_return_signature_macro() { |
| 751 | let sig = crate::method_return_signature!("RequestName" ); |
| 752 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 753 | } |
| 754 | |
| 755 | #[test ] |
| 756 | fn test_method_return_signature_macro_with_identifier() { |
| 757 | let sig = crate::method_return_signature!(member: "RequestName" ); |
| 758 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 759 | } |
| 760 | |
| 761 | #[test ] |
| 762 | fn test_method_return_signature_macro_with_interface() { |
| 763 | let sig = crate::method_return_signature!("RequestName" , "org.example.Node" ); |
| 764 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 765 | } |
| 766 | |
| 767 | #[test ] |
| 768 | fn test_method_return_signature_macro_with_interface_and_identifiers() { |
| 769 | let sig = |
| 770 | crate::method_return_signature!(member: "RequestName" , interface: "org.example.Node" ); |
| 771 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 772 | } |
| 773 | |
| 774 | #[test ] |
| 775 | fn test_method_return_signature_macro_with_argument_and_interface() { |
| 776 | let sig = crate::method_return_signature!("RequestName" , "org.example.Node" , "grape" ); |
| 777 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 778 | } |
| 779 | |
| 780 | #[test ] |
| 781 | fn test_method_return_signature_macro_with_argument_and_identifiers_and_interface() { |
| 782 | let sig = crate::method_return_signature!( |
| 783 | member: "RequestName" , |
| 784 | interface: "org.example.Node" , |
| 785 | argument: "grape" |
| 786 | ); |
| 787 | assert_eq!(sig, Signature::from_str_unchecked("u" )); |
| 788 | } |
| 789 | |
| 790 | #[test ] |
| 791 | fn test_property_type_signature_macro() { |
| 792 | let sig = crate::property_type_signature!("Features" ); |
| 793 | assert_eq!(sig, Signature::from_str_unchecked("as" )); |
| 794 | } |
| 795 | |
| 796 | #[test ] |
| 797 | fn test_property_type_signature_macro_with_identifier() { |
| 798 | let sig = crate::property_type_signature!(member: "Features" ); |
| 799 | assert_eq!(sig, Signature::from_str_unchecked("as" )); |
| 800 | } |
| 801 | |
| 802 | #[test ] |
| 803 | fn test_property_type_signature_macro_with_interface() { |
| 804 | let sig = crate::property_type_signature!("Features" , "org.example.Node" ); |
| 805 | assert_eq!(sig, Signature::from_str_unchecked("as" )); |
| 806 | } |
| 807 | |
| 808 | #[test ] |
| 809 | fn test_property_type_signature_macro_with_interface_and_identifiers() { |
| 810 | let sig = |
| 811 | crate::property_type_signature!(member: "Features" , interface: "org.example.Node" ); |
| 812 | assert_eq!(sig, Signature::from_str_unchecked("as" )); |
| 813 | } |
| 814 | } |
| 815 | |