tirea_state/lattice/
flag.rs

1use serde::{Deserialize, Serialize};
2
3use super::Lattice;
4
5/// A monotonic boolean flag: once enabled, it stays enabled forever.
6///
7/// Merge semantics: logical OR.
8#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(transparent)]
10pub struct Flag(bool);
11
12impl Flag {
13    /// Create a new disabled flag.
14    pub fn new() -> Self {
15        Self(false)
16    }
17
18    /// Create an already-enabled flag.
19    pub fn enabled() -> Self {
20        Self(true)
21    }
22
23    /// Enable the flag. This is irreversible.
24    pub fn enable(&mut self) {
25        self.0 = true;
26    }
27
28    /// Returns `true` if the flag has been enabled.
29    pub fn is_enabled(&self) -> bool {
30        self.0
31    }
32}
33
34impl Default for Flag {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39
40impl Lattice for Flag {
41    fn merge(&self, other: &Self) -> Self {
42        Self(self.0 || other.0)
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::lattice::assert_lattice_laws;
50
51    #[test]
52    fn new_is_disabled() {
53        let f = Flag::new();
54        assert!(!f.is_enabled());
55    }
56
57    #[test]
58    fn enable_sets_flag() {
59        let mut f = Flag::new();
60        f.enable();
61        assert!(f.is_enabled());
62    }
63
64    #[test]
65    fn enabled_constructor() {
66        let f = Flag::enabled();
67        assert!(f.is_enabled());
68    }
69
70    #[test]
71    fn lattice_laws() {
72        let a = Flag::new();
73        let b = Flag::enabled();
74        let c = Flag::new();
75        assert_lattice_laws(&a, &b, &c);
76
77        let a = Flag::enabled();
78        let b = Flag::enabled();
79        let c = Flag::new();
80        assert_lattice_laws(&a, &b, &c);
81    }
82
83    #[test]
84    fn merge_or_semantics() {
85        let off = Flag::new();
86        let on = Flag::enabled();
87
88        assert_eq!(off.merge(&off), Flag::new());
89        assert_eq!(off.merge(&on), Flag::enabled());
90        assert_eq!(on.merge(&off), Flag::enabled());
91        assert_eq!(on.merge(&on), Flag::enabled());
92    }
93
94    #[test]
95    fn serde_roundtrip() {
96        let f = Flag::enabled();
97        let json = serde_json::to_value(&f).unwrap();
98        assert_eq!(json, serde_json::Value::Bool(true));
99
100        let back: Flag = serde_json::from_value(json).unwrap();
101        assert_eq!(back, f);
102
103        let f = Flag::new();
104        let json = serde_json::to_value(&f).unwrap();
105        assert_eq!(json, serde_json::Value::Bool(false));
106
107        let back: Flag = serde_json::from_value(json).unwrap();
108        assert_eq!(back, f);
109    }
110}