use alloc::{borrow::Cow, vec::Vec};
use core::iter::Peekable;
#[cfg(feature = "fastrand")]
use fastrand::Rng;
use super::{Dice, DieRoll, Error, Rolled};
pub trait Roller {
#[must_use]
fn roll_die(&mut self, sides: u8) -> DieRoll;
fn roll<'d, 'r>(&mut self, dice: &'d Dice, apply_mods: bool) -> Result<Rolled<'r>, Error>
where
'd: 'r,
Self: Sized,
{
let mut rolls = Vec::with_capacity(dice.count as usize);
for _ in 0..dice.count {
rolls.push(self.roll_die(dice.sides));
}
let mut rolled = Rolled {
rolls,
dice: Cow::Borrowed(dice),
};
if apply_mods {
for modifier in &dice.modifiers {
modifier.apply(&mut rolled, self)?;
}
}
Ok(rolled)
}
}
#[cfg(feature = "fastrand")]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "std", derive(Default))]
pub struct FastRand(Rng);
#[cfg(feature = "fastrand")]
impl FastRand {
#[must_use]
#[inline]
pub const fn new(rng: Rng) -> Self {
Self(rng)
}
#[must_use]
#[inline]
pub fn with_seed(seed: u64) -> Self {
Self(Rng::with_seed(seed))
}
}
#[cfg(feature = "fastrand")]
impl Roller for FastRand {
#[inline]
fn roll_die(&mut self, sides: u8) -> DieRoll {
if sides > 0 {
DieRoll::new(self.0.u8(1..=sides))
} else {
DieRoll::new(0)
}
}
}
#[derive(Debug, Default, Clone)]
#[allow(clippy::exhaustive_structs)]
pub struct Val(pub u8);
impl Roller for Val {
#[inline]
fn roll_die(&mut self, _sides: u8) -> DieRoll {
DieRoll::new(self.0)
}
}
#[derive(Debug, Default, Clone)]
#[allow(clippy::exhaustive_structs)]
pub struct Max;
impl Roller for Max {
#[inline]
fn roll_die(&mut self, sides: u8) -> DieRoll {
DieRoll::new(sides)
}
}
#[derive(Debug, Clone)]
pub struct Iter<I: Iterator<Item = u8>>(Peekable<I>);
impl<I: Iterator<Item = u8>> Iter<I> {
#[inline]
pub fn can_roll(&mut self) -> bool {
self.0.peek().is_some()
}
#[must_use]
#[inline]
pub fn new(iter: impl IntoIterator<IntoIter = I>) -> Self {
Self(iter.into_iter().peekable())
}
}
impl<I: Iterator<Item = u8>> Roller for Iter<I> {
#[inline]
#[allow(clippy::expect_used)]
fn roll_die(&mut self, _sides: u8) -> DieRoll {
DieRoll::new(self.0.next().expect("iterator is finished"))
}
}