use anyhow::Result;
use rhai::{Engine, Scope};
use crate::protocol::StepResult;
pub fn build_engine(ctx: String) -> (Engine, Scope<'static>) {
use std::sync::{Arc, Mutex};
let ctx_arc: Arc<Mutex<String>> = Arc::new(Mutex::new(ctx.clone()));
let mut engine = Engine::new();
{
let c = Arc::clone(&ctx_arc);
engine.register_fn("ctx_len", move || c.lock().unwrap().len() as i64);
}
{
let c = Arc::clone(&ctx_arc);
engine.register_fn("ctx_slice", move |start: i64, end: i64| {
let s = c.lock().unwrap();
let start = (start as usize).min(s.len());
let end = (end as usize).min(s.len());
s[start..end].to_string()
});
}
{
let c = Arc::clone(&ctx_arc);
engine.register_fn("ctx_grep", move |pattern: String| {
let s = c.lock().unwrap();
match regex::Regex::new(&pattern) {
Ok(re) => s
.lines()
.filter(|l| re.is_match(l))
.collect::<Vec<_>>()
.join("\n"),
Err(e) => format!("regex error: {e}"),
}
});
}
let print_buf: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
{
let buf = Arc::clone(&print_buf);
engine.register_fn("print_cell", move |msg: String| {
buf.lock().unwrap().push(msg);
});
}
let final_buf: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
{
let buf = Arc::clone(&final_buf);
engine.register_fn("done", move |answer: String| {
*buf.lock().unwrap() = Some(answer);
});
}
PRINT_BUF.with(|pb| *pb.borrow_mut() = Some(Arc::clone(&print_buf)));
FINAL_BUF.with(|fb| *fb.borrow_mut() = Some(Arc::clone(&final_buf)));
let mut scope = Scope::new();
scope.push("ctx", ctx);
(engine, scope)
}
thread_local! {
static PRINT_BUF: std::cell::RefCell<Option<std::sync::Arc<std::sync::Mutex<Vec<String>>>>> =
std::cell::RefCell::new(None);
static FINAL_BUF: std::cell::RefCell<Option<std::sync::Arc<std::sync::Mutex<Option<String>>>>> =
std::cell::RefCell::new(None);
}
pub fn execute_script(engine: &Engine, scope: &mut Scope, script: &str) -> Result<StepResult> {
let exec_result = engine.eval_with_scope::<rhai::Dynamic>(scope, script);
let printed = PRINT_BUF.with(|pb| {
pb.borrow()
.as_ref()
.map(|arc| arc.lock().unwrap().drain(..).collect::<Vec<_>>().join("\n"))
.unwrap_or_default()
});
let final_answer = FINAL_BUF.with(|fb| {
fb.borrow()
.as_ref()
.and_then(|arc| arc.lock().unwrap().take())
});
if let Some(answer) = final_answer {
return Ok(StepResult::Final(answer));
}
match exec_result {
Ok(val) => {
let mut output = printed;
let val_str = val.to_string();
if !val_str.is_empty() && val_str != "()" {
if !output.is_empty() {
output.push('\n');
}
output.push_str(&val_str);
}
Ok(StepResult::Continue(output))
}
Err(e) => Ok(StepResult::Continue(format!("script error: {e}"))),
}
}