atspi_common/
state.rs

1use enumflags2::{bitflags, BitFlag, BitFlags, FromBitsError};
2use serde::{
3	de::{self, Deserializer, Visitor},
4	ser::{SerializeSeq, Serializer},
5	Deserialize, Serialize,
6};
7use std::{convert::Infallible, fmt, str::FromStr};
8use zvariant::{Signature, Type};
9
10/// Used by various interfaces indicating every possible state
11/// of an accessibility object.
12#[bitflags]
13#[non_exhaustive]
14#[repr(u64)]
15#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
16#[serde(rename_all = "kebab-case")]
17pub enum State {
18	/// Indicates an invalid state - probably an error condition.
19	#[default]
20	Invalid,
21	/// Indicates a window is currently the active window, or
22	/// an object is the active subelement within a container or table.
23	///
24	/// `Active` should not be used for objects which have
25	/// [`State::Focusable`] or [`State::Selectable`]: Those objects should use
26	/// [`State::Focused`] and [`State::Selected`] respectively.
27	///
28	/// `Active` is a means to indicate that an object which is not
29	/// focusable and not selectable is the currently-active item within its
30	/// parent container.
31	Active,
32	/// Indicates that the object is armed.
33	Armed,
34	/// Indicates the current object is busy, i.e. onscreen
35	/// representation is in the process of changing, or       the object is
36	/// temporarily unavailable for interaction due to activity already in progress.
37	Busy,
38	/// Indicates this object is currently checked.
39	Checked,
40	/// Indicates this object is collapsed.
41	Collapsed,
42	/// Indicates that this object no longer has a valid
43	/// backing widget        (for instance, if its peer object has been destroyed).
44	Defunct,
45	/// Indicates the user can change the contents of this object.
46	Editable,
47	/// Indicates that this object is enabled, i.e. that it
48	/// currently reflects some application state. Objects that are "greyed out"
49	/// may lack this state, and may lack the [`State::Sensitive`] if direct
50	/// user interaction cannot cause them to acquire `Enabled`.
51	///
52	/// See [`State::Sensitive`].
53	Enabled,
54	/// Indicates this object allows progressive
55	/// disclosure of its children.
56	Expandable,
57	/// Indicates this object is expanded.
58	Expanded,
59	/// Indicates this object can accept keyboard focus,
60	/// which means all events resulting from typing on the keyboard will
61	/// normally be passed to it when it has focus.
62	Focusable,
63	/// Indicates this object currently has the keyboard focus.
64	Focused,
65	/// Indicates that the object has an associated tooltip.
66	HasTooltip,
67	/// Indicates the orientation of this object is horizontal.
68	Horizontal,
69	/// Indicates this object is minimized and is
70	/// represented only by an icon.
71	Iconified,
72	/// Indicates something must be done with this object
73	/// before the user can interact with an object in a different window.
74	Modal,
75	/// Indicates this (text) object can contain multiple
76	/// lines of text.
77	MultiLine,
78	/// Indicates this object allows more than one of
79	/// its children to be selected at the same time, or in the case of text
80	/// objects, that the object supports non-contiguous text selections.
81	Multiselectable,
82	/// Indicates this object paints every pixel within its
83	/// rectangular region. It also indicates an alpha value of unity, if it
84	/// supports alpha blending.
85	Opaque,
86	/// Indicates this object is currently pressed.
87	Pressed,
88	/// Indicates the size of this object's size is not fixed.
89	Resizable,
90	/// Indicates this object is the child of an object
91	/// that allows its children to be selected and that this child is one of
92	/// those children       that can be selected.
93	Selectable,
94	/// Indicates this object is the child of an object that
95	/// allows its children to be selected and that this child is one of those
96	/// children that has been selected.
97	Selected,
98	/// Indicates this object is sensitive, e.g. to user
99	/// interaction. `Sensitive` usually accompanies.
100	/// [`State::Enabled`] for user-actionable controls, but may be found in the
101	/// absence of [`State::Enabled`] if the current visible state of the control
102	/// is "disconnected" from the application state.  In such cases, direct user
103	/// interaction can often result in the object gaining `Sensitive`,
104	/// for instance if a user makes an explicit selection using an object whose
105	/// current state is ambiguous or undefined.
106	///
107	/// See [`State::Enabled`], [`State::Indeterminate`].
108	Sensitive,
109	/// Indicates this object, the object's parent, the
110	/// object's parent's parent, and so on, are all 'shown' to the end-user,
111	/// i.e. subject to "exposure" if blocking or obscuring objects do not
112	/// interpose between this object and the top of the window stack.
113	Showing,
114	/// Indicates this (text) object can contain only a
115	/// single line of text.
116	SingleLine,
117	/// Indicates that the information returned for this object
118	/// may no longer be synchronized with the application state.  This can occur
119	/// if the object has [`State::Transient`], and can also occur towards the
120	/// end of the object peer's lifecycle.
121	Stale,
122	/// Indicates this object is transient.
123	Transient,
124	/// Indicates the orientation of this object is vertical;
125	/// for example this state may appear on such objects as scrollbars, text
126	/// objects (with vertical text flow), separators, etc.
127	Vertical,
128	/// Indicates this object is visible, e.g. has been
129	/// explicitly marked for exposure to the user. `Visible` is no
130	/// guarantee that the object is actually unobscured on the screen, only that
131	/// it is 'potentially' visible, barring obstruction, being scrolled or clipped
132	/// out of the field of view, or having an ancestor container that has not yet
133	/// made visible. A widget is potentially onscreen if it has both
134	/// `Visible` and [`State::Showing`]. The absence of
135	/// `Visible` and [`State::Showing`] is
136	/// semantically equivalent to saying that an object is 'hidden'.
137	Visible,
138	/// Indicates that "active-descendant-changed"
139	/// event is sent when children become 'active' (i.e. are selected or
140	/// navigated to onscreen).  Used to prevent need to enumerate all children
141	/// in very large containers, like tables. The presence of
142	/// `ManagesDescendants` is an indication to the client that the
143	/// children should not, and need not, be enumerated by the client.
144	/// Objects implementing this state are expected to provide relevant state      
145	/// notifications to listening clients, for instance notifications of
146	/// visibility changes and activation of their contained child objects, without
147	/// the client having previously requested references to those children.
148	ManagesDescendants,
149	/// Indicates that a check box or other boolean
150	/// indicator is in a state other than checked or not checked.
151	///
152	/// This usually means that the boolean value reflected or controlled by the
153	/// object does not apply consistently to the entire current context.      
154	/// For example, a checkbox for the "Bold" attribute of text may have
155	/// `Indeterminate` if the currently selected text contains a mixture
156	/// of weight attributes. In many cases interacting with a
157	/// `Indeterminate` object will cause the context's corresponding
158	/// boolean attribute to be homogenized, whereupon the object will lose
159	/// `Indeterminate` and a corresponding state-changed event will be
160	/// fired.
161	Indeterminate,
162	/// Indicates that user interaction with this object is
163	/// 'required' from the user, for instance before completing the
164	/// processing of a form.
165	Required,
166	/// Indicates that an object's onscreen content
167	/// is truncated, e.g. a text value in a spreadsheet cell.
168	Truncated,
169	/// Indicates this object's visual representation is
170	/// dynamic, not static. This state may be applied to an object during an
171	/// animated 'effect' and be removed from the object once its visual
172	/// representation becomes static. Some applications, notably content viewers,
173	/// may not be able to detect all kinds of animated content.  Therefore the
174	/// absence of this state should not be taken as
175	/// definitive evidence that the object's visual representation is      
176	/// static; this state is advisory.
177	Animated,
178	/// This object has indicated an error condition
179	/// due to failure of input validation.  For instance, a form control may
180	/// acquire this state in response to invalid or malformed user input.
181	InvalidEntry,
182	/// This state indicates that the object
183	/// in question implements some form of typeahead or       
184	/// pre-selection behavior whereby entering the first character of one or more
185	/// sub-elements causes those elements to scroll into view or become
186	/// selected. Subsequent character input may narrow the selection further as
187	/// long as one or more sub-elements match the string. This state is normally
188	/// only useful and encountered on objects that implement [`crate::interface::Interface::Selection`].
189	/// In some cases the typeahead behavior may result in full or partial
190	/// completion of the data in the input field, in which case
191	/// these input events may trigger text-changed events from the source.
192	SupportsAutocompletion,
193	/// Indicates that the object in
194	/// question supports text selection. It should only be exposed on objects
195	/// which implement the [`crate::interface::Interface::Text`] interface, in order to distinguish this state
196	/// from [`State::Selectable`], which infers that the object in question is a
197	/// selectable child of an object which implements [`crate::interface::Interface::Selection`]. While
198	/// similar, text selection and subelement selection are distinct operations.
199	SelectableText,
200	/// Indicates that the object in question is
201	/// the 'default' interaction object in a dialog, i.e. the one that gets
202	/// activated if the user presses "Enter" when the dialog is initially
203	/// posted.
204	IsDefault,
205	/// Indicates that the object (typically a
206	/// hyperlink) has already been activated or invoked, with the result that
207	/// some backing data has been downloaded or rendered.
208	Visited,
209	/// Indicates this object has the potential to
210	/// be checked, such as a checkbox or toggle-able table cell.
211	Checkable,
212	/// Indicates that the object has a popup
213	/// context menu or sub-level menu which may or may not be
214	/// showing. This means that activation renders conditional content.
215	/// Note that ordinary tooltips are not considered popups in this
216	/// context.
217	HasPopup,
218	/// Indicates that an object which is [`State::Enabled`] and
219	/// [`State::Sensitive`] has a value which can be read, but not modified, by the
220	/// user.
221	ReadOnly,
222}
223
224impl State {
225	// "Matches on enums with undeclared discriminants (such that the compiler can choose sequential values)
226	// are optimized down to a jump table"
227	// https://users.rust-lang.org/t/match-statement-efficiency/4488/2
228
229	/// Returns the state as a static string.
230	#[must_use]
231	pub fn to_static_str(&self) -> &'static str {
232		match self {
233			State::Invalid => "invalid",
234			State::Active => "active",
235			State::Armed => "armed",
236			State::Busy => "busy",
237			State::Checked => "checked",
238			State::Collapsed => "collapsed",
239			State::Defunct => "defunct",
240			State::Editable => "editable",
241			State::Enabled => "enabled",
242			State::Expandable => "expandable",
243			State::Expanded => "expanded",
244			State::Focusable => "focusable",
245			State::Focused => "focused",
246			State::HasTooltip => "has-tooltip",
247			State::Horizontal => "horizontal",
248			State::Iconified => "iconified",
249			State::Modal => "modal",
250			State::MultiLine => "multi-line",
251			State::Multiselectable => "multiselectable",
252			State::Opaque => "opaque",
253			State::Pressed => "pressed",
254			State::Resizable => "resizable",
255			State::Selectable => "selectable",
256			State::Selected => "selected",
257			State::Sensitive => "sensitive",
258			State::Showing => "showing",
259			State::SingleLine => "single-line",
260			State::Stale => "stale",
261			State::Transient => "transient",
262			State::Vertical => "vertical",
263			State::Visible => "visible",
264			State::ManagesDescendants => "manages-descendants",
265			State::Indeterminate => "indeterminate",
266			State::Required => "required",
267			State::Truncated => "truncated",
268			State::Animated => "animated",
269			State::InvalidEntry => "invalid-entry",
270			State::SupportsAutocompletion => "supports-autocompletion",
271			State::SelectableText => "selectable-text",
272			State::IsDefault => "is-default",
273			State::Visited => "visited",
274			State::Checkable => "checkable",
275			State::HasPopup => "has-popup",
276			State::ReadOnly => "read-only",
277		}
278	}
279}
280
281impl fmt::Display for State {
282	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
283		f.write_str(self.to_static_str())
284	}
285}
286
287impl From<String> for State {
288	fn from(string: String) -> State {
289		(&*string).into()
290	}
291}
292
293impl FromStr for State {
294	type Err = Infallible;
295	fn from_str(s: &str) -> Result<Self, Self::Err> {
296		Ok(s.into())
297	}
298}
299
300impl From<&str> for State {
301	fn from(string: &str) -> State {
302		match string {
303			"active" => State::Active,
304			"armed" => State::Armed,
305			"busy" => State::Busy,
306			"checked" => State::Checked,
307			"collapsed" => State::Collapsed,
308			"defunct" => State::Defunct,
309			"editable" => State::Editable,
310			"enabled" => State::Enabled,
311			"expandable" => State::Expandable,
312			"expanded" => State::Expanded,
313			"focusable" => State::Focusable,
314			"focused" => State::Focused,
315			"has-tooltip" => State::HasTooltip,
316			"horizontal" => State::Horizontal,
317			"iconified" => State::Iconified,
318			"modal" => State::Modal,
319			"multi-line" => State::MultiLine,
320			"multiselectable" => State::Multiselectable,
321			"opaque" => State::Opaque,
322			"pressed" => State::Pressed,
323			"resizable" => State::Resizable,
324			"selectable" => State::Selectable,
325			"selected" => State::Selected,
326			"sensitive" => State::Sensitive,
327			"showing" => State::Showing,
328			"single-line" => State::SingleLine,
329			"stale" => State::Stale,
330			"transient" => State::Transient,
331			"vertical" => State::Vertical,
332			"visible" => State::Visible,
333			"manages-descendants" => State::ManagesDescendants,
334			"indeterminate" => State::Indeterminate,
335			"required" => State::Required,
336			"truncated" => State::Truncated,
337			"animated" => State::Animated,
338			"invalid-entry" => State::InvalidEntry,
339			"supports-autocompletion" => State::SupportsAutocompletion,
340			"selectable-text" => State::SelectableText,
341			"is-default" => State::IsDefault,
342			"visited" => State::Visited,
343			"checkable" => State::Checkable,
344			"has-popup" => State::HasPopup,
345			"read-only" => State::ReadOnly,
346			_ => State::Invalid,
347		}
348	}
349}
350
351#[allow(clippy::module_name_repetitions)]
352#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
353/// The bitflag representation of all states an object may have.
354pub struct StateSet(BitFlags<State>);
355
356impl StateSet {
357	/// Create a new `StateSet`.
358	///
359	/// ## Example
360	/// ```rust
361	/// # use atspi_common::{State, StateSet};
362	/// let states = State::Focusable | State::Sensitive | State::Active;
363	/// let set = StateSet::new(states);
364	///
365	/// assert!(set.contains(State::Active));
366	/// assert!(!set.contains(State::Busy));
367	/// ```
368	pub fn new<B: Into<BitFlags<State>>>(value: B) -> Self {
369		Self(value.into())
370	}
371
372	/// Returns the `StateSet` that corresponds to the provided `u64`s bit pattern.
373	/// # Errors
374	/// When the argument encodes an undefined [`State`].
375	pub fn from_bits(bits: u64) -> Result<StateSet, FromBitsError<State>> {
376		Ok(StateSet(BitFlags::from_bits(bits)?))
377	}
378
379	#[must_use]
380	/// Create an empty `StateSet`
381	pub fn empty() -> StateSet {
382		StateSet(State::empty())
383	}
384
385	#[must_use]
386	/// Returns the state as represented by a u64.
387	pub fn bits(&self) -> u64 {
388		self.0.bits()
389	}
390
391	/// Whether the `StateSet` contains a [`State`].
392	pub fn contains<B: Into<BitFlags<State>>>(self, other: B) -> bool {
393		self.0.contains(other)
394	}
395
396	/// Removes a [`State`] (optionally) previously contained in the `StateSet`.
397	pub fn remove<B: Into<BitFlags<State>>>(&mut self, other: B) {
398		self.0.remove(other);
399	}
400
401	///  Inserts a [`State`] in the `StateSet`.
402	pub fn insert<B: Into<BitFlags<State>>>(&mut self, other: B) {
403		self.0.insert(other);
404	}
405
406	/// Returns an iterator that yields each set [`State`].
407	#[must_use]
408	pub fn iter(self) -> enumflags2::Iter<State> {
409		self.0.iter()
410	}
411
412	#[must_use]
413	/// Checks if all states are unset.
414	pub fn is_empty(self) -> bool {
415		self.0.is_empty()
416	}
417
418	/// Returns true if at least one flag is shared.
419	pub fn intersects<B: Into<BitFlags<State>>>(self, other: B) -> bool {
420		self.0.intersects(other)
421	}
422
423	/// Toggles the matching bits.
424	pub fn toggle<B: Into<BitFlags<State>>>(&mut self, other: B) {
425		self.0.toggle(other);
426	}
427}
428
429impl IntoIterator for StateSet {
430	type IntoIter = enumflags2::Iter<State>;
431	type Item = State;
432
433	fn into_iter(self) -> Self::IntoIter {
434		self.iter()
435	}
436}
437
438impl IntoIterator for &StateSet {
439	type IntoIter = enumflags2::Iter<State>;
440	type Item = State;
441
442	fn into_iter(self) -> Self::IntoIter {
443		self.iter()
444	}
445}
446
447impl FromIterator<State> for StateSet {
448	fn from_iter<I: IntoIterator<Item = State>>(iter: I) -> Self {
449		StateSet(iter.into_iter().collect())
450	}
451}
452
453impl<'a> FromIterator<&'a State> for StateSet {
454	fn from_iter<I: IntoIterator<Item = &'a State>>(iter: I) -> Self {
455		StateSet(iter.into_iter().copied().collect())
456	}
457}
458
459impl<'de> Deserialize<'de> for StateSet {
460	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
461	where
462		D: Deserializer<'de>,
463	{
464		struct StateSetVisitor;
465
466		impl<'de> Visitor<'de> for StateSetVisitor {
467			type Value = StateSet;
468
469			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
470				formatter
471					.write_str("a sequence comprised of two u32 that represents a valid StateSet")
472			}
473
474			fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
475			where
476				D: Deserializer<'de>,
477			{
478				match <Vec<u32> as Deserialize>::deserialize(deserializer) {
479					Ok(states) if states.len() == 2 => {
480						let mut bits = u64::from(states[0]);
481						bits |= (u64::from(states[1])) << 32;
482						StateSet::from_bits(bits).map_err(|_| de::Error::custom("invalid state"))
483					}
484					Ok(states) => Err(de::Error::invalid_length(states.len(), &"array of size 2")),
485					Err(e) => Err(e),
486				}
487			}
488		}
489
490		deserializer.deserialize_newtype_struct("StateSet", StateSetVisitor)
491	}
492}
493
494impl Serialize for StateSet {
495	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
496	where
497		S: Serializer,
498	{
499		let mut seq = serializer.serialize_seq(Some(2))?;
500		let bits = self.bits();
501
502		// This cast is safe and truncation is intentional.
503		//The shift is sound provided that `State` is `#[repr(u64)]`
504		#[allow(clippy::cast_possible_truncation)]
505		seq.serialize_element(&(bits as u32))?;
506		seq.serialize_element(&((bits >> 32) as u32))?;
507		seq.end()
508	}
509}
510
511impl Type for StateSet {
512	const SIGNATURE: &'static Signature = <Vec<u32> as Type>::SIGNATURE;
513}
514
515impl From<State> for StateSet {
516	fn from(value: State) -> Self {
517		Self(value.into())
518	}
519}
520
521impl std::ops::BitXor for StateSet {
522	type Output = StateSet;
523
524	fn bitxor(self, other: Self) -> Self::Output {
525		StateSet(self.0 ^ other.0)
526	}
527}
528impl std::ops::BitXorAssign for StateSet {
529	fn bitxor_assign(&mut self, other: Self) {
530		self.0 = self.0 ^ other.0;
531	}
532}
533impl std::ops::BitOr for StateSet {
534	type Output = StateSet;
535
536	fn bitor(self, other: Self) -> Self::Output {
537		StateSet(self.0 | other.0)
538	}
539}
540impl std::ops::BitOrAssign for StateSet {
541	fn bitor_assign(&mut self, other: Self) {
542		self.0 = self.0 | other.0;
543	}
544}
545impl std::ops::BitAnd for StateSet {
546	type Output = StateSet;
547
548	fn bitand(self, other: Self) -> Self::Output {
549		StateSet(self.0 & other.0)
550	}
551}
552impl std::ops::BitAndAssign for StateSet {
553	fn bitand_assign(&mut self, other: Self) {
554		self.0 = self.0 & other.0;
555	}
556}
557
558#[cfg(test)]
559mod tests {
560	use super::{State, StateSet};
561	use zvariant::serialized::{Context, Data};
562	use zvariant::{to_bytes, LE};
563
564	#[test]
565	fn serialize_empty_state_set() {
566		let ctxt = Context::new_dbus(LE, 0);
567		let encoded = to_bytes(ctxt, &StateSet::empty()).unwrap();
568		assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
569	}
570
571	#[test]
572	fn deserialize_empty_state_set() {
573		let ctxt = Context::new_dbus(LE, 0);
574		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt);
575		let (decoded, _) = data.deserialize::<StateSet>().unwrap();
576		assert_eq!(decoded, StateSet::empty());
577	}
578
579	#[test]
580	fn serialize_state_set_invalid() {
581		let ctxt = Context::new_dbus(LE, 0);
582		let encoded = to_bytes(ctxt, &StateSet::new(State::Invalid)).unwrap();
583		assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]);
584	}
585
586	#[test]
587	fn deserialize_state_set_invalid() {
588		let ctxt = Context::new_dbus(LE, 0);
589		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], ctxt);
590		let (decoded, _) = data.deserialize::<StateSet>().unwrap();
591		assert_eq!(decoded, StateSet::new(State::Invalid));
592	}
593
594	#[test]
595	fn serialize_state_set_manages_descendants() {
596		let ctxt = Context::new_dbus(LE, 0);
597		let encoded = to_bytes(ctxt, &StateSet::new(State::ManagesDescendants)).unwrap();
598		assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0]);
599	}
600
601	#[test]
602	fn deserialize_state_set_manages_descendants() {
603		let ctxt = Context::new_dbus(LE, 0);
604		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0], ctxt);
605		let (decoded, _) = data.deserialize::<StateSet>().unwrap();
606		assert_eq!(decoded, StateSet::new(State::ManagesDescendants));
607	}
608
609	#[test]
610	fn serialize_state_set_indeterminate() {
611		let ctxt = Context::new_dbus(LE, 0);
612		let encoded = to_bytes(ctxt, &StateSet::new(State::Indeterminate)).unwrap();
613		assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]);
614	}
615
616	#[test]
617	fn deserialize_state_set_indeterminate() {
618		let ctxt = Context::new_dbus(LE, 0);
619		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], ctxt);
620		let (decoded, _) = data.deserialize::<StateSet>().unwrap();
621		assert_eq!(decoded, StateSet::new(State::Indeterminate));
622	}
623
624	#[test]
625	fn serialize_state_set_focusable_focused() {
626		let ctxt = Context::new_dbus(LE, 0);
627		let encoded = to_bytes(ctxt, &StateSet::new(State::Focusable | State::Focused)).unwrap();
628		assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0]);
629	}
630
631	#[test]
632	fn deserialize_state_set_focusable_focused() {
633		let ctxt = Context::new_dbus(LE, 0);
634		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0], ctxt);
635		let (decoded, _) = data.deserialize::<StateSet>().unwrap();
636		assert_eq!(decoded, StateSet::new(State::Focusable | State::Focused));
637	}
638
639	#[test]
640	fn cannot_deserialize_state_set_invalid_length() {
641		let ctxt = Context::new_dbus(LE, 0);
642		let data = Data::new::<&[u8]>(&[4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt);
643		let decode_result = data.deserialize::<StateSet>();
644		assert!(decode_result.is_err());
645	}
646
647	#[test]
648	fn cannot_deserialize_state_set_invalid_flag() {
649		let ctxt = Context::new_dbus(LE, 0);
650		let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32], ctxt);
651		let decode_result = data.deserialize::<StateSet>();
652		assert!(decode_result.is_err());
653	}
654
655	#[test]
656	fn convert_state_direct_string() {
657		for state in
658			StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111).unwrap()
659		{
660			let state_str: String = state.to_string();
661			let state_two: State = state_str.clone().into();
662			assert_eq!(
663				state, state_two,
664				"The {state:?} was serialized as {state_str}, which deserializes to {state_two:?}"
665			);
666		}
667	}
668	#[test]
669	fn convert_state_direct_string_is_equal_to_serde_output() {
670		for state in
671			StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111).unwrap()
672		{
673			let serde_state_str: String = serde_plain::to_string(&state).unwrap();
674			let state_str: String = state.to_string();
675			assert_eq!(serde_state_str, state_str);
676			let state_two: State = serde_plain::from_str(&state_str).unwrap();
677			assert_eq!(state, state_two, "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?} (serde)");
678		}
679	}
680
681	#[test]
682	fn collect_stateset_from_owned_states() {
683		let states = vec![State::Active, State::Focused, State::Focusable];
684		let set = StateSet::from_iter(states);
685		assert!(set.contains(State::Active));
686		assert!(set.contains(State::Focused));
687		assert!(set.contains(State::Focusable));
688	}
689
690	#[test]
691	fn collect_stateset_from_borrowed_states() {
692		// &[T].iter() yields &T
693		let states = &[State::Active, State::Focused, State::Focusable];
694		let set = states.iter().collect::<StateSet>();
695		assert!(set.contains(State::Active));
696		assert!(set.contains(State::Focused));
697		assert!(set.contains(State::Focusable));
698	}
699
700	#[test]
701	fn into_iterator_owned_stateset() {
702		let set = StateSet::new(State::Active | State::Focused | State::Focusable);
703		let states: Vec<State> = set.into_iter().collect();
704		assert_eq!(states.len(), 3);
705		assert!(states.contains(&State::Active));
706		assert!(states.contains(&State::Focused));
707		assert!(states.contains(&State::Focusable));
708	}
709
710	#[test]
711	fn into_iterator_borrowed_stateset() {
712		let set = StateSet::new(State::Active | State::Focused | State::Focusable);
713		let states: Vec<State> = (&set).into_iter().collect();
714		assert_eq!(states.len(), 3);
715		assert!(states.contains(&State::Active));
716		assert!(states.contains(&State::Focused));
717		assert!(states.contains(&State::Focusable));
718	}
719}