use crate::{
TriHashItem,
support::{
alloc::{Allocator, Global},
hash_builder::DefaultHashBuilder,
},
tri_hash_map::TriHashMap,
};
use core::{fmt, hash::BuildHasher};
use proptest::{
arbitrary::{Arbitrary, StrategyFor, any_with},
collection::{SizeRange, VecStrategy, VecValueTree},
strategy::{NewTree, Strategy, ValueTree},
test_runner::TestRunner,
};
#[must_use = "strategies do nothing unless used"]
#[derive(Clone)]
pub struct TriHashMapStrategy<T, S = DefaultHashBuilder, A = Global>
where
T: Strategy,
{
inner: VecStrategy<T>,
hasher: S,
allocator: A,
}
impl<T, S, A> fmt::Debug for TriHashMapStrategy<T, S, A>
where
T: Strategy,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TriHashMapStrategy")
.field("inner", &self.inner)
.finish_non_exhaustive()
}
}
#[cfg(feature = "default-hasher")]
pub fn prop_strategy<T: Strategy>(
element: T,
size: impl Into<SizeRange>,
) -> TriHashMapStrategy<T, DefaultHashBuilder, Global> {
TriHashMapStrategy {
inner: proptest::collection::vec(element, size),
hasher: DefaultHashBuilder::default(),
allocator: crate::support::alloc::global_alloc(),
}
}
pub fn prop_strategy_with_hasher<T: Strategy, S>(
element: T,
size: impl Into<SizeRange>,
hasher: S,
) -> TriHashMapStrategy<T, S, Global> {
let size = size.into();
TriHashMapStrategy {
inner: proptest::collection::vec(element, size),
hasher,
allocator: crate::support::alloc::global_alloc(),
}
}
pub fn prop_strategy_with_hasher_in<T: Strategy, S, A>(
element: T,
size: impl Into<SizeRange>,
hasher: S,
allocator: A,
) -> TriHashMapStrategy<T, S, A> {
let size = size.into();
TriHashMapStrategy {
inner: proptest::collection::vec(element, size),
hasher,
allocator,
}
}
impl<'a, T, S, A> Strategy for TriHashMapStrategy<T, S, A>
where
T: Strategy,
T::Value: 'a + TriHashItem,
<T::Value as TriHashItem>::K1<'a>: fmt::Debug,
<T::Value as TriHashItem>::K2<'a>: fmt::Debug,
<T::Value as TriHashItem>::K3<'a>: fmt::Debug,
S: Clone + BuildHasher,
A: Clone + Allocator,
{
type Tree = TriHashMapValueTree<T::Tree, S, A>;
type Value = TriHashMap<T::Value, S, A>;
fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
let inner = self.inner.new_tree(runner)?;
Ok(TriHashMapValueTree {
inner,
hasher: self.hasher.clone(),
allocator: self.allocator.clone(),
})
}
}
#[derive(Clone)]
pub struct TriHashMapValueTree<T, S = DefaultHashBuilder, A = Global>
where
T: ValueTree,
{
inner: VecValueTree<T>,
hasher: S,
allocator: A,
}
impl<T, S, A> fmt::Debug for TriHashMapValueTree<T, S, A>
where
T: ValueTree + fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("TriHashMapValueTree")
.field("inner", &self.inner)
.finish_non_exhaustive()
}
}
impl<'a, T, S, A> ValueTree for TriHashMapValueTree<T, S, A>
where
T: ValueTree,
T::Value: 'a + TriHashItem,
<T::Value as TriHashItem>::K1<'a>: fmt::Debug,
<T::Value as TriHashItem>::K2<'a>: fmt::Debug,
<T::Value as TriHashItem>::K3<'a>: fmt::Debug,
S: Clone + BuildHasher,
A: Clone + Allocator,
{
type Value = TriHashMap<T::Value, S, A>;
fn current(&self) -> Self::Value {
let items = self.inner.current();
let mut map = TriHashMap::with_hasher_in(
self.hasher.clone(),
self.allocator.clone(),
);
for item in items {
map.insert_overwrite(item);
}
map
}
fn simplify(&mut self) -> bool {
self.inner.simplify()
}
fn complicate(&mut self) -> bool {
self.inner.complicate()
}
}
impl<'a, T, S, A> Arbitrary for TriHashMap<T, S, A>
where
T: 'a + TriHashItem + Arbitrary,
<T as TriHashItem>::K1<'a>: fmt::Debug,
<T as TriHashItem>::K2<'a>: fmt::Debug,
<T as TriHashItem>::K3<'a>: fmt::Debug,
S: Clone + BuildHasher + Default,
A: Clone + Allocator + Default,
{
type Parameters = (SizeRange, T::Parameters);
type Strategy = TriHashMapStrategy<StrategyFor<T>, S, A>;
fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
let (size, element_args) = args;
prop_strategy_with_hasher_in(
any_with::<T>(element_args),
size,
S::default(),
A::default(),
)
}
}