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::{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}