use std::fs;
use std::path::Path;
use rhai::{AST, Array, Dynamic, Engine, Scope};
use crate::error::{Error, Result};
pub struct ScriptRewriter {
engine: Engine,
ast: AST,
}
impl std::fmt::Debug for ScriptRewriter {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ScriptRewriter").finish_non_exhaustive()
}
}
impl ScriptRewriter {
pub fn from_source(source: &str) -> Result<Self> {
let engine = sandboxed_engine();
let ast = engine.compile(source).map_err(|e| Error::ScriptParse(e.to_string()))?;
Ok(Self { engine, ast })
}
pub fn from_file(path: &Path) -> Result<Self> {
let source = fs::read_to_string(path)
.map_err(|e| Error::Io { path: path.to_path_buf(), source: e })?;
Self::from_source(&source)
}
pub fn replace(&self, captures: &[&str]) -> Result<String> {
let mut scope = Scope::new();
let arr: Array = captures.iter().map(|s| Dynamic::from((*s).to_string())).collect();
scope.push("captures", arr);
let full = captures.first().copied().unwrap_or("").to_string();
scope.push("whole", full);
let out: Dynamic = self
.engine
.eval_ast_with_scope(&mut scope, &self.ast)
.map_err(|e| Error::ScriptRuntime(e.to_string()))?;
Ok(out.to_string())
}
}
fn sandboxed_engine() -> Engine {
let mut engine = Engine::new();
engine.set_max_operations(1_000_000);
engine.set_max_string_size(1024 * 1024);
engine.set_max_array_size(1024);
engine.set_max_expr_depths(64, 64);
engine
}
#[cfg(test)]
#[path = "script_tests.rs"]
mod tests;