diff --git a/Cargo.toml b/Cargo.toml index 9184ed1b..285e010b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ authors = ["Michael Connor Buchan ", "Tait Hoyem ::SIGNATURE, + zbus::zvariant::Signature::from_str("((so)(so)(so)a(so)assusau)").unwrap() ); + assert_eq!(::SIGNATURE, LEGACY_CACHE_ITEM_SIGNATURE,); } diff --git a/atspi-common/src/events/document.rs b/atspi-common/src/events/document.rs index d2db94b4..f0267cdf 100644 --- a/atspi-common/src/events/document.rs +++ b/atspi-common/src/events/document.rs @@ -134,7 +134,9 @@ pub struct AttributesChangedEvent { pub item: crate::events::ObjectRef, } -/// The focused page has changed. This event is usually sent only by document readers, signaling +/// The focused page has changed. +/// +/// This event is usually sent only by document readers, signaling /// that the _physical page equivalent is now different. /// This event does not encode _which_ page is the new one, only that a new page is now the primary /// one. diff --git a/atspi-common/src/events/mod.rs b/atspi-common/src/events/mod.rs index 37074a43..06c02a4d 100644 --- a/atspi-common/src/events/mod.rs +++ b/atspi-common/src/events/mod.rs @@ -17,12 +17,43 @@ pub mod window; // specific signal types with TryFrom implementations. See crate::[`identify`] // EVENT_LISTENER_SIGNATURE is a type signature used to notify when events are registered or deregistered. // CACHE_ADD_SIGNATURE and *_REMOVE have very different types -pub const ATSPI_EVENT_SIGNATURE: Signature<'_> = - Signature::from_static_str_unchecked("(siiva{sv})"); -pub const QSPI_EVENT_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked("(siiv(so))"); -pub const EVENT_LISTENER_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked("(ss)"); -pub const CACHE_ADD_SIGNATURE: Signature<'_> = - Signature::from_static_str_unchecked("((so)(so)(so)iiassusau)"); +// Same as "(siiva{sv})" +pub const ATSPI_EVENT_SIGNATURE: &Signature = &Signature::static_structure(&[ + &Signature::Str, + &Signature::I32, + &Signature::I32, + &Signature::Variant, + &Signature::Dict { + key: Child::Static { child: &Signature::Str }, + value: Child::Static { child: &Signature::Variant }, + }, +]); +pub const EVENT_NAME_SIGNATURE: &Signature = + &Signature::static_structure(&[&Signature::Str, &Signature::Str]); +// Same as "(siiv(so))" +pub const QSPI_EVENT_SIGNATURE: &Signature = &Signature::static_structure(&[ + &Signature::Str, + &Signature::I32, + &Signature::I32, + &Signature::Variant, + &Signature::Structure(Fields::Static { fields: &[&Signature::Str, &Signature::ObjectPath] }), +]); +// Same as "(so)" +pub const EVENT_LISTENER_SIGNATURE: &Signature = + &Signature::static_structure(&[&Signature::Str, &Signature::ObjectPath]); +// Same as "((so)(so)(so)iiassusau)" +pub const CACHE_ADD_SIGNATURE: &Signature = &Signature::static_structure(&[ + &Signature::Structure(Fields::Static { fields: &[&Signature::Str, &Signature::ObjectPath] }), + &Signature::Structure(Fields::Static { fields: &[&Signature::Str, &Signature::ObjectPath] }), + &Signature::Structure(Fields::Static { fields: &[&Signature::Str, &Signature::ObjectPath] }), + &Signature::I32, + &Signature::I32, + &Signature::Array(Child::Static { child: &Signature::Str }), + &Signature::Str, + &Signature::U32, + &Signature::Str, + &Signature::Array(Child::Static { child: &Signature::U32 }), +]); use std::collections::HashMap; @@ -31,7 +62,10 @@ use zbus_lockstep_macros::validate; use zbus_names::{OwnedUniqueName, UniqueName}; #[cfg(feature = "zbus")] use zvariant::OwnedObjectPath; -use zvariant::{ObjectPath, OwnedValue, Signature, Type, Value}; +use zvariant::{ + signature::{Child, Fields}, + ObjectPath, OwnedValue, Signature, Type, Value, +}; use crate::{ cache::{CacheItem, LegacyCacheItem}, @@ -319,6 +353,7 @@ impl HasRegistryEventString for EventListenerEvents { } /// All events related to the `org.a11y.atspi.Cache` interface. +/// /// Note that these are not telling the client that an item *has been added* to a cache. /// It is telling the client "here is a bunch of information to store it in your cache". #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)] @@ -568,7 +603,7 @@ impl TryFrom<&zbus::Message> for EventBodyOwned { fn try_from(message: &zbus::Message) -> Result { let body = message.body(); - let signature = body.signature().ok_or_else(|| AtspiError::MissingSignature)?; + let signature = body.signature(); if signature == QSPI_EVENT_SIGNATURE { let qt_body = body.deserialize::()?; @@ -797,8 +832,7 @@ impl TryFrom<&zbus::Message> for Event { let body = msg.body(); let header = msg.header(); - let body_signature = body.signature().ok_or(AtspiError::MissingSignature)?; - let body_signature_str = body_signature.as_str(); + let body_signature = body.signature(); let member = header.member().ok_or(AtspiError::MissingMember)?; let member_str = member.as_str(); @@ -810,48 +844,70 @@ impl TryFrom<&zbus::Message> for Event { // parentheses are not included in the signature. // However, `Cache` signals are often emitted with outer parentheses, so we also need to // match against the same signature, but with outer parentheses. - match (interface_str, member_str, body_signature_str) { - ("org.a11y.atspi.Socket", "Available", "so") => { + match (interface_str, member_str, body_signature) { + ("org.a11y.atspi.Socket", "Available", sig) + if sig == crate::object_ref::OBJECT_REF_SIGNATURE => + { Ok(AvailableEvent::try_from(msg)?.into()) } - ("org.a11y.atspi.Event.Object", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Object", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Object(ObjectEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Document", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Document", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Document(DocumentEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Window", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Window", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Window(WindowEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Terminal", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Terminal", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Terminal(TerminalEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Mouse", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Mouse", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Mouse(MouseEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Focus", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Focus", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Focus(FocusEvents::try_from(msg)?)) } - ("org.a11y.atspi.Event.Keyboard", _, "siiva{sv}" | "siiv(so)") => { + ("org.a11y.atspi.Event.Keyboard", _, sig) + if sig == ATSPI_EVENT_SIGNATURE || sig == QSPI_EVENT_SIGNATURE => + { Ok(Event::Keyboard(KeyboardEvents::try_from(msg)?)) } - ("org.a11y.atspi.Registry", "EventListenerRegistered", "ss") => { + ("org.a11y.atspi.Registry", "EventListenerRegistered", sig) + if sig == EVENT_NAME_SIGNATURE => + { Ok(EventListenerRegisteredEvent::try_from(msg)?.into()) } - ("org.a11y.atspi.Registry", "EventListenerDeregistered", "ss") => { + ("org.a11y.atspi.Registry", "EventListenerDeregistered", sig) + if sig == EVENT_NAME_SIGNATURE => + { Ok(EventListenerDeregisteredEvent::try_from(msg)?.into()) } - ( - "org.a11y.atspi.Cache", - "AddAccessible", - "(so)(so)(so)iiassusau" | "((so)(so)(so)iiassusau)", - ) => Ok(AddAccessibleEvent::try_from(msg)?.into()), - ( - "org.a11y.atspi.Cache", - "AddAccessible", - "(so)(so)(so)a(so)assusau" | "((so)(so)(so)a(so)assusau)", - ) => Ok(LegacyAddAccessibleEvent::try_from(msg)?.into()), - ("org.a11y.atspi.Cache", "RemoveAccessible", "so" | "(so)") => { + ("org.a11y.atspi.Cache", "AddAccessible", sig) + if sig == crate::cache::CACHE_ITEM_SIGNATURE => + { + Ok(AddAccessibleEvent::try_from(msg)?.into()) + } + ("org.a11y.atspi.Cache", "AddAccessible", sig) + if sig == crate::cache::LEGACY_CACHE_ITEM_SIGNATURE => + { + Ok(LegacyAddAccessibleEvent::try_from(msg)?.into()) + } + ("org.a11y.atspi.Cache", "RemoveAccessible", sig) + if sig == crate::object_ref::OBJECT_REF_SIGNATURE => + { Ok(RemoveAccessibleEvent::try_from(msg)?.into()) } (_iface, _method, sig) => Err(AtspiError::UnknownBusSignature(sig.to_string())), @@ -955,6 +1011,7 @@ pub trait BusProperties { } /// A specific trait *only* to define match rules. +/// /// This is useful for event wrappers like [`ObjectEvents`], which, while it does not have other /// information required to implement the [`BusProperties`] trait, you can indeed add a match rule /// to the `DBus` connection to capture all sub events of [`ObjectEvents`]. @@ -968,6 +1025,7 @@ pub trait HasMatchRule { } /// A specific trait *only* to define registry event matches. +/// /// This is useful for event wrappers like [`ObjectEvents`], which, while it does not have other /// information required to implement the [`BusProperties`] trait, you can indeed add a match rule /// to the AT-SPI connection to subscribe to all sub events of [`ObjectEvents`]. @@ -987,7 +1045,7 @@ mod tests { #[test] fn check_event_body_qt_signature() { - assert_eq!(&::signature(), &QSPI_EVENT_SIGNATURE); + assert_eq!(::SIGNATURE, QSPI_EVENT_SIGNATURE); } #[test] diff --git a/atspi-common/src/interface.rs b/atspi-common/src/interface.rs index c5b81573..52917a69 100644 --- a/atspi-common/src/interface.rs +++ b/atspi-common/src/interface.rs @@ -181,9 +181,7 @@ impl ser::Serialize for InterfaceSet { } impl Type for InterfaceSet { - fn signature() -> Signature<'static> { - as Type>::signature() - } + const SIGNATURE: &'static Signature = as Type>::SIGNATURE; } impl FromIterator for InterfaceSet { diff --git a/atspi-common/src/lib.rs b/atspi-common/src/lib.rs index fddc11ec..ce31c193 100644 --- a/atspi-common/src/lib.rs +++ b/atspi-common/src/lib.rs @@ -43,6 +43,7 @@ use zvariant::Type; pub type Result = std::result::Result; /// Describes a selection of text, including selections across object boundaries. +/// /// For example, selecting from the beginning of a paragraph to half way through a link would cause /// the start and end object references to be different. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Type)] @@ -163,7 +164,9 @@ pub enum ScrollType { } /// Enumeration used to indicate a type of live region and how assertive it -/// should be in terms of speaking notifications. Currently, this is only used +/// should be in terms of speaking notifications. +/// +/// Currently, this is only used /// for `Announcement` events, but it may be used for additional purposes /// in the future. /// The argument in the `Announcement` event is named `politeness`. @@ -195,9 +198,11 @@ impl TryFrom for Politeness { #[cfg(test)] mod tests { use super::*; + use std::str::FromStr; use zbus_lockstep::{ method_args_signature, method_return_signature, signal_body_type_signature, }; + use zvariant::Signature; #[test] fn convert_i32_to_live() { @@ -211,52 +216,58 @@ mod tests { #[test] fn validate_live_signature() { let signature = signal_body_type_signature!("Announcement"); - let politeness_signature = signature.slice(1..2); - assert_eq!(Politeness::signature(), politeness_signature); + let politeness_signature_str = &signature.to_string_no_parens(); + let politeness_signature = Signature::from_str(&politeness_signature_str.as_str()[1..2]) + .expect("Valid signature pattern"); + assert_eq!(*::SIGNATURE, politeness_signature); } #[test] fn validate_scroll_type_signature() { let signature = method_args_signature!(member: "ScrollTo", interface: "org.a11y.atspi.Component", argument: "type"); - assert_eq!(ScrollType::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_layer_signature() { let signature = method_return_signature!("GetLayer"); - assert_eq!(Layer::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_granularity_signature() { let signature = method_args_signature!(member: "GetStringAtOffset", interface: "org.a11y.atspi.Text", argument: "granularity"); - assert_eq!(Granularity::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_clip_type_signature() { let signature = method_args_signature!(member: "GetTextAtOffset", interface: "org.a11y.atspi.Text", argument: "type"); - assert_eq!(ClipType::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_coord_type_signature() { let signature = method_args_signature!(member: "GetImagePosition", interface: "org.a11y.atspi.Image", argument: "coordType"); - assert_eq!(CoordType::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_match_type_signature() { let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); - let match_type_signature = rule_signature.slice(3..4); - assert_eq!(MatchType::signature(), match_type_signature); + let match_type_signature_str = rule_signature.to_string(); + let match_type_signature = Signature::from_str(&match_type_signature_str.as_str()[3..4]) + .expect("Valid signature pattern"); + assert_eq!(*::SIGNATURE, match_type_signature); } #[test] fn validate_text_selection_signature() { - let selection_signature = method_args_signature!(member: "GetTextSelections", interface: "org.a11y.atspi.Document", argument: "selections") - .slice(1..); + let selection_signature = method_args_signature!(member: "GetTextSelections", interface: "org.a11y.atspi.Document", argument: "selections"); + let selection_signature_str = selection_signature.to_string(); + let selection_signature = Signature::from_str(&selection_signature_str.as_str()[1..]) + .expect("Valid signature pattern"); // this signature is written: `a(...)`, where `(...)` is the signature we want to compare against - assert_eq!(TextSelection::signature(), selection_signature); + assert_eq!(*::SIGNATURE, selection_signature); } } diff --git a/atspi-common/src/object_match.rs b/atspi-common/src/object_match.rs index 555bc28e..de382659 100644 --- a/atspi-common/src/object_match.rs +++ b/atspi-common/src/object_match.rs @@ -1,7 +1,7 @@ use std::{borrow::Borrow, collections::HashMap, marker::PhantomData}; use serde::{Deserialize, Serialize}; -use zvariant::{Signature, Type}; +use zvariant::{signature::Child, Signature, Type}; use crate::{Interface, InterfaceSet, Role, State, StateSet}; @@ -25,8 +25,9 @@ pub enum TreeTraversalType { /// Rule(s) against which we can match an object or a collection thereof. /// /// # Examples -/// ```Rust -/// let builder = MatchRule::builder(); +/// ```rust +/// # use zbus::MatchRule; +/// let builder = MatchRule::builder(); /// ``` /// #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] @@ -57,9 +58,20 @@ pub struct ObjectMatchRule { // // https://gitlab.gnome.org/federico/at-spi2-core/-/commit/4885efedeef71e0df8210622771a0b1bb10e194d impl Type for ObjectMatchRule { - fn signature() -> Signature<'static> { - Signature::from_str_unchecked("(aiia{ss}iaiiasib)") - } + const SIGNATURE: &'static Signature = &Signature::static_structure(&[ + &Signature::Array(Child::Static { child: &Signature::I32 }), + &Signature::I32, + &Signature::Dict { + key: Child::Static { child: &Signature::Str }, + value: Child::Static { child: &Signature::Str }, + }, + &Signature::I32, + &Signature::Array(Child::Static { child: &Signature::I32 }), + &Signature::I32, + &Signature::Array(Child::Static { child: &Signature::Str }), + &Signature::I32, + &Signature::Bool, + ]); } impl ObjectMatchRule { @@ -226,32 +238,35 @@ pub enum SortOrder { mod tests { use super::*; use crate::{SortOrder, State}; + use std::str::FromStr; use zbus_lockstep::method_args_signature; #[test] fn validate_match_rule_signature() { let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); - assert_eq!(ObjectMatchRule::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_match_type_signature() { let rule_signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "rule"); // The match type signature is the fourth element in the signature - let match_type_signature = rule_signature.slice(3..4); - assert_eq!(MatchType::signature(), match_type_signature); + let match_type_signature_str = rule_signature.to_string(); + let match_type_signature = Signature::from_str(&match_type_signature_str.as_str()[3..4]) + .expect("Valid signature pattern"); + assert_eq!(*::SIGNATURE, match_type_signature); } #[test] fn validate_tree_traversal_type_signature() { let signature = method_args_signature!(member: "GetMatchesTo", interface: "org.a11y.atspi.Collection", argument: "tree"); - assert_eq!(TreeTraversalType::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] fn validate_sort_order_signature() { let signature = method_args_signature!(member: "GetMatches", interface: "org.a11y.atspi.Collection", argument: "sortby"); - assert_eq!(SortOrder::signature(), signature); + assert_eq!(*::SIGNATURE, signature); } #[test] diff --git a/atspi-common/src/object_ref.rs b/atspi-common/src/object_ref.rs index e3f765ab..99e1e97e 100644 --- a/atspi-common/src/object_ref.rs +++ b/atspi-common/src/object_ref.rs @@ -3,7 +3,9 @@ use zbus_lockstep_macros::validate; use zbus_names::{OwnedUniqueName, UniqueName}; use zvariant::{ObjectPath, OwnedObjectPath, Signature, Type}; -pub const OBJECT_REF_SIGNATURE: Signature<'_> = Signature::from_static_str_unchecked("(so)"); +// Equiv to "(so)" +pub const OBJECT_REF_SIGNATURE: &Signature = + &Signature::static_structure(&[&Signature::Str, &Signature::ObjectPath]); /// `ObjectRef` type /// @@ -78,7 +80,7 @@ impl TryFrom for ObjectRef { match &*value { zvariant::Value::Structure(s) => { if s.signature() != OBJECT_REF_SIGNATURE { - return Err(zvariant::Error::SignatureMismatch(s.signature(), format!("To turn a zvariant::Value into an atspi::ObjectRef, it must be of type {}", OBJECT_REF_SIGNATURE.as_str()))); + return Err(zvariant::Error::SignatureMismatch(s.signature().clone(), format!("To turn a zvariant::Value into an atspi::ObjectRef, it must be of type {OBJECT_REF_SIGNATURE}"))); } let fields = s.fields(); let name: String = diff --git a/atspi-common/src/role.rs b/atspi-common/src/role.rs index 417f67b1..5acd6106 100644 --- a/atspi-common/src/role.rs +++ b/atspi-common/src/role.rs @@ -5,6 +5,7 @@ use crate::AtspiError; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Hash)] /// An accessible object role. +/// /// To think of it in terms of HTML, any semantic element likely has a corollary in this enum. /// For example: `