atrium_api/types/
integer.rs

1//! Lexicon integer types with minimum or maximum acceptable values.
2
3use std::num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8};
4
5use serde::{de::Error, Deserialize};
6
7macro_rules! uint {
8    ($primitive:ident, $nz:ident, $lim:ident, $lim_nz:ident, $bounded:ident) => {
9        /// An unsigned integer with a maximum value of `MAX`.
10        #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, Hash)]
11        #[repr(transparent)]
12        #[serde(transparent)]
13        pub struct $lim<const MAX: $primitive>($primitive);
14
15        impl<const MAX: $primitive> $lim<MAX> {
16            /// The smallest value that can be represented by this limited integer type.
17            pub const MIN: Self = Self(<$primitive>::MIN);
18
19            /// The largest value that can be represented by this limited integer type.
20            pub const MAX: Self = Self(MAX);
21
22            fn new(value: $primitive) -> Result<Self, String> {
23                if value > MAX {
24                    Err(format!("value is greater than {}", MAX))
25                } else {
26                    Ok(Self(value))
27                }
28            }
29        }
30
31        impl<const MAX: $primitive> TryFrom<$primitive> for $lim<MAX> {
32            type Error = String;
33
34            fn try_from(value: $primitive) -> Result<Self, Self::Error> {
35                Self::new(value)
36            }
37        }
38
39        impl<'de, const MAX: $primitive> Deserialize<'de> for $lim<MAX> {
40            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
41            where
42                D: serde::Deserializer<'de>,
43            {
44                let value = Deserialize::deserialize(deserializer)?;
45                Self::new(value).map_err(D::Error::custom)
46            }
47        }
48
49        impl<const MAX: $primitive> From<$lim<MAX>> for $primitive {
50            fn from(value: $lim<MAX>) -> Self {
51                value.0
52            }
53        }
54
55        /// An unsigned integer with a minimum value of 1 and a maximum value of `MAX`.
56        #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, Hash)]
57        #[repr(transparent)]
58        #[serde(transparent)]
59        pub struct $lim_nz<const MAX: $primitive>($nz);
60
61        impl<const MAX: $primitive> $lim_nz<MAX> {
62            /// The smallest value that can be represented by this limited non-zero
63            /// integer type.
64            pub const MIN: Self = Self($nz::MIN);
65
66            /// The largest value that can be represented by this limited non-zero integer
67            /// type.
68            pub const MAX: Self = Self(unsafe { $nz::new_unchecked(MAX) });
69
70            fn new(value: $primitive) -> Result<Self, String> {
71                if value > MAX {
72                    Err(format!("value is greater than {}", MAX))
73                } else if let Some(value) = $nz::new(value) {
74                    Ok(Self(value))
75                } else {
76                    Err("value is zero".into())
77                }
78            }
79        }
80
81        impl<const MAX: $primitive> TryFrom<$primitive> for $lim_nz<MAX> {
82            type Error = String;
83
84            fn try_from(value: $primitive) -> Result<Self, Self::Error> {
85                Self::new(value)
86            }
87        }
88
89        impl<'de, const MAX: $primitive> Deserialize<'de> for $lim_nz<MAX> {
90            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91            where
92                D: serde::Deserializer<'de>,
93            {
94                let value = Deserialize::deserialize(deserializer)?;
95                Self::new(value).map_err(D::Error::custom)
96            }
97        }
98
99        impl<const MAX: $primitive> From<$lim_nz<MAX>> for $nz {
100            fn from(value: $lim_nz<MAX>) -> Self {
101                value.0
102            }
103        }
104
105        impl<const MAX: $primitive> From<$lim_nz<MAX>> for $primitive {
106            fn from(value: $lim_nz<MAX>) -> Self {
107                value.0.into()
108            }
109        }
110
111        /// An unsigned integer with a minimum value of `MIN` and a maximum value of `MAX`.
112        ///
113        /// `MIN` must be non-zero.
114        #[derive(Clone, Copy, Debug, PartialEq, Eq, serde::Serialize, Hash)]
115        #[repr(transparent)]
116        #[serde(transparent)]
117        pub struct $bounded<const MIN: $primitive, const MAX: $primitive>($nz);
118
119        impl<const MIN: $primitive, const MAX: $primitive> $bounded<MIN, MAX> {
120            /// The smallest value that can be represented by this bounded integer type.
121            pub const MIN: Self = Self(unsafe { $nz::new_unchecked(MIN) });
122
123            /// The largest value that can be represented by this bounded integer type.
124            pub const MAX: Self = Self(unsafe { $nz::new_unchecked(MAX) });
125
126            fn new(value: $primitive) -> Result<Self, String> {
127                if value < MIN {
128                    Err(format!("value is less than {}", MIN))
129                } else if value > MAX {
130                    Err(format!("value is greater than {}", MAX))
131                } else if let Some(value) = $nz::new(value) {
132                    Ok(Self(value))
133                } else {
134                    Err("value is zero".into())
135                }
136            }
137        }
138
139        impl<const MIN: $primitive, const MAX: $primitive> TryFrom<$primitive>
140            for $bounded<MIN, MAX>
141        {
142            type Error = String;
143
144            fn try_from(value: $primitive) -> Result<Self, Self::Error> {
145                Self::new(value)
146            }
147        }
148
149        impl<'de, const MIN: $primitive, const MAX: $primitive> Deserialize<'de>
150            for $bounded<MIN, MAX>
151        {
152            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
153            where
154                D: serde::Deserializer<'de>,
155            {
156                let value = Deserialize::deserialize(deserializer)?;
157                Self::new(value).map_err(D::Error::custom)
158            }
159        }
160
161        impl<const MIN: $primitive, const MAX: $primitive> From<$bounded<MIN, MAX>> for $nz {
162            fn from(value: $bounded<MIN, MAX>) -> Self {
163                value.0
164            }
165        }
166
167        impl<const MIN: $primitive, const MAX: $primitive> From<$bounded<MIN, MAX>> for $primitive {
168            fn from(value: $bounded<MIN, MAX>) -> Self {
169                value.0.into()
170            }
171        }
172    };
173}
174
175uint!(u8, NonZeroU8, LimitedU8, LimitedNonZeroU8, BoundedU8);
176uint!(u16, NonZeroU16, LimitedU16, LimitedNonZeroU16, BoundedU16);
177uint!(u32, NonZeroU32, LimitedU32, LimitedNonZeroU32, BoundedU32);
178uint!(u64, NonZeroU64, LimitedU64, LimitedNonZeroU64, BoundedU64);
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183
184    #[test]
185    fn u8_min_max() {
186        assert_eq!(Ok(LimitedU8::<10>::MIN), 0.try_into());
187        assert_eq!(Ok(LimitedU8::<10>::MAX), 10.try_into());
188        assert_eq!(Ok(LimitedNonZeroU8::<10>::MIN), 1.try_into());
189        assert_eq!(Ok(LimitedNonZeroU8::<10>::MAX), 10.try_into());
190        assert_eq!(Ok(BoundedU8::<7, 10>::MIN), 7.try_into());
191        assert_eq!(Ok(BoundedU8::<7, 10>::MAX), 10.try_into());
192    }
193}