macro_rules! impl_event_properties {
($type:ty) => {
impl EventProperties for $type {
fn sender(&self) -> UniqueName<'_> {
self.item.name.as_ref()
}
fn path(&self) -> ObjectPath<'_> {
self.item.path.as_ref()
}
}
};
}
macro_rules! impl_from_object_ref {
($type:ty) => {
impl From<crate::ObjectRef> for $type {
fn from(obj_ref: crate::ObjectRef) -> Self {
Self { item: obj_ref }
}
}
};
}
macro_rules! impl_from_interface_event_enum_for_event {
($outer_type:ty, $outer_variant:path) => {
#[cfg(feature = "wrappers")]
impl From<$outer_type> for Event {
fn from(event_variant: $outer_type) -> Event {
$outer_variant(event_variant.into())
}
}
};
}
macro_rules! impl_try_from_event_for_interface_enum {
($outer_type:ty, $outer_variant:path) => {
impl TryFrom<Event> for $outer_type {
type Error = AtspiError;
fn try_from(generic_event: Event) -> Result<$outer_type, Self::Error> {
if let $outer_variant(event_type) = generic_event {
Ok(event_type)
} else {
Err(AtspiError::Conversion("Invalid type"))
}
}
}
};
}
macro_rules! impl_from_user_facing_event_for_interface_event_enum {
($inner_type:ty, $outer_type:ty, $inner_variant:path) => {
impl From<$inner_type> for $outer_type {
fn from(specific_event: $inner_type) -> $outer_type {
$inner_variant(specific_event)
}
}
};
}
macro_rules! impl_from_user_facing_type_for_event_enum {
($inner_type:ty, $outer_variant:path) => {
#[cfg(feature = "wrappers")]
impl From<$inner_type> for Event {
fn from(event_variant: $inner_type) -> Event {
$outer_variant(event_variant.into())
}
}
};
}
macro_rules! impl_try_from_event_for_user_facing_type {
($inner_type:ty, $inner_variant:path, $outer_variant:path) => {
#[cfg(feature = "wrappers")]
impl TryFrom<Event> for $inner_type {
type Error = AtspiError;
fn try_from(generic_event: Event) -> Result<$inner_type, Self::Error> {
if let $outer_variant($inner_variant(specific_event)) = generic_event {
Ok(specific_event)
} else {
Err(AtspiError::Conversion("Invalid type"))
}
}
}
};
}
macro_rules! impl_to_dbus_message {
($type:ty) => {
#[cfg(feature = "zbus")]
impl TryFrom<$type> for zbus::Message {
type Error = AtspiError;
fn try_from(event: $type) -> Result<Self, Self::Error> {
Ok(zbus::Message::signal(
event.path(),
<$type as BusProperties>::DBUS_INTERFACE,
<$type as BusProperties>::DBUS_MEMBER,
)?
.sender(event.sender().to_string())?
.build(&event.body())?)
}
}
};
}
macro_rules! impl_from_dbus_message {
($type:ty) => {
impl_from_dbus_message!($type, Auto);
};
($type:ty, Auto) => {
#[cfg(feature = "zbus")]
impl TryFrom<&zbus::Message> for $type {
type Error = AtspiError;
fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
use zvariant::Type;
Self::validate_interface(msg)?;
Self::validate_member(msg)?;
let body = msg.body();
let body_signature = body.signature();
let deser_body: <Self as MessageConversion>::Body = if body_signature == crate::events::EventBodyOwned::SIGNATURE {
body.deserialize_unchecked()?
} else if body_signature == crate::events::EventBodyQtOwned::SIGNATURE {
let qtbody: crate::events::EventBodyQtOwned = body.deserialize_unchecked()?;
qtbody.into()
} else {
return Err(AtspiError::SignatureMatch(format!(
"The message signature {} does not match the signal's body signature: {}",
body_signature,
<Self as MessageConversion>::Body::SIGNATURE,
)));
};
let item = msg.try_into()?;
Self::from_message_unchecked_parts(item, deser_body)
}
}
};
($type:ty, Explicit) => {
#[cfg(feature = "zbus")]
impl TryFrom<&zbus::Message> for $type {
type Error = AtspiError;
fn try_from(msg: &zbus::Message) -> Result<Self, Self::Error> {
<$type as MessageConversionExt<<$type as MessageConversion>::Body>>::try_from_message(msg)
}
}
};
}
#[cfg(test)]
macro_rules! generic_event_test_case {
($type:ty) => {
#[test]
fn generic_event_uses() {
let struct_event = <$type>::default();
assert_eq!(struct_event.path().as_str(), "/org/a11y/atspi/accessible/null");
assert_eq!(struct_event.sender().as_str(), ":0.0");
let body = struct_event.body();
let body2 = Message::method_call(
struct_event.path().as_str(),
<$type as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(struct_event.sender().as_str())
.unwrap()
.build(&(body,))
.unwrap();
let build_struct = <$type>::from_message_unchecked(&body2)
.expect("<$type as Default>'s parts should build a valid ObjectRef");
assert_eq!(struct_event, build_struct);
}
};
}
#[cfg(test)]
macro_rules! event_enum_test_case {
($type:ty) => {
#[test]
fn event_enum_conversion() {
let struct_event = <$type>::default();
let event = Event::from(struct_event.clone());
let struct_event_back = <$type>::try_from(event)
.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");
assert_eq!(struct_event, struct_event_back);
}
};
}
#[cfg(test)]
macro_rules! event_enum_transparency_test_case {
($type:ty) => {
#[test]
fn event_enum_transparency_test_case() {
let specific_event = <$type>::default();
let generic_event = Event::from(specific_event.clone());
assert_eq!(
specific_event.member(),
generic_event.member(),
"DBus member strings do not match."
);
assert_eq!(
specific_event.interface(),
generic_event.interface(),
"Registry interfaces do not match."
);
assert_eq!(
specific_event.registry_string(),
generic_event.registry_string(),
"Registry strings do not match."
);
assert_eq!(
specific_event.match_rule(),
generic_event.match_rule(),
"Match rule strings do not match."
);
assert_eq!(specific_event.path(), generic_event.path(), "Pathsdo not match.");
assert_eq!(specific_event.sender(), generic_event.sender(), "Senders do not match.");
}
};
}
#[cfg(test)]
macro_rules! zbus_message_qtspi_test_case {
($type:ty, Auto) => {
#[cfg(feature = "zbus")]
#[test]
fn zbus_message_conversion_qtspi() {
let ev = <$type>::default();
let qt: crate::events::EventBodyQtOwned = ev.body().into();
let msg = zbus::Message::signal(
ev.path(),
ev.interface(),
ev.member(),
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&(qt,))
.unwrap();
<$type>::try_from(&msg).expect("Should be able to use an EventBodyQtOwned for any type whose BusProperties::Body = EventBodyOwned");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_message_conversion_qtspi_event_enum() {
let ev = <$type>::default();
let qt: crate::events::EventBodyQtOwned = ev.body().into();
let msg = zbus::Message::signal(
ev.path(),
ev.interface(),
ev.member(),
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&(qt,))
.unwrap();
assert_matches!(Event::try_from(&msg), Ok(_));
}
};
($type:ty, Explicit) => {};
}
#[cfg(test)]
macro_rules! zbus_message_test_case {
($type:ty) => {
zbus_message_test_case!($type, Auto);
};
($type:ty, $extra:tt) => {
zbus_message_qtspi_test_case!($type, $extra);
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_to_specific_event_type() {
let struct_event = <$type>::default();
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 .");
let struct_event_back =
<$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.");
assert_eq!(struct_event, struct_event_back, "Events converted into a message and back must be the same");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_to_event_enum_type() {
let struct_event = <$type>::default();
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 .");
let event_enum_back =
Event::try_from(&msg).expect("Should convert a from `$type::default()` built `Message` into an event enum. Check the `impl_from_dbus_message` macro .");
let event_enum: Event = struct_event.into();
assert_eq!(event_enum, event_enum_back);
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_fake_msg() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.technically.valid",
"MadeUpMember",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(_), "This conversion should fail");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_validated_message_with_body() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.technically.valid",
"MadeUpMember",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$type>::default().body())
.unwrap();
let event = <$type>::from_message_unchecked(&fake_msg);
event.expect("The from_message_unchecked function should work, despite mismatching interface and member");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_interface() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$type as BusProperties>::DBUS_INTERFACE,
"MadeUpMember",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_interface_and_member() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$type as BusProperties>::DBUS_INTERFACE,
<$type as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_interface_and_member_invalid_body() -> () {
let invalid_body: (i32, u64, String, String) = (0, 0, String::new(), String::new());
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$type as BusProperties>::DBUS_INTERFACE,
<$type as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&invalid_body)
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(AtspiError::SignatureMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_body() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.accessible.technically.valid",
"FakeMember",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$type>::default().body())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(_));
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_body_and_member() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.accessible.technically.valid",
<$type as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$type>::default().body())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion_failure_correct_body_and_interface() -> () {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$type as BusProperties>::DBUS_INTERFACE,
"MadeUpMember",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$type>::default().body())
.unwrap();
let event = <$type>::try_from(&fake_msg);
assert_matches!(event, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
}
};
}
macro_rules! event_wrapper_test_cases {
($type:ty, $any_subtype:ty) => {
#[cfg(test)]
#[rename_item::rename(name($type), prefix = "events_tests_", case = "snake")]
mod foo {
use super::{$any_subtype, $type, AtspiError, Event, BusProperties, MessageConversion};
use assert_matches::assert_matches;
#[test]
fn into_and_try_from_event() {
let sub_type = <$any_subtype>::default();
let mod_type = <$type>::from(sub_type);
let event = Event::from(mod_type.clone());
let mod_type2 = <$type>::try_from(event.clone())
.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");
assert_eq!(
mod_type, mod_type2,
"Events were able to be parsed and encapsulated, but they have changed value"
);
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_invalid_interface() {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.technically.valid.lol",
<$any_subtype as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$any_subtype>::default().body())
.unwrap();
let mod_type = <$type>::try_from(&fake_msg);
let event_type = Event::try_from(&fake_msg);
assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
assert_matches!(event_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_invalid_member() {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$any_subtype as BusProperties>::DBUS_INTERFACE,
"FakeFunctionLol",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$any_subtype>::default().body())
.unwrap();
let mod_type = <$type>::try_from(&fake_msg);
assert_matches!(mod_type, Err(AtspiError::MemberMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_invalid_member_and_interface() {
let fake_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
"org.a11y.atspi.technically.allowed",
"FakeFunctionLol",
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$any_subtype>::default().body())
.unwrap();
let mod_type = <$type>::try_from(&fake_msg);
assert_matches!(mod_type, Err(AtspiError::InterfaceMatch(_)), "Wrong kind of error");
}
#[cfg(feature = "zbus")]
#[test]
fn zbus_msg_conversion() {
let valid_msg = zbus::Message::signal(
"/org/a11y/sixtynine/fourtwenty",
<$any_subtype as BusProperties>::DBUS_INTERFACE,
<$any_subtype as BusProperties>::DBUS_MEMBER,
)
.unwrap()
.sender(":0.0")
.unwrap()
.build(&<$any_subtype>::default().body())
.unwrap();
let mod_type = <$type>::try_from(&valid_msg);
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.");
}
}
};
}
macro_rules! event_test_cases {
($type:ty) => {
event_test_cases!($type, Auto);
};
($type:ty, $qt:tt) => {
#[cfg(test)]
#[rename_item::rename(name($type), prefix = "event_tests_", case = "snake")]
mod foo {
use crate::{EventTypeProperties, Event};
use super::{$type, AtspiError, BusProperties, MessageConversion, EventProperties};
use zbus::Message;
use assert_matches::assert_matches;
generic_event_test_case!($type);
event_enum_test_case!($type);
zbus_message_test_case!($type, $qt);
event_enum_transparency_test_case!($type);
}
assert_impl_all!(
$type: Clone,
std::fmt::Debug,
serde::Serialize,
serde::Deserialize<'static>,
Default,
PartialEq,
Eq,
std::hash::Hash,
crate::EventProperties,
crate::EventTypeProperties,
crate::BusProperties,
);
#[cfg(feature = "zbus")]
assert_impl_all!(zbus::Message: TryFrom<$type>);
};
}