checkito 5.0.0

A safe, efficient and simple QuickCheck-inspired library to generate shrinkable random data mainly oriented towards generative/property/exploratory testing.
Documentation
use crate::{
    cardinality,
    generate::{FullGenerate, Generate},
    shrink::Shrink,
    state::State,
    utility::tuples,
};

#[derive(Clone, Debug)]
pub struct Shrinker<S: ?Sized> {
    pub(crate) index: usize,
    pub(crate) shrinkers: S,
}

pub(crate) fn shrink<S: Shrink, I: AsMut<[S]> + Clone>(
    shrinkers: &mut I,
    index: &mut usize,
) -> Option<I> {
    loop {
        let old = shrinkers.as_mut().get_mut(*index)?;
        if let Some(new) = old.shrink() {
            let mut shrinkers = shrinkers.clone();
            shrinkers.as_mut()[*index] = new;
            break Some(shrinkers);
        } else {
            *index += 1;
        }
    }
}

mod array {
    use super::*;
    use crate::array::Array;
    use core::array;

    impl<G: FullGenerate, const N: usize> FullGenerate for [G; N] {
        type Generator = [G::Generator; N];
        type Item = [G::Item; N];

        fn generator() -> Self::Generator {
            array::from_fn(|_| G::generator())
        }
    }

    impl<G: Generate, const N: usize> Generate for [G; N] {
        type Item = [G::Item; N];
        type Shrink = Shrinker<[G::Shrink; N]>;

        const CARDINALITY: Option<u128> = Array::<G, N>::CARDINALITY;

        fn generate(&self, state: &mut State) -> Self::Shrink {
            Shrinker {
                index: 0,
                shrinkers: array::from_fn(|index| self[index].generate(state)),
            }
        }

        fn cardinality(&self) -> Option<u128> {
            self.iter()
                .map(G::cardinality)
                .fold(Some(1), cardinality::all_product)
        }
    }

    impl<S: Shrink, const N: usize> Shrink for Shrinker<[S; N]> {
        type Item = [S::Item; N];

        fn item(&self) -> Self::Item {
            array::from_fn(|index| self.shrinkers[index].item())
        }

        fn shrink(&mut self) -> Option<Self> {
            let shrinkers = shrink(&mut self.shrinkers, &mut self.index)?;
            Some(Self {
                shrinkers,
                index: self.index,
            })
        }
    }
}

mod slice {
    use super::*;

    impl<G: Generate> Generate for [G] {
        type Item = Box<[G::Item]>;
        type Shrink = Shrinker<Box<[G::Shrink]>>;

        const CARDINALITY: Option<u128> = None;

        fn generate(&self, state: &mut State) -> Self::Shrink {
            Shrinker {
                index: 0,
                shrinkers: self
                    .iter()
                    .map(|generator| generator.generate(state))
                    .collect(),
            }
        }

        fn cardinality(&self) -> Option<u128> {
            self.iter()
                .map(G::cardinality)
                .fold(Some(1), cardinality::all_product)
        }
    }

    impl<S: Shrink> Shrink for Shrinker<Box<[S]>> {
        type Item = Box<[S::Item]>;

        fn item(&self) -> Self::Item {
            self.shrinkers.iter().map(S::item).collect()
        }

        fn shrink(&mut self) -> Option<Self> {
            let shrinkers = shrink(&mut self.shrinkers, &mut self.index)?;
            Some(Self {
                shrinkers,
                index: self.index,
            })
        }
    }
}

mod vector {
    use super::*;

    impl<G: Generate> Generate for Vec<G> {
        type Item = Vec<G::Item>;
        type Shrink = Shrinker<Vec<G::Shrink>>;

        const CARDINALITY: Option<u128> = None;

        fn generate(&self, state: &mut State) -> Self::Shrink {
            Shrinker {
                index: 0,
                shrinkers: self
                    .iter()
                    .map(|generator| generator.generate(state))
                    .collect(),
            }
        }

        fn cardinality(&self) -> Option<u128> {
            self.iter()
                .map(G::cardinality)
                .fold(Some(1), cardinality::all_product)
        }
    }

    impl<S: Shrink> Shrink for Shrinker<Vec<S>> {
        type Item = Vec<S::Item>;

        fn item(&self) -> Self::Item {
            self.shrinkers.iter().map(S::item).collect()
        }

        fn shrink(&mut self) -> Option<Self> {
            let shrinkers = shrink(&mut self.shrinkers, &mut self.index)?;
            Some(Self {
                shrinkers,
                index: self.index,
            })
        }
    }
}

macro_rules! tuple {
    ($n:ident, $c:tt $(,$p:ident, $t:ident, $i:tt)*) => {
        impl<$($t: FullGenerate,)*> FullGenerate for ($($t,)*) {
            type Generator = ($($t::Generator,)*);
            type Item = ($($t::Item,)*);

            #[allow(clippy::unused_unit)]
            fn generator() -> Self::Generator {
                ($($t::generator(),)*)
            }
        }

        impl<$($t: Generate,)*> Generate for ($($t,)*) {
            type Item = ($($t::Item,)*);
            type Shrink = Shrinker<($($t::Shrink,)*)>;

            const CARDINALITY: Option<u128> = {
                let cardinality = Some(1);
                $(let cardinality = cardinality::all_product(cardinality, $t::CARDINALITY);)*
                cardinality
            };

            fn generate(&self, _state: &mut State) -> Self::Shrink {
                Shrinker {
                    index: 0,
                    shrinkers: ($($t::generate(&self.$i, _state),)*),
                }
            }

            fn cardinality(&self) -> Option<u128> {
                let cardinality = Some(1);
                $(let cardinality = cardinality::all_product(cardinality, self.$i.cardinality());)*
                cardinality
            }
        }

        impl<$($t: Shrink,)*> Shrink for Shrinker<($($t,)*)> {
            type Item = ($($t::Item,)*);

            #[allow(clippy::unused_unit)]
            fn item(&self) -> Self::Item {
                ($(self.shrinkers.$i.item(),)*)
            }

            fn shrink(&mut self) -> Option<Self> {
                loop {
                    match self.index {
                        $($i => {
                            if let Some(shrinker) = self.shrinkers.$i.shrink() {
                                let mut shrinkers = self.shrinkers.clone();
                                shrinkers.$i = shrinker;
                                break Some(Self { shrinkers, index: self.index });
                            } else {
                                self.index += 1;
                            }
                        })*
                        _ => break None,
                    }
                }
            }
        }
    };
}

tuples!(tuple);