exhaustive_map/finite.rs
1pub use exhaustive_map_macros::Finite;
2use generic_array::{ArrayLength, typenum::Unsigned};
3
4/// Represents a type that has a finite number of inhabitants.
5///
6/// If the number of inhabitants is more than `usize::MAX`,
7/// such as `usize`, `isize`, `u64`, `i64` and `f64`,
8/// then `Finite` should not be implemented for the type.
9///
10/// Example:
11/// ```
12/// use exhaustive_map::{Finite, FiniteExt, typenum::Unsigned};
13///
14/// #[derive(Finite, Debug, PartialEq)]
15/// enum Color {
16/// Red,
17/// Green,
18/// Blue,
19/// }
20///
21/// assert_eq!(<Color as Finite>::INHABITANTS::USIZE, 3);
22/// assert_eq!(Color::from_usize(0), Some(Color::Red));
23/// assert_eq!(Color::from_usize(1), Some(Color::Green));
24/// assert_eq!(Color::from_usize(2), Some(Color::Blue));
25/// assert_eq!(Color::from_usize(3), None);
26///
27/// let all: Vec<_> = Color::iter_all().collect();
28/// assert_eq!(all, vec![Color::Red, Color::Green, Color::Blue]);
29/// ```
30pub trait Finite: Sized {
31 /// The total number of different inhabitants of the type.
32 ///
33 /// This is a [`typenum::Unsigned`](crate::typenum::Unsigned) type-level number.
34 type INHABITANTS: ArrayLength + FitsInUsize;
35
36 /// Should return a number in the range `0..INHABITANTS`.
37 #[must_use]
38 fn to_usize(&self) -> usize;
39
40 /// Should be the inverse function of `to_usize`.
41 ///
42 /// This should return `Some` if and only if `i < T::INHABITANTS`.
43 #[must_use]
44 fn from_usize(i: usize) -> Option<Self>;
45}
46
47/// Implemented for [`typenum`](crate::typenum) numbers which fits in an
48/// `usize`.
49///
50/// The number of inhabitants for a [`Finite`] type must implement this trait.
51pub trait FitsInUsize: sealed::Sealed {}
52impl<T: sealed::Sealed> FitsInUsize for T {}
53
54mod sealed {
55 use crate::typenum::{B1, IsLessOrEqual, Pow, Sub1, U, U256, Unsigned};
56
57 type UsizeMax = Sub1<<U256 as Pow<U<{ size_of::<usize>() }>>>::Output>;
58
59 pub trait Sealed {}
60 impl<U: Unsigned> Sealed for U where U: IsLessOrEqual<UsizeMax, Output = B1> {}
61}
62
63/// An extension for [`Finite`] providing the [`iter_all`](FiniteExt::iter_all)
64/// method.
65pub trait FiniteExt: Finite {
66 /// An iterator over all inhabitants of the type, ordered by the order
67 /// provided by [`Finite`].
68 fn iter_all() -> IterAll<Self> {
69 IterAll((0..Self::INHABITANTS::USIZE).map(|i| {
70 Self::from_usize(i).expect("unexpected None returned from Finite::from_usize in range")
71 }))
72 }
73}
74
75impl<T: Finite> FiniteExt for T {}
76
77/// An owned iterator over all inhabitants of a type implementing [`Finite`].
78///
79/// This `struct` is created by the [`FiniteExt::iter_all`] method.
80#[must_use = "iterators are lazy and do nothing unless consumed"]
81pub struct IterAll<T>(core::iter::Map<core::ops::Range<usize>, fn(usize) -> T>);
82
83impl<T> Iterator for IterAll<T> {
84 type Item = T;
85
86 fn next(&mut self) -> Option<Self::Item> {
87 self.0.next()
88 }
89
90 fn size_hint(&self) -> (usize, Option<usize>) {
91 (self.0.len(), Some(self.0.len()))
92 }
93}
94
95impl<T> ExactSizeIterator for IterAll<T> {
96 fn len(&self) -> usize {
97 self.0.len()
98 }
99}
100
101impl<T> DoubleEndedIterator for IterAll<T> {
102 fn next_back(&mut self) -> Option<Self::Item> {
103 self.0.next_back()
104 }
105}