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}