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}