commonware_codec/
config.rs1use core::{
4 num::{NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, 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_nonzero {
80 ($nz_ty:ty, $ty:ty) => {
81 impl From<RangeCfg<$nz_ty>> for RangeCfg<$ty> {
82 fn from(value: RangeCfg<$nz_ty>) -> Self {
83 let start = match value.start {
84 Bound::Included(nz) => Bound::Included(nz.get()),
85 Bound::Excluded(nz) => Bound::Excluded(nz.get()),
86 Bound::Unbounded => Bound::Unbounded,
87 };
88 let end = match value.end {
89 Bound::Included(nz) => Bound::Included(nz.get()),
90 Bound::Excluded(nz) => Bound::Excluded(nz.get()),
91 Bound::Unbounded => Bound::Unbounded,
92 };
93 RangeCfg { start, end }
94 }
95 }
96 };
97}
98
99impl_from_nonzero!(NonZeroUsize, usize);
100impl_from_nonzero!(NonZeroU8, u8);
101impl_from_nonzero!(NonZeroU16, u16);
102impl_from_nonzero!(NonZeroU32, u32);
103impl_from_nonzero!(NonZeroU64, u64);
104
105macro_rules! impl_from_nonzero_to_usize {
106 ($from_ty:ty) => {
107 impl From<RangeCfg<$from_ty>> for RangeCfg<usize> {
108 fn from(value: RangeCfg<$from_ty>) -> Self {
109 let start = match value.start {
110 Bound::Included(v) => Bound::Included(
111 usize::try_from(v.get()).expect("range start exceeds usize"),
112 ),
113 Bound::Excluded(v) => Bound::Excluded(
114 usize::try_from(v.get()).expect("range start exceeds usize"),
115 ),
116 Bound::Unbounded => Bound::Unbounded,
117 };
118 let end = match value.end {
119 Bound::Included(v) => {
120 Bound::Included(usize::try_from(v.get()).expect("range end exceeds usize"))
121 }
122 Bound::Excluded(v) => {
123 Bound::Excluded(usize::try_from(v.get()).expect("range end exceeds usize"))
124 }
125 Bound::Unbounded => Bound::Unbounded,
126 };
127 RangeCfg { start, end }
128 }
129 }
130 };
131}
132
133impl_from_nonzero_to_usize!(NonZeroU8);
134impl_from_nonzero_to_usize!(NonZeroU16);
135impl_from_nonzero_to_usize!(NonZeroU32);
136
137macro_rules! impl_nonzero_to_nonzero_usize {
138 ($from_ty:ty) => {
139 impl From<RangeCfg<$from_ty>> for RangeCfg<NonZeroUsize> {
140 fn from(value: RangeCfg<$from_ty>) -> Self {
141 let start = match value.start {
142 Bound::Included(v) => Bound::Included(
143 NonZeroUsize::try_from(v).expect("range start exceeds usize"),
144 ),
145 Bound::Excluded(v) => Bound::Excluded(
146 NonZeroUsize::try_from(v).expect("range start exceeds usize"),
147 ),
148 Bound::Unbounded => Bound::Unbounded,
149 };
150 let end = match value.end {
151 Bound::Included(v) => {
152 Bound::Included(NonZeroUsize::try_from(v).expect("range end exceeds usize"))
153 }
154 Bound::Excluded(v) => {
155 Bound::Excluded(NonZeroUsize::try_from(v).expect("range end exceeds usize"))
156 }
157 Bound::Unbounded => Bound::Unbounded,
158 };
159 RangeCfg { start, end }
160 }
161 }
162 };
163}
164
165impl_nonzero_to_nonzero_usize!(NonZeroU8);
166impl_nonzero_to_nonzero_usize!(NonZeroU16);
167impl_nonzero_to_nonzero_usize!(NonZeroU32);
168
169impl<T: Copy + PartialOrd> RangeCfg<T> {
170 pub fn new(r: impl RangeBounds<T>) -> Self {
181 Self {
182 start: r.start_bound().cloned(),
183 end: r.end_bound().cloned(),
184 }
185 }
186
187 pub const fn exact(value: T) -> Self {
189 Self {
190 start: Bound::Included(value),
191 end: Bound::Included(value),
192 }
193 }
194
195 pub fn contains(&self, value: &T) -> bool {
197 match &self.start {
199 Bound::Included(s) if value < s => return false,
200 Bound::Excluded(s) if value <= s => return false,
201 _ => {}
202 }
203
204 match &self.end {
206 Bound::Included(e) if value > e => return false,
207 Bound::Excluded(e) if value >= e => return false,
208 _ => {}
209 }
210
211 true
213 }
214}
215
216impl<T: Copy + PartialOrd> RangeBounds<T> for RangeCfg<T> {
217 fn start_bound(&self) -> Bound<&T> {
218 self.start.as_ref()
219 }
220
221 fn end_bound(&self) -> Bound<&T> {
222 self.end.as_ref()
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use core::ops::Bound::{Excluded, Included, Unbounded};
230
231 #[test]
232 fn test_range_cfg_from() {
233 let cfg_full: RangeCfg<usize> = (..).into();
235 assert_eq!(
236 cfg_full,
237 RangeCfg {
238 start: Unbounded,
239 end: Unbounded
240 }
241 );
242
243 let cfg_start_incl: RangeCfg<usize> = (5..).into();
245 assert_eq!(
246 cfg_start_incl,
247 RangeCfg {
248 start: Included(5),
249 end: Unbounded
250 }
251 );
252
253 let cfg_end_excl: RangeCfg<usize> = (..10).into();
255 assert_eq!(
256 cfg_end_excl,
257 RangeCfg {
258 start: Unbounded,
259 end: Excluded(10)
260 }
261 );
262
263 let cfg_end_incl: RangeCfg<usize> = (..=10).into();
265 assert_eq!(
266 cfg_end_incl,
267 RangeCfg {
268 start: Unbounded,
269 end: Included(10)
270 }
271 );
272
273 let cfg_incl_excl: RangeCfg<usize> = (5..10).into();
275 assert_eq!(
276 cfg_incl_excl,
277 RangeCfg {
278 start: Included(5),
279 end: Excluded(10)
280 }
281 );
282
283 let cfg_incl_incl: RangeCfg<usize> = (5..=10).into();
285 assert_eq!(
286 cfg_incl_incl,
287 RangeCfg {
288 start: Included(5),
289 end: Included(10)
290 }
291 );
292
293 struct ExclusiveStartRange(usize, usize);
295 impl RangeBounds<usize> for ExclusiveStartRange {
296 fn start_bound(&self) -> Bound<&usize> {
297 Excluded(&self.0)
298 }
299 fn end_bound(&self) -> Bound<&usize> {
300 Included(&self.1)
301 }
302 }
303 let cfg_excl_incl = RangeCfg::new(ExclusiveStartRange(5, 10));
304 assert_eq!(
305 cfg_excl_incl,
306 RangeCfg {
307 start: Excluded(5),
308 end: Included(10)
309 }
310 );
311 }
312
313 #[test]
314 fn test_range_cfg_contains() {
315 let cfg_unbounded: RangeCfg<usize> = (..).into();
317 assert!(cfg_unbounded.contains(&0));
318 assert!(cfg_unbounded.contains(&100));
319 assert!(cfg_unbounded.contains(&usize::MAX));
320
321 let cfg_start_incl: RangeCfg<usize> = (5..).into();
323 assert!(!cfg_start_incl.contains(&4));
324 assert!(cfg_start_incl.contains(&5));
325 assert!(cfg_start_incl.contains(&6));
326 assert!(cfg_start_incl.contains(&usize::MAX));
327
328 let cfg_end_excl: RangeCfg<usize> = (..10).into();
330 assert!(cfg_end_excl.contains(&0));
331 assert!(cfg_end_excl.contains(&9));
332 assert!(!cfg_end_excl.contains(&10));
333 assert!(!cfg_end_excl.contains(&11));
334
335 let cfg_end_incl: RangeCfg<usize> = (..=10).into();
337 assert!(cfg_end_incl.contains(&0));
338 assert!(cfg_end_incl.contains(&9));
339 assert!(cfg_end_incl.contains(&10));
340 assert!(!cfg_end_incl.contains(&11));
341
342 let cfg_incl_excl: RangeCfg<usize> = (5..10).into();
344 assert!(!cfg_incl_excl.contains(&4));
345 assert!(cfg_incl_excl.contains(&5));
346 assert!(cfg_incl_excl.contains(&9));
347 assert!(!cfg_incl_excl.contains(&10));
348 assert!(!cfg_incl_excl.contains(&11));
349
350 let cfg_incl_incl: RangeCfg<usize> = (5..=10).into();
352 assert!(!cfg_incl_incl.contains(&4));
353 assert!(cfg_incl_incl.contains(&5));
354 assert!(cfg_incl_incl.contains(&9));
355 assert!(cfg_incl_incl.contains(&10));
356 assert!(!cfg_incl_incl.contains(&11));
357
358 let cfg_excl_incl = RangeCfg {
360 start: Excluded(5),
361 end: Included(10),
362 };
363 assert!(!cfg_excl_incl.contains(&4));
364 assert!(!cfg_excl_incl.contains(&5)); assert!(cfg_excl_incl.contains(&6));
366 assert!(cfg_excl_incl.contains(&10)); assert!(!cfg_excl_incl.contains(&11));
368
369 let cfg_excl_excl = RangeCfg {
371 start: Excluded(5),
372 end: Excluded(10),
373 };
374 assert!(!cfg_excl_excl.contains(&5)); assert!(cfg_excl_excl.contains(&6));
376 assert!(cfg_excl_excl.contains(&9));
377 assert!(!cfg_excl_excl.contains(&10)); }
379
380 #[test]
381 fn test_contains_empty_range() {
382 let cfg_empty_excl: RangeCfg<usize> = (5..5).into();
384 assert!(!cfg_empty_excl.contains(&4));
385 assert!(!cfg_empty_excl.contains(&5));
386 assert!(!cfg_empty_excl.contains(&6));
387
388 #[allow(clippy::reversed_empty_ranges)]
390 let cfg_empty_incl: RangeCfg<usize> = (6..=5).into();
391 assert!(!cfg_empty_incl.contains(&5));
392 assert!(!cfg_empty_incl.contains(&6));
393 }
394
395 #[test]
396 fn test_range_cfg_u8() {
397 let cfg = RangeCfg::new(0u8..=255u8);
399 assert!(cfg.contains(&0));
400 assert!(cfg.contains(&128));
401 assert!(cfg.contains(&255));
402
403 let cfg_partial = RangeCfg::new(10u8..20u8);
404 assert!(!cfg_partial.contains(&9));
405 assert!(cfg_partial.contains(&10));
406 assert!(cfg_partial.contains(&19));
407 assert!(!cfg_partial.contains(&20));
408 }
409
410 #[test]
411 fn test_range_cfg_u16() {
412 let cfg = RangeCfg::new(100u16..=1000u16);
414 assert!(!cfg.contains(&99));
415 assert!(cfg.contains(&100));
416 assert!(cfg.contains(&500));
417 assert!(cfg.contains(&1000));
418 assert!(!cfg.contains(&1001));
419 }
420
421 #[test]
422 fn test_range_cfg_u32() {
423 let cfg = RangeCfg::new(0u32..1024u32);
425 assert!(cfg.contains(&0));
426 assert!(cfg.contains(&512));
427 assert!(!cfg.contains(&1024));
428 assert!(!cfg.contains(&2000));
429 }
430
431 #[test]
432 fn test_range_cfg_u64() {
433 let cfg = RangeCfg::new(1000u64..);
435 assert!(!cfg.contains(&999));
436 assert!(cfg.contains(&1000));
437 assert!(cfg.contains(&u64::MAX));
438 }
439
440 #[test]
441 fn test_type_inference() {
442 let cfg = RangeCfg::new(0u8..10u8);
444 assert!(cfg.contains(&5u8));
445
446 let cfg: RangeCfg<u32> = RangeCfg::new(0..1000);
448 assert!(cfg.contains(&500));
449 }
450}