1use crate::{CollectionError, CollectionResult};
2use alloc::vec::Vec;
3use core::ops::Deref;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct BoundedSet<T, const MIN: usize, const MAX: usize>(Vec<T>);
25
26impl<T, const MIN: usize, const MAX: usize> BoundedSet<T, MIN, MAX> {
27 pub fn len(&self) -> usize {
29 self.0.len()
30 }
31
32 pub fn is_empty(&self) -> bool {
37 self.0.is_empty()
38 }
39
40 pub fn iter(&self) -> core::slice::Iter<'_, T> {
42 self.0.iter()
43 }
44
45 pub fn as_slice(&self) -> &[T] {
47 &self.0
48 }
49
50 pub fn into_inner(self) -> Vec<T> {
52 self.0
53 }
54
55 pub const fn min_len() -> usize {
57 MIN
58 }
59
60 pub const fn max_len() -> usize {
62 MAX
63 }
64}
65
66impl<T: PartialEq, const MIN: usize, const MAX: usize> BoundedSet<T, MIN, MAX> {
67 pub fn new(items: Vec<T>) -> CollectionResult<Self> {
72 if MIN > MAX {
73 return Err(CollectionError::InvalidBounds { min: MIN, max: MAX });
74 }
75 let actual = items.len();
76 if actual < MIN {
77 return Err(CollectionError::TooFew { min: MIN, actual });
78 }
79 if actual > MAX {
80 return Err(CollectionError::TooMany { max: MAX, actual });
81 }
82 for i in 0..items.len() {
83 for j in (i + 1)..items.len() {
84 if items[i] == items[j] {
85 return Err(CollectionError::Duplicate);
86 }
87 }
88 }
89 Ok(Self(items))
90 }
91
92 pub fn insert(&mut self, item: T) -> CollectionResult<bool> {
99 if self.0.contains(&item) {
100 return Ok(false);
101 }
102 if self.0.len() >= MAX {
103 return Err(CollectionError::TooMany {
104 max: MAX,
105 actual: self.0.len().saturating_add(1),
106 });
107 }
108 self.0.push(item);
109 Ok(true)
110 }
111
112 pub fn remove(&mut self, item: &T) -> CollectionResult<bool> {
118 let Some(idx) = self.0.iter().position(|existing| existing == item) else {
119 return Ok(false);
120 };
121 let after_remove = self.0.len() - 1;
122 if after_remove < MIN {
123 return Err(CollectionError::TooFew {
124 min: MIN,
125 actual: after_remove,
126 });
127 }
128 self.0.remove(idx);
129 Ok(true)
130 }
131
132 pub fn contains(&self, item: &T) -> bool {
134 self.0.contains(item)
135 }
136}
137
138impl<T, const MIN: usize, const MAX: usize> Deref for BoundedSet<T, MIN, MAX> {
139 type Target = [T];
140
141 fn deref(&self) -> &Self::Target {
142 self.as_slice()
143 }
144}
145
146impl<T: PartialEq, const MIN: usize, const MAX: usize> TryFrom<Vec<T>> for BoundedSet<T, MIN, MAX> {
147 type Error = CollectionError;
148
149 fn try_from(items: Vec<T>) -> Result<Self, Self::Error> {
150 Self::new(items)
151 }
152}
153
154impl<T, const MIN: usize, const MAX: usize> From<BoundedSet<T, MIN, MAX>> for Vec<T> {
155 fn from(value: BoundedSet<T, MIN, MAX>) -> Self {
156 value.into_inner()
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::BoundedSet;
163 use crate::CollectionError;
164
165 fn set_5() -> BoundedSet<i32, 0, 5> {
166 BoundedSet::new(alloc::vec![1, 2, 3]).unwrap()
167 }
168
169 #[test]
170 fn accepts_unique_elements() {
171 let s = set_5();
172 assert_eq!(s.len(), 3);
173 assert!(!s.is_empty());
174 assert!(s.contains(&2));
175 assert!(!s.contains(&9));
176 }
177
178 #[test]
179 fn rejects_duplicates() {
180 assert_eq!(
181 BoundedSet::<i32, 0, 5>::new(alloc::vec![1, 2, 2]).unwrap_err(),
182 CollectionError::Duplicate
183 );
184 }
185
186 #[test]
187 fn rejects_too_few() {
188 assert_eq!(
189 BoundedSet::<i32, 2, 5>::new(alloc::vec![1]).unwrap_err(),
190 CollectionError::TooFew { min: 2, actual: 1 }
191 );
192 }
193
194 #[test]
195 fn rejects_too_many() {
196 assert_eq!(
197 BoundedSet::<i32, 0, 2>::new(alloc::vec![1, 2, 3]).unwrap_err(),
198 CollectionError::TooMany { max: 2, actual: 3 }
199 );
200 }
201
202 #[test]
203 fn rejects_invalid_bounds() {
204 assert_eq!(
205 BoundedSet::<i32, 5, 3>::new(alloc::vec![]).unwrap_err(),
206 CollectionError::InvalidBounds { min: 5, max: 3 }
207 );
208 }
209
210 #[test]
211 fn insert_new_element() {
212 let mut s = set_5();
213 assert!(s.insert(4).unwrap());
214 assert_eq!(s.len(), 4);
215 assert!(s.contains(&4));
216 }
217
218 #[test]
219 fn insert_existing_element_is_noop() {
220 let mut s = set_5();
221 assert!(!s.insert(2).unwrap());
222 assert_eq!(s.len(), 3);
223 }
224
225 #[test]
226 fn insert_at_capacity_new_element_errors() {
227 let mut s = BoundedSet::<i32, 0, 3>::new(alloc::vec![1, 2, 3]).unwrap();
228 assert_eq!(
229 s.insert(4).unwrap_err(),
230 CollectionError::TooMany { max: 3, actual: 4 }
231 );
232 assert!(!s.insert(1).unwrap());
234 }
235
236 #[test]
237 fn remove_present_element() {
238 let mut s = set_5();
239 assert!(s.remove(&2).unwrap());
240 assert_eq!(s.len(), 2);
241 assert!(!s.contains(&2));
242 }
243
244 #[test]
245 fn remove_absent_element_is_noop() {
246 let mut s = set_5();
247 assert!(!s.remove(&9).unwrap());
248 assert_eq!(s.len(), 3);
249 }
250
251 #[test]
252 fn remove_below_minimum_errors() {
253 let mut s = BoundedSet::<i32, 2, 5>::new(alloc::vec![1, 2]).unwrap();
254 assert_eq!(
255 s.remove(&1).unwrap_err(),
256 CollectionError::TooFew { min: 2, actual: 1 }
257 );
258 assert_eq!(s.len(), 2); }
260
261 #[test]
262 fn iter_in_insertion_order() {
263 let s = set_5();
264 let collected: alloc::vec::Vec<&i32> = s.iter().collect();
265 assert_eq!(collected, alloc::vec![&1, &2, &3]);
266 }
267
268 #[test]
269 fn deref_to_slice() {
270 let s = set_5();
271 assert_eq!(&s[..], &[1, 2, 3]);
272 }
273
274 #[test]
275 fn into_inner_and_from() {
276 let s = set_5();
277 let inner: alloc::vec::Vec<i32> = alloc::vec::Vec::from(s);
278 assert_eq!(inner, alloc::vec![1, 2, 3]);
279 }
280
281 #[test]
282 fn try_from_vec() {
283 assert!(BoundedSet::<i32, 1, 3>::try_from(alloc::vec![1, 2]).is_ok());
284 assert_eq!(
285 BoundedSet::<i32, 1, 3>::try_from(alloc::vec![1, 1]).unwrap_err(),
286 CollectionError::Duplicate
287 );
288 }
289
290 #[test]
291 fn min_equals_max_exact_size() {
292 assert!(BoundedSet::<i32, 3, 3>::new(alloc::vec![1, 2, 3]).is_ok());
293 assert!(BoundedSet::<i32, 3, 3>::new(alloc::vec![1, 2]).is_err());
294 }
295
296 #[test]
297 fn min_max_len_constants() {
298 assert_eq!(BoundedSet::<i32, 2, 8>::min_len(), 2);
299 assert_eq!(BoundedSet::<i32, 2, 8>::max_len(), 8);
300 }
301}