Skip to main content

statime_config/tree/
modifiers.rs

1use serde::{Deserialize, Serialize, de};
2
3use crate::{ConfigurableAtomic, serde_util::Content};
4
5pub trait IsPositive {
6    fn is_positive(&self) -> bool;
7}
8
9macro_rules! is_positive_int {
10    ($int_type:ident) => {
11        impl IsPositive for $int_type {
12            fn is_positive(&self) -> bool {
13                *self > 0
14            }
15        }
16    };
17}
18
19macro_rules! is_positive_float {
20    ($float_type:ident) => {
21        impl IsPositive for $float_type {
22            fn is_positive(&self) -> bool {
23                *self > 0.0
24            }
25        }
26    };
27}
28
29is_positive_int!(u8);
30is_positive_int!(u16);
31is_positive_int!(u32);
32is_positive_int!(u64);
33is_positive_int!(u128);
34is_positive_int!(usize);
35is_positive_int!(i8);
36is_positive_int!(i16);
37is_positive_int!(i32);
38is_positive_int!(i64);
39is_positive_int!(i128);
40is_positive_int!(isize);
41is_positive_float!(f32);
42is_positive_float!(f64);
43
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
45pub struct Positive<T>(T);
46
47impl<T> Positive<T> {
48    pub fn into_inner(self) -> T {
49        self.0
50    }
51
52    pub fn unchecked_new(value: T) -> Self {
53        Positive(value)
54    }
55}
56
57impl<'de, T: Deserialize<'de> + IsPositive> Deserialize<'de> for Positive<T> {
58    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
59    where
60        D: serde::Deserializer<'de>,
61    {
62        let value = T::deserialize(deserializer)?;
63        if T::is_positive(&value) {
64            Ok(Positive(value))
65        } else {
66            Err(de::Error::custom("value should be positive."))
67        }
68    }
69}
70
71impl<T: Serialize> Serialize for Positive<T> {
72    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
73    where
74        S: serde::Serializer,
75    {
76        self.0.serialize(serializer)
77    }
78}
79
80impl<T: ConfigurableAtomic + IsPositive> ConfigurableAtomic for Positive<T> {}
81impl ConfigurableAtomic for Positive<f32> {}
82impl ConfigurableAtomic for Positive<f64> {}
83
84pub trait IsNonNegative {
85    fn is_non_negative(&self) -> bool;
86}
87
88macro_rules! is_nonnegative_float {
89    ($float_type:ident) => {
90        impl IsNonNegative for $float_type {
91            fn is_non_negative(&self) -> bool {
92                *self >= 0.0
93            }
94        }
95    };
96}
97
98is_nonnegative_float!(f32);
99is_nonnegative_float!(f64);
100
101#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
102pub struct NonNegative<T>(T);
103
104impl<T> NonNegative<T> {
105    pub fn into_inner(self) -> T {
106        self.0
107    }
108
109    pub fn unchecked_new(value: T) -> Self {
110        NonNegative(value)
111    }
112}
113
114impl<'de, T: Deserialize<'de> + IsNonNegative> Deserialize<'de> for NonNegative<T> {
115    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
116    where
117        D: serde::Deserializer<'de>,
118    {
119        let value = T::deserialize(deserializer)?;
120        if T::is_non_negative(&value) {
121            Ok(NonNegative(value))
122        } else {
123            Err(de::Error::custom("value should be positive."))
124        }
125    }
126}
127
128impl<T: Serialize> Serialize for NonNegative<T> {
129    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
130    where
131        S: serde::Serializer,
132    {
133        self.0.serialize(serializer)
134    }
135}
136
137impl<T: ConfigurableAtomic + IsNonNegative> ConfigurableAtomic for NonNegative<T> {}
138impl ConfigurableAtomic for NonNegative<f32> {}
139impl ConfigurableAtomic for NonNegative<f64> {}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
142pub enum InfiniteOr<T> {
143    Infinite,
144    Finite(T),
145}
146
147impl<'de, T: Deserialize<'de>> Deserialize<'de> for InfiniteOr<T> {
148    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
149    where
150        D: de::Deserializer<'de>,
151    {
152        let is_human_readable = deserializer.is_human_readable();
153        let content = Content::deserialize(deserializer)?;
154        match content {
155            Content::String(ref v) if v == "inf" => Ok(InfiniteOr::Infinite),
156            Content::BorrowedString("inf") => Ok(InfiniteOr::Infinite),
157            _ => Ok(InfiniteOr::Finite(T::deserialize(
158                content.into_deserializer(is_human_readable),
159            )?)),
160        }
161    }
162}
163
164impl<T: Serialize> Serialize for InfiniteOr<T> {
165    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
166    where
167        S: serde::Serializer,
168    {
169        match self {
170            InfiniteOr::Infinite => serializer.serialize_str("inf"),
171            InfiniteOr::Finite(v) => v.serialize(serializer),
172        }
173    }
174}
175
176impl<T: ConfigurableAtomic> ConfigurableAtomic for InfiniteOr<T> {}
177impl ConfigurableAtomic for InfiniteOr<f32> {}
178impl ConfigurableAtomic for InfiniteOr<f64> {}
179
180#[cfg(test)]
181mod test {
182    use serde::{Deserialize, Serialize};
183
184    use crate::InfiniteOr;
185
186    #[test]
187    fn test_infinite_or_finite() {
188        #[derive(Deserialize, Serialize)]
189        struct Test {
190            test: InfiniteOr<u8>,
191        }
192
193        let parsed: Test = toml::from_str(
194            r"#
195            test = 5
196        ",
197        )
198        .unwrap();
199
200        assert_eq!(parsed.test, InfiniteOr::Finite(5));
201
202        let serialized = toml::to_string(&parsed).unwrap();
203        assert_eq!(serialized, "test = 5\n");
204    }
205
206    #[test]
207    fn test_infinite_or_inf() {
208        #[derive(Deserialize, Serialize)]
209        struct Test {
210            test: InfiniteOr<u8>,
211        }
212
213        let parsed: Test = toml::from_str(
214            r#"
215            test = "inf"
216            "#,
217        )
218        .unwrap();
219
220        assert_eq!(parsed.test, InfiniteOr::Infinite);
221
222        let serialized = toml::to_string(&parsed).unwrap();
223        assert_eq!(serialized, "test = \"inf\"\n");
224    }
225}