From 295a2b4362dcc355ee0a93d2e3d3f27a40af1e3c Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Thu, 22 May 2025 15:00:21 +0900 Subject: [PATCH] Import enum-iterator 2.1.0 --- .cargo_vcs_info.json | 6 + Cargo.toml | 32 ++ Cargo.toml.orig | 14 + LICENSE | 14 + README.md | 63 +++ src/lib.rs | 1069 ++++++++++++++++++++++++++++++++++++++++++ tests/derive.rs | 183 ++++++++ 7 files changed, 1381 insertions(+) create mode 100644 .cargo_vcs_info.json create mode 100644 Cargo.toml create mode 100644 Cargo.toml.orig create mode 100644 LICENSE create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 tests/derive.rs diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json new file mode 100644 index 0000000..12e6342 --- /dev/null +++ b/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "2b1aaefe6de0ccea827b1376ebad12f3476662a9" + }, + "path_in_vcs": "enum-iterator" +} \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..669a023 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "enum-iterator" +version = "2.1.0" +authors = ["Stephane Raux "] +description = "Tools to iterate over all values of a type (e.g. all variants of an enumeration)" +homepage = "https://github.com/stephaneyfx/enum-iterator" +documentation = "https://docs.rs/enum-iterator" +readme = "README.md" +keywords = [ + "enum", + "variants", + "iterator", + "enumerate", + "cardinality", +] +license = "0BSD" +repository = "https://github.com/stephaneyfx/enum-iterator.git" + +[dependencies.enum-iterator-derive] +version = "1.4.0" diff --git a/Cargo.toml.orig b/Cargo.toml.orig new file mode 100644 index 0000000..24257c1 --- /dev/null +++ b/Cargo.toml.orig @@ -0,0 +1,14 @@ +[package] +name = "enum-iterator" +version = "2.1.0" +authors = ["Stephane Raux "] +edition = "2021" +description = "Tools to iterate over all values of a type (e.g. all variants of an enumeration)" +license = "0BSD" +homepage = "https://github.com/stephaneyfx/enum-iterator" +repository = "https://github.com/stephaneyfx/enum-iterator.git" +documentation = "https://docs.rs/enum-iterator" +keywords = ["enum", "variants", "iterator", "enumerate", "cardinality"] + +[dependencies] +enum-iterator-derive = { path = "../enum-iterator-derive", version = "1.4.0" } diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..639dbea --- /dev/null +++ b/LICENSE @@ -0,0 +1,14 @@ +BSD Zero Clause License + +Copyright (c) 2018 Stephane Raux + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..54b0c51 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ + + +# Overview +- [📦 crates.io](https://crates.io/crates/enum-iterator) +- [📖 Documentation](https://docs.rs/enum-iterator) +- [⚖ 0BSD license](https://spdx.org/licenses/0BSD.html) + +Tools to iterate over the values of a type. + +# Examples +```rust +use enum_iterator::{all, cardinality, first, last, next, previous, reverse_all, Sequence}; + +#[derive(Debug, PartialEq, Sequence)] +enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + +assert_eq!(cardinality::(), 7); +assert_eq!(all::().collect::>(), [ + Day::Monday, + Day::Tuesday, + Day::Wednesday, + Day::Thursday, + Day::Friday, + Day::Saturday, + Day::Sunday, +]); +assert_eq!(first::(), Some(Day::Monday)); +assert_eq!(last::(), Some(Day::Sunday)); +assert_eq!(next(&Day::Tuesday), Some(Day::Wednesday)); +assert_eq!(previous(&Day::Wednesday), Some(Day::Tuesday)); +assert_eq!(reverse_all::().collect::>(), [ + Day::Sunday, + Day::Saturday, + Day::Friday, + Day::Thursday, + Day::Wednesday, + Day::Tuesday, + Day::Monday, +]); +``` + +```rust +use enum_iterator::{cardinality, first, last, Sequence}; + +#[derive(Debug, PartialEq, Sequence)] +struct Foo { + a: bool, + b: u8, +} + +assert_eq!(cardinality::(), 512); +assert_eq!(first::(), Some(Foo { a: false, b: 0 })); +assert_eq!(last::(), Some(Foo { a: true, b: 255 })); +``` + +# Rust version +This crate tracks stable Rust. Minor releases may require a newer Rust version. Patch releases +must not require a newer Rust version. + +# Contribute +All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html). + + diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..16d4726 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,1069 @@ +// Copyright (c) 2018-2022 Stephane Raux. Distributed under the 0BSD license. + +//! # Overview +//! - [📦 crates.io](https://crates.io/crates/enum-iterator) +//! - [📖 Documentation](https://docs.rs/enum-iterator) +//! - [⚖ 0BSD license](https://spdx.org/licenses/0BSD.html) +//! +//! Tools to iterate over the values of a type. +//! +//! # Examples +//! ``` +//! use enum_iterator::{all, cardinality, first, last, next, previous, reverse_all, Sequence}; +//! +//! #[derive(Debug, PartialEq, Sequence)] +//! enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +//! +//! assert_eq!(cardinality::(), 7); +//! assert_eq!(all::().collect::>(), [ +//! Day::Monday, +//! Day::Tuesday, +//! Day::Wednesday, +//! Day::Thursday, +//! Day::Friday, +//! Day::Saturday, +//! Day::Sunday, +//! ]); +//! assert_eq!(first::(), Some(Day::Monday)); +//! assert_eq!(last::(), Some(Day::Sunday)); +//! assert_eq!(next(&Day::Tuesday), Some(Day::Wednesday)); +//! assert_eq!(previous(&Day::Wednesday), Some(Day::Tuesday)); +//! assert_eq!(reverse_all::().collect::>(), [ +//! Day::Sunday, +//! Day::Saturday, +//! Day::Friday, +//! Day::Thursday, +//! Day::Wednesday, +//! Day::Tuesday, +//! Day::Monday, +//! ]); +//! ``` +//! +//! ``` +//! use enum_iterator::{cardinality, first, last, Sequence}; +//! +//! #[derive(Debug, PartialEq, Sequence)] +//! struct Foo { +//! a: bool, +//! b: u8, +//! } +//! +//! assert_eq!(cardinality::(), 512); +//! assert_eq!(first::(), Some(Foo { a: false, b: 0 })); +//! assert_eq!(last::(), Some(Foo { a: true, b: 255 })); +//! ``` +//! +//! # Rust version +//! This crate tracks stable Rust. Minor releases may require a newer Rust version. Patch releases +//! must not require a newer Rust version. +//! +//! # Contribute +//! All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html). + +#![deny(missing_docs)] +#![deny(warnings)] +#![no_std] + +use core::{cmp::Ordering, iter::FusedIterator, ops::ControlFlow, task::Poll}; + +pub use enum_iterator_derive::Sequence; + +/// Returns the cardinality (number of values) of `T` +/// +/// # Example +/// ``` +/// use enum_iterator::{cardinality, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Color { Red, Green, Blue } +/// +/// assert_eq!(cardinality::(), 3); +/// ``` +pub const fn cardinality() -> usize { + T::CARDINALITY +} + +/// Returns an iterator over all values of type `T`. +/// +/// Values are yielded in the order defined by [`Sequence::next`], starting with +/// [`Sequence::first`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{all, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Color { Red, Green, Blue } +/// +/// assert_eq!( +/// all::().collect::>(), +/// [Color::Red, Color::Green, Color::Blue], +/// ); +/// ``` +pub fn all() -> All { + All(T::first()) +} + +/// Returns an iterator over all values of type `T` in the reverse order of [`all`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{reverse_all, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Color { Red, Green, Blue } +/// +/// assert_eq!( +/// reverse_all::().collect::>(), +/// [Color::Blue, Color::Green, Color::Red], +/// ); +/// ``` +pub fn reverse_all() -> ReverseAll { + ReverseAll(T::last()) +} + +/// Returns the next value of type `T` or `None` if this was the end. +/// +/// Same as [`Sequence::next`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{next, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(next(&Day::Friday), Some(Day::Saturday)); +/// ``` +pub fn next(x: &T) -> Option { + x.next() +} + +/// Returns the next value of type `T` or [`first()`](first) if this was the end. +/// +/// # Example +/// ``` +/// use enum_iterator::{next_cycle, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(next_cycle(&Day::Sunday), Day::Monday); +/// ``` +pub fn next_cycle(x: &T) -> T { + next(x) + .or_else(first) + .expect("Sequence::first returned None for inhabited type") +} + +/// Returns the previous value of type `T` or `None` if this was the beginning. +/// +/// Same as [`Sequence::previous`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{previous, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(previous(&Day::Saturday), Some(Day::Friday)); +/// ``` +pub fn previous(x: &T) -> Option { + x.previous() +} + +/// Returns the previous value of type `T` or [`last()`](last) if this was the beginning. +/// +/// # Example +/// ``` +/// use enum_iterator::{previous_cycle, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(previous_cycle(&Day::Monday), Day::Sunday); +/// ``` +pub fn previous_cycle(x: &T) -> T { + previous(x) + .or_else(last) + .expect("Sequence::last returned None for inhabited type") +} + +/// Returns the first value of type `T`. +/// +/// Same as [`Sequence::first`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{first, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(first::(), Some(Day::Monday)); +/// ``` +pub fn first() -> Option { + T::first() +} + +/// Returns the last value of type `T`. +/// +/// Same as [`Sequence::last`]. +/// +/// # Example +/// ``` +/// use enum_iterator::{last, Sequence}; +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } +/// +/// assert_eq!(last::(), Some(Day::Sunday)); +/// ``` +pub fn last() -> Option { + T::last() +} + +/// Iterator over the values of type `T`. +/// +/// Returned by [`all`]. +#[derive(Clone, Debug)] +pub struct All(Option); + +impl Iterator for All { + type Item = T; + + fn next(&mut self) -> Option { + let item = self.0.take()?; + self.0 = item.next(); + Some(item) + } +} + +impl FusedIterator for All {} + +/// Iterator over the values of type `T` in reverse order. +/// +/// Returned by [`reverse_all`]. +#[derive(Clone, Debug)] +pub struct ReverseAll(Option); + +impl Iterator for ReverseAll { + type Item = T; + + fn next(&mut self) -> Option { + let item = self.0.take()?; + self.0 = item.previous(); + Some(item) + } +} + +impl FusedIterator for ReverseAll {} + +/// Trait to iterate over the values of a type. +/// +/// The [crate root](crate) defines useful functions to work with types implementing `Sequence`. +/// +/// # Derivation +/// +/// `Sequence` can be derived for `enum` and `struct` types. Specifically, it can be derived +/// for: +/// - Enumerations whose variants meet one of the following criteria: +/// - The variant does not have fields. +/// - The variant has fields meeting all these conditions: +/// - Every field has a type that implements `Sequence`. +/// - Every field but the last one has a type that implements `Clone`. +/// - Enumerations without variants. +/// - Structures whose fields meet all these conditions: +/// - Every field has a type that implements `Sequence`. +/// - Every field but the last one has a type that implements `Clone`. +/// - Unit structures (i.e. without fields). +/// +/// The cardinality (number of values) of the type must not exceed `usize::MAX`. +/// +/// # Laws +/// +/// `T: Sequence` implies the following assertions: +/// - `T::first().and_then(|x| x.previous()).is_none()` +/// - `T::last().and_then(|x| x.next()).is_none()` +/// - `T::first().is_none()` ⇔ `T::last().is_none()` +/// - `std::iter::successors(T::first(), T::next)` must eventually yield `T::last()`. +/// - If `T` is inhabited, `T::first().is_some()`. +/// +/// If a manual implementation of `Sequence` violates any of these laws, the functions at the crate root may misbehave, including panicking. +/// +/// # Examples +/// ## C-like enumeration +/// +/// ``` +/// use enum_iterator::{all, cardinality, Sequence}; +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum Direction { North, South, West, East } +/// +/// assert_eq!(cardinality::(), 4); +/// assert_eq!(all::().collect::>(), [ +/// Direction::North, +/// Direction::South, +/// Direction::West, +/// Direction::East, +/// ]); +/// ``` +/// +/// ## Enumeration with data +/// +/// ``` +/// use enum_iterator::{all, cardinality, Sequence}; +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum Direction { North, South, West, East } +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum Greeting { +/// Hi, +/// Bye, +/// } +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum Action { +/// Move(Direction), +/// Jump, +/// Talk { greeting: Greeting, loud: bool }, +/// } +/// +/// assert_eq!(cardinality::(), 4 + 1 + 2 * 2); +/// assert_eq!(all::().collect::>(), [ +/// Action::Move(Direction::North), +/// Action::Move(Direction::South), +/// Action::Move(Direction::West), +/// Action::Move(Direction::East), +/// Action::Jump, +/// Action::Talk { greeting: Greeting::Hi, loud: false }, +/// Action::Talk { greeting: Greeting::Hi, loud: true }, +/// Action::Talk { greeting: Greeting::Bye, loud: false }, +/// Action::Talk { greeting: Greeting::Bye, loud: true }, +/// ]); +/// ``` +/// +/// ## Structure +/// +/// ``` +/// use enum_iterator::{all, cardinality, Sequence}; +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum Side { +/// Left, +/// Right, +/// } +/// +/// #[derive(Clone, Copy, Debug, PartialEq, Sequence)] +/// enum LimbKind { +/// Arm, +/// Leg, +/// } +/// +/// #[derive(Debug, PartialEq, Sequence)] +/// struct Limb { +/// kind: LimbKind, +/// side: Side, +/// } +/// +/// assert_eq!(cardinality::(), 4); +/// assert_eq!(all::().collect::>(), [ +/// Limb { kind: LimbKind::Arm, side: Side::Left }, +/// Limb { kind: LimbKind::Arm, side: Side::Right }, +/// Limb { kind: LimbKind::Leg, side: Side::Left }, +/// Limb { kind: LimbKind::Leg, side: Side::Right }, +/// ]); +/// ``` +pub trait Sequence: Sized { + /// Number of values of type `Self`. + /// + /// # Example + /// ``` + /// use enum_iterator::Sequence; + /// + /// #[derive(Sequence)] + /// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + /// + /// assert_eq!(Day::CARDINALITY, 7); + /// ``` + const CARDINALITY: usize; + + /// Returns value following `*self` or `None` if this was the end. + /// + /// Values are yielded in the following order. Comparisons between values are based on their + /// relative order as yielded by `next`; an element yielded after another is considered greater. + /// + /// - For primitive types, in increasing order (same as `Ord`). + /// - For arrays and tuples, in lexicographic order of the sequence of their elements. + /// - When derived for an enumeration, in variant definition order. + /// - When derived for a structure, in lexicographic order of the sequence of its fields taken + /// in definition order. + /// + /// The order described above is the same as `Ord` if any custom `Sequence` implementation + /// follows `Ord` and any enumeration has its variants defined in increasing order of + /// discriminant. + /// + /// # Example + /// ``` + /// use enum_iterator::Sequence; + /// + /// #[derive(Debug, PartialEq, Sequence)] + /// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + /// + /// assert_eq!(Day::Tuesday.next(), Some(Day::Wednesday)); + /// ``` + fn next(&self) -> Option; + + /// Returns value preceding `*self` or `None` if this was the beginning. + /// + /// # Example + /// ``` + /// use enum_iterator::Sequence; + /// + /// #[derive(Debug, PartialEq, Sequence)] + /// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + /// + /// assert_eq!(Day::Wednesday.previous(), Some(Day::Tuesday)); + /// ``` + fn previous(&self) -> Option; + + /// Returns the first value of type `Self`. + /// + /// # Example + /// ``` + /// use enum_iterator::Sequence; + /// + /// #[derive(Debug, PartialEq, Sequence)] + /// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + /// + /// assert_eq!(Day::first(), Some(Day::Monday)); + /// ``` + fn first() -> Option; + + /// Returns the last value of type `Self`. + /// + /// # Example + /// ``` + /// use enum_iterator::Sequence; + /// + /// #[derive(Debug, PartialEq, Sequence)] + /// enum Day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday } + /// + /// assert_eq!(Day::last(), Some(Day::Sunday)); + /// ``` + fn last() -> Option; +} + +impl Sequence for bool { + const CARDINALITY: usize = 2; + + fn next(&self) -> Option { + (!*self).then_some(true) + } + + fn previous(&self) -> Option { + (*self).then_some(false) + } + + fn first() -> Option { + Some(false) + } + + fn last() -> Option { + 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.checked_add(1) + } + + fn previous(&self) -> Option { + self.checked_sub(1) + } + + fn first() -> Option { + Some(Self::MIN) + } + + fn last() -> Option { + 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 { + None + } + + fn previous(&self) -> Option { + None + } + + fn first() -> Option { + Some(()) + } + + fn last() -> Option { + Some(()) + } +} + +impl Sequence for core::convert::Infallible { + const CARDINALITY: usize = 0; + + fn next(&self) -> Option { + None + } + + fn previous(&self) -> Option { + None + } + + fn first() -> Option { + None + } + + fn last() -> Option { + None + } +} + +impl Sequence for Ordering { + const CARDINALITY: usize = 3; + + fn next(&self) -> Option { + int_to_ordering(*self as i8 + 1) + } + + fn previous(&self) -> Option { + int_to_ordering(*self as i8 - 1) + } + + fn first() -> Option { + Some(Ordering::Less) + } + + fn last() -> Option { + Some(Ordering::Greater) + } +} + +fn int_to_ordering(i: i8) -> Option { + match i { + -1 => Some(Ordering::Less), + 0 => Some(Ordering::Equal), + 1 => Some(Ordering::Greater), + _ => None, + } +} + +impl Sequence for Option { + const CARDINALITY: usize = T::CARDINALITY + 1; + + fn next(&self) -> Option { + match self { + None => T::first().map(Some), + Some(x) => x.next().map(Some), + } + } + + fn previous(&self) -> Option { + self.as_ref().map(T::previous) + } + + fn first() -> Option { + Some(None) + } + + fn last() -> Option { + Some(T::last()) + } +} + +impl Sequence for Poll { + const CARDINALITY: usize = T::CARDINALITY + 1; + + fn next(&self) -> Option { + match self { + Poll::Ready(x) => x.next().map(Poll::Ready).or(Some(Poll::Pending)), + Poll::Pending => None, + } + } + + fn previous(&self) -> Option { + match self { + Poll::Ready(x) => x.previous().map(Poll::Ready), + Poll::Pending => T::last().map(Poll::Ready), + } + } + + fn first() -> Option { + T::first().map(Poll::Ready).or(Some(Poll::Pending)) + } + + fn last() -> Option { + Some(Poll::Pending) + } +} + +impl 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 { + advance_for_array(self, T::first) + } + + fn previous(&self) -> Option { + advance_for_array(self, T::last) + } + + fn first() -> Option { + if N == 0 { + Some(core::array::from_fn(|_| unreachable!())) + } else { + let x = T::first()?; + Some(core::array::from_fn(|_| x.clone())) + } + } + + fn last() -> Option { + 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(a: &[T; N], reset: R) -> Option<[T; N]> +where + T: Sequence + Clone, + R: Fn() -> Option, +{ + 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 { + impl_seq_advance_for_tuple!(self, next, first @ $($types,)*) + } + + fn previous(&self) -> Option { + impl_seq_advance_for_tuple!(self, previous, last @ $($types,)*) + } + + fn first() -> Option { + Some(( + $(<$types as Sequence>::first()?,)* + <$last as Sequence>::first()?, + )) + } + + fn last() -> Option { + 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() { + assert_eq!(cardinality::(), all::().count()); + } + + #[test] + fn cardinality_equals_item_count_for_bool() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_bool_values_are_yielded() { + assert!(all::().eq([false, true])); + } + + #[test] + fn all_bool_values_are_yielded_in_reverse() { + assert!(reverse_all::().eq([true, false])); + } + + #[test] + fn cardinality_equals_item_count_for_i8() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_i8_values_are_yielded() { + assert!(all::().eq(i8::MIN..=i8::MAX)); + } + + #[test] + fn all_i8_values_are_yielded_in_reverse() { + assert!(reverse_all::().eq((i8::MIN..=i8::MAX).rev())); + } + + #[test] + fn cardinality_equals_item_count_for_u8() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_u8_values_are_yielded() { + assert!(all::().eq(u8::MIN..=u8::MAX)); + } + + #[test] + fn all_u8_values_are_yielded_in_reverse() { + assert!(reverse_all::().eq((u8::MIN..=u8::MAX).rev())); + } + + #[test] + fn cardinality_equals_item_count_for_i16() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_i16_values_are_yielded() { + assert!(all::().eq(i16::MIN..=i16::MAX)); + } + + #[test] + fn all_i16_values_are_yielded_in_reverse() { + assert!(reverse_all::().eq((i16::MIN..=i16::MAX).rev())); + } + + #[test] + fn cardinality_equals_item_count_for_u16() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_u16_values_are_yielded() { + assert!(all::().eq(u16::MIN..=u16::MAX)); + } + + #[test] + fn all_u16_values_are_yielded_in_reverse() { + assert!(reverse_all::().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::(); + } + + #[test] + fn all_infallible_values_are_yielded() { + assert!(all::().next().is_none()); + } + + #[test] + fn all_infallible_values_are_yielded_in_reverse() { + assert!(reverse_all::().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::>(); + } + + #[test] + fn all_bool_option_items_are_yielded() { + assert!(all::>().eq([None, Some(false), Some(true)])); + } + + #[test] + fn all_bool_option_items_are_yielded_in_reverse() { + assert!(reverse_all::>().eq([Some(true), Some(false), None])); + } + + #[test] + fn all_infallible_option_items_are_yielded() { + assert!(all::>().eq([None])); + } + + #[test] + fn all_infallible_option_items_are_yielded_in_reverse() { + assert!(reverse_all::>().eq([None])); + } + + #[test] + fn cardinality_equals_item_count_for_ordering() { + cardinality_equals_item_count::(); + } + + #[test] + fn all_ordering_values_are_yielded() { + assert!(all::().eq([Ordering::Less, Ordering::Equal, Ordering::Greater])); + } + + #[test] + fn all_ordering_values_are_yielded_in_reverse() { + assert!(reverse_all::().eq([Ordering::Greater, Ordering::Equal, Ordering::Less])); + } + + #[test] + fn cardinality_equals_item_count_for_poll() { + cardinality_equals_item_count::>(); + } + + #[test] + fn all_bool_poll_items_are_yielded() { + assert!(all::>().eq([Poll::Ready(false), Poll::Ready(true), Poll::Pending])); + } + + #[test] + fn all_bool_poll_items_are_yielded_in_reverse() { + assert!(reverse_all::>().eq([ + Poll::Pending, + Poll::Ready(true), + Poll::Ready(false), + ])); + } + + #[test] + fn all_infallible_poll_items_are_yielded() { + assert!(all::>().eq([Poll::Pending])); + } + + #[test] + fn all_infallible_poll_items_are_yielded_in_reverse() { + assert!(reverse_all::>().eq([Poll::Pending])); + } + + #[test] + fn tuple_fields_vary_from_right_to_left() { + assert!(all::<(Option, 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; 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()); + } +} diff --git a/tests/derive.rs b/tests/derive.rs new file mode 100644 index 0000000..7b73c49 --- /dev/null +++ b/tests/derive.rs @@ -0,0 +1,183 @@ +// Copyright (c) 2018-2022 Stephane Raux. Distributed under the 0BSD license. + +use enum_iterator::{all, cardinality, reverse_all, Sequence}; +use std::{convert::Infallible, iter::once}; + +#[derive(Clone, Copy, Debug, PartialEq, Sequence)] +enum Direction { + North, + South, + West, + East, +} + +#[derive(Clone, Debug, PartialEq, Sequence)] +enum Either { + Left(L), + Right(R), +} + +#[test] +fn all_values_of_generic_type_are_yielded() { + assert_eq!(cardinality::>(), 6); + assert_eq!( + all::>().collect::>(), + [ + Either::Left(false), + Either::Left(true), + Either::Right(Direction::North), + Either::Right(Direction::South), + Either::Right(Direction::West), + Either::Right(Direction::East), + ] + ); +} + +#[derive(Clone, Debug, PartialEq, Sequence)] +struct Foo { + x: T, +} + +#[test] +fn all_values_of_generic_type_with_trait_bound_are_yielded() { + assert_eq!(cardinality::>(), 2); + assert_eq!( + all::>().collect::>(), + [Foo { x: false }, Foo { x: true }], + ); +} + +#[test] +fn all_values_of_enum_type_with_empty_variant_are_yielded() { + assert_eq!(cardinality::>(), 2); + assert_eq!( + all::>().collect::>(), + [Either::Right(false), Either::Right(true)], + ); +} + +#[derive(Debug, PartialEq, Sequence)] +struct Impossible { + a: bool, + b: Infallible, +} + +#[test] +fn all_values_of_impossible_are_yielded() { + assert_eq!(cardinality::(), 0); + assert!(all::().next().is_none()); +} + +#[derive(Debug, PartialEq, Sequence)] +enum Move { + Stay, + Basic(Direction), + Fancy(FancyMove), + Jump { + direction: Direction, + somersault: bool, + }, + Swim(Direction, SwimmingStyle), +} + +#[derive(Debug, PartialEq, Sequence)] +struct FancyMove { + direction: Direction, + fast: bool, +} + +#[derive(Debug, PartialEq, Sequence)] +enum SwimmingStyle { + Breaststroke, + FrontCrawl, +} + +fn all_moves() -> Vec { + let directions = [ + Direction::North, + Direction::South, + Direction::West, + Direction::East, + ]; + once(Move::Stay) + .chain(directions.into_iter().map(Move::Basic)) + .chain( + directions + .into_iter() + .flat_map(|d| [(d, false), (d, true)]) + .map(|(direction, fast)| FancyMove { direction, fast }) + .map(Move::Fancy), + ) + .chain( + directions + .into_iter() + .flat_map(|d| [(d, false), (d, true)]) + .map(|(direction, somersault)| Move::Jump { + direction, + somersault, + }), + ) + .chain( + directions + .into_iter() + .flat_map(|d| { + [ + (d, SwimmingStyle::Breaststroke), + (d, SwimmingStyle::FrontCrawl), + ] + }) + .map(|(direction, style)| Move::Swim(direction, style)), + ) + .collect() +} + +#[test] +fn all_works() { + assert_eq!(all::().collect::>(), all_moves()); +} + +#[test] +fn reverse_all_works() { + let expected = { + let mut moves = all_moves(); + moves.reverse(); + moves + }; + assert_eq!(reverse_all::().collect::>(), expected); +} + +#[derive(Debug, PartialEq, Sequence)] +enum Empty {} + +#[test] +fn empty_cadinality_is_zero() { + assert_eq!(cardinality::(), 0); +} + +#[test] +fn all_values_of_empty_are_yielded() { + assert_eq!(all::().collect::>(), Vec::new()); +} + +#[test] +fn all_values_of_empty_are_yielded_in_reverse() { + assert_eq!(reverse_all::().collect::>(), Vec::new()); +} + +#[derive(Debug, PartialEq, Sequence)] +struct Unit; + +#[test] +fn unit_cardinality_is_one() { + assert_eq!(cardinality::(), 1); +} + +#[test] +fn all_values_of_unit_are_yielded() { + assert_eq!(all::().collect::>(), vec![Unit]); +} + +#[test] +fn all_values_of_unit_are_yielded_in_reverse() { + assert_eq!(reverse_all::().collect::>(), vec![Unit]); +} -- 2.34.1