gongbotrs/src/botscript.rs
2025-05-10 03:27:35 +03:00

97 lines
2.7 KiB
Rust

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<T> = Result<T, ScriptError>;
// 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<Self> {
let context = Context::new(None)?;
context.add_callback("print", |a: String| {
print(a);
None::<bool>
})?;
Ok(Runner { context })
}
pub fn run_script(&self, content: &str) -> ScriptResult<JsValue> {
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}")
}
}
}