use crate::pretty::{PrettyPrintable, PrettyPrinter};
use crate::test_case::{TestCase, labels};
use std::marker::PhantomData;
use std::sync::Arc;
pub trait Generator<T> {
#[doc(hidden)]
fn do_draw(&self, tc: &TestCase) -> T;
fn map<U, F>(self, f: F) -> Mapped<T, U, F, Self>
where
Self: Sized,
F: Fn(T) -> U + Send + Sync,
{
Mapped {
source: self,
f: Arc::new(f),
_phantom: PhantomData,
}
}
fn flat_map<U, G, F>(self, f: F) -> FlatMapped<T, U, G, F, Self>
where
Self: Sized,
G: Generator<U>,
F: Fn(T) -> G + Send + Sync,
{
FlatMapped {
source: self,
f,
_phantom: PhantomData,
}
}
fn filter<F>(self, predicate: F) -> Filtered<T, F, Self>
where
Self: Sized,
F: Fn(&T) -> bool + Send + Sync,
{
Filtered {
source: self,
predicate,
_phantom: PhantomData,
}
}
fn boxed<'a>(self) -> BoxedGenerator<'a, T>
where
Self: Sized + Send + Sync + 'a,
{
BoxedGenerator {
inner: Arc::new(self),
}
}
fn print_with<F>(self, print: F) -> PrintedWith<Self, F>
where
Self: Sized,
F: Fn(&T, &mut PrettyPrinter) + Send + Sync,
{
PrintedWith {
source: self,
print,
}
}
fn print_as_value(self) -> PrintedAsValue<Self>
where
Self: Sized,
T: PrettyPrintable,
{
PrintedAsValue { source: self }
}
fn print_as_debug(self) -> PrintedAsDebug<Self>
where
Self: Sized,
T: std::fmt::Debug,
{
PrintedAsDebug { source: self }
}
}
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot print the values it draws",
label = "`{Self}` does not implement `PrintableGenerator<{T}>`",
note = "make it printable with `.print_as_debug()` (any `Debug` value), `.print_as_value()` (any `PrettyPrintable` value), or `.print_with(..)`",
note = "or draw without reporting the value via `tc.draw_silent(..)`"
)]
pub trait PrintableGenerator<T>: Generator<T> {
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T;
fn boxed_printable<'a>(self) -> BoxedPrintableGenerator<'a, T>
where
Self: Sized + Send + Sync + 'a,
{
BoxedPrintableGenerator {
inner: Arc::new(self),
}
}
}
pub(crate) fn draw_and_print_value<T: PrettyPrintable>(
generator: &impl Generator<T>,
tc: &TestCase,
printer: &mut PrettyPrinter,
) -> T {
let value = generator.do_draw(tc);
value.pretty_print(printer);
value
}
pub struct PrintedWith<G, F> {
source: G,
print: F,
}
impl<T, G, F> Generator<T> for PrintedWith<G, F>
where
G: Generator<T>,
F: Fn(&T, &mut PrettyPrinter) + Send + Sync,
{
fn do_draw(&self, tc: &TestCase) -> T {
self.source.do_draw(tc)
}
}
impl<T, G, F> PrintableGenerator<T> for PrintedWith<G, F>
where
G: Generator<T>,
F: Fn(&T, &mut PrettyPrinter) + Send + Sync,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
let value = self.source.do_draw(tc);
(self.print)(&value, printer);
value
}
}
pub struct PrintedAsValue<G> {
source: G,
}
impl<T, G> Generator<T> for PrintedAsValue<G>
where
G: Generator<T>,
T: PrettyPrintable,
{
fn do_draw(&self, tc: &TestCase) -> T {
self.source.do_draw(tc)
}
}
impl<T, G> PrintableGenerator<T> for PrintedAsValue<G>
where
G: Generator<T>,
T: PrettyPrintable,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
draw_and_print_value(&self.source, tc, printer)
}
}
pub struct PrintedAsDebug<G> {
source: G,
}
impl<T, G> Generator<T> for PrintedAsDebug<G>
where
G: Generator<T>,
T: std::fmt::Debug,
{
fn do_draw(&self, tc: &TestCase) -> T {
self.source.do_draw(tc)
}
}
impl<T, G> PrintableGenerator<T> for PrintedAsDebug<G>
where
G: Generator<T>,
T: std::fmt::Debug,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
let value = self.source.do_draw(tc);
if printer.should_print() {
crate::pretty::print_debug_repr(&format!("{value:?}"), printer);
}
value
}
}
impl<T, G: Generator<T>> Generator<T> for &G {
fn do_draw(&self, tc: &TestCase) -> T {
(*self).do_draw(tc)
}
}
impl<T, G: PrintableGenerator<T>> PrintableGenerator<T> for &G {
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
(*self).do_draw_and_print(tc, printer)
}
}
pub struct Mapped<T, U, F, G> {
source: G,
f: Arc<F>,
_phantom: PhantomData<fn(T) -> U>,
}
impl<T, U, F, G> Generator<U> for Mapped<T, U, F, G>
where
G: Generator<T>,
F: Fn(T) -> U + Send + Sync,
{
fn do_draw(&self, tc: &TestCase) -> U {
tc.start_span(labels::MAPPED);
let result = (self.f)(self.source.do_draw(tc));
tc.stop_span(false);
result
}
}
impl<T, U, F, G> PrintableGenerator<U> for Mapped<T, U, F, G>
where
G: Generator<T>,
F: Fn(T) -> U + Send + Sync,
U: PrettyPrintable,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> U {
draw_and_print_value(self, tc, printer)
}
}
pub struct FlatMapped<T, U, G2, F, G1> {
source: G1,
f: F,
_phantom: PhantomData<fn(T) -> (U, G2)>,
}
impl<T, U, G2, F, G1> FlatMapped<T, U, G2, F, G1>
where
G1: Generator<T>,
F: Fn(T) -> G2 + Send + Sync,
{
fn draw_flat_mapped(&self, tc: &TestCase, draw_next: impl FnOnce(G2, &TestCase) -> U) -> U {
tc.start_span(labels::FLAT_MAP);
let intermediate = self.source.do_draw(tc);
let next_gen = (self.f)(intermediate);
let result = draw_next(next_gen, tc);
tc.stop_span(false);
result
}
}
impl<T, U, G2, F, G1> Generator<U> for FlatMapped<T, U, G2, F, G1>
where
G1: Generator<T>,
G2: Generator<U>,
F: Fn(T) -> G2 + Send + Sync,
{
fn do_draw(&self, tc: &TestCase) -> U {
self.draw_flat_mapped(tc, |next_gen, tc| next_gen.do_draw(tc))
}
}
impl<T, U, G2, F, G1> PrintableGenerator<U> for FlatMapped<T, U, G2, F, G1>
where
G1: Generator<T>,
G2: PrintableGenerator<U>,
F: Fn(T) -> G2 + Send + Sync,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> U {
self.draw_flat_mapped(tc, |next_gen, tc| tc.draw_and_print(next_gen, printer))
}
}
pub struct Filtered<T, F, G> {
source: G,
predicate: F,
_phantom: PhantomData<fn() -> T>,
}
impl<T, F, G> Filtered<T, F, G>
where
F: Fn(&T) -> bool + Send + Sync,
{
fn draw_filtered(
&self,
tc: &TestCase,
printer: &mut PrettyPrinter,
draw: impl Fn(&G, &TestCase, &mut PrettyPrinter) -> T,
) -> T {
for _ in 0..3 {
tc.start_span(labels::FILTER);
let mut speculation = printer.speculate();
let value = draw(&self.source, tc, speculation.printer());
if (self.predicate)(&value) {
speculation.commit();
tc.stop_span(false);
return value;
}
speculation.abort();
tc.stop_span(true);
}
tc.assume(false);
unreachable!()
}
}
impl<T, F, G> Generator<T> for Filtered<T, F, G>
where
G: Generator<T>,
F: Fn(&T) -> bool + Send + Sync,
{
fn do_draw(&self, tc: &TestCase) -> T {
self.draw_filtered(tc, &mut PrettyPrinter::noop(), |source, tc, _| {
source.do_draw(tc)
})
}
}
impl<T, F, G> PrintableGenerator<T> for Filtered<T, F, G>
where
G: PrintableGenerator<T>,
F: Fn(&T) -> bool + Send + Sync,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
self.draw_filtered(tc, printer, |source, tc, printer| {
tc.draw_and_print(source, printer)
})
}
}
pub struct BoxedGenerator<'a, T> {
pub(super) inner: Arc<dyn Generator<T> + Send + Sync + 'a>,
}
impl<T> Clone for BoxedGenerator<'_, T> {
fn clone(&self) -> Self {
BoxedGenerator {
inner: Arc::clone(&self.inner),
}
}
}
impl<T> Generator<T> for BoxedGenerator<'_, T> {
fn do_draw(&self, tc: &TestCase) -> T {
self.inner.do_draw(tc)
}
fn boxed<'b>(self) -> BoxedGenerator<'b, T>
where
Self: Sized + Send + Sync + 'b,
{
BoxedGenerator { inner: self.inner }
}
}
pub struct BoxedPrintableGenerator<'a, T> {
inner: Arc<dyn PrintableGenerator<T> + Send + Sync + 'a>,
}
impl<T> Clone for BoxedPrintableGenerator<'_, T> {
fn clone(&self) -> Self {
BoxedPrintableGenerator {
inner: Arc::clone(&self.inner),
}
}
}
impl<T> Generator<T> for BoxedPrintableGenerator<'_, T> {
fn do_draw(&self, tc: &TestCase) -> T {
self.inner.do_draw(tc)
}
}
impl<T> PrintableGenerator<T> for BoxedPrintableGenerator<'_, T> {
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
self.inner.do_draw_and_print(tc, printer)
}
fn boxed_printable<'b>(self) -> BoxedPrintableGenerator<'b, T>
where
Self: Sized + Send + Sync + 'b,
{
BoxedPrintableGenerator { inner: self.inner }
}
}