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}