use rhai::{Dynamic, Engine, Scope, AST, Module}; use std::collections::HashMap; use std::cell::RefCell; use super::types::Value; use compact_str::CompactString; pub const RHEI_PREFIX: &str = "__rhei:"; pub struct RheiContext { engine: Engine, init_ast: AST, scope: RefCell>, } impl RheiContext { pub fn new(scripts: &[String]) -> Self { let mut engine = Engine::new(); engine.on_print(|s| println!("[rhei] {s}")); engine.on_debug(|s, src, pos| { eprintln!("[rhei debug @ {src:?}:{pos}] {s}"); }); let mut combined = AST::empty(); for (i, script) in scripts.iter().enumerate() { match engine.compile(script) { Ok(ast) => combined = combined.merge(&ast), Err(e) => eprintln!("⚠️ Rhei compile error (block #{i}): {e}"), } } let mut fn_ast = combined.clone(); fn_ast.clear_statements(); match Module::eval_ast_as_new(Scope::new(), &fn_ast, &engine) { Ok(module) => { engine.register_global_module(module.into()); } Err(e) => { eprintln!("⚠️ Rhei module creation error: {e}"); } } Self { engine, init_ast: combined, scope: RefCell::new(Scope::new()), } } pub fn sync_scope(&self, variables: &HashMap) { let mut scope = self.scope.borrow_mut(); for (k, v) in variables.iter() { if scope.contains(k) { if let Some(old_val) = scope.get_value::(k) { if dynamic_to_value(&old_val) == *v { continue; } } scope.set_value(k, value_to_dynamic(v)); } else { scope.push_dynamic(k.clone(), value_to_dynamic(v)); } } } pub fn initialize(&self, variables: &mut HashMap) { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &self.init_ast) { eprintln!("⚠️ Rhei initialization error: {e}"); } for (name, _, val) in scope.iter_raw() { let s_val = dynamic_to_value(&val); if variables.get(name).map(|v| v != &s_val).unwrap_or(true) { variables.insert(name.to_string(), s_val); } } } pub fn eval_expr(&self, expr: &str, variables: &HashMap) -> Value { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); match self.engine.eval_expression_with_scope::(&mut *scope, expr) { Ok(val) => dynamic_to_value(&val), Err(e) => { eprintln!("⚠️ Rhei eval_expr error: {e}"); Value::None } } } pub fn eval_condition(&self, expr: &str, variables: &HashMap) -> bool { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); match self.engine.eval_expression_with_scope::(&mut *scope, expr) { Ok(b) => b, Err(e) => { eprintln!("⚠️ Rhei eval_condition error: {e}"); false } } } pub fn execute_action(&self, script: &str, variables: &mut HashMap) { self.sync_scope(variables); let mut scope = self.scope.borrow_mut(); match self.engine.compile(script) { Ok(action_ast) => { if let Err(e) = self.engine.run_ast_with_scope(&mut *scope, &action_ast) { eprintln!("⚠️ Rhei action execution error: {e}"); } } Err(e) => { eprintln!("⚠️ Rhei action compilation error: {e}"); } } for (name, _, val) in scope.iter_raw() { let s_val = dynamic_to_value(&val); if variables.get(name).map(|v| v != &s_val).unwrap_or(true) { variables.insert(name.to_string(), s_val); } } } } impl Default for RheiContext { fn default() -> Self { Self::new(&[]) } } pub fn value_to_dynamic(v: &Value) -> Dynamic { match v { Value::Int(i) => Dynamic::from(*i), Value::Float(f) => Dynamic::from(*f), Value::Bool(b) => Dynamic::from(*b), Value::Str(s) => Dynamic::from(s.to_string()), Value::None => Dynamic::UNIT, Value::Array(arr) => { let d: rhai::Dynamic = arr.iter().map(value_to_dynamic).collect(); d } } } pub fn dynamic_to_value(d: &Dynamic) -> Value { if d.is_string() { return Value::Str(CompactString::new(d.clone().into_string().unwrap_or_default())); } if d.is_int() { return Value::Int(d.as_int().unwrap_or(0)); } if d.is_float() { return Value::Float(d.as_float().unwrap_or(0.0)); } if d.is_bool() { return Value::Bool(d.as_bool().unwrap_or(false)); } if d.is_array() { let arr = d.clone().into_array().unwrap_or_default(); return Value::Array(arr.iter().map(dynamic_to_value).collect()); } Value::None }