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_lock::Mutex;
13use engine::Engine;
14use json_mel::*;
15use melodium_core::*;
16use melodium_macro::{check, mel_model, mel_package, mel_treatment};
17use std::sync::{Arc, Weak};
18
19#[derive(Debug)]
20#[mel_model(
32 param stack_size_limit u64 1024
33 param recursion_limit u64 400
34 param loop_iteration_limit u64 4294967295
35 param strict bool false
36 param {content(javascript)} code string ""
37 initialize initialize
38 shutdown shutdown
39)]
40pub struct JavaScriptEngine {
41 model: Weak<JavaScriptEngineModel>,
42 engine: Mutex<Option<Engine>>,
43}
44
45impl JavaScriptEngine {
46 fn new(model: Weak<JavaScriptEngineModel>) -> Self {
47 Self {
48 model,
49 engine: Mutex::new(None),
50 }
51 }
52
53 fn initialize(&self) {
54 let model = self.model.upgrade().unwrap();
55
56 let engine = Engine::new(
57 model.get_stack_size_limit(),
58 model.get_recursion_limit(),
59 model.get_loop_iteration_limit(),
60 model.get_strict(),
61 model.get_code(),
62 );
63
64 *futures_lite::future::block_on(model.inner().engine.lock()) = Some(engine);
65 }
66
67 fn shutdown(&self) {
68 let model = self.model.upgrade().unwrap();
69 let engine = futures_lite::future::block_on(model.inner().engine.lock());
70 if let Some(engine) = engine.as_ref() {
71 engine.stop();
72 }
73 }
74
75 pub async fn process(
76 &self,
77 value: serde_json::Value,
78 code: String,
79 ) -> Result<Result<serde_json::Value, String>, ()> {
80 if let Some(engine) = self.engine.lock().await.as_ref() {
81 let res = engine.process(value, code).await;
82 res
83 } else {
84 Err(())
85 }
86 }
87
88 pub(crate) fn engine(&self) -> &Mutex<Option<Engine>> {
89 &self.engine
90 }
91}
92
93#[mel_treatment(
101 model engine JavaScriptEngine
102 input value Stream<Json>
103 output result Stream<Option<Json>>
104)]
105pub async fn process(#[mel(content(javascript))] code: string) {
106 let engine = JavaScriptEngineModel::into(engine);
107
108 'main: while let Ok(values) = value
109 .recv_many()
110 .await
111 .map(|values| Into::<Vec<Value>>::into(values))
112 {
113 for value in values {
114 if let Value::Data(value) = value {
115 match value.downcast_arc::<Json>() {
116 Ok(value) => {
117 let processed;
118 if let Some(engine) = engine.inner().engine().lock().await.as_ref() {
119 processed = engine.process(value.0.clone(), code.clone()).await;
120 } else {
121 break;
122 }
123
124 match processed {
125 Ok(Ok(value)) => {
126 check!(
127 result
128 .send_one_as(Some(Arc::new(Json(value)) as Arc<dyn Data>))
129 .await
130 );
131 }
132 Ok(Err(_err)) => {
133 check!(result.send_one_as(Option::<Arc<dyn Data>>::None).await);
134 }
135 Err(_) => {
136 break 'main;
137 }
138 }
139 }
140 Err(_) => break 'main,
141 }
142 }
143 }
144 }
145}
146
147mel_package!();