use std::{
array,
hash::{Hash, RandomState},
};
use crate::{Die, Limit, Prng, dice};
pub trait Dice: Sized {
const USES_LIMIT: bool;
fn die() -> impl Die<Self>;
}
pub fn die<T: Dice>() -> impl Die<T> {
T::die()
}
macro_rules! impl_dice_for_primitve {
($type:ty, $die:expr) => {
impl Dice for $type {
const USES_LIMIT: bool = false;
fn die() -> impl Die<Self> {
$die
}
}
};
}
impl_dice_for_primitve!((), dice::just(()));
impl_dice_for_primitve!(bool, dice::bool());
impl_dice_for_primitve!(usize, dice::usize(..));
impl_dice_for_primitve!(u8, dice::u8(..));
impl_dice_for_primitve!(u16, dice::u16(..));
impl_dice_for_primitve!(u32, dice::u32(..));
impl_dice_for_primitve!(u64, dice::u64(..));
impl_dice_for_primitve!(u128, dice::u128(..));
impl_dice_for_primitve!(isize, dice::isize(..));
impl_dice_for_primitve!(i8, dice::i8(..));
impl_dice_for_primitve!(i16, dice::i16(..));
impl_dice_for_primitve!(i32, dice::i32(..));
impl_dice_for_primitve!(i64, dice::i64(..));
impl_dice_for_primitve!(i128, dice::i128(..));
impl_dice_for_primitve!(f32, dice::any_f32());
impl_dice_for_primitve!(f64, dice::any_f64());
impl_dice_for_primitve!(char, dice::char());
impl_dice_for_primitve!(
std::num::NonZeroU8,
dice::u8(1..).map(|int| std::num::NonZeroU8::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroU16,
dice::u16(1..).map(|int| std::num::NonZeroU16::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroU32,
dice::u32(1..).map(|int| std::num::NonZeroU32::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroU64,
dice::u64(1..).map(|int| std::num::NonZeroU64::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroU128,
dice::u128(1..).map(|int| std::num::NonZeroU128::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroUsize,
dice::usize(1..).map(|int| std::num::NonZeroUsize::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroI8,
dice::one_of_die()
.two(dice::i8(..=-1), dice::i8(1..))
.map(|int| std::num::NonZeroI8::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroI16,
dice::one_of_die()
.two(dice::i16(..=-1), dice::i16(1..))
.map(|int| std::num::NonZeroI16::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroI32,
dice::one_of_die()
.two(dice::i32(..=-1), dice::i32(1..))
.map(|int| std::num::NonZeroI32::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroI64,
dice::one_of_die()
.two(dice::i64(..=-1), dice::i64(1..))
.map(|int| std::num::NonZeroI64::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroI128,
dice::one_of_die()
.two(dice::i128(..=-1), dice::i128(1..))
.map(|int| std::num::NonZeroI128::new(int).unwrap())
);
impl_dice_for_primitve!(
std::num::NonZeroIsize,
dice::one_of_die()
.two(dice::isize(..=-1), dice::isize(1..))
.map(|int| std::num::NonZeroIsize::new(int).unwrap())
);
impl<T: ?Sized> Dice for std::marker::PhantomData<T> {
const USES_LIMIT: bool = false;
fn die() -> impl Die<Self> {
dice::just(Self)
}
}
impl Dice for std::marker::PhantomPinned {
const USES_LIMIT: bool = false;
fn die() -> impl Die<Self> {
dice::just(Self)
}
}
impl<T: Dice> Dice for Option<T> {
const USES_LIMIT: bool = T::USES_LIMIT;
fn die() -> impl Die<Self> {
dice::option(T::die())
}
}
impl<T: Dice, E: Dice> Dice for Result<T, E> {
const USES_LIMIT: bool = T::USES_LIMIT || E::USES_LIMIT;
fn die() -> impl Die<Self> {
dice::result(T::die(), E::die())
}
}
macro_rules! impl_dice_for_wrapper {
($type:ty, $new:expr) => {
impl<T: Dice> Dice for $type {
const USES_LIMIT: bool = T::USES_LIMIT;
fn die() -> impl Die<Self> {
T::die().map($new)
}
}
};
}
impl_dice_for_wrapper!(Box<T>, Self::new);
impl_dice_for_wrapper!(std::rc::Rc<T>, Self::new);
impl_dice_for_wrapper!(std::sync::Arc<T>, Self::new);
impl_dice_for_wrapper!(std::cell::Cell<T>, Self::new);
impl_dice_for_wrapper!(std::cell::UnsafeCell<T>, Self::new);
impl_dice_for_wrapper!(std::cmp::Reverse<T>, Self);
impl<'a, T> Dice for std::borrow::Cow<'a, T>
where
T: ToOwned + ?Sized,
T::Owned: Dice,
{
const USES_LIMIT: bool = T::Owned::USES_LIMIT;
fn die() -> impl Die<Self> {
T::Owned::die().map(Self::Owned)
}
}
impl<T: Dice> Dice for std::cell::OnceCell<T> {
const USES_LIMIT: bool = T::USES_LIMIT;
fn die() -> impl Die<Self> {
dice::option(T::die()).map(|value| {
let cell = Self::new();
if let Some(value) = value {
let _result = cell.set(value);
}
cell
})
}
}
impl<T: Dice> Dice for std::task::Poll<T> {
const USES_LIMIT: bool = T::USES_LIMIT;
fn die() -> impl Die<Self> {
dice::option(T::die()).map(|value| value.map_or(Self::Pending, Self::Ready))
}
}
impl Dice for std::cmp::Ordering {
const USES_LIMIT: bool = false;
fn die() -> impl Die<Self> {
dice::one_of().three(
std::cmp::Ordering::Less,
std::cmp::Ordering::Equal,
std::cmp::Ordering::Greater,
)
}
}
impl Dice for String {
const USES_LIMIT: bool = true;
fn die() -> impl Die<Self> {
dice::string(char::die(), ..)
}
}
macro_rules! impl_dice_for_list_like_collection {
($type:ty, $builder:expr, $($constraint:ident),*) => {
impl<T> Dice for $type
where
T: Dice $(+ $constraint)*,
{
const USES_LIMIT: bool = true;
fn die() -> impl Die<Self> {
let t_die = T::die();
dice::from_fn(move |mut fate| {
let builder = fate.roll($builder);
if T::USES_LIMIT {
fate.roll(dice::outer_collection(builder, &t_die, ..))
} else {
let t_die = dice::from_fn(|mut fate| fate.with_limit(Limit(0)).roll(&t_die));
fate.roll(dice::collection(builder, &t_die, ..))
}
})
}
}
};
}
macro_rules! impl_dice_for_map_like_collection {
($type:ty, $builder:expr, $($constraint:ident),*) => {
impl<K, V> Dice for $type
where
K: Dice $(+ $constraint)*,
V: Dice,
{
const USES_LIMIT: bool = true;
fn die() -> impl Die<Self> {
let kv_die = <(K, V) as Dice>::die();
dice::from_fn(move |mut fate| {
let builder = fate.roll($builder);
if K::USES_LIMIT || V::USES_LIMIT {
fate.roll(dice::outer_collection(builder, &kv_die, ..))
} else {
let kv_die =
dice::from_fn(|mut fate| fate.with_limit(Limit(0)).roll(&kv_die));
fate.roll(dice::collection(builder, kv_die, ..))
}
})
}
}
};
}
impl_dice_for_list_like_collection!(Vec<T>, dice::just_once(dice::VecBuilder),);
impl_dice_for_list_like_collection!(
std::collections::VecDeque<T>,
dice::just_once(dice::VecDequeBuilder),
);
impl_dice_for_list_like_collection!(
std::collections::LinkedList<T>,
dice::just_once(dice::LinkedListBuilder),
);
impl_dice_for_list_like_collection!(
std::collections::BTreeSet<T>,
dice::just_once(dice::BTreeSetBuilder),
Ord
);
impl_dice_for_list_like_collection!(
std::collections::HashSet<T, Prng>,
dice::from_fn_once(|mut fate| dice::HashSetBuilder::with_hasher(fate.fork_prng())),
Eq,
Hash
);
impl_dice_for_list_like_collection!(
std::collections::HashSet<T>,
dice::from_fn_once(|_| dice::HashSetBuilder::with_hasher(RandomState::new())),
Eq,
Hash
);
impl_dice_for_list_like_collection!(
std::collections::BinaryHeap<T>,
dice::just_once(dice::BinaryHeapBuilder),
Ord
);
impl_dice_for_map_like_collection!(
std::collections::BTreeMap<K, V>,
dice::just_once(dice::BTreeMapBuilder),
Ord
);
impl_dice_for_map_like_collection!(
std::collections::HashMap<K, V, Prng>,
dice::from_fn_once(|mut fate| dice::HashMapBuilder::with_hasher(fate.fork_prng())),
Eq,
Hash
);
impl_dice_for_map_like_collection!(
std::collections::HashMap<K, V>,
dice::from_fn_once(|_| dice::HashMapBuilder::with_hasher(RandomState::new())),
Eq,
Hash
);
impl<T: Dice, const N: usize> Dice for [T; N] {
const USES_LIMIT: bool = T::USES_LIMIT;
fn die() -> impl Die<Self> {
let elem_die = T::die();
dice::from_fn(move |mut fate| {
if T::USES_LIMIT {
let limit_parts: [Limit; N] = fate.roll(dice::split_limit(fate.limit()));
array::from_fn(|i| fate.with_limit(limit_parts[i]).roll(&elem_die))
} else {
array::from_fn(|_| fate.with_limit(Limit(0)).roll(&elem_die))
}
})
}
}
macro_rules! impl_dice_for_tuple {
($($Ti:ident, $ti:ident, $die_i:ident)+) => {
impl<$($Ti: Dice,)*> Dice for ($($Ti,)*) {
const USES_LIMIT: bool = $($Ti::USES_LIMIT ||)* false;
fn die() -> impl Die<Self> {
let limit_part_count: usize = $($Ti::USES_LIMIT as usize +)* 0;
$(let $die_i = $Ti::die();)*
dice::from_fn(move |mut fate| {
if limit_part_count == 0 {
$(
let $ti = fate.with_limit(Limit(0)).roll(&$die_i);
)*
($($ti,)*)
} else {
let limit = fate.limit();
let limit_parts_die = dice::split_limit_n(limit, limit_part_count);
let mut limit_parts = fate.with_limit(Limit(0)).roll(limit_parts_die);
$(
let $ti = if $Ti::USES_LIMIT {
let limit = limit_parts.pop().unwrap();
fate.with_limit(limit).roll(&$die_i)
} else {
fate.with_limit(Limit(0)).roll(&$die_i)
};
)*
($($ti,)*)
}
})
}
}
};
}
impl_dice_for_tuple! {
T1, t1, die_1
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
T5, t5, die_5
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
T5, t5, die_5
T6, t6, die_6
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
T5, t5, die_5
T6, t6, die_6
T7, t7, die_7
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
T5, t5, die_5
T6, t6, die_6
T7, t7, die_7
T8, t8, die_8
}
impl_dice_for_tuple! {
T1, t1, die_1
T2, t2, die_2
T3, t3, die_3
T4, t4, die_4
T5, t5, die_5
T6, t6, die_6
T7, t7, die_7
T8, t8, die_8
T9, t9, die_9
}
#[cfg(test)]
mod tests {
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use crate::{Limit, Prng, prelude::*};
#[derive(Eq, Debug, PartialEq, PartialOrd, Ord)]
struct WithoutLimit {
limit: Limit,
}
impl Dice for WithoutLimit {
const USES_LIMIT: bool = false;
fn die() -> impl Die<Self> {
dice::from_fn(|fate| WithoutLimit {
limit: fate.limit(),
})
}
}
#[derive(Eq, Debug, PartialEq, PartialOrd, Ord)]
struct WithLimit {
limit: Limit,
}
impl Dice for WithLimit {
const USES_LIMIT: bool = true;
fn die() -> impl Die<Self> {
dice::from_fn(|fate| WithLimit {
limit: fate.limit(),
})
}
}
#[test]
fn unit_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: () = fate.roll(die());
});
}
#[test]
fn unit_uses_limit() {
const { assert!(!<Box<()> as Dice>::USES_LIMIT) };
}
#[test]
fn u8_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: u8 = fate.roll(die());
});
}
#[test]
fn u8_uses_limit() {
const { assert!(!<Box<u8> as Dice>::USES_LIMIT) };
}
#[test]
fn box_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: Box<u8> = fate.roll(die());
});
}
#[test]
fn box_uses_limit() {
const { assert!(!<Box<WithoutLimit> as Dice>::USES_LIMIT) };
const { assert!(<Box<WithLimit> as Dice>::USES_LIMIT) };
}
#[test]
fn box_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let elem: Box<WithoutLimit> = fate.roll(die());
assert_eq!(elem.limit, limit);
}
{
let elem: Box<WithLimit> = fate.roll(die());
assert_eq!(elem.limit, limit);
}
})
}
#[test]
fn vec_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: Vec<u8> = fate.roll(die());
});
}
#[test]
fn vec_uses_limit() {
const { assert!(<Vec<WithoutLimit> as Dice>::USES_LIMIT) };
const { assert!(<Vec<WithLimit> as Dice>::USES_LIMIT) };
}
#[test]
fn vec_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let vec: Vec<WithoutLimit> = fate.roll(die());
assert!(vec.len() <= limit.saturating_to_usize());
for elem in vec {
assert_eq!(elem.limit, Limit(0));
}
}
{
let vec: Vec<WithLimit> = fate.roll(die());
if !vec.is_empty() {
let total_size: u64 = vec.iter().map(|elem| elem.limit.0).sum();
assert_eq!(total_size, limit.0);
}
}
})
}
#[test]
fn btree_set_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: BTreeSet<u8> = fate.roll(die());
});
}
#[test]
fn btree_set_uses_limit() {
const { assert!(<BTreeSet<WithoutLimit> as Dice>::USES_LIMIT) };
const { assert!(<BTreeSet<WithLimit> as Dice>::USES_LIMIT) };
}
#[test]
fn btree_set_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let set: BTreeSet<WithoutLimit> = fate.roll(die());
assert!(set.len() <= limit.saturating_to_usize());
for elem in set {
assert_eq!(elem.limit, Limit(0));
}
}
{
let set: BTreeSet<WithLimit> = fate.roll(die());
if !set.is_empty() {
let total_size: u64 = set.iter().map(|elem| elem.limit.0).sum();
assert!(total_size <= limit.0);
}
}
})
}
#[test]
fn hash_set_prng_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: HashSet<u8, Prng> = fate.roll(die());
});
}
#[test]
fn hash_set_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: HashSet<u8> = fate.roll(die());
});
}
#[test]
fn btree_map_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: BTreeMap<u8, u8> = fate.roll(die());
});
}
#[test]
fn btree_map_uses_limit() {
const { assert!(<BTreeMap<WithoutLimit, WithoutLimit> as Dice>::USES_LIMIT) };
const { assert!(<BTreeMap<WithoutLimit, WithLimit> as Dice>::USES_LIMIT) };
const { assert!(<BTreeMap<WithLimit, WithoutLimit> as Dice>::USES_LIMIT) };
const { assert!(<BTreeMap<WithLimit, WithLimit> as Dice>::USES_LIMIT) };
}
#[test]
fn btree_map_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let map: BTreeMap<WithoutLimit, WithoutLimit> = fate.roll(die());
assert!(map.len() <= limit.saturating_to_usize());
for elem in map {
assert_eq!(elem.0.limit, Limit(0));
assert_eq!(elem.1.limit, Limit(0));
}
}
{
let map: BTreeMap<WithoutLimit, WithLimit> = fate.roll(die());
for elem in &map {
assert_eq!(elem.0.limit, Limit(0));
}
if !map.is_empty() {
let total_size: u64 = map.iter().map(|elem| elem.1.limit.0).sum();
assert!(total_size <= limit.0);
}
}
{
let map: BTreeMap<WithLimit, WithoutLimit> = fate.roll(die());
for elem in &map {
assert_eq!(elem.1.limit, Limit(0));
}
if !map.is_empty() {
let total_size: u64 = map.iter().map(|elem| elem.0.limit.0).sum();
assert!(total_size <= limit.0);
}
}
{
let map: BTreeMap<WithLimit, WithLimit> = fate.roll(die());
if !map.is_empty() {
let total_size: u64 =
map.iter().map(|elem| elem.0.limit.0 + elem.1.limit.0).sum();
assert!(total_size <= limit.0);
}
}
})
}
#[test]
fn hash_map_prng_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: HashMap<u8, u8, Prng> = fate.roll(die());
});
}
#[test]
fn hash_map_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: HashMap<u8, u8> = fate.roll(die());
});
}
#[test]
fn array_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: [u8; 0] = fate.roll(die());
let _value: [u8; 1] = fate.roll(die());
let _value: [u8; 2] = fate.roll(die());
let _value: [u8; 3] = fate.roll(die());
});
}
#[test]
fn array_uses_limit() {
const { assert!(!<[WithoutLimit; 0] as Dice>::USES_LIMIT) };
const { assert!(<[WithLimit; 0] as Dice>::USES_LIMIT) };
const { assert!(!<[WithoutLimit; 2] as Dice>::USES_LIMIT) };
const { assert!(<[WithLimit; 2] as Dice>::USES_LIMIT) };
}
#[test]
fn array_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let array: [WithoutLimit; 3] = fate.roll(die());
for elem in array {
assert_eq!(elem.limit, Limit(0));
}
}
{
let array: [WithLimit; 3] = fate.roll(die());
let total_size: u64 = array.iter().map(|elem| elem.limit.0).sum();
assert_eq!(total_size, limit.0);
}
})
}
#[test]
fn tuple_example() {
Dicetest::repeatedly().run(|mut fate| {
let _value: (u8,) = fate.roll(die());
let _value: (u8, u8) = fate.roll(die());
let _value: (u8, u8, u8) = fate.roll(die());
});
}
#[test]
fn tuple_uses_limit() {
const { assert!(!<(WithoutLimit,) as Dice>::USES_LIMIT) };
const { assert!(<(WithLimit,) as Dice>::USES_LIMIT) };
const { assert!(!<(WithoutLimit, WithoutLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithoutLimit, WithLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithLimit, WithoutLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithLimit, WithLimit) as Dice>::USES_LIMIT) };
const { assert!(!<(WithoutLimit, WithoutLimit, WithoutLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithoutLimit, WithLimit, WithoutLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithLimit, WithoutLimit, WithoutLimit) as Dice>::USES_LIMIT) };
const { assert!(<(WithLimit, WithLimit, WithLimit) as Dice>::USES_LIMIT) };
}
#[test]
fn tuple_total_size() {
Dicetest::repeatedly().run(|mut fate| {
{
let (a,): (String,) = fate.roll(die());
let total_size = a.chars().count();
assert!(total_size <= fate.limit().saturating_to_usize());
}
{
let (a, b): (String, String) = fate.roll(die());
let total_size = a.chars().count() + b.chars().count();
assert!(total_size <= fate.limit().saturating_to_usize());
}
{
let (a, b, c): (String, String, String) = fate.roll(die());
let total_size = a.chars().count() + b.chars().count() + c.chars().count();
assert!(total_size <= fate.limit().saturating_to_usize());
}
})
}
#[test]
fn tuple_limit() {
Dicetest::repeatedly().run(|mut fate| {
let limit = fate.limit();
{
let (a, b, c): (WithoutLimit, WithoutLimit, WithoutLimit) = fate.roll(die());
assert_eq!(a.limit, Limit(0));
assert_eq!(b.limit, Limit(0));
assert_eq!(c.limit, Limit(0));
}
{
let (a, b, c, d): (WithLimit, WithoutLimit, WithLimit, WithoutLimit) =
fate.roll(die());
assert!(a.limit <= limit);
assert_eq!(b.limit, Limit(0));
assert!(c.limit <= limit);
assert_eq!(d.limit, Limit(0));
assert_eq!(a.limit.0 + b.limit.0 + c.limit.0 + d.limit.0, limit.0)
}
})
}
}