Skip to main content

solti_model/domain/
flag.rs

1//! # Boolean toggle.
2//!
3//! [`Flag`] provides `enabled()`/`disabled()` constructors with serde support.
4
5use serde::{Deserialize, Serialize};
6
7/// Universal boolean flag with explicit enable/disable semantics.
8///
9/// ```text
10///  Flag::enabled()   ─► is_enabled() == true
11///  Flag::disabled()  ─► is_enabled() == false
12///  Flag::default()   ─► enabled (opt-in disable)
13/// ```
14///
15/// ```rust
16/// use solti_model::Flag;
17///
18/// let f = Flag::enabled();
19/// assert!(f.is_enabled());
20///
21/// let f: Flag = false.into();
22/// assert!(f.is_disabled());
23///
24/// let b: bool = f.into();
25/// assert!(!b);
26/// ```
27#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
28#[serde(transparent)]
29pub struct Flag(bool);
30
31impl Flag {
32    /// Create an enabled flag.
33    #[inline]
34    pub const fn enabled() -> Self {
35        Self(true)
36    }
37
38    /// Create a disabled flag.
39    #[inline]
40    pub const fn disabled() -> Self {
41        Self(false)
42    }
43
44    /// Check if the flag is enabled.
45    #[inline]
46    pub const fn is_enabled(&self) -> bool {
47        self.0
48    }
49
50    /// Check if the flag is disabled.
51    #[inline]
52    pub const fn is_disabled(&self) -> bool {
53        !self.0
54    }
55
56    /// Get the raw boolean value.
57    #[inline]
58    pub const fn value(&self) -> bool {
59        self.0
60    }
61}
62
63impl Default for Flag {
64    #[inline]
65    fn default() -> Self {
66        Self::enabled()
67    }
68}
69
70impl From<bool> for Flag {
71    #[inline]
72    fn from(b: bool) -> Self {
73        Self(b)
74    }
75}
76
77impl From<Flag> for bool {
78    #[inline]
79    fn from(f: Flag) -> Self {
80        f.0
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::Flag;
87
88    #[test]
89    fn default_is_enabled() {
90        let f = Flag::default();
91        assert!(f.is_enabled());
92        assert!(!f.is_disabled());
93        assert!(f.value());
94    }
95
96    #[test]
97    fn enabled_and_disabled_constructors_work() {
98        let e = Flag::enabled();
99        let d = Flag::disabled();
100
101        assert!(e.is_enabled());
102        assert!(!e.is_disabled());
103
104        assert!(!d.is_enabled());
105        assert!(d.is_disabled());
106    }
107
108    #[test]
109    fn from_bool_and_into_bool() {
110        let f_true: Flag = true.into();
111        let f_false: Flag = false.into();
112
113        assert!(f_true.is_enabled());
114        assert!(f_false.is_disabled());
115
116        let b1: bool = f_true.into();
117        let b2: bool = f_false.into();
118
119        assert!(b1);
120        assert!(!b2);
121    }
122
123    #[test]
124    fn serde_transparent_roundtrip() {
125        let f = Flag::disabled();
126        let json = serde_json::to_string(&f).unwrap();
127
128        assert_eq!(json, "false");
129        let back: Flag = serde_json::from_str(&json).unwrap();
130        assert!(back.is_disabled());
131    }
132}