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