use std::cell::Cell;
use rust_dynamic::value::Value;
const MAX_DEPTH: u32 = 4;
thread_local! {
static DEPTH: Cell<u32> = const { Cell::new(0) };
}
pub fn fire(name: &str, args: Vec<Value>) {
if super::is_in_eval() {
return;
}
let depth = DEPTH.with(|c| c.get());
if depth >= MAX_DEPTH {
tracing::warn!(
target: "inkhaven::scripting::hooks",
"hook {} skipped: depth {} >= max {}",
name,
depth,
MAX_DEPTH
);
return;
}
if let Err(e) = super::init_adam() {
tracing::warn!(
target: "inkhaven::scripting::hooks",
"hook {} init_adam failed: {}",
name,
e
);
return;
}
DEPTH.with(|c| c.set(depth + 1));
let _ = super::stdlib::io::drain_print_buffer();
let outcome = super::with_adam(|bund| {
if !bund.vm.lambdas.contains_key(name) {
return Ok(());
}
for arg in args {
let _ = bund.vm.stack.push(arg);
}
let bund_ref = &mut *bund;
let hook_name = name.to_string();
let evaluated = std::panic::catch_unwind(std::panic::AssertUnwindSafe(move || {
crate::crash::suppress_panic_report(move || bund_ref.eval(hook_name))
}));
match evaluated {
Ok(Ok(_)) => Ok(()),
Ok(Err(e)) => Err(anyhow::anyhow!("eval: {e}")),
Err(_) => Err(anyhow::anyhow!("hook panicked (isolated)")),
}
});
DEPTH.with(|c| c.set(depth));
let stdout = super::stdlib::io::drain_print_buffer();
if !stdout.is_empty() {
for line in stdout.lines() {
if !line.is_empty() {
tracing::info!(target: "inkhaven::hook::out", hook = name, "{line}");
}
}
}
match outcome {
Some(Ok(())) => {}
Some(Err(e)) => {
tracing::warn!(
target: "inkhaven::scripting::hooks",
"hook {} failed: {}",
name,
e
);
}
None => {
}
}
}