1use crate::{CollectionError, CollectionResult};
2use alloc::vec::Vec;
3use core::mem;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct BoundedMap<K, V, const MIN: usize, const MAX: usize>(Vec<(K, V)>);
30
31impl<K, V, const MIN: usize, const MAX: usize> BoundedMap<K, V, MIN, MAX> {
32 pub fn len(&self) -> usize {
34 self.0.len()
35 }
36
37 pub fn is_empty(&self) -> bool {
42 self.0.is_empty()
43 }
44
45 pub fn iter(&self) -> core::slice::Iter<'_, (K, V)> {
47 self.0.iter()
48 }
49
50 pub fn keys(&self) -> impl Iterator<Item = &K> {
52 self.0.iter().map(|(k, _)| k)
53 }
54
55 pub fn values(&self) -> impl Iterator<Item = &V> {
57 self.0.iter().map(|(_, v)| v)
58 }
59
60 pub fn as_slice(&self) -> &[(K, V)] {
62 &self.0
63 }
64
65 pub fn into_inner(self) -> Vec<(K, V)> {
67 self.0
68 }
69
70 pub const fn min_len() -> usize {
72 MIN
73 }
74
75 pub const fn max_len() -> usize {
77 MAX
78 }
79}
80
81impl<K: PartialEq, V, const MIN: usize, const MAX: usize> BoundedMap<K, V, MIN, MAX> {
82 pub fn new(entries: Vec<(K, V)>) -> CollectionResult<Self> {
87 if MIN > MAX {
88 return Err(CollectionError::InvalidBounds { min: MIN, max: MAX });
89 }
90 let actual = entries.len();
91 if actual < MIN {
92 return Err(CollectionError::TooFew { min: MIN, actual });
93 }
94 if actual > MAX {
95 return Err(CollectionError::TooMany { max: MAX, actual });
96 }
97 for i in 0..entries.len() {
98 for j in (i + 1)..entries.len() {
99 if entries[i].0 == entries[j].0 {
100 return Err(CollectionError::Duplicate);
101 }
102 }
103 }
104 Ok(Self(entries))
105 }
106
107 pub fn insert(&mut self, key: K, value: V) -> CollectionResult<Option<V>> {
115 if let Some(entry) = self.0.iter_mut().find(|entry| entry.0 == key) {
116 return Ok(Some(mem::replace(&mut entry.1, value)));
117 }
118 if self.0.len() >= MAX {
119 return Err(CollectionError::TooMany {
120 max: MAX,
121 actual: self.0.len().saturating_add(1),
122 });
123 }
124 self.0.push((key, value));
125 Ok(None)
126 }
127
128 pub fn remove(&mut self, key: &K) -> CollectionResult<Option<V>> {
134 let Some(idx) = self.0.iter().position(|entry| &entry.0 == key) else {
135 return Ok(None);
136 };
137 let after_remove = self.0.len() - 1;
138 if after_remove < MIN {
139 return Err(CollectionError::TooFew {
140 min: MIN,
141 actual: after_remove,
142 });
143 }
144 let (_, value) = self.0.remove(idx);
145 Ok(Some(value))
146 }
147
148 pub fn get(&self, key: &K) -> Option<&V> {
150 self.0
151 .iter()
152 .find(|entry| &entry.0 == key)
153 .map(|entry| &entry.1)
154 }
155
156 pub fn get_mut(&mut self, key: &K) -> Option<&mut V> {
158 self.0
159 .iter_mut()
160 .find(|entry| &entry.0 == key)
161 .map(|entry| &mut entry.1)
162 }
163
164 pub fn contains_key(&self, key: &K) -> bool {
166 self.0.iter().any(|entry| &entry.0 == key)
167 }
168}
169
170impl<K: PartialEq, V, const MIN: usize, const MAX: usize> TryFrom<Vec<(K, V)>>
171 for BoundedMap<K, V, MIN, MAX>
172{
173 type Error = CollectionError;
174
175 fn try_from(entries: Vec<(K, V)>) -> Result<Self, Self::Error> {
176 Self::new(entries)
177 }
178}
179
180impl<K, V, const MIN: usize, const MAX: usize> From<BoundedMap<K, V, MIN, MAX>> for Vec<(K, V)> {
181 fn from(value: BoundedMap<K, V, MIN, MAX>) -> Self {
182 value.into_inner()
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::BoundedMap;
189 use crate::CollectionError;
190
191 fn map_3() -> BoundedMap<&'static str, i32, 0, 3> {
192 BoundedMap::new(alloc::vec![("a", 1), ("b", 2)]).unwrap()
193 }
194
195 #[test]
196 fn accepts_valid_entries() {
197 let m = map_3();
198 assert_eq!(m.len(), 2);
199 assert!(!m.is_empty());
200 assert_eq!(m.get(&"a"), Some(&1));
201 assert_eq!(m.get(&"b"), Some(&2));
202 assert_eq!(m.get(&"z"), None);
203 }
204
205 #[test]
206 fn rejects_duplicate_keys() {
207 assert_eq!(
208 BoundedMap::<&str, i32, 0, 5>::new(alloc::vec![("a", 1), ("a", 2)]).unwrap_err(),
209 CollectionError::Duplicate
210 );
211 }
212
213 #[test]
214 fn rejects_too_few() {
215 assert_eq!(
216 BoundedMap::<&str, i32, 2, 5>::new(alloc::vec![("a", 1)]).unwrap_err(),
217 CollectionError::TooFew { min: 2, actual: 1 }
218 );
219 }
220
221 #[test]
222 fn rejects_too_many() {
223 assert_eq!(
224 BoundedMap::<&str, i32, 0, 1>::new(alloc::vec![("a", 1), ("b", 2)]).unwrap_err(),
225 CollectionError::TooMany { max: 1, actual: 2 }
226 );
227 }
228
229 #[test]
230 fn rejects_invalid_bounds() {
231 assert_eq!(
232 BoundedMap::<&str, i32, 5, 3>::new(alloc::vec![]).unwrap_err(),
233 CollectionError::InvalidBounds { min: 5, max: 3 }
234 );
235 }
236
237 #[test]
238 fn insert_new_key_appends() {
239 let mut m = map_3();
240 assert_eq!(m.insert("c", 3).unwrap(), None);
241 assert_eq!(m.len(), 3);
242 assert_eq!(m.get(&"c"), Some(&3));
243 }
244
245 #[test]
246 fn insert_existing_key_replaces_value() {
247 let mut m = map_3();
248 assert_eq!(m.insert("a", 99).unwrap(), Some(1));
249 assert_eq!(m.len(), 2); assert_eq!(m.get(&"a"), Some(&99));
251 }
252
253 #[test]
254 fn insert_at_capacity_new_key_errors() {
255 let mut m = BoundedMap::<&str, i32, 0, 2>::new(alloc::vec![("a", 1), ("b", 2)]).unwrap();
256 assert_eq!(
257 m.insert("c", 3).unwrap_err(),
258 CollectionError::TooMany { max: 2, actual: 3 }
259 );
260 assert_eq!(m.insert("a", 10).unwrap(), Some(1));
262 }
263
264 #[test]
265 fn remove_existing_key_returns_value() {
266 let mut m = map_3();
267 assert_eq!(m.remove(&"a").unwrap(), Some(1));
268 assert_eq!(m.len(), 1);
269 assert_eq!(m.get(&"a"), None);
270 }
271
272 #[test]
273 fn remove_absent_key_is_noop() {
274 let mut m = map_3();
275 assert_eq!(m.remove(&"z").unwrap(), None);
276 assert_eq!(m.len(), 2);
277 }
278
279 #[test]
280 fn remove_below_minimum_errors() {
281 let mut m = BoundedMap::<&str, i32, 2, 5>::new(alloc::vec![("a", 1), ("b", 2)]).unwrap();
282 assert_eq!(
283 m.remove(&"a").unwrap_err(),
284 CollectionError::TooFew { min: 2, actual: 1 }
285 );
286 assert_eq!(m.len(), 2); }
288
289 #[test]
290 fn get_mut_updates_in_place() {
291 let mut m = map_3();
292 *m.get_mut(&"b").unwrap() += 10;
293 assert_eq!(m.get(&"b"), Some(&12));
294 assert!(m.get_mut(&"z").is_none());
295 }
296
297 #[test]
298 fn contains_key_works() {
299 let m = map_3();
300 assert!(m.contains_key(&"a"));
301 assert!(!m.contains_key(&"z"));
302 }
303
304 #[test]
305 fn keys_values_iter_in_insertion_order() {
306 let m = map_3();
307 let keys: alloc::vec::Vec<&&str> = m.keys().collect();
308 assert_eq!(keys, alloc::vec![&"a", &"b"]);
309 let values: alloc::vec::Vec<&i32> = m.values().collect();
310 assert_eq!(values, alloc::vec![&1, &2]);
311 let entries: alloc::vec::Vec<&(&str, i32)> = m.iter().collect();
312 assert_eq!(entries, alloc::vec![&("a", 1), &("b", 2)]);
313 }
314
315 #[test]
316 fn into_inner_and_from() {
317 let m = map_3();
318 let inner: alloc::vec::Vec<(&str, i32)> = m.into_inner();
319 assert_eq!(inner, alloc::vec![("a", 1), ("b", 2)]);
320 }
321
322 #[test]
323 fn try_from_vec() {
324 assert!(BoundedMap::<&str, i32, 1, 3>::try_from(alloc::vec![("a", 1)]).is_ok());
325 assert_eq!(
326 BoundedMap::<&str, i32, 1, 3>::try_from(alloc::vec![("a", 1), ("a", 2)]).unwrap_err(),
327 CollectionError::Duplicate
328 );
329 }
330
331 #[test]
332 fn min_equals_max_exact_size() {
333 assert!(BoundedMap::<&str, i32, 2, 2>::new(alloc::vec![("a", 1), ("b", 2)]).is_ok());
334 assert!(BoundedMap::<&str, i32, 2, 2>::new(alloc::vec![("a", 1)]).is_err());
335 }
336
337 #[test]
338 fn equality_is_order_sensitive() {
339 let a = BoundedMap::<&str, i32, 0, 3>::new(alloc::vec![("a", 1), ("b", 2)]).unwrap();
340 let b = BoundedMap::<&str, i32, 0, 3>::new(alloc::vec![("b", 2), ("a", 1)]).unwrap();
341 assert_ne!(a, b);
342 }
343
344 #[test]
345 fn min_max_len_constants() {
346 assert_eq!(BoundedMap::<&str, i32, 2, 8>::min_len(), 2);
347 assert_eq!(BoundedMap::<&str, i32, 2, 8>::max_len(), 8);
348 }
349}