atspi_common/events/
traits.rs

1#[cfg(feature = "zbus")]
2use crate::AtspiError;
3use crate::ObjectRef;
4#[cfg(feature = "zbus")]
5use serde::{Deserialize, Serialize};
6#[cfg(feature = "zbus")]
7use zbus::message::{Body as DbusBody, Header};
8use zbus_names::UniqueName;
9use zvariant::ObjectPath;
10#[cfg(feature = "zbus")]
11use zvariant::Type;
12
13/// Describes properties of a specific event _type_.
14///
15/// - `DBus` member name
16/// - `DBus` interface name
17///
18/// Together, the member and interface name can describe a specific event _type_.
19/// Likewise, the path and sender bus name collectively make up an [`ObjectRef`], which is a way to uniquely identify an individual accessible item available to `atspi`.
20/// The latter is available via the [`EventProperties`] trait.
21///
22/// This can also be generalized, for example this is implemented for [`crate::Event`] by dispatching to the matching variants.
23/// NOTE: to use `EventProperties` on wrapper types, like `Event`, you must enable the "enum-dispatch" feature.
24///
25/// This trait *is* object-safe.
26pub trait EventTypeProperties {
27	fn member(&self) -> &'static str;
28	fn interface(&self) -> &'static str;
29	fn match_rule(&self) -> &'static str;
30	fn registry_string(&self) -> &'static str;
31}
32
33/// `EventProperties` allows access to the internals of an event, specifically:
34///
35/// - The `DBUs` name which sent the event.
36/// - The `ObjectPath`, a unique id for a given application.
37/// - Collectively, this is called an [`ObjectRef`].
38///
39/// This trait *is* object-safe.
40pub trait EventProperties {
41	fn sender(&self) -> UniqueName<'_>;
42	fn path(&self) -> ObjectPath<'_>;
43	fn object_ref(&self) -> ObjectRef {
44		ObjectRef { name: self.sender().into(), path: self.path().into() }
45	}
46}
47
48assert_obj_safe!(EventTypeProperties);
49assert_obj_safe!(EventProperties);
50
51/// A way to convert a [`zbus::Message`] without checking its interface.
52#[cfg(all(feature = "zbus", feature = "wrappers"))]
53pub(crate) trait EventWrapperMessageConversion {
54	/// # Errors
55	/// Will fail if no matching member or body signature is found.
56	fn try_from_message_interface_checked(
57		msg: &zbus::Message,
58		hdr: &Header,
59	) -> Result<Self, AtspiError>
60	where
61		Self: Sized;
62}
63
64// TODO: Document why this can't be `TryFrom<&zbus::Message>`.
65#[cfg(all(feature = "zbus", feature = "wrappers"))]
66pub(crate) trait TryFromMessage {
67	fn try_from_message(msg: &zbus::Message) -> Result<Self, AtspiError>
68	where
69		Self: Sized;
70}
71
72/// The `DBus` member for the event.
73/// For example, for an [`crate::events::object::TextChangedEvent`] this should be `"TextChanged"`
74pub trait DBusMember {
75	/// The event's `DBus` member.
76	const DBUS_MEMBER: &'static str;
77}
78
79/// The `DBus` interface name for an event - or a wrapper type.
80/// For example, for any event within the "Object" interface, this should be "org.a11y.atspi.Event.Object".
81pub trait DBusInterface {
82	/// A static interface string for `DBus`.
83	/// This should usually be a string that looks like this: `"org.a11y.atspi.Event.*"`;
84	const DBUS_INTERFACE: &'static str;
85}
86
87/// A static `DBus` match rule string.
88/// This should usually be a string that looks like this:
89/// `"type='signal',interface='org.a11y.atspi.Event.Object',member='PropertyChange'"`;
90// We cannot concat! consts, so we (at time of writing) need to have a separate trait for this.
91// Otherwise composing from `DBusMember` and `DBusInterface` would be preferred.
92pub trait DBusMatchRule {
93	/// A static match rule string for `DBus`.
94	const MATCH_RULE_STRING: &'static str;
95}
96
97/// A static `Registry` event string for registering with the `RegistryProxy` for receiving events.
98pub trait RegistryEventString {
99	/// A registry event string for registering for event receiving via the `RegistryProxy`.
100	/// This should be deprecated in favour of composing the string from [`DBusMember::DBUS_MEMBER`] and [`DBusInterface::DBUS_INTERFACE`].
101	const REGISTRY_EVENT_STRING: &'static str;
102}
103
104/// A 'alias'-trait that combines all the `DBus` related traits.
105pub trait DBusProperties: DBusMember + DBusInterface + DBusMatchRule + RegistryEventString {}
106
107#[cfg(feature = "zbus")]
108pub trait MessageConversionExt<'a, B>: 'a + MessageConversion<'a, Body<'a> = B>
109where
110	B: Type + Serialize + Deserialize<'a>,
111{
112	/// Convert a [`zbus::Message`] into this event type.
113	/// Does all the validation for you.
114	///
115	/// # Errors
116	///
117	/// - The message does not have an interface: [`type@AtspiError::MissingInterface`]
118	/// - The message interface does not match the one for the event: [`type@AtspiError::InterfaceMatch`]
119	/// - The message does not have an member: [`type@AtspiError::MissingMember`]
120	/// - The message member does not match the one for the event: [`type@AtspiError::MemberMatch`]
121	/// - The message signature does not match the one for the event: [`type@AtspiError::SignatureMatch`]
122	///
123	/// See [`MessageConversion::from_message_unchecked`] for info on panic condition that should never
124	/// happen.
125	fn try_from_message(msg: &'a zbus::Message, hdr: &Header) -> Result<Self, AtspiError>
126	where
127		Self: Sized + 'a;
128
129	/// Validate the interface string via [`zbus::message::Header::interface`] against `Self`'s assignment of [`DBusInterface::DBUS_INTERFACE`]
130	///
131	/// # Errors
132	///
133	/// - [`type@AtspiError::MissingInterface`] if there is no interface
134	/// - [`type@AtspiError::InterfaceMatch`] if the interfaces do not match
135	fn validate_interface(header: &Header) -> Result<(), AtspiError> {
136		let interface = header.interface().ok_or(AtspiError::MissingInterface)?;
137		if interface != Self::DBUS_INTERFACE {
138			return Err(AtspiError::InterfaceMatch(format!(
139				"The interface {} does not match the signal's interface: {}",
140				interface,
141				Self::DBUS_INTERFACE,
142			)));
143		}
144		Ok(())
145	}
146
147	/// Validate the member string via [`zbus::message::Header::member`] against `Self`'s assignment of [`DBusMember::DBUS_MEMBER`]
148	///
149	/// # Errors
150	///
151	/// - [`type@AtspiError::MissingMember`] if there is no member
152	/// - [`type@AtspiError::MemberMatch`] if the members do not match
153	fn validate_member(hdr: &Header) -> Result<(), AtspiError> {
154		let member = hdr.member().ok_or(AtspiError::MissingMember)?;
155		if member != Self::DBUS_MEMBER {
156			return Err(AtspiError::MemberMatch(format!(
157				"The member {} does not match the signal's member: {}",
158				// unwrap is safe here because of guard above
159				member,
160				Self::DBUS_MEMBER,
161			)));
162		}
163		Ok(())
164	}
165
166	/// Validate the body signature against the [`zvariant::Signature`] of [`MessageConversion::Body`]
167	///
168	/// # Errors
169	///
170	/// - [`type@AtspiError::SignatureMatch`] if the signatures do not match
171	fn validate_body(msg: &zbus::Message) -> Result<(), AtspiError> {
172		let body = msg.body();
173		let body_signature = body.signature();
174
175		let expected_signature = B::SIGNATURE;
176		if body_signature != expected_signature {
177			return Err(AtspiError::SignatureMatch(format!(
178				"The message signature {} does not match the signal's body signature: {}",
179				body_signature,
180				&expected_signature.to_string(),
181			)));
182		}
183		Ok(())
184	}
185}
186
187#[cfg(feature = "zbus")]
188pub trait MessageConversion<'a>: DBusProperties {
189	/// What is the body type of this event.
190	type Body<'msg>: Type + Deserialize<'msg> + Serialize
191	where
192		Self: 'msg;
193
194	/// Build an event from a [`zbus::Message`] reference.
195	/// This function will not check for any of the following error conditions:
196	///
197	/// - That the message has an interface: [`type@AtspiError::MissingInterface`]
198	/// - That the message interface matches the one for the event: [`type@AtspiError::InterfaceMatch`]
199	/// - That the message has an member: [`type@AtspiError::MissingMember`]
200	/// - That the message member matches the one for the event: [`type@AtspiError::MemberMatch`]
201	/// - That the message signature matches the one for the event: [`type@AtspiError::SignatureMatch`]
202	///
203	/// Therefore, this should only be used when one has checked the above conditions.
204	/// These must be checked manually.
205	/// Alternatively, there is the [`MessageConversionExt::try_from_message`] that will check these
206	/// conditions for you.
207	///
208	/// This type also implements `TryFrom<&zbus::Message>`; consider using this if you are not an
209	/// internal developer.
210	///
211	/// # Errors
212	///
213	/// It is possible to get a [`type@AtspiError::Zvariant`] error if you do not check the proper
214	/// conditions before calling this.
215	fn from_message_unchecked(msg: &zbus::Message, header: &Header) -> Result<Self, AtspiError>
216	where
217		Self: Sized + 'a;
218
219	/// Build an event from an [`ObjectRef`] and [`Self::Body`].
220	/// This function will not check for any of the following error conditions:
221	///
222	/// - That the message has an interface: [`type@AtspiError::MissingInterface`]
223	/// - That the message interface matches the one for the event: [`type@AtspiError::InterfaceMatch`]
224	/// - That the message has an member: [`type@AtspiError::MissingMember`]
225	/// - That the message member matches the one for the event: [`type@AtspiError::MemberMatch`]
226	///
227	/// Therefore, this should only be used when one has checked the above conditions.
228	///
229	/// # Errors
230	///
231	/// Some [`Self::Body`] types may fallibly convert data fields contained in the body.
232	/// If this happens, then the function will return an error.
233	fn from_message_unchecked_parts(obj_ref: ObjectRef, body: DbusBody) -> Result<Self, AtspiError>
234	where
235		Self: Sized;
236
237	/// The body of the object.
238	fn body(&self) -> Self::Body<'_>;
239}