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 `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 EventProperties for $type {
23			fn sender(&self) -> UniqueName<'_> {
24				self.item.name.as_ref()
25			}
26			fn path(&self) -> ObjectPath<'_> {
27				self.item.path.as_ref()
28			}
29		}
30	};
31}
32
33/// Expands to a conversion given the enclosed event type and outer `Event` variant.
34///
35/// eg
36/// ```ignore
37/// impl_from_interface_event_enum_for_event!(ObjectEvents, Event::Object);
38/// ```
39/// expands to:
40///
41/// ```ignore
42/// impl From<ObjectEvents> for Event {
43///     fn from(event_variant: ObjectEvents) -> Event {
44///         Event::Object(event_variant.into())
45///     }
46/// }
47/// ```
48macro_rules! impl_from_interface_event_enum_for_event {
49	($outer_type:ty, $outer_variant:path) => {
50		impl From<$outer_type> for Event {
51			fn from(event_variant: $outer_type) -> Event {
52				$outer_variant(event_variant.into())
53			}
54		}
55	};
56}
57
58/// Expands to a conversion given the enclosed event enum type and outer `Event` variant.
59///
60/// eg
61/// ```ignore
62/// impl_try_from_event_for_user_facing_event_type!(ObjectEvents, Event::Object);
63/// ```
64/// expands to:
65///
66/// ```ignore
67/// impl TryFrom<Event> for ObjectEvents {
68///     type Error = AtspiError;
69///     fn try_from(generic_event: Event) -> Result<ObjectEvents, Self::Error> {
70///         if let Event::Object(event_type) = generic_event {
71///             Ok(event_type)
72///         } else {
73///             Err(AtspiError::Conversion("Invalid type"))
74///         }
75///     }
76/// }
77/// ```
78macro_rules! impl_try_from_event_for_user_facing_event_type {
79	($outer_type:ty, $outer_variant:path) => {
80		impl TryFrom<Event> for $outer_type {
81			type Error = AtspiError;
82			fn try_from(generic_event: Event) -> Result<$outer_type, Self::Error> {
83				if let $outer_variant(event_type) = generic_event {
84					Ok(event_type)
85				} else {
86					Err(AtspiError::Conversion("Invalid type"))
87				}
88			}
89		}
90	};
91}
92
93/// Expands to a conversion given the user facing event type and outer `Event::Interface(<InterfaceEnum>)` variant.,
94/// the enum type and outtermost variant.
95///
96/// ```ignore                                            user facing type,  enum type,    outer variant
97/// impl_from_user_facing_event_for_interface_event_enum!(StateChangedEvent, ObjectEvents, ObjectEvents::StateChanged);
98/// ```
99///
100/// expands to:
101///
102/// ```ignore
103/// impl From<StateChangedEvent> for ObjectEvents {
104///     fn from(specific_event: StateChangedEvent) -> ObjectEvents {
105///         ObjectEvents::StateChanged(specific_event)
106///     }
107/// }
108/// ```
109macro_rules! impl_from_user_facing_event_for_interface_event_enum {
110	($inner_type:ty, $outer_type:ty, $inner_variant:path) => {
111		impl From<$inner_type> for $outer_type {
112			fn from(specific_event: $inner_type) -> $outer_type {
113				$inner_variant(specific_event)
114			}
115		}
116	};
117}
118
119/// Expands to a conversion given two arguments,
120/// 1. the user facing event type `(inner_type)`
121///    which relies on a conversion to its interface variant enum type variant.
122/// 2. the outer `Event::<Interface(<InterfaceEnum>)>` wrapper.,
123///    the enum type and outtermost variant.
124///
125/// ```ignore                                   user facing type, outer event variant
126/// impl_from_user_facing_type_for_event_enum!(StateChangedEvent, Event::Object);
127/// ```
128///
129/// expands to:
130///
131/// ```ignore
132/// impl From<StateChangedEvent> for Event {
133///    fn from(event_variant: StateChangedEvent) -> Event {
134///       Event::Object(ObjectEvents::StateChanged(event_variant))
135///   }
136/// }
137/// ```
138macro_rules! impl_from_user_facing_type_for_event_enum {
139	($inner_type:ty, $outer_variant:path) => {
140		impl From<$inner_type> for Event {
141			fn from(event_variant: $inner_type) -> Event {
142				$outer_variant(event_variant.into())
143			}
144		}
145	};
146}
147
148/// Expands to a conversion given two arguments,
149/// 1. the user facing event type `(inner_type)`
150/// 2. the outer `Event::<Interface(<InterfaceEnum>)>` wrapper.
151///
152/// eg
153/// ```ignore
154/// impl_try_from_event_for_user_facing_type!(StateChangedEvent, ObjectEvents::StateChanged);
155/// ```
156/// expands to:
157///
158/// ```ignore
159/// impl TryFrom<Event> for StateChangedEvent {
160///    type Error = AtspiError;
161///   fn try_from(generic_event: Event) -> Result<StateChangedEvent, Self::Error> {
162///      if let Event::Object(ObjectEvents::StateChanged(specific_event)) = generic_event {
163///          Ok(specific_event)
164///         } else {
165///          Err(AtspiError::Conversion("Invalid type"))
166///         }
167///   }
168/// }
169/// ```
170macro_rules! impl_try_from_event_for_user_facing_type {
171	($inner_type:ty, $inner_variant:path, $outer_variant:path) => {
172		impl TryFrom<Event> for $inner_type {
173			type Error = AtspiError;
174			fn try_from(generic_event: Event) -> Result<$inner_type, Self::Error> {
175				if let $outer_variant($inner_variant(specific_event)) = generic_event {
176					Ok(specific_event)
177				} else {
178					Err(AtspiError::Conversion("Invalid type"))
179				}
180			}
181		}
182	};
183}
184
185/// Implements the `TryFrom` trait for a given event type.
186/// Converts a user facing event type into a `zbus::Message`.
187///
188/// # Example
189/// ```ignore
190/// impl_to_dbus_message!(StateChangedEvent);
191/// ```
192/// expands to:
193///
194/// ```ignore
195/// impl TryFrom<StateChangedEvent> for zbus::Message {
196///   type Error = AtspiError;
197///   fn try_from(event: StateChangedEvent) -> Result<Self, Self::Error> {
198///     Ok(zbus::Message::signal(
199///         event.path(),
200///         StateChangedEvent::DBUS_INTERFACE,
201///         StateChangedEvent::DBUS_MEMBER,
202///     )?
203///     .sender(event.sender())?
204///     .build(&event.body())?)
205///  }
206/// }
207///
208macro_rules! impl_to_dbus_message {
209	($type:ty) => {
210		#[cfg(feature = "zbus")]
211		impl TryFrom<$type> for zbus::Message {
212			type Error = AtspiError;
213			fn try_from(event: $type) -> Result<Self, Self::Error> {
214				Ok(zbus::Message::signal(
215					event.path(),
216					<$type as BusProperties>::DBUS_INTERFACE,
217					<$type as BusProperties>::DBUS_MEMBER,
218				)?
219				.sender(event.sender().to_string())?
220				.build(&event.body())?)
221			}
222		}
223	};
224}
225
226/// Implements the `TryFrom` trait for a given event type.
227/// Converts a `zbus::Message` into a user facing event type.
228///
229/// # Example
230/// ```ignore
231/// impl_from_dbus_message!(StateChangedEvent);
232/// ```
233/// expands to:
234///
235/// ```ignore
236/// impl TryFrom<&zbus::Message> for StateChangedEvent {
237///   type Error = AtspiError;
238///   fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
239///    if msg.header().interface().ok_or(AtspiError::MissingInterface)? != StateChangedEvent::DBUS_INTERFACE {
240///       return Err(AtspiError::InterfaceMatch(format!("The interface {} does not match the signal's interface: {}",
241///         msg.header().interface().unwrap(),
242///         StateChangedEvent::DBUS_INTERFACE)));
243///     }
244///     if msg.header().member().ok_or(AtspiError::MissingMember)? != StateChangedEvent::DBUS_MEMBER {
245///       return Err(AtspiError::MemberMatch(format!("The member {} does not match the signal's member: {}",
246///         msg.header().member().unwrap(),
247///         StateChangedEvent::DBUS_MEMBER)));
248///     }
249///     StateChangedEvent::from_message_parts(msg.try_into()?, msg.body().deserialize::<StateChangedEvent::Body>()?)
250///  }
251/// }
252/// ```
253macro_rules! impl_from_dbus_message {
254	($type:ty) => {
255		#[cfg(feature = "zbus")]
256		impl TryFrom<&zbus::Message> for $type {
257			type Error = AtspiError;
258			fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
259				let header = msg.header();
260				if header.interface().ok_or(AtspiError::MissingInterface)?
261					!= <$type as BusProperties>::DBUS_INTERFACE
262				{
263					return Err(AtspiError::InterfaceMatch(format!(
264						"The interface {} does not match the signal's interface: {}",
265						header.interface().unwrap(),
266						<$type as BusProperties>::DBUS_INTERFACE
267					)));
268				}
269				if header.member().ok_or(AtspiError::MissingMember)? != <$type>::DBUS_MEMBER {
270					return Err(AtspiError::MemberMatch(format!(
271						"The member {} does not match the signal's member: {}",
272						// unwrap is safe here because of guard above
273						header.member().unwrap(),
274						<$type as BusProperties>::DBUS_MEMBER
275					)));
276				}
277				<$type>::from_message_parts(
278					msg.try_into()?,
279					msg.body().deserialize::<<$type as BusProperties>::Body>()?,
280				)
281			}
282		}
283	};
284}
285
286// We decorate the macro with a `#[cfg(test)]` attribute.
287// This prevents Clippy from complaining about the macro not being used.
288// It is being used, but only in test mode.
289//
290/// Tests `Default` and `BusProperties::from_message_parts` for a given event struct.
291///
292/// Obtains a default for the given event struct.
293/// Asserts that the path and sender are the default.
294///
295/// Breaks the struct down into item (the associated object) and body.
296/// Then tests `BusProperties::from_message_parts` with the item and body.
297#[cfg(test)]
298macro_rules! generic_event_test_case {
299	($type:ty) => {
300		#[test]
301		fn generic_event_uses() {
302			let struct_event = <$type>::default();
303			assert_eq!(struct_event.path().as_str(), "/org/a11y/atspi/accessible/null");
304			assert_eq!(struct_event.sender().as_str(), ":0.0");
305			let item = struct_event.item.clone();
306			let body = struct_event.body();
307			let build_struct = <$type>::from_message_parts(item, body)
308				.expect("<$type as Default>'s parts should build a valid ObjectRef");
309			assert_eq!(struct_event, build_struct);
310		}
311	};
312}
313
314// We decorate the macro with a `#[cfg(test)]` attribute.
315// This prevents Clippy from complaining about the macro not being used.
316// It is being used, but only in test mode.
317//
318/// Tests conversion to and from the `Event` enum.
319///
320/// Obtains a default for the given event struct.
321/// Converts the struct into the `Event` enum, wrapping the struct.
322/// Converts the `Event` enum into the given event struct.
323/// Asserts that the original struct and the converted struct are equal.
324#[cfg(test)]
325macro_rules! event_enum_test_case {
326	($type:ty) => {
327		#[test]
328		fn event_enum_conversion() {
329			let struct_event = <$type>::default();
330			let event = Event::from(struct_event.clone());
331			let struct_event_back = <$type>::try_from(event)
332				.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");
333			assert_eq!(struct_event, struct_event_back);
334		}
335	};
336}
337
338/// Tests transparency of the `EventTypeProperties` and `EventProperties` trait on the `Event` wrapper type.
339///
340/// Obtains a default for the given event struct.
341/// Converts the struct into the `Event` enum, wrapping the struct.
342/// Checks the equality of all four functions defined in the `EventTypeProiperties` and `EventProperties` traits:
343///
344/// - `member`
345/// - `interface`
346/// - `registry_string`
347/// - `match_rule`
348/// - `path`
349/// - `sender`
350///
351/// It is imperitive that these items come through with no modifications from the wrappers.
352///
353#[cfg(test)]
354macro_rules! event_enum_transparency_test_case {
355	($type:ty) => {
356		#[test]
357		fn event_enum_transparency_test_case() {
358			let specific_event = <$type>::default();
359			let generic_event = Event::from(specific_event.clone());
360			assert_eq!(
361				specific_event.member(),
362				generic_event.member(),
363				"DBus member strings do not match."
364			);
365			assert_eq!(
366				specific_event.interface(),
367				generic_event.interface(),
368				"Registry interfaces do not match."
369			);
370			assert_eq!(
371				specific_event.registry_string(),
372				generic_event.registry_string(),
373				"Registry strings do not match."
374			);
375			assert_eq!(
376				specific_event.match_rule(),
377				generic_event.match_rule(),
378				"Match rule strings do not match."
379			);
380			assert_eq!(specific_event.path(), generic_event.path(), "Pathsdo not match.");
381			assert_eq!(specific_event.sender(), generic_event.sender(), "Senders do not match.");
382		}
383	};
384}
385
386// We decorate the macro with a `#[cfg(test)]` attribute.
387// This prevents Clippy from complaining about the macro not being used.
388// It is being used, but only in test mode.
389//
390/// As of writing, this macro is expanded only once: in the `event_test_cases!` macro.
391#[cfg(test)]
392macro_rules! zbus_message_test_case {
393	($type:ty) => {
394		#[cfg(feature = "zbus")]
395		#[test]
396		fn zbus_msg_conversion_to_specific_event_type() {
397			let struct_event = <$type>::default();
398			let msg: zbus::Message = zbus::Message::try_from(struct_event.clone())
399				.expect("Should convert a `$type::default()` into a message. Check the `impl_to_dbus_message` macro .");
400			let struct_event_back =
401				<$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.");
402			assert_eq!(struct_event, struct_event_back);
403		}
404
405		#[cfg(feature = "zbus")]
406		#[test]
407		fn zbus_msg_conversion_to_event_enum_type() {
408			let struct_event = <$type>::default();
409			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 .");
410			let event_enum_back =
411				Event::try_from(&msg).expect("Should convert a from `$type::default()` built `Message` into an event enum. Check the `impl_from_dbus_message` macro .");
412			let event_enum: Event = struct_event.into();
413			assert_eq!(event_enum, event_enum_back);
414		}
415		// make want to consider parameterized tests here, no need for fuzz testing, but one level lower than that may be nice
416		// try having a matching member, matching interface, path, or body type, but that has some other piece which is not right
417		#[cfg(feature = "zbus")]
418		#[test]
419		#[should_panic(expected = "Should panic")]
420		fn zbus_msg_conversion_failure_fake_msg() -> () {
421			let fake_msg = zbus::Message::signal(
422				"/org/a11y/sixtynine/fourtwenty",
423				"org.a11y.atspi.technically.valid",
424				"MadeUpMember",
425			)
426			.unwrap()
427			.sender(":0.0")
428			.unwrap()
429			.build(&())
430			.unwrap();
431			let event = <$type>::try_from(&fake_msg);
432			event.expect("Should panic");
433		}
434
435		#[cfg(feature = "zbus")]
436		#[test]
437		#[should_panic(expected = "Should panic")]
438		fn zbus_msg_conversion_failure_correct_interface() -> () {
439			let fake_msg = zbus::Message::signal(
440				"/org/a11y/sixtynine/fourtwenty",
441				<$type as BusProperties>::DBUS_INTERFACE,
442				"MadeUpMember",
443			)
444			.unwrap()
445			.sender(":0.0")
446			.unwrap()
447			.build(&())
448			.unwrap();
449			let event = <$type>::try_from(&fake_msg);
450			event.expect("Should panic");
451		}
452
453		#[cfg(feature = "zbus")]
454		#[test]
455		#[should_panic(expected = "Should panic")]
456		fn zbus_msg_conversion_failure_correct_interface_and_member() -> () {
457			let fake_msg = zbus::Message::signal(
458				"/org/a11y/sixtynine/fourtwenty",
459				<$type as BusProperties>::DBUS_INTERFACE,
460				<$type as BusProperties>::DBUS_MEMBER,
461			)
462			.unwrap()
463			.sender(":0.0")
464			.unwrap()
465			.build(&())
466			.unwrap();
467			let event = <$type>::try_from(&fake_msg);
468			event.expect("Should panic");
469		}
470
471		#[cfg(feature = "zbus")]
472		#[test]
473		#[should_panic(expected = "Should panic")]
474		fn zbus_msg_conversion_failure_correct_body() -> () {
475			let fake_msg = zbus::Message::signal(
476				"/org/a11y/sixtynine/fourtwenty",
477				"org.a11y.atspi.accessible.technically.valid",
478				"FakeMember",
479			)
480			.unwrap()
481			.sender(":0.0")
482			.unwrap()
483			.build(&<$type>::default().body())
484			.unwrap();
485			let event = <$type>::try_from(&fake_msg);
486			event.expect("Should panic");
487		}
488
489		#[cfg(feature = "zbus")]
490		#[test]
491		#[should_panic(expected = "Should panic")]
492		fn zbus_msg_conversion_failure_correct_body_and_member() -> () {
493			let fake_msg = zbus::Message::signal(
494				"/org/a11y/sixtynine/fourtwenty",
495				"org.a11y.atspi.accessible.technically.valid",
496				<$type as BusProperties>::DBUS_MEMBER,
497			)
498			.unwrap()
499			.sender(":0.0")
500			.unwrap()
501			.build(&<$type>::default().body())
502			.unwrap();
503			let event = <$type>::try_from(&fake_msg);
504			event.expect("Should panic");
505		}
506	};
507}
508
509/// Expands to five tests:
510///
511/// 1. `into_and_try_from_event`
512/// 2. `zbus_msg_invalid_interface`
513/// 3. `zbus_msg_invalid_member`
514/// 4. `zbus_msg_invalid_member_and_interface`
515/// 5. `zbus_msg_conversion`
516///
517/// # Examples
518///
519/// ```ignore
520/// event_wrapper_test_cases!(MouseEvents, AbsEvent);
521/// ```
522/// In the macro, its first argument `$type` is the event enum type.  
523/// The second argument `$any_subtype` is the event struct type.
524///
525/// For each of the types, the macro will create a module with the name `events_tests_{foo}`
526/// where `{foo}` is the snake case of the 'interface enum' name.
527macro_rules! event_wrapper_test_cases {
528	($type:ty, $any_subtype:ty) => {
529		#[cfg(test)]
530		#[rename_item::rename(name($type), prefix = "events_tests_", case = "snake")]
531		mod foo {
532			use super::{$any_subtype, $type, Event, BusProperties};
533			#[test]
534			fn into_and_try_from_event() {
535				// Create a default event struct from its type's `Default::default()` impl.
536				let sub_type = <$any_subtype>::default();
537				// Wrap the event struct in the event enum
538				let mod_type = <$type>::from(sub_type);
539				// Wrap the inner event enum into the `Event` enum.
540				let event = Event::from(mod_type.clone());
541				// Unwrap the `Event` enum into the inner event enum.
542				let mod_type2 = <$type>::try_from(event.clone())
543					.expect("Should convert outer `Event` enum into interface enum because it was created from it. Check the `impl_try_from_event_for_user_facing_event_type` macro");
544				assert_eq!(
545					mod_type, mod_type2,
546					"Events were able to be parsed and encapsulated, but they have changed value"
547				);
548			}
549			#[cfg(feature = "zbus")]
550			#[test]
551			#[should_panic(expected = "Should panic")]
552			fn zbus_msg_invalid_interface() {
553				let fake_msg = zbus::Message::signal(
554					"/org/a11y/sixtynine/fourtwenty",
555					"org.a11y.atspi.technically.valid.lol",
556					<$any_subtype as BusProperties>::DBUS_MEMBER,
557				)
558				.unwrap()
559				.sender(":0.0")
560				.unwrap()
561				.build(&<$any_subtype>::default().body())
562				.unwrap();
563
564				// It is hard to see what eventually is tested here. Let's unravel it:
565				//
566				// Below we call `TryFrom<&zbus::Message> for $type` where `$type` the interface enum name. (eg. `MouseEvents`, `ObjectEvents`, etc.) and
567				// `mod_type` is an 'interface enum' variant (eg. `MouseEvents::Abs(AbsEvent)`).
568				// This conversion is found in the `/src/events/{iface_name}.rs`` file.
569				// This conversion in turn leans on the `impl_from_dbus_message` macro.
570				// In `MouseEvents::Abs(msg.try_into()?)`, it is the `msg.try_into()?` that should fail.
571				// The `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
572				let mod_type = <$type>::try_from(&fake_msg);
573				mod_type.expect("Should panic");
574			}
575			#[cfg(feature = "zbus")]
576			#[test]
577			#[should_panic(expected = "Should panic")]
578			fn zbus_msg_invalid_member() {
579				let fake_msg = zbus::Message::signal(
580					"/org/a11y/sixtynine/fourtwenty",
581					<$any_subtype as BusProperties>::DBUS_INTERFACE,
582					"FakeFunctionLol",
583				)
584				.unwrap()
585				.sender(":0.0")
586				.unwrap()
587				.build(&<$any_subtype>::default().body())
588				.unwrap();
589				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
590				let mod_type = <$type>::try_from(&fake_msg);
591				mod_type.expect("Should panic");
592			}
593			#[cfg(feature = "zbus")]
594			#[test]
595			#[should_panic(expected = "Should panic")]
596			fn zbus_msg_invalid_member_and_interface() {
597				let fake_msg = zbus::Message::signal(
598					"/org/a11y/sixtynine/fourtwenty",
599					"org.a11y.atspi.technically.allowed",
600					"FakeFunctionLol",
601				)
602				.unwrap()
603				.sender(":0.0")
604				.unwrap()
605				.build(&<$any_subtype>::default().body())
606				.unwrap();
607				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
608				let mod_type = <$type>::try_from(&fake_msg);
609
610				// Note that the non-matching interface is the first error, so the member match error is not reached.
611				mod_type.expect("Should panic");
612			}
613			#[cfg(feature = "zbus")]
614			#[test]
615			fn zbus_msg_conversion() {
616				let valid_msg = zbus::Message::signal(
617					"/org/a11y/sixtynine/fourtwenty",
618					<$any_subtype as BusProperties>::DBUS_INTERFACE,
619					<$any_subtype as BusProperties>::DBUS_MEMBER,
620				)
621				.unwrap()
622				.sender(":0.0")
623				.unwrap()
624				.build(&<$any_subtype>::default().body())
625				.unwrap();
626				// As above, the `msg.try_into()?` is provided through the `impl_from_dbus_message` macro.
627				let mod_type = <$type>::try_from(&valid_msg);
628				mod_type.expect("Should convert from `$any_subtype::default()` built `Message` back into a interface event enum variant wrapping an inner type. Check the `impl_from_dbus_message` macro.");
629			}
630		}
631	};
632}
633
634macro_rules! event_test_cases {
635	($type:ty) => {
636		#[cfg(test)]
637		#[rename_item::rename(name($type), prefix = "event_tests_", case = "snake")]
638		mod foo {
639			use super::{$type, Event, BusProperties, EventProperties, EventTypeProperties};
640
641			generic_event_test_case!($type);
642			event_enum_test_case!($type);
643			zbus_message_test_case!($type);
644			event_enum_transparency_test_case!($type);
645		}
646		assert_impl_all!(
647			$type: Clone,
648			std::fmt::Debug,
649			serde::Serialize,
650			serde::Deserialize<'static>,
651			Default,
652			PartialEq,
653			Eq,
654			std::hash::Hash,
655			crate::EventProperties,
656			crate::EventTypeProperties,
657			crate::BusProperties,
658		);
659		#[cfg(feature = "zbus")]
660		assert_impl_all!(zbus::Message: TryFrom<$type>);
661	};
662}