js-bridge 0.0.1

a javas runtime base deno
Documentation
use deno_core::v8;
use deno_core::JsRuntime;
use deno_core::RuntimeOptions;

pub struct Context {
    pub js_runtime: JsRuntime,
}

impl Default for Context {
    fn default() -> Self {
        Self::new(RuntimeOptions { extensions: vec![], ..Default::default() })
    }
}

impl Context {
    pub fn new(options: RuntimeOptions) -> Self {
        let js_runtime = JsRuntime::new(options);
        Self { js_runtime }
    }

    pub fn serde_global(&mut self, global: v8::Global<v8::Value>) -> anyhow::Result<serde_json::Value> {
        let scope = &mut self.js_runtime.handle_scope();
        let local = v8::Local::new(scope, global);
        let deserialized_value = serde_v8::from_v8::<serde_json::Value>(scope, local)?;
        Ok(deserialized_value)
    }

    pub fn eval(&mut self, code: &str) -> anyhow::Result<serde_json::Value> {
        let global = self.js_runtime.execute_script("eval", code.to_string())?;
        Ok(self.serde_global(global)?)
    }
    pub fn call_with_args<T: Into<serde_json::Value>>(&mut self, function_name: &str, args: Vec<T>) -> anyhow::Result<serde_json::Value> {
        let context = self.js_runtime.main_context();
        let scope: &mut v8::HandleScope<v8::Context> = &mut self.js_runtime.handle_scope();
        let context_local = v8::Local::new(scope, context);
        let global = context_local.global(scope);

        let function_key = v8::String::new(scope, function_name).unwrap();
        let function_value = global.get(scope, function_key.into()).unwrap();

        let function = v8::Local::<v8::Function>::try_from(function_value).unwrap();
        let args: Vec<serde_json::Value> = args.into_iter().map(|x| x.into()).collect::<Vec<_>>();
        let args = args.iter().map(|x| serde_v8::to_v8(scope, x).unwrap()).collect::<Vec<_>>();

        let ret = function.call(scope, global.into(), &args).unwrap();

        Ok(serde_v8::from_v8::<serde_json::Value>(scope, ret)?)
    }
    pub fn call<T: Into<serde_json::Value>>(&mut self, function_name: &str) -> anyhow::Result<serde_json::Value> {
        self.call_with_args::<T>(function_name, vec![])
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_context_eval() {
        let mut context = Context::default();
        let ret = context
            .eval(
                r#"
                let a = { a: 1, b: 2 };
                a
            "#,
            )
            .unwrap();
        assert_eq!(ret, serde_json::json!({ "a": 1, "b": 2 }));
    }

    #[test]
    fn test_context_call() {
        let mut context = Context::default();
        let _res = context
            .eval(
                r#"
            function hello(...values) {
                return "hello " + values.join('');
            }
            "#,
            )
            .unwrap();
        let ret = context.call_with_args("hello", crate::args!["world", 2, "!"]).unwrap();
        assert_eq!(ret, serde_json::json!("hello world2!"));
    }
}