use super::{Generator, TestCase};
use crate::control::LeafBudgetExceeded;
use crate::ffi::RecursionHandle;
use crate::test_case::{labels, raise_for_rc};
use std::marker::PhantomData;
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
use std::sync::Arc;
const DEFAULT_MAX_DEPTH: usize = 32;
const DEFAULT_MAX_LEAVES: usize = 100;
trait SubtreeDraw<T>: Send + Sync {
fn draw_leaf(&self, tc: &TestCase) -> T;
fn draw_branch(&self, tc: &TestCase, subtrees: SubtreeGenerator<T>) -> T;
}
struct RecursiveCore<G, F, R> {
leaf: G,
branch: F,
_phantom: PhantomData<fn() -> R>,
}
impl<T, G, F, R> SubtreeDraw<T> for RecursiveCore<G, F, R>
where
G: Generator<T> + Send + Sync,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync,
R: Generator<T>,
{
fn draw_leaf(&self, tc: &TestCase) -> T {
self.leaf.do_draw(tc)
}
fn draw_branch(&self, tc: &TestCase, subtrees: SubtreeGenerator<T>) -> T {
(self.branch)(subtrees).do_draw(tc)
}
}
pub struct SubtreeGenerator<T> {
core: Arc<dyn SubtreeDraw<T>>,
recursion: Arc<RecursionHandle>,
depth: u64,
}
impl<T> Clone for SubtreeGenerator<T> {
fn clone(&self) -> Self {
SubtreeGenerator {
core: Arc::clone(&self.core),
recursion: Arc::clone(&self.recursion),
depth: self.depth,
}
}
}
impl<T> SubtreeGenerator<T> {
fn child(&self) -> Self {
SubtreeGenerator {
core: Arc::clone(&self.core),
recursion: Arc::clone(&self.recursion),
depth: self.depth + 1,
}
}
}
impl<T> Generator<T> for SubtreeGenerator<T> {
fn do_draw(&self, tc: &TestCase) -> T {
tc.start_span(labels::RECURSIVE);
let branch = match tc.with_ctc(|ctc| ctc.recursion_branch(&self.recursion, self.depth)) {
Ok(branch) => branch,
Err(rc) => raise_for_rc(rc),
};
let result = if branch {
self.core.draw_branch(tc, self.child())
} else {
if let Err(rc) = tc.with_ctc(|ctc| ctc.recursion_leaf(&self.recursion)) {
raise_for_rc(rc);
}
self.core.draw_leaf(tc)
};
tc.stop_span(false);
result
}
}
pub struct RecursiveGenerator<T> {
core: Arc<dyn SubtreeDraw<T>>,
max_depth: usize,
max_leaves: usize,
}
impl<T> RecursiveGenerator<T> {
pub fn max_depth(mut self, max_depth: usize) -> Self {
self.max_depth = max_depth;
self
}
pub fn max_leaves(mut self, max_leaves: usize) -> Self {
self.max_leaves = max_leaves;
self
}
}
impl<T> Generator<T> for RecursiveGenerator<T> {
fn do_draw(&self, tc: &TestCase) -> T {
let base_span_depth = tc.open_span_depth();
let recursion = match tc
.with_ctc(|ctc| ctc.new_recursion(self.max_depth as u64, self.max_leaves as u64))
{
Ok(recursion) => Arc::new(recursion),
Err(rc) => raise_for_rc(rc),
};
loop {
let root = SubtreeGenerator {
core: Arc::clone(&self.core),
recursion: Arc::clone(&recursion),
depth: 0,
};
match catch_unwind(AssertUnwindSafe(|| root.do_draw(tc))) {
Ok(value) => return value,
Err(payload) if payload.downcast_ref::<LeafBudgetExceeded>().is_some() => {
match tc.with_ctc(|ctc| ctc.recursion_retry(&recursion)) {
Ok(()) => tc.reset_open_spans_to(base_span_depth),
Err(rc) => raise_for_rc(rc),
}
}
Err(payload) => resume_unwind(payload),
}
}
}
}
pub fn recursive<T, G, F, R>(leaf: G, branch: F) -> RecursiveGenerator<T>
where
T: 'static,
G: Generator<T> + Send + Sync + 'static,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync + 'static,
R: Generator<T> + 'static,
{
RecursiveGenerator {
core: Arc::new(RecursiveCore {
leaf,
branch,
_phantom: PhantomData,
}),
max_depth: DEFAULT_MAX_DEPTH,
max_leaves: DEFAULT_MAX_LEAVES,
}
}