1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4#[cfg(any(
5 all(feature = "real", feature = "mock"),
6 not(any(feature = "real", feature = "mock"))
7))]
8compile_error!("One of the two features 'real' or 'mock' must be enabled");
9
10mod engine;
11
12use async_std::sync::Mutex;
13use engine::Engine;
14use json_mel::*;
15use melodium_core::*;
16use melodium_macro::{check, mel_model, mel_package, mel_treatment};
17use std::{
18 collections::HashMap,
19 sync::{Arc, Weak},
20};
21
22#[derive(Debug)]
23#[mel_model(
35 param stack_size_limit u64 1024
36 param recursion_limit u64 400
37 param loop_iteration_limit u64 4294967295
38 param strict bool false
39 param {content(javascript)} code string ""
40 initialize initialize
41 shutdown shutdown
42)]
43pub struct JavaScriptEngine {
44 model: Weak<JavaScriptEngineModel>,
45 engine: Mutex<Option<Engine>>,
46}
47
48impl JavaScriptEngine {
49 fn new(model: Weak<JavaScriptEngineModel>) -> Self {
50 Self {
51 model,
52 engine: Mutex::new(None),
53 }
54 }
55
56 fn initialize(&self) {
57 let model = self.model.upgrade().unwrap();
58
59 let engine = Engine::new(
60 model.get_stack_size_limit(),
61 model.get_recursion_limit(),
62 model.get_loop_iteration_limit(),
63 model.get_strict(),
64 model.get_code(),
65 );
66
67 async_std::task::block_on(async move {
68 *model.inner().engine.lock().await = Some(engine);
69 });
70 }
71
72 fn shutdown(&self) {
73 let model = self.model.upgrade().unwrap();
74 async_std::task::block_on(async move {
75 if let Some(engine) = model.inner().engine.lock().await.as_ref() {
76 engine.stop();
77 }
78 });
79 }
80
81 fn invoke_source(&self, _source: &str, _params: HashMap<String, Value>) {}
82
83 pub async fn process(
84 &self,
85 value: serde_json::Value,
86 code: String,
87 ) -> Result<Result<serde_json::Value, String>, ()> {
88 if let Some(engine) = self.engine.lock().await.as_ref() {
89 let res = engine.process(value, code).await;
90 res
91 } else {
92 Err(())
93 }
94 }
95
96 pub(crate) fn engine(&self) -> &Mutex<Option<Engine>> {
97 &self.engine
98 }
99}
100
101#[mel_treatment(
109 model engine JavaScriptEngine
110 input value Stream<Json>
111 output result Stream<Option<Json>>
112)]
113pub async fn process(#[mel(content(javascript))] code: string) {
114 let engine = JavaScriptEngineModel::into(engine);
115
116 'main: while let Ok(values) = value
117 .recv_many()
118 .await
119 .map(|values| Into::<Vec<Value>>::into(values))
120 {
121 for value in values {
122 if let Value::Data(value) = value {
123 match value.downcast_arc::<Json>() {
124 Ok(value) => {
125 let processed;
126 if let Some(engine) = engine.inner().engine().lock().await.as_ref() {
127 processed = engine.process(value.0.clone(), code.clone()).await;
128 } else {
129 break;
130 }
131
132 match processed {
133 Ok(Ok(value)) => {
134 check!(
135 result
136 .send_one_as(Some(Arc::new(Json(value)) as Arc<dyn Data>))
137 .await
138 );
139 }
140 Ok(Err(_err)) => {
141 check!(result.send_one_as(Option::<Arc<dyn Data>>::None).await);
142 }
143 Err(_) => {
144 break 'main;
145 }
146 }
147 }
148 Err(_) => break 'main,
149 }
150 }
151 }
152 }
153}
154
155mel_package!();