diff --git a/Cargo.toml b/Cargo.toml index c4c6fa7..5776447 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,3 +19,5 @@ default = [] nightly_features = [] [dependencies] +typenum = { version = "1.17.0", features = ["const-generics"] } +typenum_mappings = "0.1.0" diff --git a/src/helpers.rs b/src/helpers.rs index 0549a1b..00cf8c6 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,22 +1,50 @@ -#[cfg(feature = "nightly_features")] -pub const fn min(a: usize, b: usize) -> usize { - if a < b { - a - } else { - b - } +use typenum::{operator_aliases as ty_ops, U}; + +use crate::IteratorFixed; + +/// A trait implemented for all `IterFixed` to allow constructing `IterFixed` when `N` is unnamable. +pub trait ErasedIterFixed { + /// # Safety + /// + /// The N value of the [`IterFixed`] created must be the same as the number of items in the Iterator. + unsafe fn new(iter: I) -> Self; } -#[cfg(feature = "nightly_features")] -pub const fn sub_or_zero(a: usize, b: usize) -> usize { - if a > b { - a - b - } else { - 0 +impl ErasedIterFixed for IteratorFixed { + unsafe fn new(inner: I) -> Self { + Self { inner } } } -#[cfg(feature = "nightly_features")] -pub const fn ceiling_div(x: usize, d: usize) -> usize { - x / d + (x % d != 0) as usize +pub trait TypenumToFixedIter { + type FixedIter: ErasedIterFixed; } + +typenum_mappings::impl_typenum_mapping!( + impl TypenumToFixedIter for #TypeNumName { + type FixedIter = IteratorFixed; + } +); + +pub(crate) type RunTypeNumToFixedIter = >::FixedIter; + +pub(crate) type TyNot = ::Output; +type TyCelDiv = ty_ops::Sum< + // X / D + ty_ops::Quot, + // + ! + TyNot< + ty_ops::Eq< + // X % 0 + ty_ops::Mod, + // == 0 + typenum::U0, + >, + >, +>; + +pub(crate) type RunCelDiv = TyCelDiv, U>; +pub(crate) type RunAdd = ty_ops::Sum, U>; +pub(crate) type RunSub = ty_ops::Diff, U>; +pub(crate) type RunMul = ty_ops::Prod, U>; +pub(crate) type RunMin = ty_ops::Minimum, U>; diff --git a/src/lib.rs b/src/lib.rs index db3dfbc..120496f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,17 +51,26 @@ #![warn(clippy::missing_inline_in_public_items)] #![warn(clippy::use_self)] -use core::iter; +use core::{ + iter, + ops::{Add, Div, Mul, Not, Rem, Sub}, +}; mod from; mod helpers; mod into; -#[cfg(feature = "nightly_features")] -use helpers::{ceiling_div, min, sub_or_zero}; +use helpers::*; pub use from::FromIteratorFixed; pub use into::IntoIteratorFixed; +use typenum::{Const, Eq, IsEqual, Min, Mod, ToUInt, U, U0}; + +type FlattenIter = iter::FlatMap< + I, + IteratorFixed<>::IntoIter, M>, + fn(IIF) -> IteratorFixed<>::IntoIter, M>, +>; /// Iterator of fixed size /// @@ -71,10 +80,10 @@ pub use into::IntoIteratorFixed; /// /// Just like [`Iterator`], [`IteratorFixed`] provides a lot of methods like: /// - available on stable rust: -/// [`map`], [`inspect`], [`enumerate`], [`zip`], [`rev`], [`copied`], [`cloned`] +/// [`map`], [`inspect`], [`enumerate`], [`zip`], [`rev`], [`copied`], [`cloned`], [`skip`], [`step_by`], [`chain`], [`take`], [`flatten`] /// /// - requires nightly compiler and enable `nightly_features`: -/// [`skip`], [`step_by`], [`chain`], [`take`], [`flatten`] +/// [`flat_map`] /// /// however it does not support methods like `filter` or `take_while` which will affect the length during runtime. /// @@ -90,6 +99,7 @@ pub use into::IntoIteratorFixed; /// [`chain`]: IteratorFixed::chain /// [`take`]: IteratorFixed::take /// [`flatten`]: IteratorFixed::flatten +/// [`flat_map`]: IteratorFixed::flat_map pub struct IteratorFixed { inner: I, } @@ -160,40 +170,52 @@ where // TODO: what should happen when SKIP > N? /// See [`core::iter::Iterator::skip`] - #[cfg(feature = "nightly_features")] #[inline] - pub fn skip( - self, - ) -> IteratorFixed, { sub_or_zero(N, SKIP) }> { - IteratorFixed { - inner: self.inner.skip(SKIP), - } + pub fn skip(self) -> RunTypeNumToFixedIter, RunSub> + where + Const: ToUInt, + Const: ToUInt, + + U: Sub, Output: TypenumToFixedIter>>, + { + unsafe { ErasedIterFixed::new(self.inner.skip(SKIP)) } } /// See [`core::iter::Iterator::step_by`] - #[cfg(feature = "nightly_features")] #[inline] pub fn step_by( self, - ) -> IteratorFixed, { ceiling_div(N, STEP) }> { - IteratorFixed { - inner: self.inner.step_by(STEP), - } + ) -> RunTypeNumToFixedIter, RunCelDiv> + where + Const: ToUInt, + Const: ToUInt, + + U: Rem, Output: IsEqual>, + U: Div< + U, + Output: Add< + TyNot, U>, U0>>, + Output: TypenumToFixedIter>, + >, + >, + { + unsafe { ErasedIterFixed::new(self.inner.step_by(STEP)) } } /// See [`core::iter::Iterator::chain`] - #[cfg(feature = "nightly_features")] #[inline] pub fn chain( self, other: IIF, - ) -> IteratorFixed, { N + M }> + ) -> RunTypeNumToFixedIter, RunAdd> where + Const: ToUInt, + Const: ToUInt, + IIF: IntoIteratorFixed, + U: Add, Output: TypenumToFixedIter>>, { - IteratorFixed { - inner: self.inner.chain(other.into_iter_fixed().inner), - } + unsafe { ErasedIterFixed::new(self.inner.chain(other.into_iter_fixed().inner)) } } /// See [`core::iter::Iterator::enumerate`] @@ -205,14 +227,15 @@ where } /// See [`core::iter::Iterator::take`] - #[cfg(feature = "nightly_features")] #[inline] - pub fn take( - self, - ) -> IteratorFixed, { min(TAKE, N) }> { - IteratorFixed { - inner: self.inner.take(TAKE), - } + pub fn take(self) -> RunTypeNumToFixedIter, RunMin> + where + Const: ToUInt, + Const: ToUInt, + + U: Min, Output: TypenumToFixedIter>>, + { + unsafe { ErasedIterFixed::new(self.inner.take(TAKE)) } } /// See [`core::iter::Iterator::zip`] @@ -251,21 +274,22 @@ where } } - #[cfg(feature = "nightly_features")] #[inline] pub fn flatten( self, - ) -> IteratorFixed, { M * N }> + ) -> RunTypeNumToFixedIter, RunMul> where + Const: ToUInt, + Const: ToUInt, + U: Mul, Output: TypenumToFixedIter>>, + I: Iterator, IIF: IntoIteratorFixed, { // The call to into_iter_fixed is needed because we cannot trust that // let x: I::Item; // x.into_iterator() == x.into_iter_fixed().into_iterator() - IteratorFixed { - inner: self.inner.flat_map(IntoIteratorFixed::into_iter_fixed), - } + unsafe { ErasedIterFixed::new(self.inner.flat_map(IIF::into_iter_fixed as fn(_) -> _)) } } #[cfg(feature = "nightly_features")] diff --git a/tests/test.rs b/tests/test.rs index 3c36708..6bcddc0 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -22,7 +22,6 @@ fn test() { assert_eq!(res, [(1, 42), (2, 42), (3, 42)]); } -#[cfg(feature = "nightly_features")] #[test] fn test_changing_length() { let res: [_; 3] = [1, 2, 3, 4].into_iter_fixed().skip::<1>().collect(); @@ -59,14 +58,17 @@ fn test_changing_length() { assert_eq!(res, [1, 2, 3, 4]); - let res: [_; 6] = [1, 2, 3].into_iter_fixed().flat_map(|x| [x, x]).collect(); + #[cfg(feature = "nightly_features")] + { + let res: [_; 6] = [1, 2, 3].into_iter_fixed().flat_map(|x| [x, x]).collect(); - assert_eq!(res, [1, 1, 2, 2, 3, 3]); + assert_eq!(res, [1, 1, 2, 2, 3, 3]); - let res: [_; 6] = [1, 2, 3] - .into_iter_fixed() - .flat_map(|x| IntoIteratorFixed::<2>::into_iter_fixed(core::iter::repeat(x))) - .collect(); + let res: [_; 6] = [1, 2, 3] + .into_iter_fixed() + .flat_map(|x| IntoIteratorFixed::<2>::into_iter_fixed(core::iter::repeat(x))) + .collect(); - assert_eq!(res, [1, 1, 2, 2, 3, 3]); + assert_eq!(res, [1, 1, 2, 2, 3, 3]); + } }