#![doc = include_str!("../README.md")]
#![no_std]
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "std")]
#[macro_use]
extern crate std;
pub mod _guide;
pub mod error;
mod log;
pub mod mutators;
mod rng;
use core::ops;
pub use error::{Error, Result};
pub use rng::Rng;
#[cfg(feature = "check")]
pub mod check;
#[cfg(feature = "derive")]
pub use mutatis_derive::Mutate;
#[derive(Debug)]
pub struct Session {
context: Context,
}
impl Default for Session {
fn default() -> Self {
Self::new()
}
}
impl Session {
pub fn new() -> Self {
Self {
context: Context {
rng: Rng::default(),
shrink: false,
generate_via_mutate_depth: 0,
},
}
}
pub fn seed(mut self, seed: u64) -> Self {
self.context.rng = Rng::new(seed);
self
}
pub fn shrink(mut self, shrink: bool) -> Self {
self.context.shrink = shrink;
self
}
pub fn mutate<T>(&mut self, value: &mut T) -> Result<()>
where
T: DefaultMutate,
{
self.context.mutate(value)
}
pub fn mutate_with<T>(
&mut self,
mutator: &mut (impl Mutate<T> + ?Sized),
value: &mut T,
) -> Result<()> {
self.context.mutate_with(mutator, value)
}
pub fn generate<T>(&mut self) -> Result<T>
where
T: DefaultMutate,
T::DefaultMutate: Generate<T>,
{
let mut generator = T::DefaultMutate::default();
generator.generate(&mut self.context)
}
pub fn generate_with<T>(&mut self, generator: &mut impl Generate<T>) -> Result<T> {
generator.generate(&mut self.context)
}
}
#[derive(Debug)]
pub struct Context {
rng: Rng,
shrink: bool,
generate_via_mutate_depth: u32,
}
impl Context {
#[inline]
#[must_use]
pub fn rng(&mut self) -> &mut Rng {
&mut self.rng
}
#[inline]
#[must_use]
pub fn shrink(&self) -> bool {
self.shrink
}
#[inline]
pub(crate) fn mutate<T>(&mut self, value: &mut T) -> Result<()>
where
T: DefaultMutate,
{
let mut mutator = mutators::default::<T>();
self.mutate_with(&mut mutator, value)
}
#[inline]
pub(crate) fn mutate_with<T>(
&mut self,
mutator: &mut (impl Mutate<T> + ?Sized),
value: &mut T,
) -> Result<()> {
self.choose_and_apply_mutation(value, |c, value| mutator.mutate(c, value))
}
fn choose_and_apply_mutation<T>(
&mut self,
value: &mut T,
mut mutate_impl: impl FnMut(&mut Candidates, &mut T) -> Result<()>,
) -> Result<()> {
log::trace!("=== choosing an applying a mutation ===");
let mut candidates = Candidates {
context: self,
phase: Phase::Count(0),
applied_mutation: false,
};
mutate_impl(&mut candidates, value)?;
let count = match candidates.phase {
Phase::Count(count) => usize::try_from(count).unwrap(),
Phase::Mutate { .. } => unreachable!(),
};
log::trace!("counted {count} mutations");
if count == 0 {
log::trace!("mutator exhausted");
return Err(Error::exhausted());
}
let target = candidates.context.rng().gen_index(count).unwrap();
log::trace!("targeting mutation {target}");
debug_assert!(target < count);
candidates.phase = Phase::Mutate {
current: 0,
target: u32::try_from(target).unwrap(),
};
match mutate_impl(&mut candidates, value) {
Err(e) if e.is_early_exit() => {
log::trace!("mutation applied successfully");
Ok(())
}
Err(e) => {
log::error!("failed to apply mutation: {e}");
Err(e)
}
Ok(()) if candidates.applied_mutation => {
panic!(
"We applied a mutation but did not receive an early-exit error \
from the mutator. This means that errors are not always being \
propagated, for example a `?` is missing from a call to the \
`Candidates::mutation` method. Errors must be propagated \
in `Mutate::mutate` et al method implementations; failure to do \
so can lead to bugs, panics, and degraded performance.",
)
}
Ok(()) => {
let current = match candidates.phase {
Phase::Mutate { current, .. } => current,
_ => unreachable!(),
};
panic!(
"Nondeterministic mutator implementation: did not enumerate the \
same set of mutations when given the same value! Counted {count} \
mutations in the first pass, but only found {current} mutations on \
the second pass. Mutators must be deterministic.",
)
}
}
}
#[inline]
pub(crate) fn mutate_in_range_with<T>(
&mut self,
mutator: &mut impl MutateInRange<T>,
value: &mut T,
range: &ops::RangeInclusive<T>,
) -> Result<()> {
self.choose_and_apply_mutation(value, |c, value| mutator.mutate_in_range(c, value, range))
}
const MAX_DEPTH: u32 = 8;
pub(crate) fn with_generate_via_mutate_scope(
&mut self,
mut f: impl FnMut(&mut Self) -> Result<()>,
) -> Result<()> {
self.generate_via_mutate_depth += 1;
let result = if !self.shrink() && self.generate_via_mutate_depth < Self::MAX_DEPTH {
f(self)
} else {
Ok(())
};
self.generate_via_mutate_depth -= 1;
result
}
}
#[derive(Clone, Copy)]
enum Phase {
Count(u32),
Mutate { current: u32, target: u32 },
}
pub struct Candidates<'a> {
context: &'a mut Context,
phase: Phase,
applied_mutation: bool,
}
impl<'a> Candidates<'a> {
#[inline]
pub fn mutation(&mut self, mut f: impl FnMut(&mut Context) -> Result<()>) -> Result<()> {
match &mut self.phase {
Phase::Count(count) => {
*count += 1;
Ok(())
}
Phase::Mutate { current, target } => {
assert!(
*current <= *target,
"{current} <= {target}; did you forget to `?`-propagate the \
result of a `Candidates::mutation` call?",
);
if *current == *target {
self.applied_mutation = true;
f(&mut self.context)?;
Err(Error::early_exit())
} else {
*current += 1;
Ok(())
}
}
}
}
pub fn shrink(&self) -> bool {
self.context.shrink()
}
}
pub trait Mutate<T>
where
T: ?Sized,
{
fn mutate(&mut self, mutations: &mut Candidates<'_>, value: &mut T) -> Result<()>;
fn generate_via_mutate(&mut self, context: &mut Context, iters: usize) -> Result<T>
where
T: Sized + Default,
{
let mut value = T::default();
context.with_generate_via_mutate_scope(|context| {
for _ in 0..iters {
context.mutate_with(self, &mut value)?;
}
Ok(())
})?;
Ok(value)
}
fn or<M>(self, other: M) -> mutators::Or<Self, M>
where
Self: Sized,
{
mutators::Or {
left: self,
right: other,
}
}
#[inline]
#[must_use = "mutator combinators do nothing until you call their `mutate` method"]
fn map<F>(self, f: F) -> mutators::Map<Self, F>
where
Self: Sized,
F: FnMut(&mut Context, &mut T) -> Result<()>,
{
mutators::Map { mutator: self, f }
}
#[inline]
#[must_use = "mutator combinators do nothing until you call their `mutate` method"]
fn proj<F, U>(self, f: F) -> mutators::Proj<Self, F>
where
Self: Sized,
F: FnMut(&mut U) -> &mut T,
{
mutators::Proj { mutator: self, f }
}
#[inline]
#[must_use = "mutator combinators do nothing until you call their `mutate` method"]
fn by_ref(&mut self) -> &mut Self
where
Self: Sized,
{
self
}
}
fn _static_assert_object_safety(
_: &dyn Mutate<u8>,
_: &dyn Generate<u8>,
_: &dyn MutateInRange<u8>,
) {
}
impl<M, T> Mutate<T> for &mut M
where
M: Mutate<T>,
{
fn mutate(&mut self, c: &mut Candidates, value: &mut T) -> Result<()> {
(**self).mutate(c, value)
}
}
impl<M, T> Generate<T> for &mut M
where
M: Generate<T>,
{
fn generate(&mut self, context: &mut Context) -> Result<T> {
(**self).generate(context)
}
}
pub trait DefaultMutate {
type DefaultMutate: Mutate<Self> + Default;
}
pub trait Generate<T>: Mutate<T> {
fn generate(&mut self, context: &mut Context) -> Result<T>;
}
pub trait MutateInRange<T>: Mutate<T> {
fn mutate_in_range(
&mut self,
mutations: &mut Candidates,
value: &mut T,
range: &ops::RangeInclusive<T>,
) -> Result<()>;
}