atspi_common/
macros.rs

1/// Expands to implement the required methods for the [`crate::EventProperties`] trait.
2/// This depends on the struct to have an `item` field of type [`crate::ObjectRef`].
3///
4/// ```ignore
5/// impl_from_interface_event_enum_for_event!(TextCaretMovedEvent);
6/// ```
7///
8/// Expands to:
9///
10/// ```ignore
11/// impl EventProperties for TextCaretMovedEvent {
12///   fn sender(&self) -> UniqueName<'_> {
13///     self.item.name.as_ref()
14///   }
15///   fn path(&self) -> ObjectPath<'_> {
16///     self.item.path.as_ref()
17///   }
18/// }
19/// ```
20macro_rules! impl_event_properties {
21	($type:ty) => {
22		impl crate::EventProperties for $type {
23			fn sender(&self) -> zbus_names::UniqueName<'_> {
24				self.item.name.as_ref()
25			}
26			fn path(&self) -> zvariant::ObjectPath<'_> {
27				self.item.path.as_ref()
28			}
29		}
30	};
31}
32
33/// Expands to implement From for [`crate::ObjectRef`].
34/// This depends on the struct to have an `item` field of type [`crate::ObjectRef`].
35///
36/// ```ignore
37/// impl_from_object_ref!(TextCaretMovedEvent);
38/// ```
39///
40/// Exapnds to:
41///
42/// ```ignore
43/// impl From<ObjectRef> for TextCaretMovedItem {
44///     fn from(obj_ref: ObjectRef) -> Self {
45///         Self { item: obj_ref }
46///     }
47/// }
48/// ```
49macro_rules! impl_from_object_ref {
50	($type:ty) => {
51		impl From<crate::ObjectRef> for $type {
52			fn from(obj_ref: crate::ObjectRef) -> Self {
53				Self { item: obj_ref }
54			}
55		}
56	};
57}
58
59#[cfg(feature = "wrappers")]
60/// Expands to a conversion given the enclosed event type and outer `Event` variant.
61///
62/// eg
63/// ```ignore
64/// impl_from_interface_event_enum_for_event!(ObjectEvents, Event::Object);
65/// ```
66/// expands to:
67///
68/// ```ignore
69/// impl From<ObjectEvents> for Event {
70///     fn from(event_variant: ObjectEvents) -> Event {
71///         Event::Object(event_variant.into())
72///     }
73/// }
74/// ```
75macro_rules! impl_from_interface_event_enum_for_event {
76	($outer_type:ty, $outer_variant:path) => {
77		#[cfg(feature = "wrappers")]
78		impl From<$outer_type> for Event {
79			fn from(event_variant: $outer_type) -> Event {
80				$outer_variant(event_variant.into())
81			}
82		}
83	};
84}
85
86#[cfg(feature = "wrappers")]
87/// Expands to a conversion given the enclosed event enum type and outer `Event` variant.
88///
89/// eg
90/// ```ignore
91/// impl_try_from_event_for_interface_enum!(ObjectEvents, Event::Object);
92/// ```
93/// expands to:
94///
95/// ```ignore
96/// impl TryFrom<Event> for ObjectEvents {
97///     type Error = AtspiError;
98///     fn try_from(generic_event: Event) -> Result<ObjectEvents, Self::Error> {
99///         if let Event::Object(event_type) = generic_event {
100///             Ok(event_type)
101///         } else {
102///             Err(AtspiError::Conversion("Invalid type"))
103///         }
104///     }
105/// }
106/// ```
107macro_rules! impl_try_from_event_for_interface_enum {
108	($outer_type:ty, $outer_variant:path) => {
109		impl TryFrom<Event> for $outer_type {
110			type Error = AtspiError;
111			fn try_from(generic_event: Event) -> Result<$outer_type, Self::Error> {
112				if let $outer_variant(event_type) = generic_event {
113					Ok(event_type)
114				} else {
115					Err(AtspiError::Conversion("Invalid type"))
116				}
117			}
118		}
119	};
120}
121
122#[cfg(feature = "wrappers")]
123/// Expands to a conversion given the user facing event type,
124/// the wrapping interface enum variant, and the outer `Event` variant.
125///
126/// ```ignore
127/// impl_from_user_facing_event_for_interface_event_enum!(StateChangedEvent, ObjectEvents, ObjectEvents::StateChanged);
128/// ```
129///
130/// expands to:
131///
132/// ```ignore
133/// impl From<StateChangedEvent> for ObjectEvents {
134///     fn from(specific_event: StateChangedEvent) -> ObjectEvents {
135///         ObjectEvents::StateChanged(specific_event)
136///     }
137/// }
138/// ```
139macro_rules! impl_from_user_facing_event_for_interface_event_enum {
140	($inner_type:ty, $outer_type:ty, $inner_variant:path) => {
141		impl From<$inner_type> for $outer_type {
142			fn from(specific_event: $inner_type) -> $outer_type {
143				$inner_variant(specific_event)
144			}
145		}
146	};
147}
148
149#[cfg(feature = "wrappers")]
150/// Expands to a conversion given two arguments,
151/// 1. the user facing event type `(inner_type)`
152///    which relies on a conversion to its interface variant enum type variant.
153/// 2. the outer `Event::<Interface(<InterfaceEnum>)>` wrapper.,
154///    the enum type and outtermost variant.
155///
156/// ```ignore                                   user facing type, outer event variant
157/// impl_from_user_facing_type_for_event_enum!(StateChangedEvent, Event::Object);
158/// ```
159///
160/// expands to:
161///
162/// ```ignore
163/// impl From<StateChangedEvent> for Event {
164///    fn from(event_variant: StateChangedEvent) -> Event {
165///       Event::Object(ObjectEvents::StateChanged(event_variant))
166///   }
167/// }
168/// ```
169macro_rules! impl_from_user_facing_type_for_event_enum {
170	($inner_type:ty, $outer_variant:path) => {
171		#[cfg(feature = "wrappers")]
172		impl From<$inner_type> for Event {
173			fn from(event_variant: $inner_type) -> Event {
174				$outer_variant(event_variant.into())
175			}
176		}
177	};
178}
179
180#[cfg(feature = "wrappers")]
181/// Expands to a `TryFrom<Event> for T` where T is the user facing type.
182/// The macro takes three arguments:
183///
184/// 1. The user facing type.
185/// 2. The inner variant of the user facing type.
186/// 3. The outer variant of the `Event` enum.
187///
188/// ```ignore
189/// impl_try_from_event_for_user_facing_type!(
190///     StateChangedEvent,
191///     ObjectEvents::StateChanged,
192///     Event::Object
193/// );
194/// ```
195/// expands to:
196///
197/// ```ignore
198/// impl TryFrom<Event> for StateChangedEvent {
199///     type Error = AtspiError;
200///     fn try_from(generic_event: Event) -> Result<StateChangedEvent, Self::Error> {
201///         if let Event::Object(ObjectEvents::StateChanged(specific_event)) = generic_event {
202///             Ok(specific_event)
203///         } else {
204///             Err(AtspiError::Conversion("Invalid type"))
205///         }
206///     }
207/// }
208/// ```
209///
210macro_rules! impl_try_from_event_for_user_facing_type {
211	($inner_type:ty, $inner_variant:path, $outer_variant:path) => {
212		#[cfg(feature = "wrappers")]
213		impl TryFrom<crate::Event> for $inner_type {
214			type Error = AtspiError;
215			fn try_from(generic_event: crate::Event) -> Result<$inner_type, Self::Error> {
216				if let $outer_variant($inner_variant(specific_event)) = generic_event {
217					Ok(specific_event)
218				} else {
219					Err(AtspiError::Conversion("Invalid type"))
220				}
221			}
222		}
223	};
224}
225
226/// Implements the `TryFrom` trait for a given event type.
227/// Converts a user facing event type into a `zbus::Message`.
228///
229/// # Example
230/// ```ignore
231/// impl_to_dbus_message!(StateChangedEvent);
232/// ```
233/// expands to:
234///
235/// ```ignore
236/// impl TryFrom<StateChangedEvent> for zbus::Message {
237///   type Error = AtspiError;
238///   fn try_from(event: StateChangedEvent) -> Result<Self, Self::Error> {
239///     Ok(zbus::Message::signal(
240///         event.path(),
241///         StateChangedEvent::DBUS_INTERFACE,
242///         StateChangedEvent::DBUS_MEMBER,
243///     )?
244///     .sender(event.sender())?
245///     .build(&event.body())?)
246///  }
247/// }
248///
249macro_rules! impl_to_dbus_message {
250	($type:ty) => {
251		#[cfg(feature = "zbus")]
252		impl TryFrom<$type> for zbus::Message {
253			type Error = AtspiError;
254			fn try_from(event: $type) -> Result<Self, Self::Error> {
255				use crate::events::{DBusInterface, DBusMember, MessageConversion};
256				Ok(zbus::Message::signal(
257					event.path(),
258					<$type as DBusInterface>::DBUS_INTERFACE,
259					<$type as DBusMember>::DBUS_MEMBER,
260				)?
261				.sender(event.sender().to_string())?
262				.build(&event.body())?)
263			}
264		}
265	};
266}
267
268/// Implements the `TryFrom` trait for a given event type.
269/// Converts a `zbus::Message` into a user facing event type.
270///
271/// See [`crate::events::MessageConversion`] for details on implementation.
272///
273/// # Example
274/// ```ignore
275/// impl_from_dbus_message!(StateChangedEvent);
276/// ```
277/// expands to:
278///
279/// ```ignore
280/// impl TryFrom<&zbus::Message> for StateChangedEvents {
281///   type Error = AtspiError;
282///   fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
283///    let hdr = msg.header();
284///     <$type as MessageConversion>::try_from_message(msg, hdr)
285///   }
286/// }
287/// ```
288///
289/// There is also a variant that can be used for events whose [`crate::events::MessageConversion::Body`] is not
290/// [`crate::events::event_body::EventBodyOwned`]. You can call this by setting the second parameter to `Explicit`.
291macro_rules! impl_from_dbus_message {
292	($type:ty) => {
293		impl_from_dbus_message!($type, Auto);
294	};
295
296	($type:ty, Auto) => {
297		#[cfg(feature = "zbus")]
298		impl<'msg> TryFrom<&'msg zbus::Message> for $type {
299			type Error = AtspiError;
300			fn try_from(msg: &'msg zbus::Message) -> Result<Self, Self::Error> {
301				use crate::events::{EventBody, EventBodyQtBorrowed};
302				use crate::events::traits::{MessageConversion, MessageConversionExt};
303				use zvariant::Type;
304				use crate::ObjectRef;
305
306				let hdr = msg.header();
307				<Self as MessageConversionExt<<Self as MessageConversion>::Body<'_>>>::validate_interface(&hdr)?;
308				<Self as MessageConversionExt<<Self as MessageConversion>::Body<'_>>>::validate_member(&hdr)?;
309				let item = ObjectRef::try_from(&hdr)?;
310
311				let body = msg.body();
312				let signature = body.signature();
313
314				if signature == EventBody::SIGNATURE || signature == EventBodyQtBorrowed::SIGNATURE {
315					Ok(Self::from_message_unchecked_parts(item, body)?)
316				} else {
317					Err(AtspiError::SignatureMatch(format!(
318						"signature mismatch: expected: {}, signal body: {}",
319						msg.body().signature(),
320						<Self as MessageConversion>::Body::SIGNATURE,
321					)))
322				}
323			}
324		}
325	};
326
327	($type:ty, Explicit) => {
328		#[cfg(feature = "zbus")]
329		impl TryFrom<&zbus::Message> for $type {
330			type Error = crate::AtspiError;
331			fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
332				let hdr = msg.header();
333				<$type as crate::events::MessageConversionExt<<$type as MessageConversion>::Body<'_>>>::try_from_message(msg, &hdr)
334			}
335		}
336	};
337}
338
339// We decorate the macro with a `#[cfg(test)]` attribute.
340// This prevents Clippy from complaining about the macro not being used.
341// It is being used, but only in test mode.
342//
343/// Tests `Default` and `BusProperties::from_message_unchecked` for a given event struct.
344///
345/// Obtains a default for the given event struct.
346/// Asserts that the path and sender are the default.
347///
348/// Breaks the struct down into item (the associated object) and body.
349/// Then tests `BusProperties::from_message_unchecked` with the item and body.
350#[cfg(test)]
351macro_rules! generic_event_test_case {
352	($type:ty) => {
353		#[test]
354		fn generic_event_uses() {
355			use crate::events::traits::MessageConversion;
356			let struct_event = <$type>::default();
357			assert_eq!(struct_event.path().as_str(), "/org/a11y/atspi/accessible/null");
358			assert_eq!(struct_event.sender().as_str(), ":0.0");
359			let body = struct_event.body();
360			let body2 = Message::method_call(
361				struct_event.path().as_str(),
362				<$type as crate::events::DBusMember>::DBUS_MEMBER,
363			)
364			.unwrap()
365			.sender(struct_event.sender().as_str())
366			.unwrap()
367			.build(&(body,))
368			.unwrap();
369			let header = body2.header();
370			let build_struct = <$type>::from_message_unchecked(&body2, &header)
371				.expect("<$type as Default>'s parts should build a valid ObjectRef");
372			assert_eq!(struct_event, build_struct);
373		}
374	};
375}
376
377// We decorate the macro with a `#[cfg(test)]` attribute.
378// This prevents Clippy from complaining about the macro not being used.
379// It is being used, but only in test mode.
380//
381/// Tests conversion to and from the `Event` enum.
382///
383/// Obtains a default for the given event struct.
384/// Converts the struct into the `Event` enum, wrapping the struct.
385/// Converts the `Event` enum into the given event struct.
386/// Asserts that the original struct and the converted struct are equal.
387#[cfg(test)]
388macro_rules! event_has_matching_xml_definition {
389	($type:ty) => {
390		#[test]
391		fn event_has_matching_xml_definition() {
392      use zbus_xml;
393				use crate::events::{DBusInterface, DBusMember};
394
395			let fname = match <$type>::DBUS_INTERFACE.split(".").last().expect("Has last section") {
396				"Cache" => "xml/Cache.xml",
397				"Socket" => "xml/Socket.xml",
398				"Registry" => "xml/Registry.xml",
399				_ => "xml/Event.xml",
400			};
401      let reader = std::fs::File::open(fname).expect("Valid file path!");
402      let xml = zbus_xml::Node::from_reader(reader).expect("Valid DBus XML file!");
403      let Some(interface) = xml.interfaces().iter().find(|int| int.name() == <$type>::DBUS_INTERFACE) else {
404          let possible_names: Vec<String> = xml.interfaces().iter().map(|int| int.name().as_str().to_string()).collect();
405          panic!("{} has interface name {}, but it was not found in the list of interfaces defined in the XML: {:?}", std::any::type_name::<$type>(), <$type>::DBUS_INTERFACE, possible_names);
406      };
407      let Some(_member) = interface.signals().iter().find(|mem| mem.name() == <$type>::DBUS_MEMBER) else {
408          let possible_names: Vec<String> = interface.signals().iter().map(|mem| mem.name().as_str().to_string()).collect();
409          panic!("{} has interface name {} and member name {}, but it was not found in the list of members defined in the corresponding interface in the XML: {:?}", std::any::type_name::<$type>(), <$type>::DBUS_INTERFACE, <$type>::DBUS_MEMBER, possible_names);
410      };
411		}
412	};
413}
414
415#[cfg(test)]
416macro_rules! zbus_message_qtspi_test_case {
417    ($type:ty, Auto) => {
418      #[cfg(feature = "zbus")]
419     #[test]
420    fn zbus_message_conversion_qtspi() {
421		use crate::events::EventTypeProperties;
422		use crate::events::MessageConversion;
423
424		// in the case that the body type is EventBodyOwned, we need to also check successful
425      // conversion from a QSPI-style body.
426      let ev = <$type>::default();
427      let qt: crate::events::EventBodyQtOwned = ev.body().into();
428      let msg = zbus::Message::signal(
429          ev.path(),
430          ev.interface(),
431          ev.member(),
432      )
433        .unwrap()
434        .sender(":0.0")
435        .unwrap()
436        .build(&(qt,))
437        .unwrap();
438          <$type>::try_from(&msg).expect("Should be able to use an EventBodyQtOwned for any type whose BusProperties::Body = EventBodyOwned");
439    }
440    #[cfg(feature = "zbus")]
441    #[test]
442    fn zbus_message_conversion_qtspi_event_enum() {
443	  use crate::events::EventTypeProperties;
444	  use crate::events::MessageConversion;
445	  use crate::Event;
446
447
448      // in the case that the body type is EventBodyOwned, we need to also check successful
449      // conversion from a QSPI-style body.
450      let ev = <$type>::default();
451      let qt: crate::events::EventBodyQtOwned = ev.body().into();
452      let msg = zbus::Message::signal(
453        ev.path(),
454        ev.interface(),
455        ev.member(),
456      )
457        .unwrap()
458        .sender(":0.0")
459        .unwrap()
460        .build(&(qt,))
461        .unwrap();
462        assert_matches!(Event::try_from(&msg), Ok(_));
463     }
464    };
465    ($type:ty, Explicit) => {};
466}
467
468// We decorate the macro with a `#[cfg(test)]` attribute.
469// This prevents Clippy from complaining about the macro not being used.
470// It is being used, but only in test mode.
471//
472/// As of writing, this macro is expanded only once: in the `event_test_cases!` macro.
473#[cfg(test)]
474macro_rules! zbus_message_test_case {
475  ($type:ty) => {
476      zbus_message_test_case!($type, Auto);
477    };
478	($type:ty, $extra:tt) => {
479    zbus_message_qtspi_test_case!($type, $extra);
480		#[cfg(feature = "zbus")]
481		#[test]
482		fn zbus_msg_conversion_to_specific_event_type() {
483			let struct_event = <$type>::default();
484			let msg: zbus::Message = zbus::Message::try_from(<$type>::default()).expect("Should convert a `$type::default()` into a message. Check the `impl_to_dbus_message` macro .");
485
486			let struct_event_back =
487				<$type>::try_from(&msg).expect("Should convert from `$type::default()` originated `Message` back into a specific event type. Check the `impl_from_dbus_message` macro.");
488        	assert_eq!(struct_event, struct_event_back, "Events converted into a message and back must be the same");
489		}
490
491		#[cfg(feature = "zbus")]
492		#[test]
493		fn zbus_msg_conversion_to_event_enum_type() {
494			let struct_event = <$type>::default();
495			let msg: zbus::Message = zbus::Message::try_from(struct_event.clone()).expect("Should convert a `$type::default()` into a message. Check the `impl_to_dbus_message` macro.");
496			let event_enum_back =
497				crate::Event::try_from(&msg).expect("Should convert a from `$type::default()` built `Message` into an event enum. Check the `impl_from_dbus_message` macro.");
498			let event_enum: crate::Event = struct_event.into();
499			assert_eq!(event_enum, event_enum_back);
500		}
501		// make want to consider parameterized tests here, no need for fuzz testing, but one level lower than that may be nice
502		// try having a matching member, matching interface, path, or body type, but that has some other piece which is not right
503		#[cfg(feature = "zbus")]
504		#[test]
505		fn zbus_msg_conversion_failure_fake_msg() -> () {
506			let fake_msg = zbus::Message::signal(
507				"/org/a11y/sixtynine/fourtwenty",
508				"org.a11y.atspi.technically.valid",
509				"MadeUpMember",
510			)
511			.unwrap()
512			.sender(":0.0")
513			.unwrap()
514			.build(&())
515			.unwrap();
516			let event = <$type>::try_from(&fake_msg);
517      		assert_matches!(event, Err(_), "This conversion should fail");
518		}
519
520		#[cfg(feature = "zbus")]
521		#[test]
522		fn zbus_msg_conversion_validated_message_with_body() -> () {
523			use crate::events::MessageConversion;
524
525			let fake_msg = zbus::Message::signal(
526				"/org/a11y/sixtynine/fourtwenty",
527				"org.a11y.atspi.technically.valid",
528				"MadeUpMember",
529			)
530			.unwrap()
531			.sender(":0.0")
532			.unwrap()
533			.build(&<$type>::default().body())
534			.unwrap();
535			let hdr = fake_msg.header();
536			let event = <$type>::from_message_unchecked(&fake_msg, &hdr);
537      event.expect("The from_message_unchecked function should work, despite mismatching interface and member");
538		}
539
540		#[cfg(feature = "zbus")]
541		#[test]
542		fn zbus_msg_conversion_failure_correct_interface() -> () {
543			let fake_msg = zbus::Message::signal(
544				"/org/a11y/sixtynine/fourtwenty",
545				<$type as crate::events::DBusInterface>::DBUS_INTERFACE,
546				"MadeUpMember",
547			)
548			.unwrap()
549			.sender(":0.0")
550			.unwrap()
551			.build(&())
552			.unwrap();
553			let event = <$type>::try_from(&fake_msg);
554      assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
555		}
556
557		#[cfg(feature = "zbus")]
558		#[test]
559		fn zbus_msg_conversion_failure_correct_interface_and_member() -> () {
560			let fake_msg = zbus::Message::signal(
561				"/org/a11y/sixtynine/fourtwenty",
562				<$type as crate::events::DBusInterface>::DBUS_INTERFACE,
563				<$type as crate::events::DBusMember>::DBUS_MEMBER,
564			)
565			.unwrap()
566			.sender(":0.0")
567			.unwrap()
568			.build(&())
569			.unwrap();
570			let event = <$type>::try_from(&fake_msg);
571      assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error");
572		}
573
574		#[cfg(feature = "zbus")]
575		#[test]
576		fn zbus_msg_conversion_failure_correct_interface_and_member_invalid_body() -> () {
577      // known invalid body for AT-SPI events
578      let invalid_body: (i32, u64, String, String) = (0, 0, String::new(), String::new());
579			let fake_msg = zbus::Message::signal(
580				"/org/a11y/sixtynine/fourtwenty",
581				<$type as crate::events::DBusInterface>::DBUS_INTERFACE,
582				<$type as crate::events::DBusMember>::DBUS_MEMBER,
583			)
584			.unwrap()
585			.sender(":0.0")
586			.unwrap()
587			.build(&invalid_body)
588			.unwrap();
589			let event = <$type>::try_from(&fake_msg);
590      assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error");
591		}
592
593		#[cfg(feature = "zbus")]
594		#[test]
595		fn zbus_msg_conversion_failure_correct_body() -> () {
596			use crate::events::MessageConversion;
597			let fake_msg = zbus::Message::signal(
598				"/org/a11y/sixtynine/fourtwenty",
599				"org.a11y.atspi.accessible.technically.valid",
600				"FakeMember",
601			)
602			.unwrap()
603			.sender(":0.0")
604			.unwrap()
605			.build(&<$type>::default().body())
606			.unwrap();
607			let event = <$type>::try_from(&fake_msg);
608      assert_matches!(event, Err(_));
609		}
610
611		#[cfg(feature = "zbus")]
612		#[test]
613		fn zbus_msg_conversion_failure_correct_body_and_member() -> () {
614			use crate::events::MessageConversion;
615
616			let fake_msg = zbus::Message::signal(
617				"/org/a11y/sixtynine/fourtwenty",
618				"org.a11y.atspi.accessible.technically.valid",
619				<$type as crate::events::DBusMember>::DBUS_MEMBER,
620			)
621			.unwrap()
622			.sender(":0.0")
623			.unwrap()
624			.build(&<$type>::default().body())
625			.unwrap();
626			let event = <$type>::try_from(&fake_msg);
627      assert_matches!(event, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
628		}
629		#[cfg(feature = "zbus")]
630		#[test]
631		fn zbus_msg_conversion_failure_correct_body_and_interface() -> () {
632			use crate::events::MessageConversion;
633
634			let fake_msg = zbus::Message::signal(
635				"/org/a11y/sixtynine/fourtwenty",
636				<$type as crate::events::DBusInterface>::DBUS_INTERFACE,
637				"MadeUpMember",
638			)
639			.unwrap()
640			.sender(":0.0")
641			.unwrap()
642			.build(&<$type>::default().body())
643			.unwrap();
644			let event = <$type>::try_from(&fake_msg);
645      assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
646		}
647	};
648}
649
650#[cfg(feature = "wrappers")]
651/// Expands to five tests:
652///
653/// 1. `into_and_try_from_event`
654/// 2. `zbus_msg_invalid_interface`
655/// 3. `zbus_msg_invalid_member`
656/// 4. `zbus_msg_invalid_member_and_interface`
657/// 5. `zbus_msg_conversion`
658///
659/// The macro takes two arguments:
660/// 1. The event's interface enum type.
661/// 2. Any user facing event type that is wrapped by the interface enum.
662///
663/// # Examples
664///
665/// ```ignore
666/// event_wrapper_test_cases!(MouseEvents, AbsEvent);
667/// ```
668///
669/// For each of the types, the macro will create a module with the name `events_tests_{foo}`
670/// where `{foo}` is the snake case of the 'interface enum' name.
671macro_rules! event_wrapper_test_cases {
672	// The macro takes two arguments: the interface enum type and the user facing event type (ufet).
673	($iface_enum:ty, $ufet:ty) => {
674		#[cfg(test)]
675		#[rename_item::rename(name($iface_enum), prefix = "events_tests_", case = "snake")]
676		mod foo {
677		use super::{$ufet, $iface_enum, AtspiError, Event, MessageConversion};
678
679		// TODO: replace with [`std::assert_matches::assert_matches`] when stabilized
680		use assert_matches::assert_matches;
681
682		#[test]
683		fn into_and_try_from_user_facing_event() {
684			// Create a default event struct from its type's `Default::default()` impl.
685			let sub_type = <$ufet>::default();
686
687			// Wrap the event struct in the event enum
688			let mod_type = <$iface_enum>::from(sub_type);
689			let hint_iface = "Check macro `impl_from_user_facing_event_for_interface_event_enum!`";
690
691			// Wrap the inner event enum into the `Event` enum.
692			let event = Event::from(mod_type.clone());
693			let hint_event = "Check macro `impl_from_interface_event_enum_for_event!`";
694
695			// Unwrap the `Event` enum into the inner event enum.
696			let hint_event_try = "Check macro `impl_try_from_event_for_interface_enum!`";
697			let mod_type2 = <$iface_enum>::try_from(event.clone())
698				.expect("Should convert outer `Event` enum into interface enum. hints: {hint_event} and {hint_event_try}");
699
700			assert_eq!(
701				mod_type, mod_type2,
702				"Interface enums should match. hint: {hint_iface}, {hint_event} and {hint_event_try}"
703			);
704		}
705
706		#[cfg(feature = "zbus")]
707		#[test]
708		fn zbus_msg_invalid_interface() {
709			let fake_msg = zbus::Message::signal(
710				"/org/a11y/sixtynine/fourtwenty",
711				"org.a11y.atspi.technically.valid.lol",
712				<$ufet as crate::events::DBusMember>::DBUS_MEMBER,
713			)
714			.unwrap()
715			.sender(":0.0")
716			.unwrap()
717			.build(&<$ufet>::default().body())
718			.unwrap();
719
720			// It is hard to see what eventually is tested here. Let's unravel it:
721			//
722			// Below we call `TryFrom<&zbus::Message> for $type` where `$type` the interface enum name. (eg. `MouseEvents`, `ObjectEvents`, etc.) and
723			// `mod_type` is an 'interface enum' variant (eg. `MouseEvents::Abs(AbsEvent)`).
724			// This conversion is found in the `/src/events/{iface_name}.rs`` file.
725			// This conversion in turn leans on the `impl_from_dbus_message` macro.
726			// In `MouseEvents::Abs(msg.try_into()?)`, it is the `msg.try_into()?` that should fail.
727			// The `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
728
729			// Additioanlly, we check against the same method in `Event`; the overarchive enum that
730			// contains all other events as variants.
731
732			let mod_type = <$iface_enum>::try_from(&fake_msg);
733			let event_type = Event::try_from(&fake_msg);
734
735			assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
736			assert_matches!(event_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
737			}
738
739			#[cfg(feature = "zbus")]
740			#[test]
741			fn zbus_msg_invalid_member() {
742				let fake_msg = zbus::Message::signal(
743					"/org/a11y/sixtynine/fourtwenty",
744					<$ufet as crate::events::DBusInterface>::DBUS_INTERFACE,
745					"FakeFunctionLol",
746				)
747				.unwrap()
748				.sender(":0.0")
749				.unwrap()
750				.build(&<$ufet>::default().body())
751				.unwrap();
752				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
753				let mod_type = <$iface_enum>::try_from(&fake_msg);
754        		assert_matches!(mod_type, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
755			}
756			#[cfg(feature = "zbus")]
757			#[test]
758			fn zbus_msg_invalid_member_and_interface() {
759				let fake_msg = zbus::Message::signal(
760					"/org/a11y/sixtynine/fourtwenty",
761					"org.a11y.atspi.technically.allowed",
762					"FakeFunctionLol",
763				)
764				.unwrap()
765				.sender(":0.0")
766				.unwrap()
767				.build(&<$ufet>::default().body())
768				.unwrap();
769				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
770				let mod_type = <$iface_enum>::try_from(&fake_msg);
771
772				// Note that the non-matching interface is the first error, so the member match error is not reached.
773        		assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
774			}
775
776			#[cfg(feature = "zbus")]
777			#[test]
778			fn zbus_msg_conversion() {
779				let valid_msg = zbus::Message::signal(
780					"/org/a11y/sixtynine/fourtwenty",
781					<$ufet as crate::events::DBusInterface>::DBUS_INTERFACE,
782					<$ufet as crate::events::DBusMember>::DBUS_MEMBER,
783				)
784				.unwrap()
785				.sender(":0.0")
786				.unwrap()
787				.build(&<$ufet>::default().body())
788				.unwrap();
789				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
790				let mod_type = <$iface_enum>::try_from(&valid_msg);
791				mod_type.expect("Should convert from `$ufet::default()` built `Message` back into a interface event enum variant wrapping an inner type. Check the `impl_from_dbus_message` macro.");
792			}
793
794
795		}
796	};
797}
798
799macro_rules! event_test_cases {
800  ($ufet:ty) => {
801      event_test_cases!($ufet, Auto);
802  };
803	($ufet:ty, $qt:tt) => {
804		#[cfg(test)]
805		#[rename_item::rename(name($ufet), prefix = "event_tests_", case = "snake")]
806		mod foo {
807		use super::{$ufet, AtspiError, EventProperties };
808		use crate::events::traits::EventTypeProperties;
809        use zbus::Message;
810		use crate::Event;
811
812        // TODO: use [`std::assert_matches::assert_matches`] when stabilized
813        use assert_matches::assert_matches;
814
815			#[test]
816			#[cfg(feature = "wrappers")]
817			fn event_enum_conversion() {
818				let event_struct = <$ufet>::default();
819				let event_enum = Event::from(event_struct.clone());
820				let event_struct_back = <$ufet>::try_from(event_enum)
821					.expect("Should convert event enum into specific event type because it was created from it. Check the `impl_from_interface_event_enum_for_event` macro");
822				assert_eq!(event_struct, event_struct_back);
823			}
824
825			#[test]
826			#[cfg(feature = "wrappers")]
827			fn event_enum_transparency_test_case() {
828			let specific_event = <$ufet>::default();
829
830			let generic_event = Event::from(specific_event.clone());
831			let hint = "Check macro `impl_from_user_facing_type_for_event_enum!`.";
832
833			assert_eq!(
834				specific_event.member(),
835				generic_event.member(),
836				"DBus members do not match. hint: {hint}"
837			);
838			assert_eq!(
839				specific_event.interface(),
840				generic_event.interface(),
841				"DBus interfaces do not match. hint: {hint}"
842			);
843			assert_eq!(
844				specific_event.registry_string(),
845				generic_event.registry_string(),
846				"Registry strings do not match. hint: {hint}"
847			);
848			assert_eq!(
849				specific_event.match_rule(),
850				generic_event.match_rule(),
851				"Match rule strings do not match. hint: {hint}"
852			);
853			assert_eq!(specific_event.path(), generic_event.path(), "Paths do not match. hint: {hint}");
854			assert_eq!(specific_event.sender(), generic_event.sender(), "Senders do not match. hint: {hint}");
855			}
856
857			zbus_message_test_case!($ufet, $qt);
858      event_has_matching_xml_definition!($ufet);
859			generic_event_test_case!($ufet);
860		}
861		assert_impl_all!(
862			$ufet: Clone,
863			std::fmt::Debug,
864			serde::Serialize,
865			serde::Deserialize<'static>,
866			Default,
867			PartialEq,
868			Eq,
869			std::hash::Hash,
870			crate::events::traits::EventProperties,
871			crate::events::traits::EventTypeProperties,
872			crate::events::traits::DBusInterface,
873			crate::events::traits::DBusMember,
874			crate::events::traits::DBusMatchRule,
875			crate::events::traits::RegistryEventString
876		);
877
878		#[cfg(feature = "zbus")]
879		assert_impl_all!(zbus::Message: TryFrom<$ufet>);
880	};
881}
882
883/// Implements `MessageConversionExt` for a given target event type with a given body type.
884///
885/// # Example
886/// ```ignore
887/// # // 'ignore'd for bevity's sake because `impl`s require that the *example crate* defines traits `MessageConversionExt`,
888/// # // `MessageConversion` as well as the body and target types.
889///
890/// impl_msg_conversion_ext_for_target_type_with_specified_body_type!(target: RemoveAccessibleEvent, body: ObjectRef);
891/// ```
892/// expands to:
893///
894/// ```ignore
895/// #[cfg(feature = "zbus")]
896/// impl<'a> MessageConversionExt<'_, ObjectRef> for RemoveAccessibleEvent {
897///     fn try_from_message(msg: &zbus::Message, hdr: &Header) -> Result<Self, AtspiError> {
898///         <Self as MessageConversionExt<$body_type>>::validate_interface(hdr)?;
899///         <Self as MessageConversionExt<$body_type>>::validate_member(hdr)?;
900///         <Self as MessageConversionExt<$body_type>>::validate_body(msg)?;
901///         <Self as MessageConversion<'a>>::from_message_unchecked(msg, hdr)
902///     }
903/// }
904/// ```
905macro_rules! impl_msg_conversion_ext_for_target_type_with_specified_body_type {
906	(target: $target_type:ty, body: $body_type:ty) => {
907		#[cfg(feature = "zbus")]
908		impl<'a> crate::events::MessageConversionExt<'a, $body_type> for $target_type {
909			fn try_from_message(msg: &zbus::Message, hdr: &Header) -> Result<Self, AtspiError> {
910				use crate::events::MessageConversionExt;
911				<Self as MessageConversionExt<$body_type>>::validate_interface(hdr)?;
912				<Self as MessageConversionExt<$body_type>>::validate_member(hdr)?;
913				<Self as MessageConversionExt<$body_type>>::validate_body(msg)?;
914				<Self as MessageConversion<'a>>::from_message_unchecked(msg, hdr)
915			}
916		}
917	};
918}
919
920/// Implements `MessageConversionExt` for a given target event type.
921///
922/// # Example
923///
924/// ```ignore
925/// impl_msg_conversion_ext_for_target_type!(LoadCompleteEvent);
926/// ```
927/// expands to:
928///
929/// ```ignore
930/// #[cfg(feature = "zbus")]
931/// impl<'msg> MessageConversionExt<'msg, EventBody<'msg>> for LoadCompleteEvent {
932///     fn try_from_message(msg: &'msg zbus::Message, header: &Header) -> Result<Self, AtspiError> {
933///         Self::validate_interface(header)?;
934///         Self::validate_member(header)?;
935///
936///         let item = crate::events::ObjectRef::try_from(header)?;
937///         let msg_body = msg.body();
938///         let signature = msg_body.signature();
939///
940///         if signature == crate::events::EventBodyOwned::SIGNATURE
941///             || signature == crate::events::EventBodyQtOwned::SIGNATURE
942///         {
943///             Self::from_message_unchecked_parts(item, msg_body)
944///         } else {
945///             Err(AtspiError::SignatureMatch(format!(
946///                 "The message signature {} does not match a valid signal body signature: {} or {}",
947///                 msg.body().signature(),
948///                 crate::events::EventBodyOwned::SIGNATURE,
949///                 crate::events::EventBodyQtOwned::SIGNATURE,
950///             )))
951///         }
952///     }
953/// }
954/// ```
955macro_rules! impl_msg_conversion_ext_for_target_type {
956	($target_type:ty) => {
957		#[cfg(feature = "zbus")]
958		impl<'msg> crate::events::MessageConversionExt<'msg, crate::events::EventBody<'msg>> for $target_type {
959			fn try_from_message(msg: &'msg zbus::Message, header: &Header) -> Result<Self, AtspiError> {
960				use zvariant::Type;
961				use crate::events::traits::MessageConversion;
962				Self::validate_interface(header)?;
963				Self::validate_member(header)?;
964
965				let item = crate::events::ObjectRef::try_from(header)?;
966				let msg_body = msg.body();
967				let signature = msg_body.signature();
968
969				if signature == crate::events::EventBodyOwned::SIGNATURE
970					|| signature == crate::events::EventBodyQtOwned::SIGNATURE
971				{
972					Self::from_message_unchecked_parts(item, msg_body)
973				} else {
974					Err(AtspiError::SignatureMatch(format!(
975						"The message signature {} does not match a valid signal body signature: {} or {}",
976						msg.body().signature(),
977						crate::events::EventBodyOwned::SIGNATURE,
978						crate::events::EventBodyQtOwned::SIGNATURE,
979					)))
980				}
981			}
982		}
983	};
984}
985
986#[cfg(feature = "wrappers")]
987/// Implements `TryFromMessage` for a given event wrapper type.
988///
989/// # Example
990/// ```ignore
991/// impl_tryfrommessage_for_event_wrapper!(StateChangedEvent);
992/// ```
993/// expands to:
994///
995/// ```ignore
996/// #[cfg(feature = "zbus")]
997/// impl TryFromMessage for StateChangedEvent {
998///     fn try_from_message(msg: &zbus::Message) -> Result<StateChangedEvent, AtspiError> {
999///        let header = msg.header();
1000///        let interface = header.interface().ok_or(AtspiError::MissingInterface)?;
1001///        if interface != Self::DBUS_INTERFACE {
1002///            return Err(AtspiError::InterfaceMatch(format!(
1003///                "Interface {} does not match require interface for event: {}",
1004///                interface,
1005///                Self::DBUS_INTERFACE
1006///            )));
1007///        }
1008///        Self::try_from_message_interface_checked(msg, &header)
1009///     }
1010/// }
1011/// ```
1012macro_rules! impl_tryfrommessage_for_event_wrapper {
1013	($wrapper:ty) => {
1014		#[cfg(feature = "zbus")]
1015		impl crate::events::traits::TryFromMessage for $wrapper {
1016			fn try_from_message(msg: &zbus::Message) -> Result<$wrapper, AtspiError> {
1017				use crate::events::traits::EventWrapperMessageConversion;
1018
1019				let header = msg.header();
1020				let interface = header.interface().ok_or(AtspiError::MissingInterface)?;
1021				if interface != Self::DBUS_INTERFACE {
1022					return Err(AtspiError::InterfaceMatch(format!(
1023						"Interface {} does not match require interface for event: {}",
1024						interface,
1025						Self::DBUS_INTERFACE
1026					)));
1027				}
1028				Self::try_from_message_interface_checked(msg, &header)
1029			}
1030		}
1031	};
1032}
1033
1034/// Implement the `MessageConversion` trait for the given types.
1035///
1036/// This macro is used to implement the `MessageConversion` trait for types that are built from an
1037/// `ObjectRef` and a `zbus::message::Body` only - no `EventBody` needed.
1038///
1039/// # Example
1040///
1041/// ```ignore
1042/// impl_msg_conversion_for_types_built_from_object_ref!(FocusEvent, FocusEvents);
1043/// ```
1044///
1045/// This will generate the following implementations:
1046///
1047/// ```ignore
1048/// #[cfg(feature = "zbus")]
1049/// impl MessageConversion<'_> for FocusEvent {
1050///     type Body<'msg> = crate::events::EventBody<'msg>;
1051///
1052///     fn from_message_unchecked_parts(
1053///         obj_ref: crate::events::ObjectRef,
1054///         _body: zbus::message::Body,
1055///     ) -> Result<Self, AtspiError> {
1056///         Ok(obj_ref.into())
1057///     }
1058///
1059///     fn from_message_unchecked(_: &zbus::Message, header: &Header) -> Result<Self, AtspiError> {
1060///         let obj_ref: crate::events::ObjectRef = header.try_into()?;
1061///         Ok(obj_ref.into())
1062///     }
1063///
1064///     fn body(&self) -> Self::Body<'_> {
1065///         crate::events::EventBodyOwned::default().into()
1066///     }
1067/// }
1068/// ```
1069macro_rules! impl_msg_conversion_for_types_built_from_object_ref {
1070	($($type:ty),*) => {
1071		$(
1072			#[cfg(feature = "zbus")]
1073			impl crate::events::MessageConversion<'_> for $type {
1074				type Body<'msg> = crate::events::EventBody<'msg>;
1075
1076				fn from_message_unchecked_parts(
1077					obj_ref: crate::events::ObjectRef,
1078					_body: zbus::message::Body,
1079				) -> Result<Self, AtspiError> {
1080					Ok(obj_ref.into())
1081				}
1082
1083				fn from_message_unchecked(_: &zbus::Message, header: &Header) -> Result<Self, AtspiError> {
1084					let obj_ref: crate::events::ObjectRef = header.try_into()?;
1085					Ok(obj_ref.into())
1086				}
1087
1088				fn body(&self) -> Self::Body<'_> {
1089					crate::events::EventBodyOwned::default().into()
1090				}
1091			}
1092		)*
1093	};
1094}
1095
1096/// Implement `DBusMember`, `DBusInterface`, `DBusMatchRule`, and `RegistryEventString`
1097/// for a given event type.
1098///
1099/// This macro takes 5 arguments in the order:
1100/// - The target type
1101/// - The member string
1102/// - The interface string
1103/// - The registry string
1104/// - The match rule string
1105///
1106/// # Example
1107/// ```ignore
1108/// impl_member_interface_registry_string_and_match_rule_for_event!(
1109/// FocusEvent, "Focus", "org.a11y.atspi.Event.Focus", "focus",
1110/// "type='signal',interface='org.a11y.atspi.Event.Focus'");
1111/// ```
1112/// expands to:
1113///
1114/// ```ignore
1115/// impl DBusMember for FocusEvent {
1116///    const DBUS_MEMBER: &'static str = "Focus";
1117/// }
1118/// impl DBusInterface for FocusEvent {
1119///   const DBUS_INTERFACE: &'static str = "org.a11y.atspi.Event.Focus";
1120/// }
1121/// impl MatchRule for FocusEvent {
1122///  const MATCH_RULE: &'static str = "type='signal',interface='org.a11y.atspi.Event.Focus'";
1123/// }
1124/// impl RegistryEventString for FocusEvent {
1125///  const REGISTRY_STRING: &'static str = "focus";
1126/// }
1127/// impl DBusProperties for FocusEvent {}
1128/// ```
1129macro_rules! impl_member_interface_registry_string_and_match_rule_for_event {
1130	($target_type:ty, $member_str:literal, $interface_str:literal, $registry_str:literal, $match_rule_str:literal) => {
1131		impl crate::events::DBusMember for $target_type {
1132			const DBUS_MEMBER: &'static str = $member_str;
1133		}
1134		impl crate::events::DBusInterface for $target_type {
1135			const DBUS_INTERFACE: &'static str = $interface_str;
1136		}
1137		impl crate::events::DBusMatchRule for $target_type {
1138			const MATCH_RULE_STRING: &'static str = $match_rule_str;
1139		}
1140		impl crate::events::RegistryEventString for $target_type {
1141			const REGISTRY_EVENT_STRING: &'static str = $registry_str;
1142		}
1143		impl crate::events::DBusProperties for $target_type {}
1144	};
1145}
1146
1147/// Implement `EventTypeProperties` for a given event type.
1148///
1149/// This macro takes one argument: the target type.
1150///
1151/// # Example
1152/// ```ignore
1153/// impl_event_type_properties_for_event!(FocusEvent);
1154/// ```
1155/// expands to:
1156///
1157/// ```ignore
1158/// impl EventTypeProperties for FocusEvent {
1159///    fn member(&self) -> &'static str {
1160///       Self::DBUS_MEMBER
1161///   }
1162///  fn interface(&self) -> &'static str {
1163///   Self::DBUS_INTERFACE
1164/// }
1165/// fn registry_string(&self) -> &'static str {
1166///  Self::REGISTRY_EVENT_STRING
1167/// }
1168/// fn match_rule(&self) -> &'static str {
1169/// Self::MATCH_RULE_STRING
1170/// }
1171/// }
1172///
1173macro_rules! impl_event_type_properties_for_event {
1174	($target_type:ty) => {
1175		impl crate::events::EventTypeProperties for $target_type {
1176			fn member(&self) -> &'static str {
1177				Self::DBUS_MEMBER
1178			}
1179
1180			fn interface(&self) -> &'static str {
1181				Self::DBUS_INTERFACE
1182			}
1183
1184			fn registry_string(&self) -> &'static str {
1185				Self::REGISTRY_EVENT_STRING
1186			}
1187
1188			fn match_rule(&self) -> &'static str {
1189				Self::MATCH_RULE_STRING
1190			}
1191		}
1192	};
1193}