use core::fmt::Debug;
use proptest::prelude::RngCore;
use proptest::test_runner::TestRunner;
use std::marker::PhantomData;
pub trait ArbInterop: for<'a> arbitrary::Arbitrary<'a> + 'static + Debug + Clone {}
impl<A> ArbInterop for A where A: for<'a> arbitrary::Arbitrary<'a> + 'static + Debug + Clone {}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ArbStrategy<A: ArbInterop> {
size: usize,
_ph: PhantomData<A>,
}
#[derive(Debug)]
pub struct ArbValueTree<A: Debug> {
bytes: Vec<u8>,
curr: A,
prev: Option<A>,
next: usize,
}
impl<A: ArbInterop> proptest::strategy::ValueTree for ArbValueTree<A> {
type Value = A;
fn current(&self) -> Self::Value {
self.curr.clone()
}
fn simplify(&mut self) -> bool {
if self.next == 0 {
return false;
}
self.next -= 1;
let Ok(simpler) = Self::gen_one_with_size(&self.bytes, self.next) else {
return false;
};
self.prev = Some(core::mem::replace(&mut self.curr, simpler));
true
}
fn complicate(&mut self) -> bool {
let Some(prev) = self.prev.take() else {
return false;
};
self.curr = prev;
true
}
}
impl<A: ArbInterop> ArbStrategy<A> {
pub fn new(size: usize) -> Self {
Self {
size,
_ph: PhantomData,
}
}
}
impl<A: ArbInterop> ArbValueTree<A> {
fn gen_one_with_size(bytes: &[u8], size: usize) -> Result<A, arbitrary::Error> {
let mut unstructured = arbitrary::Unstructured::new(&bytes[0..size]);
A::arbitrary(&mut unstructured)
}
pub fn new(bytes: Vec<u8>) -> Result<Self, arbitrary::Error> {
let next = bytes.len();
let curr = Self::gen_one_with_size(&bytes, next)?;
Ok(Self {
bytes,
prev: None,
curr,
next,
})
}
}
impl<A: ArbInterop> proptest::strategy::Strategy for ArbStrategy<A> {
type Tree = ArbValueTree<A>;
type Value = A;
fn new_tree(&self, run: &mut TestRunner) -> proptest::strategy::NewTree<Self> {
loop {
let mut bytes = vec![0; self.size];
run.rng().fill_bytes(&mut bytes);
match ArbValueTree::new(bytes) {
Ok(v) => return Ok(v),
Err(e @ arbitrary::Error::IncorrectFormat) => run.reject_local(format!("{e}"))?,
Err(e) => return Err(format!("{e}").into()),
}
}
}
}
pub fn arb_sized<A: ArbInterop>(size: usize) -> ArbStrategy<A> {
ArbStrategy::new(size)
}
pub fn arb<A: ArbInterop>() -> ArbStrategy<A> {
let (low, opt_high) = A::size_hint(0);
let Some(high) = opt_high else {
return arb_sized(256.max(2 * low));
};
arb_sized(high)
}