js_bridge/
context.rs

1use deno_core::v8;
2use deno_core::JsRuntime;
3use deno_core::RuntimeOptions;
4
5pub struct Context {
6    pub js_runtime: JsRuntime,
7}
8
9impl Default for Context {
10    fn default() -> Self {
11        Self::new(RuntimeOptions { extensions: vec![], ..Default::default() })
12    }
13}
14
15impl Context {
16    pub fn new(options: RuntimeOptions) -> Self {
17        let js_runtime = JsRuntime::new(options);
18        Self { js_runtime }
19    }
20
21    pub fn serde_global(&mut self, global: v8::Global<v8::Value>) -> anyhow::Result<serde_json::Value> {
22        let scope = &mut self.js_runtime.handle_scope();
23        let local = v8::Local::new(scope, global);
24        let deserialized_value = serde_v8::from_v8::<serde_json::Value>(scope, local)?;
25        Ok(deserialized_value)
26    }
27
28    pub fn eval(&mut self, code: &str) -> anyhow::Result<serde_json::Value> {
29        let global = self.js_runtime.execute_script("eval", code.to_string())?;
30        Ok(self.serde_global(global)?)
31    }
32    pub fn call_with_args<T: Into<serde_json::Value>>(&mut self, function_name: &str, args: Vec<T>) -> anyhow::Result<serde_json::Value> {
33        let context = self.js_runtime.main_context();
34        let scope: &mut v8::HandleScope<v8::Context> = &mut self.js_runtime.handle_scope();
35        let context_local = v8::Local::new(scope, context);
36        let global = context_local.global(scope);
37
38        let function_key = v8::String::new(scope, function_name).unwrap();
39        let function_value = global.get(scope, function_key.into()).unwrap();
40
41        let function = v8::Local::<v8::Function>::try_from(function_value).unwrap();
42        let args: Vec<serde_json::Value> = args.into_iter().map(|x| x.into()).collect::<Vec<_>>();
43        let args = args.iter().map(|x| serde_v8::to_v8(scope, x).unwrap()).collect::<Vec<_>>();
44
45        let ret = function.call(scope, global.into(), &args).unwrap();
46
47        Ok(serde_v8::from_v8::<serde_json::Value>(scope, ret)?)
48    }
49    pub fn call<T: Into<serde_json::Value>>(&mut self, function_name: &str) -> anyhow::Result<serde_json::Value> {
50        self.call_with_args::<T>(function_name, vec![])
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57
58    #[test]
59    fn test_context_eval() {
60        let mut context = Context::default();
61        let ret = context
62            .eval(
63                r#"
64                let a = { a: 1, b: 2 };
65                a
66            "#,
67            )
68            .unwrap();
69        assert_eq!(ret, serde_json::json!({ "a": 1, "b": 2 }));
70    }
71
72    #[test]
73    fn test_context_call() {
74        let mut context = Context::default();
75        let _res = context
76            .eval(
77                r#"
78            function hello(...values) {
79                return "hello " + values.join('');
80            }
81            "#,
82            )
83            .unwrap();
84        let ret = context.call_with_args("hello", crate::args!["world", 2, "!"]).unwrap();
85        assert_eq!(ret, serde_json::json!("hello world2!"));
86    }
87}