atspi_common/
object_ref.rs

1use serde::{Deserialize, Serialize};
2use zbus_lockstep_macros::validate;
3use zbus_names::{OwnedUniqueName, UniqueName};
4use zvariant::{ObjectPath, OwnedObjectPath, Type, Value};
5
6/// A unique identifier for an object in the accessibility tree.
7///
8/// A ubiquitous type used to refer to an object in the accessibility tree.
9///
10/// In AT-SPI2, objects in the applications' UI object tree are uniquely identified
11/// using a server name and object path. "(so)"
12///
13/// Emitted by `RemoveAccessible` and `Available`
14#[validate(signal: "Available")]
15#[derive(Debug, Clone, Serialize, Deserialize, Type, PartialEq, Eq, Hash)]
16pub struct ObjectRef {
17	pub name: OwnedUniqueName,
18	pub path: OwnedObjectPath,
19}
20
21impl Default for ObjectRef {
22	fn default() -> Self {
23		ObjectRef {
24			name: UniqueName::from_static_str_unchecked(":0.0").into(),
25			path: ObjectPath::from_static_str_unchecked("/org/a11y/atspi/accessible/null").into(),
26		}
27	}
28}
29
30impl ObjectRef {
31	/// Create a new `ObjectRef`
32	#[must_use]
33	pub fn new<'a>(sender: UniqueName<'a>, path: ObjectPath<'a>) -> Self {
34		Self { name: sender.into(), path: path.into() }
35	}
36
37	/// Create a new `ObjectRef`, unchecked, with the static string values.
38	///
39	/// # Safety
40	/// The caller must ensure that the strings are valid.
41	#[must_use]
42	pub fn from_static_str_unchecked(sender: &'static str, path: &'static str) -> Self {
43		Self {
44			name: UniqueName::from_static_str_unchecked(sender).into(),
45			path: ObjectPath::from_static_str_unchecked(path).into(),
46		}
47	}
48}
49
50/// A unique identifier for an object in the accessibility tree.
51///
52/// This is a borrowed version of [`ObjectRef`].
53///
54/// A ubiquitous type used to refer to an object in the accessibility tree.
55///
56/// In AT-SPI2, objects in the applications' UI object tree are uniquely identified
57/// using a server name and object path. "(so)"
58///
59/// Emitted by `RemoveAccessible` and `Available`
60#[validate(signal: "Available")]
61#[derive(Debug, Clone, Serialize, Deserialize, Type, PartialEq, Eq, Hash)]
62pub struct ObjectRefBorrowed<'a> {
63	#[serde(borrow)]
64	pub name: UniqueName<'a>,
65	#[serde(borrow)]
66	pub path: ObjectPath<'a>,
67}
68
69impl ObjectRefBorrowed<'_> {
70	/// Convert a partially borrowed `ObjectRefBorrowed` into a fully owned `ObjectRef`
71	// A derived clone would clone the owned fields and create new borrows for the borrowed fields.
72	// Whereas sometimes we want to convert the borrowed fields into owned fields.
73	#[must_use]
74	pub fn to_fully_owned(&self) -> ObjectRef {
75		let name = OwnedUniqueName::from(self.name.clone());
76		let path = OwnedObjectPath::from(self.path.clone());
77		ObjectRef { name, path }
78	}
79}
80
81impl Default for ObjectRefBorrowed<'_> {
82	fn default() -> Self {
83		ObjectRefBorrowed {
84			name: UniqueName::from_static_str_unchecked(":0.0"),
85			path: ObjectPath::from_static_str_unchecked("/org/a11y/atspi/accessible/null"),
86		}
87	}
88}
89
90impl<'a> TryFrom<zvariant::Value<'a>> for ObjectRef {
91	type Error = zvariant::Error;
92	fn try_from(value: zvariant::Value<'a>) -> Result<Self, Self::Error> {
93		// Relies on `TryFrom<OwnedValue> for (T0, T1)` implementation
94		let (name, path): (OwnedUniqueName, OwnedObjectPath) = value.try_to_owned()?.try_into()?;
95		Ok(ObjectRef { name, path })
96	}
97}
98
99impl TryFrom<zvariant::OwnedValue> for ObjectRef {
100	type Error = zvariant::Error;
101	fn try_from(value: zvariant::OwnedValue) -> Result<Self, Self::Error> {
102		let (name, path): (OwnedUniqueName, OwnedObjectPath) = value.try_into()?;
103		Ok(ObjectRef { name, path })
104	}
105}
106
107impl<'a> TryFrom<Value<'a>> for ObjectRefBorrowed<'a> {
108	type Error = zvariant::Error;
109	fn try_from(value: zvariant::Value<'a>) -> Result<Self, Self::Error> {
110		let (name, path): (UniqueName, ObjectPath) = value.try_into()?;
111		Ok(ObjectRefBorrowed { name, path })
112	}
113}
114
115impl From<ObjectRef> for zvariant::Structure<'_> {
116	fn from(obj: ObjectRef) -> Self {
117		(obj.name, obj.path).into()
118	}
119}
120
121#[cfg(feature = "zbus")]
122impl TryFrom<&zbus::message::Header<'_>> for ObjectRef {
123	type Error = crate::AtspiError;
124	fn try_from(header: &zbus::message::Header) -> Result<Self, Self::Error> {
125		let path = header.path().ok_or(crate::AtspiError::MissingPath)?;
126		let owned_path: OwnedObjectPath = path.clone().into();
127
128		let sender: UniqueName<'_> = header.sender().ok_or(crate::AtspiError::MissingName)?.into();
129		let name: OwnedUniqueName = sender.to_owned().into();
130
131		Ok(ObjectRef { name, path: owned_path })
132	}
133}
134
135#[cfg(test)]
136mod test {
137	use crate::{object_ref::ObjectRefBorrowed, ObjectRef};
138	use zvariant::Value;
139
140	#[test]
141	fn test_accessible_from_dbus_ctxt_to_object_ref() {
142		use zvariant::serialized::Context;
143		use zvariant::{to_bytes, Value, LE};
144
145		let acc = ObjectRef::default();
146		let ctxt = Context::new_dbus(LE, 0);
147		let acc_value: Value<'_> = acc.into();
148		let data = to_bytes(ctxt, &acc_value).unwrap();
149		let (value, _) = data.deserialize::<Value>().unwrap();
150		let accessible: ObjectRef = value.try_into().unwrap();
151
152		assert_eq!(accessible.name.as_str(), ":0.0");
153		assert_eq!(accessible.path.as_str(), "/org/a11y/atspi/accessible/null");
154	}
155
156	#[test]
157	fn test_accessible_value_wrapped_from_dbus_ctxt_to_object_ref() {
158		use zvariant::serialized::Context;
159		use zvariant::{to_bytes, Value, LE};
160
161		let acc = ObjectRef::default();
162		let value: zvariant::Value = acc.into();
163		let ctxt = Context::new_dbus(LE, 0);
164		let encoded = to_bytes(ctxt, &value).unwrap();
165		let (value, _) = encoded.deserialize::<Value>().unwrap();
166		let accessible: ObjectRef = value.try_into().unwrap();
167
168		assert_eq!(accessible.name.as_str(), ":0.0");
169		assert_eq!(accessible.path.as_str(), "/org/a11y/atspi/accessible/null");
170	}
171
172	#[test]
173	fn test_try_from_value_for_object_ref() {
174		use zvariant::Value;
175
176		let oref = ObjectRef::default();
177		let value: Value = oref.into();
178		let obj: ObjectRef = value.try_into().unwrap();
179
180		assert_eq!(obj.name.as_str(), ":0.0");
181		assert_eq!(obj.path.as_str(), "/org/a11y/atspi/accessible/null");
182	}
183
184	#[test]
185	fn test_try_from_owned_value_for_object_ref() {
186		use zvariant::OwnedValue;
187		use zvariant::Value;
188
189		let oref = ObjectRef::default();
190		let value: Value = oref.into();
191		let value: OwnedValue = value.try_into().unwrap();
192		let obj: ObjectRef = value.try_into().unwrap();
193
194		assert_eq!(obj.name.as_str(), ":0.0");
195		assert_eq!(obj.path.as_str(), "/org/a11y/atspi/accessible/null");
196	}
197
198	#[test]
199	fn must_fail_test_try_from_invalid_value_for_object_ref() {
200		let value = zvariant::Value::from(42);
201		let obj: Result<ObjectRef, _> = value.try_into();
202		assert!(obj.is_err());
203	}
204
205	#[test]
206	fn test_try_from_value_for_object_ref_borrow() {
207		use zvariant::Value;
208
209		let oref = ObjectRef::default();
210		let value: Value = oref.into();
211		let obj_borrow: ObjectRefBorrowed = value.try_into().unwrap();
212
213		assert_eq!(obj_borrow.name.as_str(), ":0.0");
214		assert_eq!(obj_borrow.path.as_str(), "/org/a11y/atspi/accessible/null");
215	}
216
217	#[test]
218	fn must_fail_test_try_from_invalid_value_for_object_ref_borrow() {
219		let value = zvariant::Value::from((42, true));
220		let obj: Result<ObjectRefBorrowed, _> = value.try_into();
221		assert!(obj.is_err());
222	}
223
224	#[test]
225	fn test_objectref_default_doesnt_panic() {
226		let objr = ObjectRef::default();
227		assert_eq!(objr.name.as_str(), ":0.0");
228		assert_eq!(objr.path.as_str(), "/org/a11y/atspi/accessible/null");
229	}
230
231	#[test]
232	fn try_into_value() {
233		let objr = ObjectRef::default();
234		let value_struct = Value::from(objr);
235		let Value::Structure(structure) = value_struct else {
236			panic!("Unable to destructure a structure out of the Value.");
237		};
238		let vals = structure.into_fields();
239		assert_eq!(vals.len(), 2);
240		let Value::Str(bus_name) = vals.first().unwrap() else {
241			panic!("Unable to destructure field value: {:?}", vals.first().unwrap());
242		};
243		assert_eq!(bus_name, ":0.0");
244		let Value::ObjectPath(path) = vals.last().unwrap() else {
245			panic!("Unable to destructure field value: {:?}", vals.get(1).unwrap());
246		};
247		assert_eq!(path.as_str(), "/org/a11y/atspi/accessible/null");
248	}
249}