#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
use core::{cmp::Ordering, iter::FusedIterator, ops::ControlFlow, task::Poll};
pub use enum_iterator_derive::Sequence;
pub const fn cardinality<T: Sequence>() -> usize {
T::CARDINALITY
}
pub fn all<T: Sequence>() -> All<T> {
All(T::first())
}
pub fn reverse_all<T: Sequence>() -> ReverseAll<T> {
ReverseAll(T::last())
}
pub fn next<T: Sequence>(x: &T) -> Option<T> {
x.next()
}
pub fn next_cycle<T: Sequence>(x: &T) -> T {
next(x)
.or_else(first)
.expect("Sequence::first returned None for inhabited type")
}
pub fn previous<T: Sequence>(x: &T) -> Option<T> {
x.previous()
}
pub fn previous_cycle<T: Sequence>(x: &T) -> T {
previous(x)
.or_else(last)
.expect("Sequence::last returned None for inhabited type")
}
pub fn first<T: Sequence>() -> Option<T> {
T::first()
}
pub fn last<T: Sequence>() -> Option<T> {
T::last()
}
#[derive(Clone, Debug)]
pub struct All<T>(Option<T>);
impl<T: Sequence> Iterator for All<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
let item = self.0.take()?;
self.0 = item.next();
Some(item)
}
}
impl<T: Sequence> FusedIterator for All<T> {}
#[derive(Clone, Debug)]
pub struct ReverseAll<T>(Option<T>);
impl<T: Sequence> Iterator for ReverseAll<T> {
type Item = T;
fn next(&mut self) -> Option<T> {
let item = self.0.take()?;
self.0 = item.previous();
Some(item)
}
}
impl<T: Sequence> FusedIterator for ReverseAll<T> {}
pub trait Sequence: Sized {
const CARDINALITY: usize;
fn next(&self) -> Option<Self>;
fn previous(&self) -> Option<Self>;
fn first() -> Option<Self>;
fn last() -> Option<Self>;
}
impl Sequence for bool {
const CARDINALITY: usize = 2;
fn next(&self) -> Option<Self> {
(!*self).then_some(true)
}
fn previous(&self) -> Option<Self> {
(*self).then_some(false)
}
fn first() -> Option<Self> {
Some(false)
}
fn last() -> Option<Self> {
Some(true)
}
}
macro_rules! impl_sequence_for_int {
($ty:ty) => {
impl Sequence for $ty {
const CARDINALITY: usize = 1 << <$ty>::BITS;
fn next(&self) -> Option<Self> {
self.checked_add(1)
}
fn previous(&self) -> Option<Self> {
self.checked_sub(1)
}
fn first() -> Option<Self> {
Some(Self::MIN)
}
fn last() -> Option<Self> {
Some(Self::MAX)
}
}
};
}
impl_sequence_for_int!(i8);
impl_sequence_for_int!(u8);
impl_sequence_for_int!(i16);
impl_sequence_for_int!(u16);
impl Sequence for () {
const CARDINALITY: usize = 1;
fn next(&self) -> Option<Self> {
None
}
fn previous(&self) -> Option<Self> {
None
}
fn first() -> Option<Self> {
Some(())
}
fn last() -> Option<Self> {
Some(())
}
}
impl Sequence for core::convert::Infallible {
const CARDINALITY: usize = 0;
fn next(&self) -> Option<Self> {
None
}
fn previous(&self) -> Option<Self> {
None
}
fn first() -> Option<Self> {
None
}
fn last() -> Option<Self> {
None
}
}
impl Sequence for Ordering {
const CARDINALITY: usize = 3;
fn next(&self) -> Option<Self> {
int_to_ordering(*self as i8 + 1)
}
fn previous(&self) -> Option<Self> {
int_to_ordering(*self as i8 - 1)
}
fn first() -> Option<Self> {
Some(Ordering::Less)
}
fn last() -> Option<Self> {
Some(Ordering::Greater)
}
}
fn int_to_ordering(i: i8) -> Option<Ordering> {
match i {
-1 => Some(Ordering::Less),
0 => Some(Ordering::Equal),
1 => Some(Ordering::Greater),
_ => None,
}
}
impl<T: Sequence> Sequence for Option<T> {
const CARDINALITY: usize = T::CARDINALITY + 1;
fn next(&self) -> Option<Self> {
match self {
None => T::first().map(Some),
Some(x) => x.next().map(Some),
}
}
fn previous(&self) -> Option<Self> {
self.as_ref().map(T::previous)
}
fn first() -> Option<Self> {
Some(None)
}
fn last() -> Option<Self> {
Some(T::last())
}
}
impl<T: Sequence> Sequence for Poll<T> {
const CARDINALITY: usize = T::CARDINALITY + 1;
fn next(&self) -> Option<Self> {
match self {
Poll::Ready(x) => x.next().map(Poll::Ready).or(Some(Poll::Pending)),
Poll::Pending => None,
}
}
fn previous(&self) -> Option<Self> {
match self {
Poll::Ready(x) => x.previous().map(Poll::Ready),
Poll::Pending => T::last().map(Poll::Ready),
}
}
fn first() -> Option<Self> {
T::first().map(Poll::Ready).or(Some(Poll::Pending))
}
fn last() -> Option<Self> {
Some(Poll::Pending)
}
}
impl<const N: usize, T: Sequence + Clone> Sequence for [T; N] {
const CARDINALITY: usize = {
let tc = T::CARDINALITY;
let mut c = 1;
let mut i = 0;
loop {
if i == N {
break c;
}
c *= tc;
i += 1;
}
};
fn next(&self) -> Option<Self> {
advance_for_array(self, T::first)
}
fn previous(&self) -> Option<Self> {
advance_for_array(self, T::last)
}
fn first() -> Option<Self> {
if N == 0 {
Some(core::array::from_fn(|_| unreachable!()))
} else {
let x = T::first()?;
Some(core::array::from_fn(|_| x.clone()))
}
}
fn last() -> Option<Self> {
if N == 0 {
Some(core::array::from_fn(|_| unreachable!()))
} else {
let x = T::last()?;
Some(core::array::from_fn(|_| x.clone()))
}
}
}
fn advance_for_array<const N: usize, T, R>(a: &[T; N], reset: R) -> Option<[T; N]>
where
T: Sequence + Clone,
R: Fn() -> Option<T>,
{
let mut a = a.clone();
let keep = a.iter_mut().rev().try_fold((), |_, x| match x.next() {
Some(new_x) => {
*x = new_x;
ControlFlow::Break(true)
}
None => match reset() {
Some(new_x) => {
*x = new_x;
ControlFlow::Continue(())
}
None => ControlFlow::Break(false),
},
});
Some(a).filter(|_| matches!(keep, ControlFlow::Break(true)))
}
macro_rules! impl_seq_advance_for_tuple {
(
$this:ident,
$advance:ident,
$reset:ident,
$carry:ident
@ $($values:expr,)*
@
@ $($placeholders:pat,)*
) => {
Some(($($values,)*)).filter(|_| !$carry)
};
(
$this:ident,
$advance:ident,
$reset:ident,
$carry:ident
@ $($values:expr,)*
@ $ty:ident, $($types:ident,)*
@ $($placeholders:pat,)*
) => {{
let (.., item, $($placeholders,)*) = $this;
let (x, new_carry) = if $carry {
match Sequence::$advance(item) {
Some(x) => (x, false),
None => (Sequence::$reset()?, true),
}
} else {
(item.clone(), false)
};
impl_seq_advance_for_tuple!(
$this,
$advance,
$reset,
new_carry
@ x, $($values,)*
@ $($types,)*
@ _, $($placeholders,)*
)
}};
($this:ident, $advance:ident, $reset:ident @ $($types:ident,)*) => {{
let (.., item) = $this;
let (x, carry) = match Sequence::$advance(item) {
Some(x) => (x, false),
None => (Sequence::$reset()?, true),
};
impl_seq_advance_for_tuple!($this, $advance, $reset, carry @ x, @ $($types,)* @ _,)
}};
}
macro_rules! impl_sequence_for_tuple {
($($types:ident,)* @ $last:ident) => {
impl<$($types,)* $last> Sequence for ($($types,)* $last,)
where
$($types: Sequence + Clone,)*
$last: Sequence,
{
const CARDINALITY: usize =
$(<$types as Sequence>::CARDINALITY *)* <$last as Sequence>::CARDINALITY;
fn next(&self) -> Option<Self> {
impl_seq_advance_for_tuple!(self, next, first @ $($types,)*)
}
fn previous(&self) -> Option<Self> {
impl_seq_advance_for_tuple!(self, previous, last @ $($types,)*)
}
fn first() -> Option<Self> {
Some((
$(<$types as Sequence>::first()?,)*
<$last as Sequence>::first()?,
))
}
fn last() -> Option<Self> {
Some((
$(<$types as Sequence>::last()?,)*
<$last as Sequence>::last()?,
))
}
}
};
}
macro_rules! impl_sequence_for_tuples {
($($types:ident,)*) => {
impl_sequence_for_tuples!(@ $($types,)*);
};
($($types:ident,)* @ $head:ident, $($tail:ident,)*) => {
impl_sequence_for_tuple!($($types,)* @ $head);
impl_sequence_for_tuples!($($types,)* $head, @ $($tail,)*);
};
($($types:ident,)* @) => {};
}
impl_sequence_for_tuples!(
T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20,
T21, T22, T23, T24, T25, T26, T27, T28, T29, T30, T31,
);
#[cfg(test)]
mod tests {
use crate::{all, cardinality, reverse_all, Sequence};
use core::{cmp::Ordering, convert::Infallible, task::Poll};
fn cardinality_equals_item_count<T: Sequence>() {
assert_eq!(cardinality::<T>(), all::<T>().count());
}
#[test]
fn cardinality_equals_item_count_for_bool() {
cardinality_equals_item_count::<bool>();
}
#[test]
fn all_bool_values_are_yielded() {
assert!(all::<bool>().eq([false, true]));
}
#[test]
fn all_bool_values_are_yielded_in_reverse() {
assert!(reverse_all::<bool>().eq([true, false]));
}
#[test]
fn cardinality_equals_item_count_for_i8() {
cardinality_equals_item_count::<i8>();
}
#[test]
fn all_i8_values_are_yielded() {
assert!(all::<i8>().eq(i8::MIN..=i8::MAX));
}
#[test]
fn all_i8_values_are_yielded_in_reverse() {
assert!(reverse_all::<i8>().eq((i8::MIN..=i8::MAX).rev()));
}
#[test]
fn cardinality_equals_item_count_for_u8() {
cardinality_equals_item_count::<u8>();
}
#[test]
fn all_u8_values_are_yielded() {
assert!(all::<u8>().eq(u8::MIN..=u8::MAX));
}
#[test]
fn all_u8_values_are_yielded_in_reverse() {
assert!(reverse_all::<u8>().eq((u8::MIN..=u8::MAX).rev()));
}
#[test]
fn cardinality_equals_item_count_for_i16() {
cardinality_equals_item_count::<i16>();
}
#[test]
fn all_i16_values_are_yielded() {
assert!(all::<i16>().eq(i16::MIN..=i16::MAX));
}
#[test]
fn all_i16_values_are_yielded_in_reverse() {
assert!(reverse_all::<i16>().eq((i16::MIN..=i16::MAX).rev()));
}
#[test]
fn cardinality_equals_item_count_for_u16() {
cardinality_equals_item_count::<u16>();
}
#[test]
fn all_u16_values_are_yielded() {
assert!(all::<u16>().eq(u16::MIN..=u16::MAX));
}
#[test]
fn all_u16_values_are_yielded_in_reverse() {
assert!(reverse_all::<u16>().eq((u16::MIN..=u16::MAX).rev()));
}
#[test]
fn cardinality_equals_item_count_for_unit() {
cardinality_equals_item_count::<()>();
}
#[test]
fn all_unit_values_are_yielded() {
assert!(all::<()>().eq([()]));
}
#[test]
fn all_unit_values_are_yielded_in_reverse() {
assert!(reverse_all::<()>().eq([()]));
}
#[test]
fn cardinality_equals_item_count_for_infallible() {
cardinality_equals_item_count::<Infallible>();
}
#[test]
fn all_infallible_values_are_yielded() {
assert!(all::<Infallible>().next().is_none());
}
#[test]
fn all_infallible_values_are_yielded_in_reverse() {
assert!(reverse_all::<Infallible>().next().is_none());
}
#[test]
fn cardinality_equals_item_count_for_tuple_with_infallible() {
cardinality_equals_item_count::<(bool, Infallible)>();
}
#[test]
fn all_tuple_with_infallible_values_are_yielded() {
assert!(all::<(bool, Infallible)>().next().is_none());
}
#[test]
fn all_tuple_with_infallible_values_are_yielded_in_reverse() {
assert!(reverse_all::<(bool, Infallible)>().next().is_none());
}
#[test]
fn cardinality_equals_item_count_for_singleton() {
cardinality_equals_item_count::<(u8,)>();
}
#[test]
fn cardinality_equals_item_count_for_pair() {
cardinality_equals_item_count::<(u8, bool)>();
}
#[test]
fn cardinality_equals_item_count_for_triple() {
cardinality_equals_item_count::<(bool, u8, bool)>();
}
#[test]
fn cardinality_equals_item_count_for_option() {
cardinality_equals_item_count::<Option<u8>>();
}
#[test]
fn all_bool_option_items_are_yielded() {
assert!(all::<Option<bool>>().eq([None, Some(false), Some(true)]));
}
#[test]
fn all_bool_option_items_are_yielded_in_reverse() {
assert!(reverse_all::<Option<bool>>().eq([Some(true), Some(false), None]));
}
#[test]
fn all_infallible_option_items_are_yielded() {
assert!(all::<Option<Infallible>>().eq([None]));
}
#[test]
fn all_infallible_option_items_are_yielded_in_reverse() {
assert!(reverse_all::<Option<Infallible>>().eq([None]));
}
#[test]
fn cardinality_equals_item_count_for_ordering() {
cardinality_equals_item_count::<Ordering>();
}
#[test]
fn all_ordering_values_are_yielded() {
assert!(all::<Ordering>().eq([Ordering::Less, Ordering::Equal, Ordering::Greater]));
}
#[test]
fn all_ordering_values_are_yielded_in_reverse() {
assert!(reverse_all::<Ordering>().eq([Ordering::Greater, Ordering::Equal, Ordering::Less]));
}
#[test]
fn cardinality_equals_item_count_for_poll() {
cardinality_equals_item_count::<Poll<u8>>();
}
#[test]
fn all_bool_poll_items_are_yielded() {
assert!(all::<Poll<bool>>().eq([Poll::Ready(false), Poll::Ready(true), Poll::Pending]));
}
#[test]
fn all_bool_poll_items_are_yielded_in_reverse() {
assert!(reverse_all::<Poll<bool>>().eq([
Poll::Pending,
Poll::Ready(true),
Poll::Ready(false),
]));
}
#[test]
fn all_infallible_poll_items_are_yielded() {
assert!(all::<Poll<Infallible>>().eq([Poll::Pending]));
}
#[test]
fn all_infallible_poll_items_are_yielded_in_reverse() {
assert!(reverse_all::<Poll<Infallible>>().eq([Poll::Pending]));
}
#[test]
fn tuple_fields_vary_from_right_to_left() {
assert!(all::<(Option<bool>, bool)>().eq([
(None, false),
(None, true),
(Some(false), false),
(Some(false), true),
(Some(true), false),
(Some(true), true),
]));
}
#[test]
fn cardinality_of_empty_array_is_one() {
assert_eq!(cardinality::<[u8; 0]>(), 1);
}
#[test]
fn cardinality_equals_item_count_for_empty_array() {
cardinality_equals_item_count::<[u8; 0]>();
}
#[test]
fn cardinality_equals_item_count_for_array() {
cardinality_equals_item_count::<[u8; 3]>();
}
#[test]
fn array_items_vary_from_right_to_left() {
assert!(all::<[Option<bool>; 2]>().eq([
[None, None],
[None, Some(false)],
[None, Some(true)],
[Some(false), None],
[Some(false), Some(false)],
[Some(false), Some(true)],
[Some(true), None],
[Some(true), Some(false)],
[Some(true), Some(true)],
]));
}
#[test]
fn all_empty_array_items_are_yielded() {
assert!(all::<[bool; 0]>().eq([[]]));
}
#[test]
fn cardinality_of_empty_infallible_array_is_one() {
assert_eq!(cardinality::<[Infallible; 0]>(), 1);
}
#[test]
fn cardinality_of_non_empty_infallible_array_is_zero() {
assert_eq!(cardinality::<[Infallible; 1]>(), 0);
}
#[test]
fn all_empty_infallible_array_items_are_yielded() {
assert!(all::<[Infallible; 0]>().eq([[]]));
}
#[test]
fn all_non_empty_infallible_array_items_are_yielded() {
assert!(all::<[Infallible; 1]>().next().is_none());
}
}