diff --git a/src/botscript.rs b/src/botscript.rs new file mode 100644 index 0000000..8724e27 --- /dev/null +++ b/src/botscript.rs @@ -0,0 +1,96 @@ +use quickjs_rusty::Context; +use quickjs_rusty::ContextError; +use quickjs_rusty::ExecutionError; +use quickjs_rusty::OwnedJsValue as JsValue; + +#[derive(thiserror::Error, Debug)] +pub enum ScriptError { + #[error("error context: {0:?}")] + ContextError(#[from] ContextError), + #[error("error running: {0:?}")] + ExecutionError(#[from] ExecutionError), +} + +pub type ScriptResult = Result; + +// TODO: remove this function since it is suitable only for early development +#[allow(clippy::print_stdout)] +fn print(s: String) { + println!("{s}"); +} + +pub struct Runner { + context: Context, +} + +impl Runner { + pub fn init() -> ScriptResult { + let context = Context::new(None)?; + + context.add_callback("print", |a: String| { + print(a); + + None:: + })?; + + Ok(Runner { context }) + } + + pub fn run_script(&self, content: &str) -> ScriptResult { + let ctx = &self.context; + + let val = ctx.eval(content, false)?; + + Ok(val) + } +} + +#[cfg(test)] +// allowing this since it is better for debugging tests) +#[allow(clippy::unwrap_used)] +#[allow(clippy::print_stdout)] +mod tests { + use super::*; + + #[test] + fn test_run_script_valid() { + let runner = Runner::init().unwrap(); + let val = runner.run_script(r#"print"#).unwrap(); + println!("Val: {:?}", val); + let val = runner.run_script(r#"print('Hello from JS!');"#).unwrap(); + println!("Val: {:?}", val); + assert!(val.is_null()); + let val = runner.run_script(r#"const a = 1+2; a"#).unwrap(); + println!("Val: {:?}", val); + assert_eq!(val.to_int(), Ok(3)); + let val = runner.run_script(r#"a + 39"#).unwrap(); + println!("Val: {:?}", val); + assert_eq!(val.to_int(), Ok(42)); + } + + #[test] + fn test_run_script_file_main() { + let runner = Runner::init().unwrap(); + let val = runner.run_script(include_str!("../mainbot.js")).unwrap(); + println!("config: {:?}", val); + let val = runner.run_script("start_buttons()").unwrap(); + println!("Val: {:?}", val.to_string()); + } + + #[test] + fn test_run_script_invalid() { + let runner = Runner::init().unwrap(); + let result = runner.run_script(r#"invalid_script();"#); + + assert!(result.is_err()); + let errstr = + if let Err(ScriptError::ExecutionError(ExecutionError::Exception(errstr))) = result { + errstr.to_string().unwrap() + } else { + panic!("test returned wrong error!, {result:?}"); + }; + if errstr != "ReferenceError: invalid_script is not defined" { + panic!("test returned an error, but the wrong one, {errstr}") + } + } +} diff --git a/src/main.rs b/src/main.rs index a5fd667..1e47d0b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ pub mod admin; +pub mod botscript; pub mod db; pub mod mongodb_storage; pub mod utils;