commonware_utils/
range.rs1use bytes::{Buf, BufMut};
4use commonware_codec::{EncodeSize, Error as CodecError, Read, Write};
5use core::{fmt, ops::Range};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
9#[error("range is empty")]
10pub struct EmptyRange;
11
12#[derive(Clone, PartialEq, Eq, Hash)]
14pub struct NonEmptyRange<Idx>(Range<Idx>);
15
16impl<Idx: fmt::Debug> fmt::Debug for NonEmptyRange<Idx> {
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 self.0.fmt(f)
19 }
20}
21
22impl<Idx: PartialOrd> NonEmptyRange<Idx> {
23 pub fn new(range: Range<Idx>) -> Result<Self, EmptyRange> {
25 (range.start < range.end)
26 .then_some(Self(range))
27 .ok_or(EmptyRange)
28 }
29}
30
31impl<Idx: Copy> NonEmptyRange<Idx> {
32 pub const fn start(&self) -> Idx {
34 self.0.start
35 }
36
37 pub const fn end(&self) -> Idx {
39 self.0.end
40 }
41}
42
43impl<Idx: PartialOrd> TryFrom<Range<Idx>> for NonEmptyRange<Idx> {
44 type Error = EmptyRange;
45
46 fn try_from(range: Range<Idx>) -> Result<Self, Self::Error> {
47 Self::new(range)
48 }
49}
50
51impl<Idx> From<NonEmptyRange<Idx>> for Range<Idx> {
52 fn from(r: NonEmptyRange<Idx>) -> Self {
53 r.0
54 }
55}
56
57impl<Idx> IntoIterator for NonEmptyRange<Idx>
58where
59 Range<Idx>: Iterator,
60{
61 type Item = <Range<Idx> as Iterator>::Item;
62 type IntoIter = Range<Idx>;
63
64 fn into_iter(self) -> Self::IntoIter {
65 self.0
66 }
67}
68
69impl<Idx: Write + PartialOrd> Write for NonEmptyRange<Idx> {
70 #[inline]
71 fn write(&self, buf: &mut impl BufMut) {
72 self.0.write(buf);
73 }
74}
75
76impl<Idx: EncodeSize> EncodeSize for NonEmptyRange<Idx> {
77 #[inline]
78 fn encode_size(&self) -> usize {
79 self.0.encode_size()
80 }
81}
82
83impl<Idx: Read + PartialOrd> Read for NonEmptyRange<Idx> {
84 type Cfg = Idx::Cfg;
85
86 #[inline]
87 fn read_cfg(buf: &mut impl Buf, cfg: &Self::Cfg) -> Result<Self, CodecError> {
88 let range = Range::<Idx>::read_cfg(buf, cfg)?;
89 if !range
90 .start
91 .partial_cmp(&range.end)
92 .is_some_and(|o| o.is_lt())
93 {
94 return Err(CodecError::Invalid("NonEmptyRange", "start must be < end"));
95 }
96 Ok(Self(range))
97 }
98}
99
100#[cfg(feature = "arbitrary")]
101impl<'a, Idx: arbitrary::Arbitrary<'a> + Ord> arbitrary::Arbitrary<'a> for NonEmptyRange<Idx> {
102 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
103 let a = Idx::arbitrary(u)?;
104 let b = Idx::arbitrary(u)?;
105 let (start, end) = if a < b {
106 (a, b)
107 } else if b < a {
108 (b, a)
109 } else {
110 return Err(arbitrary::Error::IncorrectFormat);
111 };
112 Ok(Self(start..end))
113 }
114}
115
116#[macro_export]
118macro_rules! non_empty_range {
119 ($start:expr, $end:expr) => {
120 $crate::range::NonEmptyRange::new($start..$end).expect("range must be non-empty")
121 };
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use commonware_codec::{DecodeExt, Encode};
128
129 #[test]
130 fn test_non_empty_range_valid() {
131 let r = NonEmptyRange::new(0u32..5).unwrap();
132 assert_eq!(r.start(), 0);
133 assert_eq!(r.end(), 5);
134 assert_eq!(Range::from(r), 0..5);
135 }
136
137 #[test]
138 fn test_non_empty_range_single_element() {
139 let r = NonEmptyRange::new(3u32..4).unwrap();
140 assert_eq!(r.start(), 3);
141 assert_eq!(r.end(), 4);
142 }
143
144 #[test]
145 fn test_non_empty_range_empty() {
146 assert_eq!(NonEmptyRange::new(5u32..5), Err(EmptyRange));
147 #[allow(clippy::reversed_empty_ranges)]
148 let reversed = NonEmptyRange::new(5u32..3);
149 assert_eq!(reversed, Err(EmptyRange));
150 }
151
152 #[test]
153 fn test_non_empty_range_into() {
154 let r = NonEmptyRange::new(1u32..10).unwrap();
155 let range: Range<u32> = r.into();
156 assert_eq!(range, 1..10);
157 }
158
159 #[test]
160 fn test_non_empty_range_debug() {
161 let r = NonEmptyRange::new(1u32..5).unwrap();
162 assert_eq!(format!("{r:?}"), "1..5");
163 }
164
165 #[test]
166 fn test_non_empty_range_iter() {
167 let r = NonEmptyRange::new(0u32..4).unwrap();
168 let items: Vec<_> = r.into_iter().collect();
169 assert_eq!(items, vec![0, 1, 2, 3]);
170 }
171
172 #[test]
173 fn test_non_empty_range_encode_decode() {
174 let r = NonEmptyRange::new(10u32..20).unwrap();
175 let encoded = r.encode();
176 let decoded = NonEmptyRange::<u32>::decode(encoded).unwrap();
177 assert_eq!(r, decoded);
178 }
179
180 #[test]
181 fn test_non_empty_range_decode_invalid() {
182 let mut buf = Vec::new();
184 buf.extend_from_slice(&20u32.to_be_bytes());
185 buf.extend_from_slice(&10u32.to_be_bytes());
186 assert!(NonEmptyRange::<u32>::decode(bytes::Bytes::from(buf)).is_err());
187
188 let empty = Range {
190 start: 5u32,
191 end: 5u32,
192 };
193 let encoded = empty.encode();
194 assert!(NonEmptyRange::<u32>::decode(encoded).is_err());
195 }
196
197 #[cfg(feature = "arbitrary")]
198 mod conformance {
199 use super::*;
200 use commonware_codec::conformance::CodecConformance;
201
202 commonware_conformance::conformance_tests! {
203 CodecConformance<NonEmptyRange<u32>>,
204 CodecConformance<NonEmptyRange<u64>>,
205 }
206 }
207}