use anyhow::{Result, bail};
use reblessive::tree::Stk;
use crate::ctx::FrozenContext;
use crate::dbs::Options;
use crate::doc::CursorDoc;
use crate::exe::FlowResultExt as _;
use crate::exec::Error as ExecError;
use crate::expr::idiom::recursion::{Recursion, clean_iteration, get_final, is_final};
use crate::val::{Array, Value};
pub(crate) async fn compute_idiom_recursion(
stk: &mut Stk,
ctx: &FrozenContext,
opt: &Options,
doc: Option<&CursorDoc>,
rec: Recursion<'_>,
) -> Result<Value> {
let limit = ctx.config.exec.idiom_recursion_limit;
let marked_recursive = rec.plan.is_some();
if marked_recursive && is_final(rec.current) {
return Ok(get_final(rec.current));
}
let mut i = rec.iterated.to_owned();
let mut current = rec.current.to_owned();
let mut finished = vec![];
macro_rules! output {
() => {
if rec.instruction.is_some() {
Value::from(finished)
} else {
current
}
};
}
if marked_recursive {
if let Some(max) = rec.max {
if i >= max {
return Ok(current);
}
} else if i >= limit {
bail!(ExecError::IdiomRecursionLimitExceeded {
limit,
});
}
}
loop {
i += 1;
let v = match rec.instruction {
Some(instruction) => {
crate::legacy::recurse_instruction_compute(
instruction,
stk,
ctx,
opt,
doc,
rec.with_iterated(i).with_current(¤t),
&mut finished,
)
.await?
}
_ => stk
.run(|stk| crate::legacy::value_get(¤t, stk, ctx, opt, doc, rec.path))
.await
.catch_return()?,
};
let v = match rec.plan {
Some(p) => {
crate::legacy::recursion_plan_compute(
p,
stk,
ctx,
opt,
doc,
rec.with_iterated(i).with_current(&v),
)
.await?
}
_ => v,
};
let v = if rec.instruction.is_none() {
clean_iteration(v)
} else {
v
};
match v {
v if is_final(&v) || v == current => {
let res: Value = match rec.instruction {
Some(_) if i < rec.min => Value::Array(Array::new()),
Some(_) => Value::from(finished),
None if i <= rec.min => get_final(&v),
None => output!(),
};
return Ok(res);
}
v => {
current = v;
}
};
if let Some(max) = rec.max {
if i >= max {
return Ok(output!());
}
} else if i >= limit {
bail!(ExecError::IdiomRecursionLimitExceeded {
limit,
});
}
if marked_recursive {
return Ok(current);
}
}
}