commonware_codec/
config.rs1use core::{
4 num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroUsize},
5 ops::{Bound, RangeBounds},
6};
7
8#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
35pub struct RangeCfg<T: Copy + PartialOrd> {
36 start: Bound<T>,
38
39 end: Bound<T>,
41}
42
43impl<T: Copy + PartialOrd> From<core::ops::Range<T>> for RangeCfg<T> {
44 fn from(r: core::ops::Range<T>) -> Self {
45 Self::new(r)
46 }
47}
48
49impl<T: Copy + PartialOrd> From<core::ops::RangeInclusive<T>> for RangeCfg<T> {
50 fn from(r: core::ops::RangeInclusive<T>) -> Self {
51 Self::new(r)
52 }
53}
54
55impl<T: Copy + PartialOrd> From<core::ops::RangeFrom<T>> for RangeCfg<T> {
56 fn from(r: core::ops::RangeFrom<T>) -> Self {
57 Self::new(r)
58 }
59}
60
61impl<T: Copy + PartialOrd> From<core::ops::RangeTo<T>> for RangeCfg<T> {
62 fn from(r: core::ops::RangeTo<T>) -> Self {
63 Self::new(r)
64 }
65}
66
67impl<T: Copy + PartialOrd> From<core::ops::RangeToInclusive<T>> for RangeCfg<T> {
68 fn from(r: core::ops::RangeToInclusive<T>) -> Self {
69 Self::new(r)
70 }
71}
72
73impl<T: Copy + PartialOrd> From<core::ops::RangeFull> for RangeCfg<T> {
74 fn from(_: core::ops::RangeFull) -> Self {
75 Self::new(..)
76 }
77}
78
79macro_rules! impl_from {
80 ($from:ty => $to:ty, $f:expr) => {
81 impl From<RangeCfg<$from>> for RangeCfg<$to> {
82 fn from(value: RangeCfg<$from>) -> Self {
83 value.map($f)
84 }
85 }
86 };
87}
88
89impl_from!(NonZeroUsize => usize, usize::from);
90impl_from!(NonZeroU8 => u8, u8::from);
91impl_from!(NonZeroU16 => u16, u16::from);
92impl_from!(NonZeroU32 => u32, u32::from);
93impl_from!(NonZeroU64 => u64, u64::from);
94
95impl_from!(NonZeroU8 => usize, |v| usize::from(v.get()));
96impl_from!(NonZeroU16 => usize, |v| usize::from(v.get()));
97impl_from!(NonZeroU32 => usize, |v| usize::try_from(v.get()).expect("range bound exceeds usize"));
98
99impl_from!(NonZeroU8 => NonZeroUsize, NonZeroUsize::from);
100impl_from!(NonZeroU16 => NonZeroUsize, NonZeroUsize::from);
101impl_from!(NonZeroU32 => NonZeroUsize, |v| {
102 NonZeroUsize::try_from(v).expect("range bound exceeds usize")
103});
104
105impl<T: Copy + PartialOrd> RangeCfg<T> {
106 pub fn new(r: impl RangeBounds<T>) -> Self {
117 Self {
118 start: r.start_bound().cloned(),
119 end: r.end_bound().cloned(),
120 }
121 }
122
123 pub const fn exact(value: T) -> Self {
125 Self {
126 start: Bound::Included(value),
127 end: Bound::Included(value),
128 }
129 }
130
131 pub fn contains(&self, value: &T) -> bool {
133 match &self.start {
135 Bound::Included(s) if value < s => return false,
136 Bound::Excluded(s) if value <= s => return false,
137 _ => {}
138 }
139
140 match &self.end {
142 Bound::Included(e) if value > e => return false,
143 Bound::Excluded(e) if value >= e => return false,
144 _ => {}
145 }
146
147 true
149 }
150
151 fn map<U: Copy + PartialOrd>(self, f: impl Fn(T) -> U) -> RangeCfg<U> {
153 RangeCfg {
154 start: self.start.map(&f),
155 end: self.end.map(&f),
156 }
157 }
158}
159
160impl<T: Copy + PartialOrd> RangeBounds<T> for RangeCfg<T> {
161 fn start_bound(&self) -> Bound<&T> {
162 self.start.as_ref()
163 }
164
165 fn end_bound(&self) -> Bound<&T> {
166 self.end.as_ref()
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use super::*;
173 use core::ops::Bound::{Excluded, Included, Unbounded};
174
175 #[test]
176 fn test_range_cfg_from() {
177 let cfg_full: RangeCfg<usize> = (..).into();
179 assert_eq!(
180 cfg_full,
181 RangeCfg {
182 start: Unbounded,
183 end: Unbounded
184 }
185 );
186
187 let cfg_start_incl: RangeCfg<usize> = (5..).into();
189 assert_eq!(
190 cfg_start_incl,
191 RangeCfg {
192 start: Included(5),
193 end: Unbounded
194 }
195 );
196
197 let cfg_end_excl: RangeCfg<usize> = (..10).into();
199 assert_eq!(
200 cfg_end_excl,
201 RangeCfg {
202 start: Unbounded,
203 end: Excluded(10)
204 }
205 );
206
207 let cfg_end_incl: RangeCfg<usize> = (..=10).into();
209 assert_eq!(
210 cfg_end_incl,
211 RangeCfg {
212 start: Unbounded,
213 end: Included(10)
214 }
215 );
216
217 let cfg_incl_excl: RangeCfg<usize> = (5..10).into();
219 assert_eq!(
220 cfg_incl_excl,
221 RangeCfg {
222 start: Included(5),
223 end: Excluded(10)
224 }
225 );
226
227 let cfg_incl_incl: RangeCfg<usize> = (5..=10).into();
229 assert_eq!(
230 cfg_incl_incl,
231 RangeCfg {
232 start: Included(5),
233 end: Included(10)
234 }
235 );
236
237 struct ExclusiveStartRange(usize, usize);
239 impl RangeBounds<usize> for ExclusiveStartRange {
240 fn start_bound(&self) -> Bound<&usize> {
241 Excluded(&self.0)
242 }
243 fn end_bound(&self) -> Bound<&usize> {
244 Included(&self.1)
245 }
246 }
247 let cfg_excl_incl = RangeCfg::new(ExclusiveStartRange(5, 10));
248 assert_eq!(
249 cfg_excl_incl,
250 RangeCfg {
251 start: Excluded(5),
252 end: Included(10)
253 }
254 );
255 }
256
257 #[test]
258 fn test_range_cfg_contains() {
259 let cfg_unbounded: RangeCfg<usize> = (..).into();
261 assert!(cfg_unbounded.contains(&0));
262 assert!(cfg_unbounded.contains(&100));
263 assert!(cfg_unbounded.contains(&usize::MAX));
264
265 let cfg_start_incl: RangeCfg<usize> = (5..).into();
267 assert!(!cfg_start_incl.contains(&4));
268 assert!(cfg_start_incl.contains(&5));
269 assert!(cfg_start_incl.contains(&6));
270 assert!(cfg_start_incl.contains(&usize::MAX));
271
272 let cfg_end_excl: RangeCfg<usize> = (..10).into();
274 assert!(cfg_end_excl.contains(&0));
275 assert!(cfg_end_excl.contains(&9));
276 assert!(!cfg_end_excl.contains(&10));
277 assert!(!cfg_end_excl.contains(&11));
278
279 let cfg_end_incl: RangeCfg<usize> = (..=10).into();
281 assert!(cfg_end_incl.contains(&0));
282 assert!(cfg_end_incl.contains(&9));
283 assert!(cfg_end_incl.contains(&10));
284 assert!(!cfg_end_incl.contains(&11));
285
286 let cfg_incl_excl: RangeCfg<usize> = (5..10).into();
288 assert!(!cfg_incl_excl.contains(&4));
289 assert!(cfg_incl_excl.contains(&5));
290 assert!(cfg_incl_excl.contains(&9));
291 assert!(!cfg_incl_excl.contains(&10));
292 assert!(!cfg_incl_excl.contains(&11));
293
294 let cfg_incl_incl: RangeCfg<usize> = (5..=10).into();
296 assert!(!cfg_incl_incl.contains(&4));
297 assert!(cfg_incl_incl.contains(&5));
298 assert!(cfg_incl_incl.contains(&9));
299 assert!(cfg_incl_incl.contains(&10));
300 assert!(!cfg_incl_incl.contains(&11));
301
302 let cfg_excl_incl = RangeCfg {
304 start: Excluded(5),
305 end: Included(10),
306 };
307 assert!(!cfg_excl_incl.contains(&4));
308 assert!(!cfg_excl_incl.contains(&5)); assert!(cfg_excl_incl.contains(&6));
310 assert!(cfg_excl_incl.contains(&10)); assert!(!cfg_excl_incl.contains(&11));
312
313 let cfg_excl_excl = RangeCfg {
315 start: Excluded(5),
316 end: Excluded(10),
317 };
318 assert!(!cfg_excl_excl.contains(&5)); assert!(cfg_excl_excl.contains(&6));
320 assert!(cfg_excl_excl.contains(&9));
321 assert!(!cfg_excl_excl.contains(&10)); }
323
324 #[test]
325 fn test_contains_empty_range() {
326 let cfg_empty_excl: RangeCfg<usize> = (5..5).into();
328 assert!(!cfg_empty_excl.contains(&4));
329 assert!(!cfg_empty_excl.contains(&5));
330 assert!(!cfg_empty_excl.contains(&6));
331
332 #[allow(clippy::reversed_empty_ranges)]
334 let cfg_empty_incl: RangeCfg<usize> = (6..=5).into();
335 assert!(!cfg_empty_incl.contains(&5));
336 assert!(!cfg_empty_incl.contains(&6));
337 }
338
339 #[test]
340 fn test_range_cfg_u8() {
341 let cfg = RangeCfg::new(0u8..=255u8);
343 assert!(cfg.contains(&0));
344 assert!(cfg.contains(&128));
345 assert!(cfg.contains(&255));
346
347 let cfg_partial = RangeCfg::new(10u8..20u8);
348 assert!(!cfg_partial.contains(&9));
349 assert!(cfg_partial.contains(&10));
350 assert!(cfg_partial.contains(&19));
351 assert!(!cfg_partial.contains(&20));
352 }
353
354 #[test]
355 fn test_range_cfg_u16() {
356 let cfg = RangeCfg::new(100u16..=1000u16);
358 assert!(!cfg.contains(&99));
359 assert!(cfg.contains(&100));
360 assert!(cfg.contains(&500));
361 assert!(cfg.contains(&1000));
362 assert!(!cfg.contains(&1001));
363 }
364
365 #[test]
366 fn test_range_cfg_u32() {
367 let cfg = RangeCfg::new(0u32..1024u32);
369 assert!(cfg.contains(&0));
370 assert!(cfg.contains(&512));
371 assert!(!cfg.contains(&1024));
372 assert!(!cfg.contains(&2000));
373 }
374
375 #[test]
376 fn test_range_cfg_u64() {
377 let cfg = RangeCfg::new(1000u64..);
379 assert!(!cfg.contains(&999));
380 assert!(cfg.contains(&1000));
381 assert!(cfg.contains(&u64::MAX));
382 }
383
384 #[test]
385 fn test_type_inference() {
386 let cfg = RangeCfg::new(0u8..10u8);
388 assert!(cfg.contains(&5u8));
389
390 let cfg: RangeCfg<u32> = RangeCfg::new(0..1000);
392 assert!(cfg.contains(&500));
393 }
394}