#![deny(missing_docs)]
#![deny(warnings)]
#![no_std]
pub use enum_iterator_derive::IntoEnumIterator;
pub trait IntoEnumIterator: Sized {
type Iterator: Iterator<Item = Self> + Clone;
const ITEM_COUNT: usize;
fn into_enum_iter() -> Self::Iterator;
}
impl IntoEnumIterator for bool {
type Iterator = core::array::IntoIter<bool, 2>;
const ITEM_COUNT: usize = 2;
fn into_enum_iter() -> Self::Iterator {
[false, true].into_iter()
}
}
impl IntoEnumIterator for i8 {
type Iterator = core::ops::RangeInclusive<i8>;
const ITEM_COUNT: usize = 1 << 8;
fn into_enum_iter() -> Self::Iterator {
i8::MIN..=i8::MAX
}
}
impl IntoEnumIterator for u8 {
type Iterator = core::ops::RangeInclusive<u8>;
const ITEM_COUNT: usize = 1 << 8;
fn into_enum_iter() -> Self::Iterator {
u8::MIN..=u8::MAX
}
}
impl IntoEnumIterator for i16 {
type Iterator = core::ops::RangeInclusive<i16>;
const ITEM_COUNT: usize = 1 << 16;
fn into_enum_iter() -> Self::Iterator {
i16::MIN..=i16::MAX
}
}
impl IntoEnumIterator for u16 {
type Iterator = core::ops::RangeInclusive<u16>;
const ITEM_COUNT: usize = 1 << 16;
fn into_enum_iter() -> Self::Iterator {
u16::MIN..=u16::MAX
}
}
impl IntoEnumIterator for () {
type Iterator = TupleEnumIterator<bool>;
const ITEM_COUNT: usize = 1;
fn into_enum_iter() -> Self::Iterator {
TupleEnumIterator(false)
}
}
impl Iterator for TupleEnumIterator<bool> {
type Item = ();
fn next(&mut self) -> Option<()> {
if self.0 {
None
} else {
self.0 = true;
Some(())
}
}
}
impl<T: IntoEnumIterator> IntoEnumIterator for Option<T> {
type Iterator = OptionEnumIterator<T>;
const ITEM_COUNT: usize = T::ITEM_COUNT + 1;
fn into_enum_iter() -> Self::Iterator {
OptionEnumIterator::new()
}
}
macro_rules! tuple_next {
(reverse $this:ident, $carry:expr, $($values:expr,)* @ $($reversed:ty,)* @ $head:ty, $($tail:ty,)*) => {
tuple_next!(reverse $this, $carry, $($values,)* @ $head, $($reversed,)* @ $($tail,)*)
};
(reverse $this:ident, $carry:expr, $($values:expr,)* @ $($reversed:ty,)* @) => {
tuple_next!($this, $carry, $($values,)* @ _, _, @ $($reversed,)*)
};
($this:ident, $carry:expr, $($values:expr,)* @ $($placeholders:pat,)* @) => {
Some(($($values,)*)).filter(|_| !$carry)
};
($this:ident, $carry:expr, $($values:expr,)* @ $($placeholders:pat,)* @ $head:ty, $($tail:ty,)*) => {{
let (.., cache, it, $($placeholders,)*) = $this;
let (x, new_carry) = match cache.as_ref().filter(|_| !$carry).cloned() {
Some(x) => Some((x, false)),
None => {
let (x, new_carry) = match it.next() {
Some(x) => Some((x, false)),
None => {
*it = <$head as IntoEnumIterator>::into_enum_iter();
it.next().map(|x| (x, true))
}
}?;
*cache = Some(x.clone());
Some((x, new_carry))
}
}?;
tuple_next!($this, new_carry, x, $($values,)* @ $($placeholders,)* _, _, @ $($tail,)*)
}};
}
macro_rules! impl_tuple {
($($tys:ident,)* @ $last:ident) => {
impl<$($tys,)* $last> IntoEnumIterator for ($($tys,)* $last,)
where
$($tys: IntoEnumIterator + Clone,)*
$last: IntoEnumIterator,
{
type Iterator = TupleEnumIterator<(
$(Option<$tys>, <$tys as IntoEnumIterator>::Iterator,)*
core::marker::PhantomData<$last>,
<$last as IntoEnumIterator>::Iterator,
)>;
const ITEM_COUNT: usize = $(<$tys as IntoEnumIterator>::ITEM_COUNT *)* <$last as IntoEnumIterator>::ITEM_COUNT;
fn into_enum_iter() -> Self::Iterator {
TupleEnumIterator((
$(None, <$tys as IntoEnumIterator>::into_enum_iter(),)*
core::marker::PhantomData,
<$last as IntoEnumIterator>::into_enum_iter(),
))
}
}
impl<$($tys,)* $last> Iterator for TupleEnumIterator<(
$(Option<$tys>, <$tys as IntoEnumIterator>::Iterator,)*
core::marker::PhantomData<$last>,
<$last as IntoEnumIterator>::Iterator,
)>
where
$($tys: IntoEnumIterator + Clone,)*
$last: IntoEnumIterator,
{
type Item = ($($tys,)* $last,);
fn next(&mut self) -> Option<Self::Item> {
let inner = &mut self.0;
let (.., it) = inner;
let (x, carry) = match it.next() {
Some(x) => Some((x, false)),
None => {
*it = <$last as IntoEnumIterator>::into_enum_iter();
it.next().map(|x| (x, true))
}
}?;
tuple_next!(reverse inner, carry, x, @ @ $($tys,)*)
}
}
};
}
macro_rules! impl_tuples {
($($types:ident,)*) => {
impl_tuples!(@ $($types,)*);
};
($($types:ident,)* @ $head:ident, $($tail:ident,)*) => {
impl_tuple!($($types,)* @ $head);
impl_tuples!($($types,)* $head, @ $($tail,)*);
};
($($types:ident,)* @) => {};
}
impl_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,
);
mod prv {
use crate::IntoEnumIterator;
#[derive(Clone, Debug)]
pub struct TupleEnumIterator<T>(pub(crate) T);
pub struct OptionEnumIterator<T: IntoEnumIterator>(OptionEnumIteratorInner<T>);
impl<T: IntoEnumIterator> OptionEnumIterator<T> {
pub(crate) fn new() -> Self {
Self(OptionEnumIteratorInner::None)
}
}
impl<T: IntoEnumIterator> Clone for OptionEnumIterator<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<T: IntoEnumIterator> Iterator for OptionEnumIterator<T> {
type Item = Option<T>;
fn next(&mut self) -> Option<Option<T>> {
match &mut self.0 {
OptionEnumIteratorInner::None => {
self.0 = OptionEnumIteratorInner::Some(T::into_enum_iter());
Some(None)
}
OptionEnumIteratorInner::Some(it) => it.next().map(Some),
}
}
}
enum OptionEnumIteratorInner<T: IntoEnumIterator> {
None,
Some(T::Iterator),
}
impl<T: IntoEnumIterator> Clone for OptionEnumIteratorInner<T> {
fn clone(&self) -> Self {
match self {
Self::None => Self::None,
Self::Some(it) => Self::Some(it.clone()),
}
}
}
}
use prv::{OptionEnumIterator, TupleEnumIterator};
#[cfg(test)]
mod tests {
use crate::IntoEnumIterator;
fn item_count_matches_length<T: IntoEnumIterator>() {
assert_eq!(T::ITEM_COUNT, T::into_enum_iter().count());
}
#[test]
fn item_count_matches_length_for_bool() {
item_count_matches_length::<bool>();
}
#[test]
fn item_count_matches_length_for_i8() {
item_count_matches_length::<i8>();
}
#[test]
fn item_count_matches_length_for_u8() {
item_count_matches_length::<u8>();
}
#[test]
fn item_count_matches_length_for_i16() {
item_count_matches_length::<i16>();
}
#[test]
fn item_count_matches_length_for_u16() {
item_count_matches_length::<u16>();
}
#[test]
fn item_count_matches_length_for_unit() {
item_count_matches_length::<()>();
}
#[test]
fn item_count_matches_length_for_singleton() {
item_count_matches_length::<(u8,)>();
}
#[test]
fn item_count_matches_length_for_pair() {
item_count_matches_length::<(u8, bool)>();
}
#[test]
fn item_count_matches_length_for_triple() {
item_count_matches_length::<(bool, u8, bool)>();
}
#[test]
fn item_count_matches_length_for_option() {
item_count_matches_length::<Option<u8>>();
}
#[test]
fn check_option_items() {
assert!(Option::<bool>::into_enum_iter().eq([None, Some(false), Some(true)]));
}
#[test]
fn tuple_fields_vary_from_right_to_left() {
assert!(<(Option<bool>, bool)>::into_enum_iter().eq([
(None, false),
(None, true),
(Some(false), false),
(Some(false), true),
(Some(true), false),
(Some(true), true),
]));
}
}