1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4mod engine;
5
6use async_std::sync::RwLock;
7use engine::Engine;
8use json_mel::*;
9use melodium_core::*;
10use melodium_macro::{check, mel_model, mel_package, mel_treatment};
11use std::{
12 collections::HashMap,
13 sync::{Arc, Weak},
14};
15
16#[derive(Debug)]
28#[mel_model(
29 param stack_size_limit u64 1024
30 param recursion_limit u64 400
31 param loop_iteration_limit u64 4294967295
32 param strict bool false
33 param {content(javascript)} code string ""
34 initialize initialize
35 shutdown shutdown
36)]
37pub struct JavaScriptEngine {
38 model: Weak<JavaScriptEngineModel>,
39 engine: RwLock<Option<Engine>>,
40}
41
42impl JavaScriptEngine {
43 fn new(model: Weak<JavaScriptEngineModel>) -> Self {
44 Self {
45 model,
46 engine: RwLock::new(None),
47 }
48 }
49
50 fn initialize(&self) {
51 let model = self.model.upgrade().unwrap();
52
53 let engine = Engine::new(
54 model.get_stack_size_limit(),
55 model.get_recursion_limit(),
56 model.get_loop_iteration_limit(),
57 model.get_strict(),
58 model.get_code(),
59 );
60
61 async_std::task::block_on(async move {
62 *model.inner().engine.write().await = Some(engine);
63 });
64 }
65
66 fn shutdown(&self) {
67 let model = self.model.upgrade().unwrap();
68 async_std::task::block_on(async move {
69 if let Some(engine) = model.inner().engine.write().await.as_ref() {
70 engine.stop();
71 }
72 });
73 }
74
75 fn invoke_source(&self, _source: &str, _params: HashMap<String, Value>) {}
76
77 pub(crate) fn engine(&self) -> &RwLock<Option<Engine>> {
78 &self.engine
79 }
80}
81
82#[mel_treatment(
90 model engine JavaScriptEngine
91 input value Stream<Json>
92 output result Stream<Option<Json>>
93)]
94pub async fn process(#[mel(content(javascript))] code: string) {
95 let engine = JavaScriptEngineModel::into(engine);
96
97 'main: while let Ok(values) = value
98 .recv_many()
99 .await
100 .map(|values| Into::<Vec<Value>>::into(values))
101 {
102 for value in values {
103 if let Value::Data(value) = value {
104 match value.downcast_arc::<Json>() {
105 Ok(value) => {
106 let processed;
107 if let Some(engine) = engine.inner().engine().read().await.as_ref() {
108 processed = engine.process(value.0.clone(), code.clone()).await;
109 } else {
110 break;
111 }
112
113 match processed {
114 Ok(Ok(value)) => {
115 check!(
116 result
117 .send_one(
118 Some(Arc::new(Json(value)) as Arc<dyn Data>).into()
119 )
120 .await
121 );
122 }
123 Ok(Err(_err)) => {
124 check!(result.send_one(Option::<Arc<dyn Data>>::None.into()).await);
125 }
126 Err(_) => {
127 break 'main;
128 }
129 }
130 }
131 Err(_) => break 'main,
132 }
133 }
134 }
135 }
136}
137
138mel_package!();