| 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::{State, StateSet}; |
| 513 | use zvariant::serialized::{Context, Data}; |
| 514 | use zvariant::{to_bytes, LE}; |
| 515 | |
| 516 | #[test ] |
| 517 | fn serialize_empty_state_set() { |
| 518 | let ctxt = Context::new_dbus(LE, 0); |
| 519 | let encoded = to_bytes(ctxt, &StateSet::empty()).unwrap(); |
| 520 | assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); |
| 521 | } |
| 522 | |
| 523 | #[test ] |
| 524 | fn deserialize_empty_state_set() { |
| 525 | let ctxt = Context::new_dbus(LE, 0); |
| 526 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt); |
| 527 | let (decoded, _) = data.deserialize::<StateSet>().unwrap(); |
| 528 | assert_eq!(decoded, StateSet::empty()); |
| 529 | } |
| 530 | |
| 531 | #[test ] |
| 532 | fn serialize_state_set_invalid() { |
| 533 | let ctxt = Context::new_dbus(LE, 0); |
| 534 | let encoded = to_bytes(ctxt, &StateSet::new(State::Invalid)).unwrap(); |
| 535 | assert_eq!(encoded.bytes(), &[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::new_dbus(LE, 0); |
| 541 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0], ctxt); |
| 542 | let (decoded, _) = data.deserialize::<StateSet>().unwrap(); |
| 543 | assert_eq!(decoded, StateSet::new(State::Invalid)); |
| 544 | } |
| 545 | |
| 546 | #[test ] |
| 547 | fn serialize_state_set_manages_descendants() { |
| 548 | let ctxt = Context::new_dbus(LE, 0); |
| 549 | let encoded = to_bytes(ctxt, &StateSet::new(State::ManagesDescendants)).unwrap(); |
| 550 | assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0]); |
| 551 | } |
| 552 | |
| 553 | #[test ] |
| 554 | fn deserialize_state_set_manages_descendants() { |
| 555 | let ctxt = Context::new_dbus(LE, 0); |
| 556 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0], ctxt); |
| 557 | let (decoded, _) = data.deserialize::<StateSet>().unwrap(); |
| 558 | assert_eq!(decoded, StateSet::new(State::ManagesDescendants)); |
| 559 | } |
| 560 | |
| 561 | #[test ] |
| 562 | fn serialize_state_set_indeterminate() { |
| 563 | let ctxt = Context::new_dbus(LE, 0); |
| 564 | let encoded = to_bytes(ctxt, &StateSet::new(State::Indeterminate)).unwrap(); |
| 565 | assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0]); |
| 566 | } |
| 567 | |
| 568 | #[test ] |
| 569 | fn deserialize_state_set_indeterminate() { |
| 570 | let ctxt = Context::new_dbus(LE, 0); |
| 571 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0], ctxt); |
| 572 | let (decoded, _) = data.deserialize::<StateSet>().unwrap(); |
| 573 | assert_eq!(decoded, StateSet::new(State::Indeterminate)); |
| 574 | } |
| 575 | |
| 576 | #[test ] |
| 577 | fn serialize_state_set_focusable_focused() { |
| 578 | let ctxt = Context::new_dbus(LE, 0); |
| 579 | let encoded = to_bytes(ctxt, &StateSet::new(State::Focusable | State::Focused)).unwrap(); |
| 580 | assert_eq!(encoded.bytes(), &[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0]); |
| 581 | } |
| 582 | |
| 583 | #[test ] |
| 584 | fn deserialize_state_set_focusable_focused() { |
| 585 | let ctxt = Context::new_dbus(LE, 0); |
| 586 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 24, 0, 0, 0, 0, 0, 0], ctxt); |
| 587 | let (decoded, _) = data.deserialize::<StateSet>().unwrap(); |
| 588 | assert_eq!(decoded, StateSet::new(State::Focusable | State::Focused)); |
| 589 | } |
| 590 | |
| 591 | #[test ] |
| 592 | fn cannot_deserialize_state_set_invalid_length() { |
| 593 | let ctxt = Context::new_dbus(LE, 0); |
| 594 | let data = Data::new::<&[u8]>(&[4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ctxt); |
| 595 | let decode_result = data.deserialize::<StateSet>(); |
| 596 | assert!(decode_result.is_err()); |
| 597 | } |
| 598 | |
| 599 | #[test ] |
| 600 | fn cannot_deserialize_state_set_invalid_flag() { |
| 601 | let ctxt = Context::new_dbus(LE, 0); |
| 602 | let data = Data::new::<&[u8]>(&[8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32], ctxt); |
| 603 | let decode_result = data.deserialize::<StateSet>(); |
| 604 | assert!(decode_result.is_err()); |
| 605 | } |
| 606 | |
| 607 | #[test ] |
| 608 | fn convert_state_direct_string() { |
| 609 | for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) |
| 610 | .unwrap() |
| 611 | .iter() |
| 612 | { |
| 613 | let state_str: String = state.into(); |
| 614 | let state_two: State = state_str.clone().into(); |
| 615 | assert_eq!( |
| 616 | state, state_two, |
| 617 | "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?}" |
| 618 | ); |
| 619 | } |
| 620 | } |
| 621 | #[test ] |
| 622 | fn convert_state_direct_string_is_equal_to_serde_output() { |
| 623 | for state in StateSet::from_bits(0b1111_1111_1111_1111_1111_1111_1111_1111_1111_1111_1111) |
| 624 | .unwrap() |
| 625 | .iter() |
| 626 | { |
| 627 | let serde_state_str: String = serde_plain::to_string(&state).unwrap(); |
| 628 | let state_str: String = state.into(); |
| 629 | assert_eq!(serde_state_str, state_str); |
| 630 | let state_two: State = serde_plain::from_str(&state_str).unwrap(); |
| 631 | assert_eq!(state, state_two, "The {state:?} was serialized as {state_str}, which deserializes to {state_two:?} (serde)" ); |
| 632 | } |
| 633 | } |
| 634 | } |
| 635 | |