Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add lazy, cheaply guarded proxy-conversions. #202

Merged
merged 5 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion atspi-common/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
/// 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),

Expand Down Expand Up @@ -90,6 +93,9 @@
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())

Check warning on line 97 in atspi-common/src/error.rs

View check run for this annotation

Codecov / codecov/patch

atspi-common/src/error.rs#L96-L97

Added lines #L96 - L97 were not covered by tests
}
Self::UnknownInterface => f.write_str("Unknown interface."),
Self::MissingInterface => f.write_str("Missing interface."),
Self::MissingMember => f.write_str("Missing member."),
Expand Down Expand Up @@ -197,7 +203,7 @@
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: ")?;

Check warning on line 206 in atspi-common/src/error.rs

View check run for this annotation

Codecov / codecov/patch

atspi-common/src/error.rs#L206

Added line #L206 was not covered by tests
e.fmt(f)
}
}
Expand Down
4 changes: 3 additions & 1 deletion atspi-common/src/role.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<u32>` 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!(
Expand Down
1 change: 1 addition & 0 deletions atspi-proxies/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub mod device_event_controller;
pub mod device_event_listener;
pub mod document;
pub mod editable_text;
pub mod proxy_ext;
pub use common::{events, AtspiError, CoordType, Interface, InterfaceSet};

pub mod hyperlink;
Expand Down
317 changes: 317 additions & 0 deletions atspi-proxies/src/proxy_ext.rs
Original file line number Diff line number Diff line change
@@ -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` 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 ProxyExt<'a> {
/// Get `Proxies` for the current object.
fn proxies(&self) -> impl std::future::Future<Output = Result<Proxies<'a>>>;
}

/// 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<ActionProxy<'a>>,
application: Option<ApplicationProxy<'a>>,
cache: Option<CacheProxy<'a>>,
collection: Option<CollectionProxy<'a>>,
component: Option<ComponentProxy<'a>>,
document: Option<DocumentProxy<'a>>,
editable_text: Option<EditableTextProxy<'a>>,
hyperlink: Option<HyperlinkProxy<'a>>,
hypertext: Option<HypertextProxy<'a>>,
image: Option<ImageProxy<'a>>,
selection: Option<SelectionProxy<'a>>,
table: Option<TableProxy<'a>>,
table_cell: Option<TableCellProxy<'a>>,
text: Option<TextProxy<'a>>,
value: Option<ValueProxy<'a>>,
}

impl<'a> ProxyExt<'a> for AccessibleProxy<'a> {
async fn proxies(&self) -> Result<Proxies<'a>> {
let iface_set: InterfaceSet = self.get_interfaces().await?;
let proxy = self.inner().clone();

Ok(Proxies { interfaces: iface_set, proxy, inner: InnerProxies::default() })
}

Check warning on line 59 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L54-L59

Added lines #L54 - L59 were not covered by tests
}

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)

Check warning on line 74 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L68-L74

Added lines #L68 - L74 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Action"))

Check warning on line 76 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L76

Added line #L76 was not covered by tests
}
}

Check warning on line 78 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L78

Added line #L78 was not covered by tests

/// 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)

Check warning on line 91 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L85-L91

Added lines #L85 - L91 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Application"))

Check warning on line 93 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L93

Added line #L93 was not covered by tests
}
}

Check warning on line 95 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L95

Added line #L95 was not covered by tests

/// 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)

Check warning on line 108 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L102-L108

Added lines #L102 - L108 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Cache"))

Check warning on line 110 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L110

Added line #L110 was not covered by tests
}
}

Check warning on line 112 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L112

Added line #L112 was not covered by tests

/// 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)

Check warning on line 125 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L119-L125

Added lines #L119 - L125 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Collection"))

Check warning on line 127 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L127

Added line #L127 was not covered by tests
}
}

Check warning on line 129 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L129

Added line #L129 was not covered by tests

/// 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)

Check warning on line 142 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L136-L142

Added lines #L136 - L142 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Component"))

Check warning on line 144 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L144

Added line #L144 was not covered by tests
}
}

Check warning on line 146 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L146

Added line #L146 was not covered by tests

/// 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)

Check warning on line 159 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L153-L159

Added lines #L153 - L159 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Document"))

Check warning on line 161 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L161

Added line #L161 was not covered by tests
}
}

Check warning on line 163 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L163

Added line #L163 was not covered by tests

/// 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)

Check warning on line 176 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L170-L176

Added lines #L170 - L176 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("EditableText"))

Check warning on line 178 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L178

Added line #L178 was not covered by tests
}
}

Check warning on line 180 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L180

Added line #L180 was not covered by tests

/// 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)

Check warning on line 193 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L187-L193

Added lines #L187 - L193 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Hyperlink"))

Check warning on line 195 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L195

Added line #L195 was not covered by tests
}
}

Check warning on line 197 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L197

Added line #L197 was not covered by tests

/// 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)

Check warning on line 210 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L204-L210

Added lines #L204 - L210 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Hypertext"))

Check warning on line 212 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L212

Added line #L212 was not covered by tests
}
}

Check warning on line 214 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L214

Added line #L214 was not covered by tests

/// 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)

Check warning on line 227 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L221-L227

Added lines #L221 - L227 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Image"))

Check warning on line 229 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L229

Added line #L229 was not covered by tests
}
}

Check warning on line 231 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L231

Added line #L231 was not covered by tests

/// 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)

Check warning on line 244 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L238-L244

Added lines #L238 - L244 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Selection"))

Check warning on line 246 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L246

Added line #L246 was not covered by tests
}
}

Check warning on line 248 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L248

Added line #L248 was not covered by tests

/// 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)

Check warning on line 261 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L255-L261

Added lines #L255 - L261 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Table"))

Check warning on line 263 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L263

Added line #L263 was not covered by tests
}
}

Check warning on line 265 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L265

Added line #L265 was not covered by tests

/// 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)

Check warning on line 278 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L272-L278

Added lines #L272 - L278 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("TableCell"))

Check warning on line 280 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L280

Added line #L280 was not covered by tests
}
}

Check warning on line 282 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L282

Added line #L282 was not covered by tests

/// 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)

Check warning on line 295 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L289-L295

Added lines #L289 - L295 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Text"))

Check warning on line 297 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L297

Added line #L297 was not covered by tests
}
}

Check warning on line 299 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L299

Added line #L299 was not covered by tests

/// 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)

Check warning on line 312 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L306-L312

Added lines #L306 - L312 were not covered by tests
} else {
Err(AtspiError::InterfaceNotAvailable("Value"))

Check warning on line 314 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L314

Added line #L314 was not covered by tests
}
}

Check warning on line 316 in atspi-proxies/src/proxy_ext.rs

View check run for this annotation

Codecov / codecov/patch

atspi-proxies/src/proxy_ext.rs#L316

Added line #L316 was not covered by tests
}
Loading