1use enumflags2::{bitflags, BitFlag, BitFlags, FromBitsError};
2use serde::{
3 de::{self, Deserializer, Visitor},
4 ser::{SerializeSeq, Serializer},
5 Deserialize, Serialize,
6};
7use std::fmt;
8use zvariant::{Signature, Type};
9
10#[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 #[default]
20 Invalid,
21 Active,
32 Armed,
34 Busy,
38 Checked,
40 Collapsed,
42 Defunct,
45 Editable,
47 Enabled,
54 Expandable,
57 Expanded,
59 Focusable,
63 Focused,
65 HasTooltip,
67 Horizontal,
69 Iconified,
72 Modal,
75 MultiLine,
78 Multiselectable,
82 Opaque,
86 Pressed,
88 Resizable,
90 Selectable,
94 Selected,
98 Sensitive,
109 Showing,
114 SingleLine,
117 Stale,
122 Transient,
124 Vertical,
128 Visible,
138 ManagesDescendants,
149 Indeterminate,
162 Required,
166 Truncated,
169 Animated,
178 InvalidEntry,
182 SupportsAutocompletion,
193 SelectableText,
200 IsDefault,
205 Visited,
209 Checkable,
212 HasPopup,
218 ReadOnly,
222}
223
224impl 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
276impl From<String> for State {
277 fn from(string: String) -> State {
278 (&*string).into()
279 }
280}
281
282impl 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)]
335pub struct StateSet(BitFlags<State>);
337
338impl StateSet {
339 pub fn new<B: Into<BitFlags<State>>>(value: B) -> Self {
350 Self(value.into())
351 }
352
353 pub fn from_bits(bits: u64) -> Result<StateSet, FromBitsError<State>> {
357 Ok(StateSet(BitFlags::from_bits(bits)?))
358 }
359
360 #[must_use]
361 pub fn empty() -> StateSet {
363 StateSet(State::empty())
364 }
365
366 #[must_use]
367 pub fn bits(&self) -> u64 {
369 self.0.bits()
370 }
371
372 pub fn contains<B: Into<BitFlags<State>>>(self, other: B) -> bool {
374 self.0.contains(other)
375 }
376
377 pub fn remove<B: Into<BitFlags<State>>>(&mut self, other: B) {
379 self.0.remove(other);
380 }
381
382 pub fn insert<B: Into<BitFlags<State>>>(&mut self, other: B) {
384 self.0.insert(other);
385 }
386
387 pub fn iter(self) -> impl Iterator<Item = State> {
389 self.0.iter()
390 }
391
392 #[must_use]
393 pub fn is_empty(self) -> bool {
395 self.0.is_empty()
396 }
397
398 pub fn intersects<B: Into<BitFlags<State>>>(self, other: B) -> bool {
400 self.0.intersects(other)
401 }
402
403 pub fn toggle<B: Into<BitFlags<State>>>(&mut self, other: B) {
405 self.0.toggle(other);
406 }
407}
408
409impl<'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
444impl 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 = serializer.serialize_seq(Some(2))?;
450 let bits = self.bits();
451
452 #[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
461impl Type for StateSet {
462 fn signature() -> Signature<'static> {
463 <Vec<u32> as Type>::signature()
464 }
465}
466
467impl From<State> for StateSet {
468 fn from(value: State) -> Self {
469 Self(value.into())
470 }
471}
472
473impl 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}
480impl std::ops::BitXorAssign for StateSet {
481 fn bitxor_assign(&mut self, other: Self) {
482 self.0 = self.0 ^ other.0;
483 }
484}
485impl 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}
492impl std::ops::BitOrAssign for StateSet {
493 fn bitor_assign(&mut self, other: Self) {
494 self.0 = self.0 | other.0;
495 }
496}
497impl 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}
504impl 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)]
511mod 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}