From a22c6d2abe16399bc90ed7386dee22a2ad01513e Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Sat, 23 Nov 2024 08:49:09 -0700 Subject: [PATCH 1/5] Both enum variants should be here; fix conflict --- atspi-common/src/error.rs | 11 + atspi-proxies/src/lib.rs | 1 + atspi-proxies/src/related_proxies.rs | 317 +++++++++++++++++++++++++++ 3 files changed, 329 insertions(+) create mode 100644 atspi-proxies/src/related_proxies.rs diff --git a/atspi-common/src/error.rs b/atspi-common/src/error.rs index 9a024c88..98e0d2a9 100644 --- a/atspi-common/src/error.rs +++ b/atspi-common/src/error.rs @@ -18,6 +18,9 @@ pub enum AtspiError { /// On specific types, if the kind (string variant) does not match the Event's kind. KindMatch(String), + /// When an interface is not available. + InterfaceNotAvailable(&'static str), + /// To indicate a match or equality test on a signal body signature failed. SignatureMatch(String), @@ -84,11 +87,19 @@ impl std::fmt::Display for AtspiError { f.write_str("atspi: interface mismatch in conversion: ")?; e.fmt(f) } +<<<<<<< HEAD Self::KindMatch(e) => { f.write_str(format!("atspi: kind mismatch in conversion: {e}").as_str()) } Self::SignatureMatch(e) => { f.write_str(format!("atspi: body signature mismatch in conversion: {e:?}").as_str()) +======= + Self::InterfaceNotAvailable(e) => { + f.write_str(format!("atspi: interface not available: {e}").as_str()) + } + Self::UnknownBusSignature(e) => { + f.write_str(format!("atspi: Unknown bus body signature: {e:?}").as_str()) +>>>>>>> efbff968 (Add lazy, cheaply guarded proxy-conversions.) } Self::UnknownInterface => f.write_str("Unknown interface."), Self::MissingInterface => f.write_str("Missing interface."), diff --git a/atspi-proxies/src/lib.rs b/atspi-proxies/src/lib.rs index 71eb5cc5..77bf435d 100644 --- a/atspi-proxies/src/lib.rs +++ b/atspi-proxies/src/lib.rs @@ -20,6 +20,7 @@ pub mod device_event_controller; pub mod device_event_listener; pub mod document; pub mod editable_text; +pub mod related_proxies; pub use common::{events, AtspiError, CoordType, Interface, InterfaceSet}; pub mod hyperlink; diff --git a/atspi-proxies/src/related_proxies.rs b/atspi-proxies/src/related_proxies.rs new file mode 100644 index 00000000..b9c93d64 --- /dev/null +++ b/atspi-proxies/src/related_proxies.rs @@ -0,0 +1,317 @@ +use crate::{ + accessible::AccessibleProxy, action::ActionProxy, application::ApplicationProxy, + cache::CacheProxy, collection::CollectionProxy, component::ComponentProxy, + document::DocumentProxy, editable_text::EditableTextProxy, hyperlink::HyperlinkProxy, + hypertext::HypertextProxy, image::ImageProxy, selection::SelectionProxy, table::TableProxy, + table_cell::TableCellProxy, text::TextProxy, value::ValueProxy, AtspiError, +}; +use atspi_common::{Interface, InterfaceSet, Result}; + +/// Easily acquire the other interface proxies an object may have. +/// +/// Equip objects with conversions to proxies of the objects' further implemented interfaces +/// by extending `AccessibleProxy`. +/// +/// The `proxies` method returns a `Proxies` object, which contains the related interface proxies. +/// +/// # Lazy initialization and cheap checks +/// +/// Proxies are lazily initialized, so they are only created when requested. +/// Interface availability is checked before creating the proxy and is a cheap bitop. +pub trait ObjectProxies<'a> { + /// Get `Proxies` for the current object. + fn proxies(&self) -> impl std::future::Future>>; +} + +/// An object for safe conversion to the related interface proxies. +#[derive(Clone, Debug)] +pub struct Proxies<'a> { + interfaces: InterfaceSet, + proxy: zbus::Proxy<'a>, + inner: InnerProxies<'a>, +} + +#[derive(Clone, Debug, Default)] +struct InnerProxies<'a> { + action: Option>, + application: Option>, + cache: Option>, + collection: Option>, + component: Option>, + document: Option>, + editable_text: Option>, + hyperlink: Option>, + hypertext: Option>, + image: Option>, + selection: Option>, + table: Option>, + table_cell: Option>, + text: Option>, + value: Option>, +} + +impl<'a> ObjectProxies<'a> for AccessibleProxy<'a> { + async fn proxies(&self) -> Result> { + let iface_set: InterfaceSet = self.get_interfaces().await?; + let proxy = self.inner().clone(); + + Ok(Proxies { interfaces: iface_set, proxy, inner: InnerProxies::default() }) + } +} + +impl<'a> Proxies<'a> { + /// Get the `Action` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn action(&mut self) -> Result<&mut ActionProxy<'a>> { + if self.interfaces.contains(Interface::Action) { + let proxy_ref = self + .inner + .action + .get_or_insert_with(|| ActionProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Action")) + } + } + + /// Get the `Application` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn application(&mut self) -> Result<&mut ApplicationProxy<'a>> { + if self.interfaces.contains(Interface::Application) { + let proxy_ref = self + .inner + .application + .get_or_insert_with(|| ApplicationProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Application")) + } + } + + /// Get the `Cache` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn cache(&mut self) -> Result<&mut CacheProxy<'a>> { + if self.interfaces.contains(Interface::Cache) { + let proxy_ref = self + .inner + .cache + .get_or_insert_with(|| CacheProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Cache")) + } + } + + /// Get the `Collection` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn collection(&mut self) -> Result<&mut CollectionProxy<'a>> { + if self.interfaces.contains(Interface::Collection) { + let proxy_ref = self + .inner + .collection + .get_or_insert_with(|| CollectionProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Collection")) + } + } + + /// Get the `Component` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn component(&mut self) -> Result<&mut ComponentProxy<'a>> { + if self.interfaces.contains(Interface::Component) { + let proxy_ref = self + .inner + .component + .get_or_insert_with(|| ComponentProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Component")) + } + } + + /// Get the `Document` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn document(&mut self) -> Result<&mut DocumentProxy<'a>> { + if self.interfaces.contains(Interface::Document) { + let proxy_ref = self + .inner + .document + .get_or_insert_with(|| DocumentProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Document")) + } + } + + /// Get the `EditableText` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn editable_text(&mut self) -> Result<&mut EditableTextProxy<'a>> { + if self.interfaces.contains(Interface::EditableText) { + let proxy_ref = self + .inner + .editable_text + .get_or_insert_with(|| EditableTextProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("EditableText")) + } + } + + /// Get the `Hyperlink` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn hyperlink(&mut self) -> Result<&mut HyperlinkProxy<'a>> { + if self.interfaces.contains(Interface::Hyperlink) { + let proxy_ref = self + .inner + .hyperlink + .get_or_insert_with(|| HyperlinkProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Hyperlink")) + } + } + + /// Get the `Hypertext` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn hypertext(&mut self) -> Result<&mut HypertextProxy<'a>> { + if self.interfaces.contains(Interface::Hypertext) { + let proxy_ref = self + .inner + .hypertext + .get_or_insert_with(|| HypertextProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Hypertext")) + } + } + + /// Get the `Image` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn image(&mut self) -> Result<&mut ImageProxy<'a>> { + if self.interfaces.contains(Interface::Image) { + let proxy_ref = self + .inner + .image + .get_or_insert_with(|| ImageProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Image")) + } + } + + /// Get the `Registry` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn selection(&mut self) -> Result<&mut SelectionProxy<'a>> { + if self.interfaces.contains(Interface::Selection) { + let proxy_ref = self + .inner + .selection + .get_or_insert_with(|| SelectionProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Selection")) + } + } + + /// Get the `Table` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn table(&mut self) -> Result<&mut TableProxy<'a>> { + if self.interfaces.contains(Interface::Table) { + let proxy_ref = self + .inner + .table + .get_or_insert_with(|| TableProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Table")) + } + } + + /// Get the `TableCell` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn table_cell(&mut self) -> Result<&mut TableCellProxy<'a>> { + if self.interfaces.contains(Interface::TableCell) { + let proxy_ref = self + .inner + .table_cell + .get_or_insert_with(|| TableCellProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("TableCell")) + } + } + + /// Get the `Text` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn text(&mut self) -> Result<&mut TextProxy<'a>> { + if self.interfaces.contains(Interface::Text) { + let proxy_ref = self + .inner + .text + .get_or_insert_with(|| TextProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Text")) + } + } + + /// Get the `Value` interface proxy. + /// + /// # Errors + /// + /// Returns an error if the interface is not available. + pub fn value(&mut self) -> Result<&mut ValueProxy<'a>> { + if self.interfaces.contains(Interface::Value) { + let proxy_ref = self + .inner + .value + .get_or_insert_with(|| ValueProxy::from(self.proxy.clone())); + Ok(proxy_ref) + } else { + Err(AtspiError::InterfaceNotAvailable("Value")) + } + } +} From 5fb3c28241f158be7c34ecacc9609a448e1c099b Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Sat, 23 Nov 2024 08:49:32 -0700 Subject: [PATCH 2/5] Fix second conflict in enum --- atspi-common/src/error.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/atspi-common/src/error.rs b/atspi-common/src/error.rs index 98e0d2a9..f1164660 100644 --- a/atspi-common/src/error.rs +++ b/atspi-common/src/error.rs @@ -87,19 +87,16 @@ impl std::fmt::Display for AtspiError { f.write_str("atspi: interface mismatch in conversion: ")?; e.fmt(f) } -<<<<<<< HEAD Self::KindMatch(e) => { f.write_str(format!("atspi: kind mismatch in conversion: {e}").as_str()) } Self::SignatureMatch(e) => { f.write_str(format!("atspi: body signature mismatch in conversion: {e:?}").as_str()) -======= Self::InterfaceNotAvailable(e) => { f.write_str(format!("atspi: interface not available: {e}").as_str()) } Self::UnknownBusSignature(e) => { f.write_str(format!("atspi: Unknown bus body signature: {e:?}").as_str()) ->>>>>>> efbff968 (Add lazy, cheaply guarded proxy-conversions.) } Self::UnknownInterface => f.write_str("Unknown interface."), Self::MissingInterface => f.write_str("Missing interface."), From 7966920b40d5e17b57f3b809434b23ebcb918cec Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Sun, 1 Dec 2024 14:59:45 -0700 Subject: [PATCH 3/5] Fix formatting error --- atspi-common/src/error.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/atspi-common/src/error.rs b/atspi-common/src/error.rs index f1164660..4b3231e4 100644 --- a/atspi-common/src/error.rs +++ b/atspi-common/src/error.rs @@ -92,12 +92,10 @@ impl std::fmt::Display for AtspiError { } Self::SignatureMatch(e) => { f.write_str(format!("atspi: body signature mismatch in conversion: {e:?}").as_str()) + } Self::InterfaceNotAvailable(e) => { f.write_str(format!("atspi: interface not available: {e}").as_str()) } - Self::UnknownBusSignature(e) => { - f.write_str(format!("atspi: Unknown bus body signature: {e:?}").as_str()) - } Self::UnknownInterface => f.write_str("Unknown interface."), Self::MissingInterface => f.write_str("Missing interface."), Self::MissingMember => f.write_str("Missing member."), From d69efce3dba26406869348013982df5653ba341f Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Mon, 30 Dec 2024 15:46:51 -0700 Subject: [PATCH 4/5] Naming and docs changes --- atspi-proxies/src/lib.rs | 2 +- atspi-proxies/src/{related_proxies.rs => proxy_ext.rs} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename atspi-proxies/src/{related_proxies.rs => proxy_ext.rs} (97%) diff --git a/atspi-proxies/src/lib.rs b/atspi-proxies/src/lib.rs index 77bf435d..6cfe696f 100644 --- a/atspi-proxies/src/lib.rs +++ b/atspi-proxies/src/lib.rs @@ -20,7 +20,7 @@ pub mod device_event_controller; pub mod device_event_listener; pub mod document; pub mod editable_text; -pub mod related_proxies; +pub mod proxy_ext; pub use common::{events, AtspiError, CoordType, Interface, InterfaceSet}; pub mod hyperlink; diff --git a/atspi-proxies/src/related_proxies.rs b/atspi-proxies/src/proxy_ext.rs similarity index 97% rename from atspi-proxies/src/related_proxies.rs rename to atspi-proxies/src/proxy_ext.rs index b9c93d64..51dab524 100644 --- a/atspi-proxies/src/related_proxies.rs +++ b/atspi-proxies/src/proxy_ext.rs @@ -12,13 +12,13 @@ use atspi_common::{Interface, InterfaceSet, Result}; /// Equip objects with conversions to proxies of the objects' further implemented interfaces /// by extending `AccessibleProxy`. /// -/// The `proxies` method returns a `Proxies` object, which contains the related interface proxies. +/// The `proxies` method returns a `Proxies` struct, which contains lazily loaded proxy accessors. /// /// # Lazy initialization and cheap checks /// /// Proxies are lazily initialized, so they are only created when requested. /// Interface availability is checked before creating the proxy and is a cheap bitop. -pub trait ObjectProxies<'a> { +pub trait ProxyExt<'a> { /// Get `Proxies` for the current object. fn proxies(&self) -> impl std::future::Future>>; } @@ -50,7 +50,7 @@ struct InnerProxies<'a> { value: Option>, } -impl<'a> ObjectProxies<'a> for AccessibleProxy<'a> { +impl<'a> ProxyExt<'a> for AccessibleProxy<'a> { async fn proxies(&self) -> Result> { let iface_set: InterfaceSet = self.get_interfaces().await?; let proxy = self.inner().clone(); From 4c59c24dd6bd6776ca6c3b3fbfeda3451d1d593a Mon Sep 17 00:00:00 2001 From: Tait Hoyem Date: Mon, 30 Dec 2024 15:57:17 -0700 Subject: [PATCH 5/5] Fix new lint: literal_string_with_formatting_args --- atspi-common/src/error.rs | 2 +- atspi-common/src/role.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/atspi-common/src/error.rs b/atspi-common/src/error.rs index 4b3231e4..6e92ccb5 100644 --- a/atspi-common/src/error.rs +++ b/atspi-common/src/error.rs @@ -203,7 +203,7 @@ impl std::fmt::Display for ObjectPathConversionError { match self { Self::NoIdAvailable => f.write_str("No ID available in the path."), Self::ParseError(e) => { - f.write_str("Failure to parse: {e}")?; + f.write_str("Failure to parse: ")?; e.fmt(f) } } diff --git a/atspi-common/src/role.rs b/atspi-common/src/role.rs index 2bbfcb70..f4123ddb 100644 --- a/atspi-common/src/role.rs +++ b/atspi-common/src/role.rs @@ -635,7 +635,9 @@ pub mod tests { .unwrap_or_else(|_| panic!("Unable to encode {from_role}")); println!("ENCODED: {encoded:?}"); - let (zbus_role, _) = encoded.deserialize().expect("Unable to decode {encoded:?}"); + let (zbus_role, _) = encoded + .deserialize() + .unwrap_or_else(|_| panic!("Unable to decode {encoded:?}")); assert_eq!(from_role, zbus_role, "The serde `Data::deserialize` and `From` impls produced different results. The number used was {role_num}, it produced a Role of {from_role}, but the from_slice(...) implementation produced {zbus_role}"); assert_eq!(