flipflop 0.2.0

Stress-tester for double-ended iterators.
Documentation
//! Various iterator types.
use core::num::NonZero;

use crate::{accum::Finished, error::FailedStrategy, side::Side};

/// Iterator that applies a strategy to another iterator.
#[derive(Debug)]
pub struct Apply<I, S> {
    /// Iterator containing items.
    iter: I,

    /// Iterator containing strategy decisions.
    strategy: S,
}
impl<I: DoubleEndedIterator, S: Iterator<Item = Side>> Apply<I, S> {
    /// Creates a new iterator from its parts.
    #[inline]
    pub(crate) fn new(iter: I, strategy: S) -> Apply<I, S> {
        Apply { iter, strategy }
    }
}
impl<I: DoubleEndedIterator, S: Iterator<Item = Side>> Iterator for Apply<I, S> {
    type Item = (Side, I::Item);

    #[inline]
    fn next(&mut self) -> Option<(Side, I::Item)> {
        match self.strategy.next().expect("strategies should be infinite") {
            Side::Forward => self.iter.next().map(|next| (Side::Forward, next)),
            Side::Backward => self
                .iter
                .next_back()
                .map(|next_back| (Side::Backward, next_back)),
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        self.iter.size_hint()
    }
}

/// Iterator returned by [`Strategy::indexed_strategy`].
///
/// [`Strategy::indexed_strategy`]: crate::strategy::Strategy::indexed_strategy
#[derive(Clone, Debug)]
pub struct Indices<I> {
    /// Iterator from strategy.
    iter: I,

    /// Previously yielded forward index.
    forward: isize,

    /// Previously yielded backward index.
    backward: isize,

    /// Previously yielded side.
    prev: Side,
}
impl<I> Indices<I> {
    /// Creates a new iterator from its parts.
    pub(crate) fn new(iter: I) -> Indices<I> {
        Indices {
            iter,
            forward: -1,
            backward: 0,
            prev: Side::Forward,
        }
    }

    /// Convert the iterator into an error.
    ///
    /// This assumes that the previously yielded index was an error.
    #[inline]
    pub fn into_error<'a, T, S: ?Sized, V>(
        self,
        accum: Finished<'a, T>,
        strategy: &'a S,
        cause: V,
    ) -> FailedStrategy<'a, T, S, V> {
        FailedStrategy {
            side: self.prev,
            forward: (self.forward + 1).unsigned_abs(),
            backward: accum.len() - self.backward.unsigned_abs(),
            accum,
            strategy,
            cause,
        }
    }
}
impl<I: Iterator<Item = Side>> Iterator for Indices<I> {
    type Item = isize;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        self.prev = self.iter.next().expect("strategies should be infinite");
        match self.prev {
            Side::Forward => {
                self.forward += 1;
                Some(self.forward)
            }
            Side::Backward => {
                self.backward -= 1;
                Some(self.backward)
            }
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (isize::MAX.unsigned_abs(), Some(usize::MAX))
    }
}

/// Iterator returned by [`Alternate`].
///
/// [`Alternate`]: crate::strategy::Alternate
#[derive(Clone, Debug)]
pub struct Alternating {
    /// Current side to return.
    curr: Side,

    /// Number of iterations before alternating.
    left: usize,

    /// Period between alternations.
    period: NonZero<usize>,
}
impl Alternating {
    /// Makes a new alternating iterator.
    pub(crate) fn new(start: Side, period: NonZero<usize>) -> Alternating {
        Alternating {
            curr: start,
            left: period.get(),
            period,
        }
    }
}
impl Iterator for Alternating {
    type Item = Side;

    #[inline]
    fn next(&mut self) -> Option<Side> {
        if self.left > 0 {
            self.left -= 1;
            Some(self.curr)
        } else {
            self.left = self.period.get() - 1;
            self.curr = self.curr.swap();
            Some(self.curr)
        }
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        (usize::MAX, None)
    }
}

/// Iterator returned by [`Todo`].
///
/// [`Todo`]: crate::strategy::Todo
#[derive(Clone, Debug)]
pub struct Todo;
impl Iterator for Todo {
    type Item = Side;

    #[inline]
    fn next(&mut self) -> Option<Side> {
        unimplemented!()
    }
}

/// Things that require the [`rand`] crate.
#[cfg(any(doc, feature = "rand"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "rand")))]
mod rand {
    use crate::side::Side;

    /// Random strategy iterator.
    #[derive(Debug)]
    pub struct Randomly<R> {
        /// RNG.
        rng: R,

        /// Probability of choosing forward direction.
        forward_prob: f64,
    }
    impl<#[cfg(feature = "rand")] R: rand::SeedableRng, #[cfg(not(feature = "rand"))] R> Randomly<R> {
        /// Creates an iterator from its parts.
        #[inline]
        pub(crate) fn new(
            #[cfg(feature = "rand")] seed: R::Seed,
            #[cfg(not(feature = "rand"))] seed: (),
            forward_prob: f64,
        ) -> Randomly<R> {
            #[cfg(feature = "rand")]
            {
                Randomly {
                    rng: R::from_seed(seed),
                    forward_prob,
                }
            }
            #[cfg(not(feature = "rand"))]
            {
                unimplemented!()
            }
        }
    }
    impl<#[cfg(feature = "rand")] R: rand::RngCore, #[cfg(not(feature = "rand"))] R> Iterator
        for Randomly<R>
    {
        type Item = Side;

        #[inline]
        fn next(&mut self) -> Option<Side> {
            #[cfg(feature = "rand")]
            {
                if rand::Rng::random_bool(&mut self.rng, self.forward_prob) {
                    Some(Side::Forward)
                } else {
                    Some(Side::Backward)
                }
            }
            #[cfg(not(feature = "rand"))]
            {
                unimplemented!()
            }
        }
    }
}

#[cfg(any(doc, feature = "rand"))]
#[cfg_attr(feature = "nightly", doc(cfg(feature = "rand")))]
pub use self::rand::Randomly;