tor_basic_utils/n_key_list.rs
1//! Declaration for an n-keyed list type, allowing access to each of its members by each of N
2//! different keys.
3
4// Re-export dependencies that we use to make this macro work.
5#[doc(hidden)]
6pub mod deps {
7 pub use paste::paste;
8 pub use slab::Slab;
9 pub use smallvec::SmallVec;
10}
11
12/// Declare a structure that can hold elements with multiple unique keys.
13///
14/// Each element can be looked up by any of its keys. The keys themselves can be any type that
15/// supports `Hash`, `Eq`, and `Clone`. Elements can have multiple keys of the same type: for
16/// example, a person can have a username `String` and an irc_handle `String`.
17///
18/// Multiple values can be stored for a given key: a lookup of one key returns all elements with
19/// that key.
20///
21/// Keys may be accessed from elements either by field access or by an accessor function.
22///
23/// Keys may be optional. If all keys are optional, then we require additionally that every element
24/// must have at least one key.
25///
26/// # Examples
27///
28/// ```
29/// use tor_basic_utils::n_key_list;
30///
31/// // We declare a person struct with several different fields.
32/// pub struct Person {
33/// username: String,
34/// irc_handle: String,
35/// student_id: Option<u64>,
36/// favorite_joke: Option<String>,
37/// }
38///
39/// n_key_list! {
40/// pub struct PersonList for Person {
41/// // See note on "Key syntax" below. The ".foo" syntax
42/// // here means that the value for the key is returned
43/// // by accessing a given field.
44/// username: String { .username },
45/// irc_handle: String { .irc_handle },
46/// (Option) student_id: u64 { .student_id }
47/// }
48/// }
49///
50/// let mut people = PersonList::new();
51/// people.insert(Person {
52/// username: "mina".into(),
53/// irc_handle: "pashMina".into(),
54/// student_id: None,
55/// favorite_joke: None,
56/// });
57/// assert_eq!(people.by_username("mina").len(), 1);
58/// assert_eq!(people.by_irc_handle("pashMina").len(), 1);
59/// ```
60///
61/// # Key syntax
62///
63/// You can tell the map to access the keys of an element in any of several ways.
64///
65/// * `name : type { func() }` - A key whose name is `name` and type is `type`, that can be accessed
66/// from a given element by calling `element.func()`.
67/// * `name : type { .field }` - A key whose name is `name` and type is `type`, that can be accessed
68/// from a given element by calling `&element.field`.
69/// * `name : type` - Short for as `name : type { name() }`.
70///
71/// If a key declaration is preceded with `(Option)`, then the key is treated as optional, and
72/// accessor functions are expected to return `Option<&Type>`.
73///
74/// # Additional features
75///
76/// You can put generic parameters and `where` constraints on your structure. The `where` clause (if
77/// present) must be wrapped in square brackets.
78///
79/// If you need to use const generics or lifetimes in your structure, you need to use square
80/// brackets instead of angle brackets, and specify both the generic parameters *and* the type that
81/// you are implementing. (This is due to limitations in the Rust macro system.) For example:
82///
83/// ```
84/// # use tor_basic_utils::n_key_list;
85/// n_key_list!{
86/// struct['a, T, const N: usize] ArrayMap2['a, T, N] for (String, [&'a T;N])
87/// [ where T: Clone + 'a ]
88/// {
89/// name: String { .0 }
90/// }
91/// }
92/// ```
93#[macro_export]
94macro_rules! n_key_list {
95{
96 $(#[$meta:meta])*
97 $vis:vis struct $mapname:ident $(<$($P:ident),*>)? for $V:ty
98 $( where [ $($constr:tt)+ ] )?
99 {
100 $($body:tt)+
101 }
102} => {
103n_key_list!{
104 $(#[$meta])*
105 $vis struct [$($($P),*)?] $mapname [$($($P),*)?] for $V
106 $( [ where $($constr)+ ] )?
107 {
108 $( $body )+
109 }
110}
111};
112{
113 $(#[$meta:meta])*
114 $vis:vis struct [$($($G:tt)+)?] $mapname:ident [$($($P:tt)+)?] for $V:ty
115 $( [ where $($constr:tt)+ ])?
116 {
117 $( $(( $($flag:ident)+ ))? $key:ident : $KEY:ty $({ $($source:tt)+ })? ),+
118 $(,)?
119 }
120} => {
121$crate::n_key_list::deps::paste!{
122 $( #[$meta] )*
123 /// # General information
124 ///
125 #[doc = concat!(
126 "A list of elements of type `", stringify!($V), "` whose members can be accessed by multiple keys."
127 )]
128 ///
129 /// The keys are:
130 ///
131 #[doc = $( "- `" $key "` (`" $KEY "`)" $(" (" $($flag)+ ")\n" )? )+]
132 ///
133 /// Each element has a value for *each* required key, and up to one value for *each* optional
134 /// key. There can be many elements for a given key value.
135 ///
136 /// ## Requirements
137 ///
138 /// Key types must have consistent `Hash` and `Eq` implementations, as they will be used as keys
139 /// in a `HashMap`.
140 ///
141 /// If all keys are optional, then every element inserted must have at least one non-`None` key.
142 ///
143 /// An element must not change its keys over time through interior mutability.
144 ///
145 /// <div class='warning'>
146 ///
147 /// If *any* of these rules is violated, the consequences are unspecified, and could include
148 /// panics or wrong answers (but not memory-unsafety).
149 ///
150 /// </div>
151 $vis struct $mapname $(<$($G)*>)?
152 where
153 $( $KEY : std::hash::Hash + Eq + Clone , )+
154 $($($constr)+, )?
155 {
156 /// The $key fields here are a set of maps from each of the key values to the lists of the
157 /// positions of values with the same key within the Slab.
158 ///
159 /// Invariants:
160 /// - There is an entry K=>idx in the map `$key` if and only if values[idx].$accessor() ==
161 /// K.
162 /// - Every value in `values` has at least one key.
163 /// - A list should never be empty.
164 ///
165 /// The map values (the lists) are effectively a set, but using an inline vec should have
166 /// better cache performance than something like HashSet.
167 ///
168 /// The SmallVec size of 4 was chosen arbitrarily under the assumption that a given key will
169 /// have a small number of values on average. The exact constant probably won't matter, but
170 /// inlining most of the lists should be good even if some spill into separate memory
171 /// allocations. It's not worth exposing this level of internal detail to the macro caller
172 /// unless there's a reason we need to.
173 $([<$key _map>]: std::collections::HashMap<$KEY, $crate::n_key_list::deps::SmallVec<[usize; 4]>> , )+
174
175 /// A map from the indices to the values.
176 values: $crate::n_key_list::deps::Slab<$V>,
177 }
178
179 #[allow(dead_code)] // may be needed if this is not public
180 impl $(<$($G)*>)? $mapname $(<$($P)*>)?
181 where
182 $( $KEY : std::hash::Hash + Eq + Clone , )+
183 $($($constr)+)?
184 {
185 #[doc = "Construct a new [`" $mapname "`](Self)."]
186 $vis fn new() -> Self {
187 Self::with_capacity(0)
188 }
189
190 #[doc = "Construct a new [`" $mapname "`](Self) with a given capacity."]
191 $vis fn with_capacity(n: usize) -> Self {
192 Self {
193 $([<$key _map>]: std::collections::HashMap::with_capacity(n),)*
194 values: $crate::n_key_list::deps::Slab::with_capacity(n),
195 }
196 }
197
198 // for each key type
199 $(
200 #[doc = "Return an iterator of the elements whose `" $key "` is `key`."]
201 ///
202 /// The iteration order is arbitrary.
203 $vis fn [<by_ $key>] <BorrowAsKey_>(&self, key: &BorrowAsKey_) -> [<$mapname Iter>] <'_, $V>
204 where
205 $KEY : std::borrow::Borrow<BorrowAsKey_>,
206 BorrowAsKey_: std::hash::Hash + Eq + ?Sized,
207 {
208 [<$mapname Iter>] {
209 iter: self.[<$key _map>].get(key).map(|set| set.iter()).unwrap_or([].iter()),
210 values: &self.values,
211 }
212 }
213
214 #[doc = "Return `true` if this list contains an element whose `" $key "` is `key`."]
215 $vis fn [<contains_ $key>] <BorrowAsKey_>(&mut self, key: &BorrowAsKey_) -> bool
216 where
217 $KEY : std::borrow::Borrow<BorrowAsKey_>,
218 BorrowAsKey_: std::hash::Hash + Eq + ?Sized,
219 {
220 let Some(list) = self.[<$key _map>].get(key) else {
221 return false;
222 };
223
224 if list.is_empty() {
225 // we're not supposed to let this happen, so panic in debug builds
226 #[cfg(debug_assertions)]
227 panic!("Should not have an empty list");
228 #[cfg(not(debug_assertions))]
229 return false;
230 }
231
232 true
233 }
234
235 #[doc = "Remove and return the elements whose `" $key "` is `key`"]
236 /// and where `filter` returns `true`.
237 $vis fn [<remove_by_ $key>] <BorrowAsKey_>(
238 &mut self,
239 key: &BorrowAsKey_,
240 mut filter: impl FnMut(&$V) -> bool,
241 ) -> Vec<$V>
242 where
243 $KEY : std::borrow::Borrow<BorrowAsKey_>,
244 BorrowAsKey_: std::hash::Hash + Eq + ?Sized,
245 {
246 let idx_list: Vec<usize> = {
247 let Some(set) = self.[<$key _map>].get(key) else {
248 return Vec::new();
249 };
250
251 set
252 .iter()
253 .filter(|&&idx| filter(self.values.get(idx).expect("inconsistent state")))
254 .copied()
255 .collect()
256 };
257
258 let mut removed = Vec::with_capacity(idx_list.len());
259 for idx in idx_list {
260 removed.push(self.remove_at(idx).expect("inconsistent state"));
261 }
262
263 removed
264 }
265 )+
266
267 fn remove_at(&mut self, idx: usize) -> Option<$V> {
268 if let Some(removed) = self.values.try_remove(idx) {
269 $(
270 let $key = $crate::n_key_list!( @access(removed, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
271 if let Some($key) = $key {
272 let set = self.[<$key _map>].get_mut($key).expect("inconsistent state");
273
274 #[cfg(debug_assertions)]
275 let size_before_remove = set.len();
276
277 // a `swap_retain` if it existed might be nice here, but the set should be small
278 // so shifting all later elements should be fine
279 set.retain(|x| *x != idx);
280
281 #[cfg(debug_assertions)]
282 assert_ne!(set.len(), size_before_remove, "should have removed at least one element");
283
284 // don't leave entries around with empty lists
285 if set.is_empty() {
286 self.[<$key _map>].remove($key);
287 }
288 }
289 )*
290 Some(removed)
291 } else {
292 None
293 }
294 }
295
296 /// Return an iterator over the elements in this container.
297 $vis fn values(&self) -> impl Iterator<Item=&$V> + '_ {
298 self.values.iter().map(|(_, v)| v)
299 }
300
301 /// Consume this container and return an iterator of its values.
302 $vis fn into_values(self) -> impl Iterator<Item=$V> {
303 self.values.into_iter().map(|(_, v)| v)
304 }
305
306 /// Try to insert `value`.
307 ///
308 /// Return `Error::NoKeys` if all the keys are optional, and `value` has no keys at all.
309 $vis fn try_insert(&mut self, value: $V) -> Result<(), $crate::n_key_list::Error> {
310 if self.capacity() > 32 && self.len() < self.capacity() / 4 {
311 // we have the opportunity to free up a fair amount of space; let's take it
312 self.compact()
313 }
314
315 let mut some_key_found = false;
316
317 $(
318 let $key = $crate::n_key_list!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
319 some_key_found |= $key.is_some();
320 )*
321
322 if !some_key_found {
323 // exit early before we add it to `values`
324 return Err($crate::n_key_list::Error::NoKeys);
325 }
326
327 let idx = self.values.insert(value);
328 let value = self.values.get(idx).expect("inconsistent state");
329
330 $(
331 let $key = $crate::n_key_list!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
332 if let Some($key) = $key {
333 let set = self.[<$key _map>].entry($key.to_owned()).or_default();
334 set.push(idx);
335
336 // we don't want the list's capacity to grow unbounded, so in the (hopefully) rare
337 // case that the list grows large and then small again, try to free some of the
338 // memory
339 if set.capacity() > 64 && set.len() < set.capacity() / 4 {
340 set.shrink_to_fit();
341 }
342
343 // TODO: would it be beneficial to aggressively shrink the list if `len()` is
344 // smaller than `inline_size()`?
345 }
346 )*
347
348 Ok(())
349 }
350
351 /// See [`try_insert`](Self::try_insert). Panicks on errors.
352 $vis fn insert(&mut self, value: $V) {
353 self.try_insert(value)
354 .expect("tried to add a value with no key")
355 }
356
357 /// Return the number of elements in this container.
358 $vis fn len(&self) -> usize {
359 self.values.len()
360 }
361
362 /// Return `true` if there are no elements in this container.
363 $vis fn is_empty(&self) -> bool {
364 let is_empty = self.len() == 0;
365
366 #[cfg(debug_assertions)]
367 if is_empty {
368 $(assert!(self.[<$key _map>].is_empty());)*
369 }
370
371 is_empty
372 }
373
374 /// Return the number of elements for which this container has allocated storage.
375 $vis fn capacity(&self) -> usize {
376 self.values.capacity()
377 }
378
379 /// Remove every element that does not satisfy the predicate `pred`.
380 $vis fn retain<F>(&mut self, mut pred: F)
381 where
382 F: FnMut(&$V) -> bool,
383 {
384 for idx in 0..self.values.capacity() {
385 if self.values.get(idx).map(&mut pred) == Some(false) {
386 self.remove_at(idx);
387 }
388 }
389 }
390
391 /// An empty iterator.
392 ///
393 /// **NOTE:** This function is weird and will be removed in the future. We can fix this once
394 /// we support a minimum rust version of 1.79.
395 // TODO: The problem is that we need to assign `values` some value. In rust 1.79 we can just
396 // use a constant expression, but without constant expressions, there's no way to get a
397 // reference to a `Slab` with the generic types of `$V`. Once we support a minimum rust
398 // version of 1.79, remove this function and uncomment the `Default` impl for the iterator
399 // below.
400 #[deprecated]
401 $vis fn empty_iterator(&self) -> [<$mapname Iter>] <'_, $V> {
402 [<$mapname Iter>] {
403 iter: [].iter(),
404 values: &self.values,
405 }
406 }
407
408 /// Re-index all the values in this map, so that the map can use a more compact
409 /// representation.
410 ///
411 /// This should be done infrequently; it's expensive.
412 fn compact(&mut self) {
413 let old_value = std::mem::replace(self, Self::with_capacity(self.len()));
414 for item in old_value.into_values() {
415 self.insert(item);
416 }
417 }
418
419 /// Assert that this list appears to be in an internally consistent state.
420 ///
421 /// This method can be very expensive, and it should never fail unless your code has a bug.
422 ///
423 /// # Panics
424 ///
425 /// Panics if it finds bugs in this object, or constraint violations in its elements. See
426 /// the (type documentation)[Self#Requirements] for a list of constraints.
427 // it would be nice to run this after every operation that mutates internal state in debug
428 // builds, but this function is way too slow for that
429 fn check_consistency(&self) {
430 // ensure each value is in exactly the correct maps
431 for (idx, value) in &self.values {
432 $(
433 let $key = $crate::n_key_list!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
434 if let Some($key) = $key {
435 // check that it exists in the set that it should be in
436 let set = self.[<$key _map>].get($key).expect("inconsistent state");
437 assert!(set.contains(&idx));
438 // check that it does not exist in any set that it should not be in
439 for (_key, set) in self.[<$key _map>].iter().filter(|(key, _)| *key != $key) {
440 assert!(!set.contains(&idx));
441 }
442 } else {
443 // check that it does not exist in any set
444 for set in self.[<$key _map>].values() {
445 assert!(!set.contains(&idx));
446 }
447 }
448 )*
449 }
450
451 $(
452 for set in self.[<$key _map>].values() {
453 // ensure no sets have dangling idxs
454 for idx in set {
455 assert!(self.values.contains(*idx));
456 }
457
458 // ensure no sets have duplicate idxs
459 let mut set_iter = set.iter();
460 while let Some(idx) = set_iter.next() {
461 assert!(!set_iter.clone().any(|x| x == idx));
462 }
463
464 // ensure no sets are empty
465 assert!(!set.is_empty());
466 }
467 )*
468
469 // ensure that if a value is in a key's map, then the value really has that key
470 $(
471 for (key, set) in &self.[<$key _map>] {
472 for idx in set {
473 let value = self.values.get(*idx).expect("inconsistent state");
474 let $key = $crate::n_key_list!( @access(value, ($($($flag)+)?) $key : $KEY $({$($source)+})?) );
475 let $key = $key.expect("inconsistent state");
476 assert!(key == $key);
477 }
478 }
479 )*
480 }
481 }
482
483 impl $(<$($G)*>)? Default for $mapname $(<$($P)*>)?
484 where
485 $( $KEY : std::hash::Hash + Eq + Clone , )+
486 $($($constr)+)?
487 {
488 fn default() -> Self {
489 $mapname::new()
490 }
491 }
492
493 impl $(<$($G)*>)? std::iter::FromIterator<$V> for $mapname $(<$($P)*>)?
494 where
495 $( $KEY : std::hash::Hash + Eq + Clone , )*
496 $($($constr)+)?
497 {
498 fn from_iter<IntoIter_>(iter: IntoIter_) -> Self
499 where
500 IntoIter_: std::iter::IntoIterator<Item = $V>,
501 {
502 let iter = iter.into_iter();
503 let mut list = Self::with_capacity(iter.size_hint().0);
504 for value in iter {
505 list.insert(value);
506 }
507 list
508 }
509 }
510
511 #[doc = "An iterator for [`" $mapname "`](" $mapname ")."]
512 $vis struct [<$mapname Iter>] <'a, T> {
513 iter: std::slice::Iter<'a, usize>,
514 values: &'a $crate::n_key_list::deps::Slab<T>,
515 }
516
517 impl<'a, T> std::iter::Iterator for [<$mapname Iter>] <'a, T> {
518 type Item = &'a T;
519
520 fn next(&mut self) -> std::option::Option<Self::Item> {
521 self.iter.next().map(|idx| self.values.get(*idx).expect("inconsistent state"))
522 }
523
524 #[inline]
525 fn size_hint(&self) -> (usize, std::option::Option<usize>) {
526 self.iter.size_hint()
527 }
528 }
529
530 impl<'a, T> std::iter::ExactSizeIterator for [<$mapname Iter>] <'a, T>
531 where
532 // no harm in specifying it here, even though it should always be true
533 std::slice::Iter<'a, usize>: std::iter::ExactSizeIterator,
534 {
535 #[inline]
536 fn len(&self) -> usize {
537 self.iter.len()
538 }
539 }
540
541 // TODO: see comments on 'empty_iterator' above
542 /*
543 impl<'a, T> std::default::Default for [<$mapname Iter>] <'a, T> {
544 fn default() -> Self {
545 [<$mapname Iter>] {
546 iter: [].iter(),
547 values: const { &$crate::n_key_list::deps::Slab::new() },
548 }
549 }
550 }
551 */
552}
553};
554
555// Helper: Generate an expression to access a specific key and return an `Option<&TYPE>` for that
556// key. This is the part of the macro that parses key descriptions.
557
558{ @access($ex:expr, (Option) $key:ident : $t:ty ) } => {
559 $ex.key()
560};
561{ @access($ex:expr, () $key:ident : $t:ty) } => {
562 Some($ex.key())
563};
564{ @access($ex:expr, (Option) $key:ident : $t:ty { . $field:tt } ) } => {
565 $ex.$field.as_ref()
566};
567{ @access($ex:expr, () $key:ident : $t:ty { . $field:tt } ) } => {
568 Some(&$ex.$field)
569};
570{ @access($ex:expr, (Option) $key:ident : $t:ty { $func:ident () } ) } => {
571 $ex.$func()
572};
573{ @access($ex:expr, () $key:ident : $t:ty { $func:ident () } ) } => {
574 Some($ex.$func())
575};
576}
577
578/// An error returned from an operation on an [`n_key_list`].
579#[derive(Clone, Debug, thiserror::Error)]
580#[non_exhaustive]
581pub enum Error {
582 /// We tried to insert a value into a set where all keys were optional, but every key on that
583 /// value was `None`.
584 #[error("Tried to insert a value with no keys")]
585 NoKeys,
586}
587
588#[cfg(test)]
589mod test {
590 // @@ begin test lint list maintained by maint/add_warning @@
591 #![allow(clippy::bool_assert_comparison)]
592 #![allow(clippy::clone_on_copy)]
593 #![allow(clippy::dbg_macro)]
594 #![allow(clippy::mixed_attributes_style)]
595 #![allow(clippy::print_stderr)]
596 #![allow(clippy::print_stdout)]
597 #![allow(clippy::single_char_pattern)]
598 #![allow(clippy::unwrap_used)]
599 #![allow(clippy::unchecked_duration_subtraction)]
600 #![allow(clippy::useless_vec)]
601 #![allow(clippy::needless_pass_by_value)]
602 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
603
604 fn sort<T: std::cmp::Ord>(i: impl Iterator<Item = T>) -> Vec<T> {
605 let mut v: Vec<_> = i.collect();
606 v.sort();
607 v
608 }
609
610 n_key_list! {
611 #[derive(Clone, Debug)]
612 struct Tuple2List<A,B> for (A,B) {
613 first: A { .0 },
614 second: B { .1 },
615 }
616 }
617
618 #[test]
619 #[allow(clippy::cognitive_complexity)]
620 fn basic() {
621 let mut list = Tuple2List::new();
622 assert!(list.is_empty());
623
624 // add a single element and do some sanity checks
625 list.insert((0_u32, 99_u16));
626 assert_eq!(list.len(), 1);
627 assert_eq!(list.contains_first(&0), true);
628 assert_eq!(list.contains_second(&99), true);
629 assert_eq!(list.contains_first(&99), false);
630 assert_eq!(list.contains_second(&0), false);
631 assert_eq!(sort(list.by_first(&0)), [&(0, 99)]);
632 assert_eq!(sort(list.by_second(&99)), [&(0, 99)]);
633 assert_eq!(list.by_first(&99).len(), 0);
634 assert_eq!(list.by_second(&0).len(), 0);
635 list.check_consistency();
636
637 // lookup by a key that has never existed in the map
638 assert_eq!(list.by_first(&1000000).len(), 0);
639
640 // inserting the same element again should add it to the list
641 assert_eq!(list.len(), 1);
642 list.insert((0_u32, 99_u16));
643 assert_eq!(list.len(), 2);
644 list.check_consistency();
645
646 // add two new entries
647 list.insert((12, 34));
648 list.insert((0, 34));
649 assert_eq!(list.len(), 4);
650 assert!(list.capacity() >= 4);
651 assert_eq!(sort(list.by_first(&0)), [&(0, 34), &(0, 99), &(0, 99)]);
652 assert_eq!(sort(list.by_first(&12)), [&(12, 34)]);
653 list.check_consistency();
654
655 // remove some elements
656 assert_eq!(
657 list.remove_by_first(&0, |(_, b)| *b == 99),
658 vec![(0, 99), (0, 99)]
659 );
660 assert_eq!(list.remove_by_first(&0, |_| true), vec![(0, 34)]);
661 assert_eq!(list.len(), 1);
662 list.check_consistency();
663
664 // test adding an element again
665 assert_eq!(sort(list.by_first(&12)), [&(12, 34)]);
666 list.insert((12, 123));
667 assert_eq!(list.len(), 2);
668 assert_eq!(sort(list.by_first(&12)), [&(12, 34), &(12, 123)]);
669 assert_eq!(sort(list.by_second(&34)), [&(12, 34)]);
670 assert_eq!(sort(list.by_second(&123)), [&(12, 123)]);
671 list.check_consistency();
672
673 // test iterators
674 list.insert((56, 78));
675 assert_eq!(sort(list.values()), [&(12, 34), &(12, 123), &(56, 78)]);
676 assert_eq!(sort(list.into_values()), [(12, 34), (12, 123), (56, 78)]);
677 }
678
679 #[test]
680 fn retain_and_compact() {
681 let mut list: Tuple2List<String, String> = (1..=1000)
682 .map(|idx| (format!("A={}", idx), format!("B={}", idx)))
683 .collect();
684
685 assert_eq!(list.len(), 1000);
686 let cap_orig = list.capacity();
687 assert!(cap_orig >= list.len());
688 list.check_consistency();
689
690 // retain only the values whose first key is 3 characters long; that's 9 values out of 1000
691 list.retain(|(a, _)| a.len() <= 3);
692 assert_eq!(list.len(), 9);
693 // we don't shrink till we next insert
694 assert_eq!(list.capacity(), cap_orig);
695 list.check_consistency();
696
697 // insert should cause the list to shrink
698 list.insert(("A=0".to_string(), "B=0".to_string()));
699 assert!(list.capacity() < cap_orig);
700 assert_eq!(list.len(), 10);
701 for idx in 0..=9 {
702 assert!(list.contains_first(&format!("A={}", idx)));
703 }
704 list.check_consistency();
705 }
706
707 n_key_list! {
708 #[derive(Clone, Debug)]
709 struct AllOptional<A,B> for (Option<A>,Option<B>) {
710 (Option) first: A { .0 },
711 (Option) second: B { .1 },
712 }
713 }
714
715 #[test]
716 fn optional() {
717 let mut list = AllOptional::<u8, u8>::new();
718
719 // should be able to insert values with at least one key
720 list.insert((Some(1), Some(2)));
721 list.insert((None, Some(2)));
722 list.insert((Some(1), None));
723 list.check_consistency();
724
725 assert_eq!(
726 sort(list.by_first(&1)),
727 [&(Some(1), None), &(Some(1), Some(2))],
728 );
729
730 // check that inserting a value with no keys results in an error
731 assert!(matches!(
732 list.try_insert((None, None)),
733 Err(super::Error::NoKeys),
734 ));
735 }
736
737 #[allow(dead_code)]
738 struct Weekday {
739 dow: u8,
740 name: &'static str,
741 lucky_number: Option<u16>,
742 }
743 #[allow(dead_code)]
744 impl Weekday {
745 fn dow(&self) -> &u8 {
746 &self.dow
747 }
748 fn name(&self) -> &str {
749 self.name
750 }
751 fn lucky_number(&self) -> Option<&u16> {
752 self.lucky_number.as_ref()
753 }
754 }
755 n_key_list! {
756 struct WeekdaySet for Weekday {
757 idx: u8 { dow() },
758 (Option) lucky: u16 { lucky_number() },
759 name: String { name() }
760 }
761 }
762
763 n_key_list! {
764 struct['a] ArrayMap['a] for (String, [&'a u32;10]) {
765 name: String { .0 }
766 }
767 }
768
769 n_key_list! {
770 struct['a, const N:usize] ArrayMap2['a, N] for (String, [&'a u32;N]) {
771 name: String { .0 }
772 }
773 }
774}