use crate::{boxed, utility::tuples};
use std::num::NonZeroUsize;
#[derive(Clone, Debug)]
pub struct All<T: ?Sized> {
pub index: usize,
pub inner: T,
}
pub trait FullShrink {
type Item;
type Shrink: Shrink<Item = Self::Item>;
fn shrinker(item: Self::Item) -> Option<Self::Shrink>;
}
pub trait IntoShrink {
type Item;
type Shrink: Shrink<Item = Self::Item>;
fn shrinker(&self, item: Self::Item) -> Option<Self::Shrink>;
}
pub trait Shrink: Clone {
type Item;
fn item(&self) -> Self::Item;
fn shrink(&mut self) -> Option<Self>;
fn boxed(self) -> boxed::Shrinker<Self::Item>
where
Self: 'static,
{
boxed::Shrinker::new(self)
}
}
impl<T: FullShrink> FullShrink for &T {
type Item = T::Item;
type Shrink = T::Shrink;
fn shrinker(item: Self::Item) -> Option<Self::Shrink> {
T::shrinker(item)
}
}
impl<T: FullShrink> FullShrink for &mut T {
type Item = T::Item;
type Shrink = T::Shrink;
fn shrinker(item: Self::Item) -> Option<Self::Shrink> {
T::shrinker(item)
}
}
impl<T: IntoShrink> IntoShrink for &T {
type Item = T::Item;
type Shrink = T::Shrink;
fn shrinker(&self, item: Self::Item) -> Option<Self::Shrink> {
T::shrinker(self, item)
}
}
impl<T: IntoShrink> IntoShrink for &mut T {
type Item = T::Item;
type Shrink = T::Shrink;
fn shrinker(&self, item: Self::Item) -> Option<Self::Shrink> {
T::shrinker(self, item)
}
}
impl<T> All<T> {
pub const fn new(inner: T) -> Self {
Self { inner, index: 0 }
}
}
macro_rules! tuple {
($n:ident, $c:tt $(,$p:ident, $t:ident, $i:tt)*) => {
impl<$($t: FullShrink,)*> FullShrink for ($($t,)*) {
type Item = ($($t::Item,)*);
type Shrink = All<($($t::Shrink,)*)>;
fn shrinker(_item: Self::Item) -> Option<Self::Shrink> {
Some(All::new(($($t::shrinker(_item.$i)?,)*)))
}
}
impl<$($t: IntoShrink,)*> IntoShrink for ($($t,)*) {
type Item = ($($t::Item,)*);
type Shrink = All<($($t::Shrink,)*)>;
fn shrinker(&self, _item: Self::Item) -> Option<Self::Shrink> {
Some(All::new(($(self.$i.shrinker(_item.$i)?,)*)))
}
}
impl<$($t: Shrink,)*> Shrink for All<($($t,)*)> {
type Item = ($($t::Item,)*);
#[allow(clippy::unused_unit)]
fn item(&self) -> Self::Item {
($(self.inner.$i.item(),)*)
}
fn shrink(&mut self) -> Option<Self> {
let count = NonZeroUsize::new($c)?;
let start = self.index;
self.index += 1;
for i in 0..count.get() {
let index = (start + i) % count.get();
match index {
$($i => {
if let Some(shrink) = self.inner.$i.shrink() {
let mut shrinks = self.inner.clone();
shrinks.$i = shrink;
return Some(Self {
inner: shrinks,
index: self.index
});
}
})*
_ => {}
}
}
None
}
}
};
}
tuples!(tuple);