1 | use enumflags2::{bitflags , BitFlag, BitFlags, FromBitsError}; |
2 | use serde::{ |
3 | de::{self, Deserializer, Visitor}, |
4 | ser::{SerializeSeq, Serializer}, |
5 | Deserialize, Serialize, |
6 | }; |
7 | use std::fmt; |
8 | use 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" )] |
17 | pub 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 | |
224 | impl From<State> for String { |
225 | fn from(state: State) -> String { |
226 | match state { |
227 | State::Invalid => "invalid" , |
228 | State::Active => "active" , |
229 | State::Armed => "armed" , |
230 | State::Busy => "busy" , |
231 | State::Checked => "checked" , |
232 | State::Collapsed => "collapsed" , |
233 | State::Defunct => "defunct" , |
234 | State::Editable => "editable" , |
235 | State::Enabled => "enabled" , |
236 | State::Expandable => "expandable" , |
237 | State::Expanded => "expanded" , |
238 | State::Focusable => "focusable" , |
239 | State::Focused => "focused" , |
240 | State::HasTooltip => "has-tooltip" , |
241 | State::Horizontal => "horizontal" , |
242 | State::Iconified => "iconified" , |
243 | State::Modal => "modal" , |
244 | State::MultiLine => "multi-line" , |
245 | State::Multiselectable => "multiselectable" , |
246 | State::Opaque => "opaque" , |
247 | State::Pressed => "pressed" , |
248 | State::Resizable => "resizable" , |
249 | State::Selectable => "selectable" , |
250 | State::Selected => "selected" , |
251 | State::Sensitive => "sensitive" , |
252 | State::Showing => "showing" , |
253 | State::SingleLine => "single-line" , |
254 | State::Stale => "stale" , |
255 | State::Transient => "transient" , |
256 | State::Vertical => "vertical" , |
257 | State::Visible => "visible" , |
258 | State::ManagesDescendants => "manages-descendants" , |
259 | State::Indeterminate => "indeterminate" , |
260 | State::Required => "required" , |
261 | State::Truncated => "truncated" , |
262 | State::Animated => "animated" , |
263 | State::InvalidEntry => "invalid-entry" , |
264 | State::SupportsAutocompletion => "supports-autocompletion" , |
265 | State::SelectableText => "selectable-text" , |
266 | State::IsDefault => "is-default" , |
267 | State::Visited => "visited" , |
268 | State::Checkable => "checkable" , |
269 | State::HasPopup => "has-popup" , |
270 | State::ReadOnly => "read-only" , |
271 | } |
272 | .to_string() |
273 | } |
274 | } |
275 | |
276 | impl From<String> for State { |
277 | fn from(string: String) -> State { |
278 | (&*string).into() |
279 | } |
280 | } |
281 | |
282 | impl From<&str> for State { |
283 | fn from(string: &str) -> State { |
284 | match string { |
285 | "active" => State::Active, |
286 | "armed" => State::Armed, |
287 | "busy" => State::Busy, |
288 | "checked" => State::Checked, |
289 | "collapsed" => State::Collapsed, |
290 | "defunct" => State::Defunct, |
291 | "editable" => State::Editable, |
292 | "enabled" => State::Enabled, |
293 | "expandable" => State::Expandable, |
294 | "expanded" => State::Expanded, |
295 | "focusable" => State::Focusable, |
296 | "focused" => State::Focused, |
297 | "has-tooltip" => State::HasTooltip, |
298 | "horizontal" => State::Horizontal, |
299 | "iconified" => State::Iconified, |
300 | "modal" => State::Modal, |
301 | "multi-line" => State::MultiLine, |
302 | "multiselectable" => State::Multiselectable, |
303 | "opaque" => State::Opaque, |
304 | "pressed" => State::Pressed, |
305 | "resizable" => State::Resizable, |
306 | "selectable" => State::Selectable, |
307 | "selected" => State::Selected, |
308 | "sensitive" => State::Sensitive, |
309 | "showing" => State::Showing, |
310 | "single-line" => State::SingleLine, |
311 | "stale" => State::Stale, |
312 | "transient" => State::Transient, |
313 | "vertical" => State::Vertical, |
314 | "visible" => State::Visible, |
315 | "manages-descendants" => State::ManagesDescendants, |
316 | "indeterminate" => State::Indeterminate, |
317 | "required" => State::Required, |
318 | "truncated" => State::Truncated, |
319 | "animated" => State::Animated, |
320 | "invalid-entry" => State::InvalidEntry, |
321 | "supports-autocompletion" => State::SupportsAutocompletion, |
322 | "selectable-text" => State::SelectableText, |
323 | "is-default" => State::IsDefault, |
324 | "visited" => State::Visited, |
325 | "checkable" => State::Checkable, |
326 | "has-popup" => State::HasPopup, |
327 | "read-only" => State::ReadOnly, |
328 | _ => State::Invalid, |
329 | } |
330 | } |
331 | } |
332 | |
333 | #[allow (clippy::module_name_repetitions)] |
334 | #[derive (Clone, Copy, Debug, PartialEq, Eq, Hash, Default)] |
335 | /// The bitflag representation of all states an object may have. |
336 | pub struct StateSet(BitFlags<State>); |
337 | |
338 | impl StateSet { |
339 | /// Create a new `StateSet`. |
340 | /// |
341 | ///## Example |
342 | ///```Rust |
343 | /// let states = State::Focusable | State::Sensitive | State::Active; |
344 | /// let set = StateSet::new(states); |
345 | /// |
346 | /// assert!(set.contains(State::Active)); |
347 | /// assert!(!set.contains(State::Busy)); |
348 | /// ``` |
349 | pub fn new<B: Into<BitFlags<State>>>(value: B) -> Self { |
350 | Self(value.into()) |
351 | } |
352 | |
353 | /// Returns the `StateSet` that corresponds to the provided `u64`s bit pattern. |
354 | ///# Errors |
355 | /// When the argument encodes an undefined [`State`]. |
356 | pub fn from_bits(bits: u64) -> Result<StateSet, FromBitsError<State>> { |
357 | Ok(StateSet(BitFlags::from_bits(bits)?)) |
358 | } |
359 | |
360 | #[must_use ] |
361 | /// Create an empty `StateSet` |
362 | pub fn empty() -> StateSet { |
363 | StateSet(State::empty()) |
364 | } |
365 | |
366 | #[must_use ] |
367 | /// Returns the state as represented by a u64. |
368 | pub fn bits(&self) -> u64 { |
369 | self.0.bits() |
370 | } |
371 | |
372 | /// Whether the `StateSet` contains a [`State`]. |
373 | pub fn contains<B: Into<BitFlags<State>>>(self, other: B) -> bool { |
374 | self.0.contains(other) |
375 | } |
376 | |
377 | /// Removes a [`State`] (optionally) previously contained in the `StateSet`. |
378 | pub fn remove<B: Into<BitFlags<State>>>(&mut self, other: B) { |
379 | self.0.remove(other); |
380 | } |
381 | |
382 | /// Inserts a [`State`] in the `StateSet`. |
383 | pub fn insert<B: Into<BitFlags<State>>>(&mut self, other: B) { |
384 | self.0.insert(other); |
385 | } |
386 | |
387 | /// Returns an iterator that yields each set [`State`]. |
388 | pub fn iter(self) -> impl Iterator<Item = State> { |
389 | self.0.iter() |
390 | } |
391 | |
392 | #[must_use ] |
393 | /// Checks if all states are unset. |
394 | pub fn is_empty(self) -> bool { |
395 | self.0.is_empty() |
396 | } |
397 | |
398 | /// Returns true if at least one flag is shared. |
399 | pub fn intersects<B: Into<BitFlags<State>>>(self, other: B) -> bool { |
400 | self.0.intersects(other) |
401 | } |
402 | |
403 | /// Toggles the matching bits. |
404 | pub fn toggle<B: Into<BitFlags<State>>>(&mut self, other: B) { |
405 | self.0.toggle(other); |
406 | } |
407 | } |
408 | |
409 | impl<'de> Deserialize<'de> for StateSet { |
410 | fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
411 | where |
412 | D: Deserializer<'de>, |
413 | { |
414 | struct StateSetVisitor; |
415 | |
416 | impl<'de> Visitor<'de> for StateSetVisitor { |
417 | type Value = StateSet; |
418 | |
419 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
420 | formatter |
421 | .write_str("a sequence comprised of two u32 that represents a valid StateSet" ) |
422 | } |
423 | |
424 | fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error> |
425 | where |
426 | D: Deserializer<'de>, |
427 | { |
428 | match <Vec<u32> as Deserialize>::deserialize(deserializer) { |
429 | Ok(states) if states.len() == 2 => { |
430 | let mut bits = u64::from(states[0]); |
431 | bits |= (u64::from(states[1])) << 32; |
432 | StateSet::from_bits(bits).map_err(|_| de::Error::custom("invalid state" )) |
433 | } |
434 | Ok(states) => Err(de::Error::invalid_length(states.len(), &"array of size 2" )), |
435 | Err(e) => Err(e), |
436 | } |
437 | } |
438 | } |
439 | |
440 | deserializer.deserialize_newtype_struct("StateSet" , StateSetVisitor) |
441 | } |
442 | } |
443 | |
444 | impl Serialize for StateSet { |
445 | fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
446 | where |
447 | S: Serializer, |
448 | { |
449 | let mut seq: ::SerializeSeq = serializer.serialize_seq(len:Some(2))?; |
450 | let bits: u64 = self.bits(); |
451 | |
452 | // This cast is safe and truncation is intentional. |
453 | //The shift is sound provided that `State` is `#[repr(u64)]` |
454 | #[allow (clippy::cast_possible_truncation)] |
455 | seq.serialize_element(&(bits as u32))?; |
456 | seq.serialize_element(&((bits >> 32) as u32))?; |
457 | seq.end() |
458 | } |
459 | } |
460 | |
461 | impl Type for StateSet { |
462 | fn signature() -> Signature<'static> { |
463 | <Vec<u32> as Type>::signature() |
464 | } |
465 | } |
466 | |
467 | impl From<State> for StateSet { |
468 | fn from(value: State) -> Self { |
469 | Self(value.into()) |
470 | } |
471 | } |
472 | |
473 | impl std::ops::BitXor for StateSet { |
474 | type Output = StateSet; |
475 | |
476 | fn bitxor(self, other: Self) -> Self::Output { |
477 | StateSet(self.0 ^ other.0) |
478 | } |
479 | } |
480 | impl std::ops::BitXorAssign for StateSet { |
481 | fn bitxor_assign(&mut self, other: Self) { |
482 | self.0 = self.0 ^ other.0; |
483 | } |
484 | } |
485 | impl std::ops::BitOr for StateSet { |
486 | type Output = StateSet; |
487 | |
488 | fn bitor(self, other: Self) -> Self::Output { |
489 | StateSet(self.0 | other.0) |
490 | } |
491 | } |
492 | impl std::ops::BitOrAssign for StateSet { |
493 | fn bitor_assign(&mut self, other: Self) { |
494 | self.0 = self.0 | other.0; |
495 | } |
496 | } |
497 | impl std::ops::BitAnd for StateSet { |
498 | type Output = StateSet; |
499 | |
500 | fn bitand(self, other: Self) -> Self::Output { |
501 | StateSet(self.0 & other.0) |
502 | } |
503 | } |
504 | impl std::ops::BitAndAssign for StateSet { |
505 | fn bitand_assign(&mut self, other: Self) { |
506 | self.0 = self.0 & other.0; |
507 | } |
508 | } |
509 | |
510 | #[cfg (test)] |
511 | mod tests { |
512 | use super::*; |
513 | use byteorder::LE; |
514 | use serde_plain; |
515 | use zbus::zvariant::{from_slice, to_bytes, EncodingContext as Context}; |
516 | |
517 | #[test ] |
518 | fn serialize_empty_state_set() { |
519 | let ctxt = Context::<LE>::new_dbus(0); |
520 | let encoded = to_bytes(ctxt, &StateSet::empty()).unwrap(); |
521 | assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); |
522 | } |
523 | |
524 | #[test ] |
525 | fn deserialize_empty_state_set() { |
526 | let ctxt = Context::<LE>::new_dbus(0); |
527 | let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt).unwrap(); |
528 | assert_eq!(decoded, StateSet::empty()); |
529 | } |
530 | |
531 | #[test ] |
532 | fn serialize_state_set_invalid() { |
533 | let ctxt = Context::<LE>::new_dbus(0); |
534 | let encoded = to_bytes(ctxt, &StateSet::new(State::Invalid)).unwrap(); |
535 | assert_eq!(encoded, &[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0]); |
536 | } |
537 | |
538 | #[test ] |
539 | fn deserialize_state_set_invalid() { |
540 | let ctxt = Context::<LE>::new_dbus(0); |
541 | let decoded: StateSet = from_slice(&[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], ctxt).unwrap(); |
542 | assert_eq!(decoded, StateSet::new(State::Invalid)); |
543 | } |
544 | |
545 | #[test ] |
546 | fn serialize_state_set_manages_descendants() { |
547 | let ctxt = Context::<LE>::new_dbus(0); |
548 | let encoded = to_bytes(ctxt, &StateSet::new(State::ManagesDescendants)).unwrap(); |
549 | assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0]); |
550 | } |
551 | |
552 | #[test ] |
553 | fn deserialize_state_set_manages_descendants() { |
554 | let ctxt = Context::<LE>::new_dbus(0); |
555 | let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0], ctxt).unwrap(); |
556 | assert_eq!(decoded, StateSet::new(State::ManagesDescendants)); |
557 | } |
558 | |
559 | #[test ] |
560 | fn serialize_state_set_indeterminate() { |
561 | let ctxt = Context::<LE>::new_dbus(0); |
562 | let encoded = to_bytes(ctxt, &StateSet::new(State::Indeterminate)).unwrap(); |
563 | assert_eq!(encoded, &[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]); |
564 | } |
565 | |
566 | #[test ] |
567 | fn deserialize_state_set_indeterminate() { |
568 | let ctxt = Context::<LE>::new_dbus(0); |
569 | let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], ctxt).unwrap(); |
570 | assert_eq!(decoded, StateSet::new(State::Indeterminate)); |
571 | } |
572 | |
573 | #[test ] |
574 | fn serialize_state_set_focusable_focused() { |
575 | let ctxt = Context::<LE>::new_dbus(0); |
576 | let encoded = to_bytes(ctxt, &StateSet::new(State::Focusable | State::Focused)).unwrap(); |
577 | assert_eq!(encoded, &[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0]); |
578 | } |
579 | |
580 | #[test ] |
581 | fn deserialize_state_set_focusable_focused() { |
582 | let ctxt = Context::<LE>::new_dbus(0); |
583 | let decoded: StateSet = from_slice(&[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0], ctxt).unwrap(); |
584 | assert_eq!(decoded, StateSet::new(State::Focusable | State::Focused)); |
585 | } |
586 | |
587 | #[test ] |
588 | fn cannot_deserialize_state_set_invalid_length() { |
589 | let ctxt = Context::<LE>::new_dbus(0); |
590 | let decoded = from_slice::<_, StateSet>(&[4, 0, 0, 0, 0, 0, 0, 0], ctxt); |
591 | assert!(decoded.is_err()); |
592 | } |
593 | |
594 | #[test ] |
595 | fn cannot_deserialize_state_set_invalid_flag() { |
596 | let ctxt = Context::<LE>::new_dbus(0); |
597 | let decoded = from_slice::<_, StateSet>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32], ctxt); |
598 | assert!(decoded.is_err()); |
599 | } |
600 | |
601 | #[test ] |
602 | fn convert_state_direct_string() { |
603 | for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) |
604 | .unwrap() |
605 | .iter() |
606 | { |
607 | let state_str: String = state.into(); |
608 | let state_two: State = state_str.clone().into(); |
609 | assert_eq!( |
610 | state, state_two, |
611 | "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?}" |
612 | ); |
613 | } |
614 | } |
615 | #[test ] |
616 | fn convert_state_direct_string_is_equal_to_serde_output() { |
617 | for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) |
618 | .unwrap() |
619 | .iter() |
620 | { |
621 | let serde_state_str: String = serde_plain::to_string(&state).unwrap(); |
622 | let state_str: String = state.into(); |
623 | assert_eq!(serde_state_str, state_str); |
624 | let state_two: State = serde_plain::from_str(&state_str).unwrap(); |
625 | assert_eq!(state, state_two, "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?} (serde)" ); |
626 | } |
627 | } |
628 | } |
629 | |