/ crates / tor-basic-utils / src / n_key_list.rs
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)]
  6  pub 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]
 94  macro_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  } => {
103  n_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]
581  pub 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)]
589  mod 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  }