1#![allow(unused_macros)]
2#![allow(dead_code)]
3#![allow(unused_imports)]
4
5use std::{fs, path::PathBuf, str::FromStr};
6
7use crate::Result;
8
9pub 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 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 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 xml = env_xml_path.as_ref().map(|s| s.as_str()).ok();
106 }
107
108 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 let xml = PathBuf::from_str(xml.unwrap())?;
118 Ok(xml.canonicalize()?)
119}
120
121#[doc(hidden)]
123#[macro_export]
124macro_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 for entry in read_dir {
140 let entry = entry.expect("Failed to read entry");
141
142 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 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 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#[macro_export]
258macro_rules! method_return_signature {
259 ($member:expr) => {{
260 use $crate::MsgType;
261 let member = $member;
262
263 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 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 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 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 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 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#[macro_export]
377macro_rules! method_args_signature {
378 ($member:expr) => {{
379 use $crate::MsgType;
380 let member = $member;
381
382 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 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 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 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 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 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#[macro_export]
493macro_rules! signal_body_type_signature {
494 ($member:expr) => {{
495 use $crate::MsgType;
496 let member = $member;
497
498 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 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 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 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 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 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#[macro_export]
605macro_rules! property_type_signature {
606 ($member:expr) => {{
607 use $crate::MsgType;
608 let member = $member;
609
610 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 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 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 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)]
658mod test {
659 use zvariant::Signature;
660
661 use crate::signal_body_type_signature;
662
663 #[test]
664 fn test_signal_body_signature_macro() {
665 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}