From 2d29a887db7247a2443d2581f160de0d7071440b Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Fri, 15 Mar 2024 15:50:04 +0100 Subject: [PATCH 01/37] remove static-assertions from dev-dependencies. There is no need to have the same dependency twice as `[dev-dependencies]` is the sum of `[dependencies]` and what it adds. --- atspi-common/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/atspi-common/Cargo.toml b/atspi-common/Cargo.toml index de47cd0e..e40bba00 100644 --- a/atspi-common/Cargo.toml +++ b/atspi-common/Cargo.toml @@ -33,7 +33,6 @@ atspi-connection = { path = "../atspi-connection" } atspi-proxies = { path = "../atspi-proxies" } rename-item = "0.1.0" serde_plain = "1.0.1" -static_assertions = "1.1.0" tokio-stream = { version = "0.1", default-features = false, features = ["time"] } tokio = { version = "1", default-features = false, features = ["macros", "rt-multi-thread"] } tokio-test = "0.4.2" From 701f880ed305b540b0f18d5e6cc15cae2d8de4fd Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Mon, 1 Apr 2024 15:21:31 +0200 Subject: [PATCH 02/37] Add strum as dependency We are exploring deriving simple functionality on public enums using strum. --- atspi-common/Cargo.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/atspi-common/Cargo.toml b/atspi-common/Cargo.toml index e40bba00..f33a16e3 100644 --- a/atspi-common/Cargo.toml +++ b/atspi-common/Cargo.toml @@ -22,6 +22,7 @@ tokio = ["zbus/tokio"] enumflags2 = "0.7.7" serde = "1.0" static_assertions = "1.1.0" +strum = { version = "0.26", features = ["derive"] } zbus-lockstep = "0.4.4" zbus-lockstep-macros = "0.4.4" zbus_names = "3.0" @@ -30,7 +31,7 @@ zbus = { workspace = true, optional = true, default-features = false } [dev-dependencies] atspi-connection = { path = "../atspi-connection" } -atspi-proxies = { path = "../atspi-proxies" } +atspi-proxies = { path = "../atspi-proxies" } rename-item = "0.1.0" serde_plain = "1.0.1" tokio-stream = { version = "0.1", default-features = false, features = ["time"] } From 3bcb82cbd7c02dd91dec18af15fef57cae2d1d5b Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Tue, 2 Apr 2024 10:02:10 +0200 Subject: [PATCH 03/37] atspi-proxies remove serde_plain as it is not in use in this crate. --- atspi-proxies/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/atspi-proxies/Cargo.toml b/atspi-proxies/Cargo.toml index 6485da98..64ea636a 100644 --- a/atspi-proxies/Cargo.toml +++ b/atspi-proxies/Cargo.toml @@ -35,7 +35,6 @@ byteorder = "1.4" futures-lite = { version = "2", default-features = false } rename-item = "0.1.0" serde_json = "1.0.96" -serde_plain = "1.0.1" tokio = { version = "1", default_features = false, features = ["macros", "rt-multi-thread"] } tokio-stream = "0.1" tokio-test = "0.4.2" From 7716ab3fb52a7a14b7cc2640e4732f0cb7b6694a Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Tue, 2 Apr 2024 12:42:02 +0200 Subject: [PATCH 04/37] Changes `Interface` to have IntoStaticStr and Display derived - Remove consts as we cannot use these inside the attribute macros anyway. (likely because these are expanded after proc macros get run.) - Make up for loss of consts by adding tests for each. Note that this does not include strum::FromRepr because the representation differs from what is used on the bus, this makes the internal representatation meaningless to users. Instead I implemented FromStr, which makes more sense. Because of internal representation and bus representations iffer, we need custom Serialize and Deserialize implementations. Note that the Deserializer received a small optimization. No longer is the interface string required to be copied to stack before converting it to an `Interface`, instead the `Deserialize` implementation now takes a reference to the string and converts it. This also expands tests to cover the changes. --- atspi-common/src/interface.rs | 410 +++++++++++++++++++++++++++++++++- 1 file changed, 405 insertions(+), 5 deletions(-) diff --git a/atspi-common/src/interface.rs b/atspi-common/src/interface.rs index f9c7d3fd..ac86334b 100644 --- a/atspi-common/src/interface.rs +++ b/atspi-common/src/interface.rs @@ -9,76 +9,161 @@ use serde::{ ser::{self, Serializer}, Deserialize, Serialize, }; -use std::fmt; +use std::{fmt, str::FromStr}; +use strum::{Display, IntoStaticStr}; use zvariant::{Signature, Type}; +use crate::AtspiError; + /// AT-SPI interfaces an accessible object can implement. #[bitflags] #[repr(u32)] -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Copy, Debug, Display, IntoStaticStr, PartialEq, Eq, Serialize, Deserialize)] pub enum Interface { - /// Interface to indicate implementation of `AccessibleProxy`. + /// Interface to indicate implementation of `AccessibleProxy`. #[serde(rename = "org.a11y.atspi.Accessible")] + #[strum(serialize = "org.a11y.atspi.Accessible")] Accessible, + /// Interface to indicate implementation of `ActionProxy`. #[serde(rename = "org.a11y.atspi.Action")] + #[strum(serialize = "org.a11y.atspi.Action")] Action, + /// Interface to indicate implementation of `ApplicationProxy`. #[serde(rename = "org.a11y.atspi.Application")] + #[strum(serialize = "org.a11y.atspi.Application")] Application, + /// Interface to indicate implementation of `CacheProxy`. #[serde(rename = "org.a11y.atspi.Cache")] + #[strum(serialize = "org.a11y.atspi.Cache")] Cache, + /// Interface to indicate implementation of `CollectionProxy`. #[serde(rename = "org.a11y.atspi.Collection")] + #[strum(serialize = "org.a11y.atspi.Collection")] Collection, + /// Interface to indicate implementation of `ComponentProxy`. #[serde(rename = "org.a11y.atspi.Component")] + #[strum(serialize = "org.a11y.atspi.Component")] Component, + /// Interface to indicate implementation of `DocumentProxy`. #[serde(rename = "org.a11y.atspi.Document")] + #[strum(serialize = "org.a11y.atspi.Document")] Document, + /// Interface to indicate implementation of `DeviceEventControllerProxy`. #[serde(rename = "org.a11y.atspi.DeviceEventController")] + #[strum(serialize = "org.a11y.atspi.DeviceEventController")] DeviceEventController, + /// Interface to indicate implementation of `DeviceEventListenerProxy`. #[serde(rename = "org.a11y.atspi.DeviceEventListener")] + #[strum(serialize = "org.a11y.atspi.DeviceEventListener")] DeviceEventListener, + /// Interface to indicate implementation of `EditableTextProxy`. #[serde(rename = "org.a11y.atspi.EditableText")] + #[strum(serialize = "org.a11y.atspi.EditableText")] EditableText, + /// Interface to indicate implementation of `HyperlinkProxy`. #[serde(rename = "org.a11y.atspi.Hyperlink")] + #[strum(serialize = "org.a11y.atspi.Hyperlink")] Hyperlink, + /// Interface to indicate implementation of `HypertextProxy`. #[serde(rename = "org.a11y.atspi.Hypertext")] + #[strum(serialize = "org.a11y.atspi.Hypertext")] Hypertext, + /// Interface to indicate implementation of `ImageProxy`. #[serde(rename = "org.a11y.atspi.Image")] + #[strum(serialize = "org.a11y.atspi.Image")] Image, + /// Interface to indicate implementation of `RegistryProxy`. #[serde(rename = "org.a11y.atspi.Registry")] + #[strum(serialize = "org.a11y.atspi.Registry")] Registry, + /// Interface to indicate implementation of `SelectionProxy`. #[serde(rename = "org.a11y.atspi.Selection")] + #[strum(serialize = "org.a11y.atspi.Selection")] Selection, + /// Interface to indicate implementation of `SocketProxy`. #[serde(rename = "org.a11y.atspi.Socket")] + #[strum(serialize = "org.a11y.atspi.Socket")] Socket, + /// Interface to indicate implementation of `TableProxy`. #[serde(rename = "org.a11y.atspi.Table")] + #[strum(serialize = "org.a11y.atspi.Table")] Table, + /// Interface to indicate implementation of `TableCellProxy`. #[serde(rename = "org.a11y.atspi.TableCell")] + #[strum(serialize = "org.a11y.atspi.TableCell")] TableCell, + /// Interface to indicate implementation of `TextProxy`. #[serde(rename = "org.a11y.atspi.Text")] + #[strum(serialize = "org.a11y.atspi.Text")] Text, + /// Interface to indicate implementation of `ValueProxy`. #[serde(rename = "org.a11y.atspi.Value")] + #[strum(serialize = "org.a11y.atspi.Value")] Value, } +impl Type for Interface { + fn signature() -> Signature<'static> { + ::signature() + } +} + +impl FromStr for Interface { + type Err = AtspiError; + + fn from_str(s: &str) -> Result { + let prefix = "org.a11y.atspi."; + if s.starts_with(prefix) { + match &s[prefix.len()..] { + "Accessible" => Ok(Interface::Accessible), + "Action" => Ok(Interface::Action), + "Application" => Ok(Interface::Application), + "Cache" => Ok(Interface::Cache), + "Collection" => Ok(Interface::Collection), + "Component" => Ok(Interface::Component), + "Document" => Ok(Interface::Document), + "DeviceEventController" => Ok(Interface::DeviceEventController), + "DeviceEventListener" => Ok(Interface::DeviceEventListener), + "EditableText" => Ok(Interface::EditableText), + "Hyperlink" => Ok(Interface::Hyperlink), + "Hypertext" => Ok(Interface::Hypertext), + "Image" => Ok(Interface::Image), + "Registry" => Ok(Interface::Registry), + "Selection" => Ok(Interface::Selection), + "Socket" => Ok(Interface::Socket), + "Table" => Ok(Interface::Table), + "TableCell" => Ok(Interface::TableCell), + "Text" => Ok(Interface::Text), + "Value" => Ok(Interface::Value), + _ => Err(AtspiError::InterfaceMatch(format!( + "No interface found for conversion: {s}" + ))), + } + } else { + Err(AtspiError::InterfaceMatch(format!("No interface found for conversion: {s}"))) + } + } +} + /// A collection type which encodes the AT-SPI interfaces an accessible object has implemented. #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct InterfaceSet(BitFlags); @@ -191,11 +276,305 @@ impl std::ops::BitOr for InterfaceSet { } } +impl std::fmt::Display for InterfaceSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut iter = self.0.iter(); + if let Some(first) = iter.next() { + write!(f, "{first}")?; + for iface in iter { + write!(f, ", {iface}")?; + } + } + Ok(()) + } +} + #[cfg(test)] mod tests { use super::{Interface, InterfaceSet}; - use zvariant::serialized::Data; - use zvariant::{serialized::Context, to_bytes, LE}; + use std::str::FromStr; + use zvariant::{ + serialized::{Context, Data}, + to_bytes, Type, LE, + }; + + #[test] + fn interface_into_static_str_impl() { + let iface: Interface = Interface::Accessible; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Accessible"); + + let iface: Interface = Interface::Action; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Action"); + + let iface: Interface = Interface::Application; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Application"); + + let iface: Interface = Interface::Cache; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Cache"); + + let iface: Interface = Interface::Collection; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Collection"); + + let iface: Interface = Interface::Component; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Component"); + + let iface: Interface = Interface::Document; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Document"); + + let iface: Interface = Interface::DeviceEventController; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.DeviceEventController"); + + let iface: Interface = Interface::DeviceEventListener; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.DeviceEventListener"); + + let iface: Interface = Interface::EditableText; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.EditableText"); + + let iface: Interface = Interface::Hyperlink; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Hyperlink"); + + let iface: Interface = Interface::Hypertext; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Hypertext"); + + let iface: Interface = Interface::Image; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Image"); + + let iface: Interface = Interface::Registry; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Registry"); + + let iface: Interface = Interface::Selection; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Selection"); + + let iface: Interface = Interface::Socket; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Socket"); + + let iface: Interface = Interface::Table; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Table"); + + let iface: Interface = Interface::TableCell; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.TableCell"); + + let iface: Interface = Interface::Text; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Text"); + + let iface = Interface::Value; + let iface_str: &'static str = iface.into(); + assert_eq!(iface_str, "org.a11y.atspi.Value"); + } + + #[test] + fn interface_from_str_impl() { + let iface = Interface::from_str("org.a11y.atspi.Accessible").unwrap(); + assert_eq!(iface, Interface::Accessible); + + let iface = Interface::from_str("org.a11y.atspi.Action").unwrap(); + assert_eq!(iface, Interface::Action); + + let iface = Interface::from_str("org.a11y.atspi.Application").unwrap(); + assert_eq!(iface, Interface::Application); + + let iface = Interface::from_str("org.a11y.atspi.Cache").unwrap(); + assert_eq!(iface, Interface::Cache); + + let iface = Interface::from_str("org.a11y.atspi.Collection").unwrap(); + assert_eq!(iface, Interface::Collection); + + let iface = Interface::from_str("org.a11y.atspi.Component").unwrap(); + assert_eq!(iface, Interface::Component); + + let iface = Interface::from_str("org.a11y.atspi.Document").unwrap(); + assert_eq!(iface, Interface::Document); + + let iface = Interface::from_str("org.a11y.atspi.DeviceEventController").unwrap(); + assert_eq!(iface, Interface::DeviceEventController); + + let iface = Interface::from_str("org.a11y.atspi.DeviceEventListener").unwrap(); + assert_eq!(iface, Interface::DeviceEventListener); + + let iface = Interface::from_str("org.a11y.atspi.EditableText").unwrap(); + assert_eq!(iface, Interface::EditableText); + + let iface = Interface::from_str("org.a11y.atspi.Hyperlink").unwrap(); + assert_eq!(iface, Interface::Hyperlink); + + let iface = Interface::from_str("org.a11y.atspi.Hypertext").unwrap(); + assert_eq!(iface, Interface::Hypertext); + + let iface = Interface::from_str("org.a11y.atspi.Image").unwrap(); + assert_eq!(iface, Interface::Image); + + let iface = Interface::from_str("org.a11y.atspi.Registry").unwrap(); + assert_eq!(iface, Interface::Registry); + + let iface = Interface::from_str("org.a11y.atspi.Selection").unwrap(); + assert_eq!(iface, Interface::Selection); + + let iface = Interface::from_str("org.a11y.atspi.Socket").unwrap(); + assert_eq!(iface, Interface::Socket); + + let iface = Interface::from_str("org.a11y.atspi.Table").unwrap(); + assert_eq!(iface, Interface::Table); + + let iface = Interface::from_str("org.a11y.atspi.TableCell").unwrap(); + assert_eq!(iface, Interface::TableCell); + + let iface = Interface::from_str("org.a11y.atspi.Text").unwrap(); + assert_eq!(iface, Interface::Text); + + let iface = Interface::from_str("org.a11y.atspi.Value").unwrap(); + assert_eq!(iface, Interface::Value); + } + + #[test] + fn interface_from_str_impl_no_match() { + let res = Interface::from_str("org.a11y.atspi.Foo"); + assert!(res.is_err()); + + let res = Interface::from_str("com.a11y.atspi.Accessible"); + assert!(res.is_err()); + } + + #[test] + fn interface_display_impl() { + assert_eq!(format!("{}", Interface::Accessible), "org.a11y.atspi.Accessible".to_owned()); + assert_eq!(format!("{}", Interface::Action), "org.a11y.atspi.Action".to_owned()); + assert_eq!(format!("{}", Interface::Application), "org.a11y.atspi.Application".to_owned()); + assert_eq!(format!("{}", Interface::Cache), "org.a11y.atspi.Cache".to_owned()); + assert_eq!(format!("{}", Interface::Collection), "org.a11y.atspi.Collection".to_owned()); + assert_eq!(format!("{}", Interface::Component), "org.a11y.atspi.Component".to_owned()); + assert_eq!(format!("{}", Interface::Document), "org.a11y.atspi.Document".to_owned()); + assert_eq!( + format!("{}", Interface::DeviceEventController), + "org.a11y.atspi.DeviceEventController".to_owned() + ); + assert_eq!( + format!("{}", Interface::DeviceEventListener), + "org.a11y.atspi.DeviceEventListener".to_owned() + ); + assert_eq!( + format!("{}", Interface::EditableText), + "org.a11y.atspi.EditableText".to_owned() + ); + assert_eq!(format!("{}", Interface::Hyperlink), "org.a11y.atspi.Hyperlink".to_owned()); + assert_eq!(format!("{}", Interface::Hypertext), "org.a11y.atspi.Hypertext".to_owned()); + assert_eq!(format!("{}", Interface::Image), "org.a11y.atspi.Image".to_owned()); + assert_eq!(format!("{}", Interface::Registry), "org.a11y.atspi.Registry".to_owned()); + assert_eq!(format!("{}", Interface::Selection), "org.a11y.atspi.Selection".to_owned()); + assert_eq!(format!("{}", Interface::Socket), "org.a11y.atspi.Socket".to_owned()); + assert_eq!(format!("{}", Interface::Table), "org.a11y.atspi.Table".to_owned()); + assert_eq!(format!("{}", Interface::TableCell), "org.a11y.atspi.TableCell".to_owned()); + assert_eq!(format!("{}", Interface::Text), "org.a11y.atspi.Text".to_owned()); + assert_eq!(format!("{}", Interface::Value), "org.a11y.atspi.Value".to_owned()); + } + + #[test] + fn interface_set_display_impl() { + let ifaceset = InterfaceSet::new(Interface::Accessible); + assert_eq!(format!("{}", ifaceset), "org.a11y.atspi.Accessible"); + + let ifaceset = InterfaceSet::new(Interface::Accessible | Interface::Action); + assert_eq!(format!("{}", ifaceset), "org.a11y.atspi.Accessible, org.a11y.atspi.Action"); + + let ifaceset = + InterfaceSet::new(Interface::Accessible | Interface::Action | Interface::Component); + assert_eq!( + format!("{}", ifaceset), + "org.a11y.atspi.Accessible, org.a11y.atspi.Action, org.a11y.atspi.Component" + ); + } + + #[test] + fn interface_type_signature() { + assert_eq!(Interface::signature().as_str(), "s"); + } + + #[test] + fn interface_set_type_signature() { + assert_eq!(InterfaceSet::signature().as_str(), "as"); + } + + #[test] + fn serialize_and_deserialize_accessible_interface() { + let ctxt = Context::new_dbus(LE, 0); + let encoded = to_bytes(ctxt, &Interface::Accessible).unwrap(); + assert_eq!( + encoded.bytes(), + &[ + 25, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, + 65, 99, 99, 101, 115, 115, 105, 98, 108, 101, 0 + ] + ); + + let (decoded, _) = encoded.deserialize::().unwrap(); + assert_eq!(decoded, Interface::Accessible); + } + + #[test] + fn serialize_and_deserialize_editable_text_interface() { + let ctxt = Context::new_dbus(LE, 0); + let encoded = to_bytes(ctxt, &Interface::EditableText).unwrap(); + assert_eq!( + encoded.bytes(), + &[ + 27, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, + 69, 100, 105, 116, 97, 98, 108, 101, 84, 101, 120, 116, 0 + ] + ); + + let (decoded, _) = encoded.deserialize::().unwrap(); + assert_eq!(decoded, Interface::EditableText); + } + + #[test] + fn serialize_and_deserialize_hyperlink_interface() { + let ctxt = Context::new_dbus(LE, 0); + let encoded = to_bytes(ctxt, &Interface::Hyperlink).unwrap(); + assert_eq!( + encoded.bytes(), + &[ + 24, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, + 72, 121, 112, 101, 114, 108, 105, 110, 107, 0 + ] + ); + + let (decoded, _) = encoded.deserialize::().unwrap(); + assert_eq!(decoded, Interface::Hyperlink); + } + + #[test] + fn serialize_and_deserialize_value_interface() { + let ctxt = Context::new_dbus(LE, 0); + let encoded = to_bytes(ctxt, &Interface::Value).unwrap(); + assert_eq!( + encoded.bytes(), + &[ + 20, 0, 0, 0, 111, 114, 103, 46, 97, 49, 49, 121, 46, 97, 116, 115, 112, 105, 46, + 86, 97, 108, 117, 101, 0 + ] + ); + + let (decoded, _) = encoded.deserialize::().unwrap(); + assert_eq!(decoded, Interface::Value); + } #[test] fn serialize_empty_interface_set() { @@ -249,4 +628,25 @@ mod tests { let (decoded, _) = encoded.deserialize::().unwrap(); assert!(object == decoded); } + + #[test] + fn match_various_de_serialization_methods() { + for iface in InterfaceSet::all().iter() { + let displayed = format!("{iface}"); + let serde_val = serde_plain::to_string(&iface) + .unwrap_or_else(|_| panic!("Unable to serialize {iface}")); + + // Check that the Display trait and Serde's serialization match. + assert_eq!( + displayed, serde_val, + "Serde's serialization does not match the Display trait implementation." + ); + + // Check that the Display trait and TryFrom<&str> match. + let from_str = Interface::from_str(&displayed).unwrap(); + assert_eq!(iface, from_str, "The display trait for {iface} became \"{displayed}\", but was re-serialized as {from_str} via TryFrom<&str>"); + let serde_from_str: Interface = serde_plain::from_str(&serde_val).unwrap(); + assert_eq!(serde_from_str, iface, "Serde's deserialization does not match its serialization. {iface} was serialized to \"{serde_val}\", but deserialized into {serde_from_str}"); + } + } } From 33d5a2639c6539e2812fdc000e63bcb1deccea8b Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Tue, 2 Apr 2024 12:55:59 +0200 Subject: [PATCH 05/37] trailing space policing --- atspi-proxies/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atspi-proxies/Cargo.toml b/atspi-proxies/Cargo.toml index 64ea636a..dafdc543 100644 --- a/atspi-proxies/Cargo.toml +++ b/atspi-proxies/Cargo.toml @@ -25,7 +25,7 @@ tokio = ["zbus/tokio", "atspi-common/tokio"] [dependencies] atspi-common = { path = "../atspi-common", version = "0.5.0", default-features = false } serde = { version = "^1.0", default-features = false, features = ["derive"] } -zbus = { workspace = true } +zbus = { workspace = true } zvariant = { version = "4.0", default-features = false } [dev-dependencies] From d13c4b3376917b35efca49ac60ed7d68396bf81e Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Thu, 4 Apr 2024 15:26:34 +0200 Subject: [PATCH 06/37] Apply strum traits that make sense to RelationType RelationType is now also checked with zbus_lockstep. As a consequence the type validation document is updated too. --- atspi-common/src/relation_type.rs | 32 +++++++++++++++++++++++++++++-- type_validation.md | 1 + 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/atspi-common/src/relation_type.rs b/atspi-common/src/relation_type.rs index 93f04e61..2fbc794a 100644 --- a/atspi-common/src/relation_type.rs +++ b/atspi-common/src/relation_type.rs @@ -1,8 +1,24 @@ use serde::{Deserialize, Serialize}; +use strum::{Display, FromRepr, IntoStaticStr}; use zvariant::Type; /// Describes a relationship between one object and another. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Hash)] +#[derive( + Clone, + Copy, + Debug, + Display, + PartialEq, + Eq, + FromRepr, + IntoStaticStr, + Serialize, + Deserialize, + Type, + Hash, +)] +#[strum(serialize_all = "snake_case")] +#[repr(u32)] pub enum RelationType { /// Not a meaningful relationship; clients should not normally encounter this value. Null = 0, @@ -59,7 +75,7 @@ pub enum RelationType { /// that live in a separate process space from the embedding context. EmbeddedBy, - ///Denotes that the object is a transient window or frame associated with another + /// Denotes that the object is a transient window or frame associated with another /// onscreen object. Similar to `TooltipFor`, but more general. /// Useful for windows which are technically toplevels but which, for one or more reasons, /// do not explicitly cause their associated window to lose 'window focus'. @@ -118,3 +134,15 @@ pub enum RelationType { /// Included in upstream [AT-SPI2-CORE](https://gitlab.gnome.org/GNOME/at-spi2-core) since 2.26. ErrorFor, } + +#[cfg(test)] +mod tests { + use super::RelationType; + use zvariant::Type; + + #[test] + fn validate_relation_type_signature() { + let signature = zbus_lockstep::method_return_signature!("GetRelationSet"); + assert_eq!(RelationType::signature(), signature.slice(2..3)); + } +} diff --git a/type_validation.md b/type_validation.md index 5edc8cea..40d7cecd 100644 --- a/type_validation.md +++ b/type_validation.md @@ -31,3 +31,4 @@ We leverage [zbus-lockstep](https://github.com/luukvanderduim/zbus-lockstep/) to | `Layer` | ✓ | `Component::GetLayer` return type.| | `ScrollType` | ✓ | `Component::ScrollTo` argument `type`| | `Live` | ✓ | `Event.Object::Announcement` argument `politeness`| +| `RelationType` | ✓ | Part of `Accessible::GetRelationSet` return type | From c3c47200477d33ac63c51fe9bc849a090f8324f7 Mon Sep 17 00:00:00 2001 From: Luuk van der Duim Date: Thu, 4 Apr 2024 15:30:03 +0200 Subject: [PATCH 07/37] Role received strum traits `FromRepr`, `Display`, `IntoStaticStr` and `EnumIter` I changed two variants that stood out odd: `Role::Editbar` -> `Role::EditBar` ``Role::CHART` -> `Role::Chart` In the case of `EditBar`, this means it is now displayed as "edit bar" instead of "editbar". If this raises concerns, please inform me why. the `EnumIter` may be useful in tests. That being said, this enum has a simple `u32` representation. fSo checking whether each gets serialized and deserialized feels overly cautious because `zvariant` is surely able to (de)serialize these. In other words, it is not our concern to test that. We added no logic to get this done. Same goes for `&'static str` matching with the `Display` impl. It does not hurt to test, but it is not our responsibility. --- atspi-common/src/role.rs | 586 ++++++++++++++++++--------------------- 1 file changed, 277 insertions(+), 309 deletions(-) diff --git a/atspi-common/src/role.rs b/atspi-common/src/role.rs index 417f67b1..37d209e7 100644 --- a/atspi-common/src/role.rs +++ b/atspi-common/src/role.rs @@ -1,289 +1,488 @@ use serde::{Deserialize, Serialize}; +use strum::{Display, EnumIter, FromRepr, IntoStaticStr}; use zvariant::Type; -use crate::AtspiError; - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, Type, Hash)] -/// An accessible object role. +/// The accessible role that an object represents. +/// +/// Roles make it possible for various UI toolkits to expose their controls to assistive +/// technologies (ATs) with a standard interface, regardless of toolkit. +/// +/// For example, a widget that acts like a conventional push button (appears unpressed; presses +/// when acted upon; invokes a certain action when pressed) can expose a `Role::PushButton` role. +/// /// To think of it in terms of HTML, any semantic element likely has a corollary in this enum. /// For example: `