use super::{Generator, PrintableGenerator, TestCase};
use crate::control::{AttemptMispriced, LeafBudgetExceeded, raise_control};
use crate::ffi::RecursionHandle;
use crate::pretty::PrettyPrinter;
use crate::test_case::{labels, raise_for_rc};
use hegel_c::hegel_result_t;
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, printer: &mut PrettyPrinter) -> T;
fn draw_branch(
&self,
tc: &TestCase,
subtrees: SubtreeGenerator<T>,
printer: &mut PrettyPrinter,
) -> T;
}
struct SilentCore<G, F, R> {
leaf: Arc<G>,
branch: Arc<F>,
_phantom: PhantomData<fn() -> R>,
}
impl<T, G, F, R> SubtreeDraw<T> for SilentCore<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, _printer: &mut PrettyPrinter) -> T {
self.leaf.do_draw(tc)
}
fn draw_branch(
&self,
tc: &TestCase,
subtrees: SubtreeGenerator<T>,
_printer: &mut PrettyPrinter,
) -> T {
(self.branch)(subtrees).do_draw(tc)
}
}
struct PrintingCore<G, F, R> {
leaf: Arc<G>,
branch: Arc<F>,
_phantom: PhantomData<fn() -> R>,
}
impl<T, G, F, R> SubtreeDraw<T> for PrintingCore<G, F, R>
where
G: PrintableGenerator<T> + Send + Sync,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync,
R: PrintableGenerator<T>,
{
fn draw_leaf(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
tc.draw_and_print(&*self.leaf, printer)
}
fn draw_branch(
&self,
tc: &TestCase,
subtrees: SubtreeGenerator<T>,
printer: &mut PrettyPrinter,
) -> T {
tc.draw_and_print((self.branch)(subtrees), printer)
}
}
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,
}
}
fn draw_subtree(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> 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(), printer)
} else {
if let Err(rc) = tc.with_ctc(|ctc| ctc.recursion_leaf(&self.recursion)) {
raise_for_rc(rc);
}
self.core.draw_leaf(tc, printer)
};
if self.depth == 0 {
if let Err(rc) = tc.with_ctc(|ctc| ctc.recursion_finish(&self.recursion)) {
if rc == hegel_result_t::HEGEL_E_RETRY {
raise_control(AttemptMispriced);
}
raise_for_rc(rc);
}
}
tc.stop_span(false);
result
}
}
impl<T> Generator<T> for SubtreeGenerator<T> {
fn do_draw(&self, tc: &TestCase) -> T {
self.draw_subtree(tc, &mut PrettyPrinter::noop())
}
}
impl<T> PrintableGenerator<T> for SubtreeGenerator<T> {
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
self.draw_subtree(tc, printer)
}
}
pub struct RecursiveGenerator<T, G, F, R> {
leaf: Arc<G>,
branch: Arc<F>,
max_depth: usize,
max_leaves: usize,
_phantom: PhantomData<fn() -> (T, R)>,
}
impl<T, G, F, R> RecursiveGenerator<T, G, F, R> {
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
}
fn draw_recursive(
&self,
tc: &TestCase,
core: Arc<dyn SubtreeDraw<T>>,
printer: &mut PrettyPrinter,
) -> 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(&core),
recursion: Arc::clone(&recursion),
depth: 0,
};
let mut speculation = printer.speculate();
match catch_unwind(AssertUnwindSafe(|| {
root.draw_subtree(tc, speculation.printer())
})) {
Ok(value) => {
speculation.commit();
return value;
}
Err(payload) if payload.downcast_ref::<LeafBudgetExceeded>().is_some() => {
speculation.abort();
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) if payload.downcast_ref::<AttemptMispriced>().is_some() => {
speculation.abort();
tc.reset_open_spans_to(base_span_depth);
}
Err(payload) => resume_unwind(payload),
}
}
}
}
impl<T, G, F, R> Generator<T> for RecursiveGenerator<T, G, F, R>
where
T: 'static,
G: Generator<T> + Send + Sync + 'static,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync + 'static,
R: Generator<T> + 'static,
{
fn do_draw(&self, tc: &TestCase) -> T {
let core: Arc<dyn SubtreeDraw<T>> = Arc::new(SilentCore {
leaf: Arc::clone(&self.leaf),
branch: Arc::clone(&self.branch),
_phantom: PhantomData,
});
self.draw_recursive(tc, core, &mut PrettyPrinter::noop())
}
}
impl<T, G, F, R> PrintableGenerator<T> for RecursiveGenerator<T, G, F, R>
where
T: 'static,
G: PrintableGenerator<T> + Send + Sync + 'static,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync + 'static,
R: PrintableGenerator<T> + 'static,
{
fn do_draw_and_print(&self, tc: &TestCase, printer: &mut PrettyPrinter) -> T {
let core: Arc<dyn SubtreeDraw<T>> = Arc::new(PrintingCore {
leaf: Arc::clone(&self.leaf),
branch: Arc::clone(&self.branch),
_phantom: PhantomData,
});
self.draw_recursive(tc, core, printer)
}
}
pub fn recursive<T, G, F, R>(leaf: G, branch: F) -> RecursiveGenerator<T, G, F, R>
where
T: 'static,
G: Generator<T> + Send + Sync + 'static,
F: Fn(SubtreeGenerator<T>) -> R + Send + Sync + 'static,
R: Generator<T> + 'static,
{
RecursiveGenerator {
leaf: Arc::new(leaf),
branch: Arc::new(branch),
max_depth: DEFAULT_MAX_DEPTH,
max_leaves: DEFAULT_MAX_LEAVES,
_phantom: PhantomData,
}
}