moduforge_rules_engine/handler/function/module/
zen.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use crate::handler::custom_node_adapter::CustomNodeAdapter;
6use crate::handler::function::error::{FunctionResult, ResultExt};
7use crate::handler::function::listener::{RuntimeEvent, RuntimeListener};
8use crate::handler::function::module::export_default;
9use crate::handler::function::serde::JsValue;
10use crate::handler::graph::{DecisionGraph, DecisionGraphConfig};
11use crate::loader::DecisionLoader;
12use rquickjs::module::{Declarations, Exports, ModuleDef};
13use rquickjs::prelude::{Async, Func, Opt};
14use rquickjs::{CatchResultExt, Ctx, Function, Object};
15
16pub struct ZenListener<Loader, Adapter> {
17    pub loader: Arc<Loader>,
18    pub adapter: Arc<Adapter>,
19}
20
21impl<Loader: DecisionLoader + 'static, Adapter: CustomNodeAdapter + 'static>
22    RuntimeListener for ZenListener<Loader, Adapter>
23{
24    fn on_event<'js>(
25        &self,
26        ctx: Ctx<'js>,
27        event: RuntimeEvent,
28    ) -> Pin<Box<dyn Future<Output = FunctionResult> + 'js>> {
29        let loader = self.loader.clone();
30        let adapter = self.adapter.clone();
31
32        Box::pin(async move {
33            if event != RuntimeEvent::Startup {
34                return Ok(());
35            };
36
37            ctx.globals()
38                .set(
39                    "__evaluate",
40                    Func::from(Async(
41                        move |ctx: Ctx<'js>,
42                              key: String,
43                              context: JsValue,
44                              opts: Opt<Object<'js>>| {
45                            let loader = loader.clone();
46                            let adapter = adapter.clone();
47
48                            async move {
49                                let config: Object = ctx
50                                    .globals()
51                                    .get("config")
52                                    .or_throw(&ctx)?;
53
54                                let iteration: u8 =
55                                    config.get("iteration").or_throw(&ctx)?;
56                                let max_depth: u8 =
57                                    config.get("maxDepth").or_throw(&ctx)?;
58                                let trace = opts
59                                    .0
60                                    .map(|opt| {
61                                        opt.get::<_, bool>("trace")
62                                            .unwrap_or_default()
63                                    })
64                                    .unwrap_or_default();
65
66                                let load_result =
67                                    loader.load(key.as_str()).await;
68                                let decision_content =
69                                    load_result.or_throw(&ctx)?;
70                                let mut sub_tree = DecisionGraph::try_new(
71                                    DecisionGraphConfig {
72                                        content: decision_content,
73                                        max_depth,
74                                        loader,
75                                        adapter,
76                                        iteration: iteration + 1,
77                                        trace,
78                                        validator_cache: None,
79                                    },
80                                )
81                                .or_throw(&ctx)?;
82
83                                let response = sub_tree
84                                    .evaluate(context.0)
85                                    .await
86                                    .or_throw(&ctx)?;
87                                let k = serde_json::to_value(response)
88                                    .or_throw(&ctx)?
89                                    .into();
90
91                                return rquickjs::Result::Ok(JsValue(k));
92                            }
93                        },
94                    )),
95                )
96                .catch(&ctx)?;
97
98            Ok(())
99        })
100    }
101}
102
103fn evaluate_expression<'js>(
104    ctx: Ctx<'js>,
105    expression: String,
106    context: JsValue,
107) -> rquickjs::Result<JsValue> {
108    let s = moduforge_rules_expression::evaluate_expression(
109        expression.as_str(),
110        context.0,
111    )
112    .or_throw(&ctx)?;
113
114    Ok(JsValue(s))
115}
116
117fn evaluate_unary_expression<'js>(
118    ctx: Ctx<'js>,
119    expression: String,
120    context: JsValue,
121) -> rquickjs::Result<bool> {
122    let s = moduforge_rules_expression::evaluate_unary_expression(
123        expression.as_str(),
124        context.0,
125    )
126    .or_throw(&ctx)?;
127
128    Ok(s)
129}
130
131fn evaluate<'js>(
132    ctx: Ctx<'js>,
133    key: String,
134    context: JsValue,
135    opts: Opt<Object<'js>>,
136) -> rquickjs::Result<rquickjs::Value<'js>> {
137    let s: Function = ctx.globals().get("__evaluate").or_throw(&ctx)?;
138    let result: rquickjs::Value =
139        s.call((key, context, opts)).or_throw(&ctx)?;
140    Ok(result)
141}
142
143pub struct ZenModule;
144
145impl ModuleDef for ZenModule {
146    fn declare<'js>(decl: &Declarations<'js>) -> rquickjs::Result<()> {
147        decl.declare("evaluateExpression")?;
148        decl.declare("evaluateUnaryExpression")?;
149        decl.declare("evaluate")?;
150
151        decl.declare("default")?;
152
153        Ok(())
154    }
155
156    fn evaluate<'js>(
157        ctx: &Ctx<'js>,
158        exports: &Exports<'js>,
159    ) -> rquickjs::Result<()> {
160        export_default(ctx, exports, |default| {
161            default
162                .set("evaluateExpression", Func::from(evaluate_expression))?;
163            default.set(
164                "evaluateUnaryExpression",
165                Func::from(evaluate_unary_expression),
166            )?;
167            default.set("evaluate", Func::from(evaluate))?;
168
169            Ok(())
170        })
171    }
172}