use std::cell::Cell;
use cljrs_value::{CljxFn, Value};
use crate::env::env::Env;
use crate::env::error::{EvalError, EvalResult};
pub const DEPTH_EXCEEDED_MSG: &str = "cljrs-tx: transaction call depth exceeded";
thread_local! {
static CALL_DEPTH: Cell<Option<(u64, u64)>> = const { Cell::new(None) };
}
pub struct DepthGuard;
impl DepthGuard {
pub fn install(limit: u64) -> Self {
CALL_DEPTH.with(|cell| cell.set(Some((limit, 0))));
Self
}
}
impl Drop for DepthGuard {
fn drop(&mut self) {
CALL_DEPTH.with(|cell| cell.set(None));
}
}
#[allow(clippy::result_large_err)]
pub fn call_cljrs_fn(f: &CljxFn, args: &[Value], env: &mut Env) -> EvalResult {
let Some((limit, depth)) = CALL_DEPTH.with(Cell::get) else {
return crate::interp::apply::call_cljrs_fn(f, args, env);
};
if depth >= limit {
return Err(EvalError::Runtime(DEPTH_EXCEEDED_MSG.into()));
}
CALL_DEPTH.with(|cell| cell.set(Some((limit, depth + 1))));
let result = crate::interp::apply::call_cljrs_fn(f, args, env);
CALL_DEPTH.with(|cell| {
if let Some((limit, current)) = cell.get() {
cell.set(Some((limit, current.saturating_sub(1))));
}
});
result
}