moduforge_rules_engine/handler/function/module/
custom.rs

1//! # 自定义函数监听器模块
2//!
3//! 本模块实现了CustomListener,用于在JavaScript运行时环境中注册和管理自定义函数。
4//!
5//! ## 主要功能
6//!
7//! - **函数注册**: 在运行时启动时自动将Rust自定义函数注册到JavaScript的md命名空间
8//! - **命名空间管理**: 创建和管理md作用域,避免全局命名冲突
9//! - **类型转换**: 处理Rust和JavaScript之间的数据类型转换
10//! - **异步支持**: 提供异步函数调用支持,确保不阻塞JavaScript执行
11//! - **错误处理**: 完善的错误捕获和处理机制
12//!
13//! ## 使用场景
14//!
15//! 该监听器主要用于规则引擎中,允许在规则表达式中通过`md.functionName()`的形式
16//! 调用预定义的Rust函数,从而扩展JavaScript运行时的功能。
17//!
18//! ## 架构说明
19//!
20//! ```text
21//! CustomFunctionRegistry → CustomListener → JavaScript Runtime (md namespace)
22//!        ↓                      ↓                    ↓
23//!    函数定义存储           函数注册处理          md.functionName() 调用执行
24//! ```
25
26use std::future::Future;
27use std::pin::Pin;
28use crate::handler::function::error::{FunctionResult, ResultExt};
29use crate::handler::function::listener::{RuntimeEvent, RuntimeListener};
30use crate::handler::function::module::export_default;
31use crate::handler::function::serde::JsValue;
32use moduforge_rules_expression::{
33    CustomFunctionRegistry, functions::arguments::Arguments,
34};
35use rquickjs::module::{Declarations, Exports, ModuleDef};
36use rquickjs::prelude::{Async, Func};
37use rquickjs::{CatchResultExt, Ctx};
38
39/// 自定义函数监听器
40///
41/// 该监听器负责在JavaScript运行时启动时,将所有注册的自定义函数
42/// 绑定到JavaScript的md命名空间中,使得这些函数可以在规则表达式中通过
43/// `md.functionName()`的形式被调用
44///
45/// # 工作流程
46/// 1. 监听运行时启动事件
47/// 2. 创建或获取md命名空间对象
48/// 3. 从CustomFunctionRegistry获取所有已注册的函数
49/// 4. 将每个函数包装为异步JavaScript函数
50/// 5. 注册到JavaScript的md命名空间中
51pub struct ModuforgeListener {
52    // 目前为空结构体,后续可以添加配置或状态字段
53}
54
55impl RuntimeListener for ModuforgeListener {
56    /// 处理运行时事件的核心方法
57    ///
58    /// # 参数
59    /// - `ctx`: QuickJS上下文,用于操作JavaScript环境
60    /// - `event`: 运行时事件类型
61    ///
62    /// # 返回值
63    /// 返回一个异步Future,包含操作结果
64    fn on_event<'js>(
65        &self,
66        ctx: Ctx<'js>,
67        event: RuntimeEvent,
68    ) -> Pin<Box<dyn Future<Output = FunctionResult> + 'js>> {
69        Box::pin(async move {
70            // 只在运行时启动事件时执行函数注册
71            if event != RuntimeEvent::Startup {
72                return Ok(());
73            };
74
75            // 设置全局函数及变量
76            // 创建或获取 md 命名空间对象
77            let md_namespace = if ctx.globals().contains_key("md")? {
78                // 如果 md 已存在,获取它
79                ctx.globals().get("md")?
80            } else {
81                // 如果 md 不存在,创建一个新的空对象
82                let md_obj = rquickjs::Object::new(ctx.clone())?;
83                ctx.globals().set("md", md_obj.clone())?;
84                md_obj
85            };
86
87            // 从自定义函数注册表中获取所有函数名称
88            let functions_keys = CustomFunctionRegistry::list_functions();
89
90            // 遍历每个注册的函数
91            for function_key in functions_keys {
92                // 根据函数名获取函数定义
93                let function_definition =
94                    CustomFunctionRegistry::get_definition(&function_key);
95
96                if let Some(function_definition) = function_definition {
97                    // 将Rust函数包装为JavaScript异步函数并注册到md命名空间下
98
99                    let function_definition = function_definition.clone();
100                    let parameters = function_definition.required_parameters();
101                    match parameters {
102                        0 => {
103                            md_namespace
104                                .set(
105                                    function_key, // 函数名作为md对象的属性名
106                                    Func::from(Async(move |ctx: Ctx<'js>| {
107                                        // 克隆函数定义以避免生命周期问题
108                                        let function_definition =
109                                            function_definition.clone();
110
111                                        async move {
112                                            // 调用Rust函数,传入JavaScript参数
113                                            let response = function_definition
114                                                .call(Arguments(&[]))
115                                                .or_throw(&ctx)?;
116
117                                            // 将Rust函数的返回值序列化为JSON,再转换为JavaScript值
118                                            let k =
119                                                serde_json::to_value(response)
120                                                    .or_throw(&ctx)?
121                                                    .into();
122
123                                            return rquickjs::Result::Ok(
124                                                JsValue(k),
125                                            );
126                                        }
127                                    })),
128                                )
129                                .catch(&ctx)?; // 捕获并处理可能的JavaScript异常
130                        },
131                        1 => {
132                            md_namespace
133                            .set(
134                                function_key, // 函数名作为md对象的属性名
135                                Func::from(Async(
136                                    move |ctx: Ctx<'js>, context: JsValue| {
137                                        // 克隆函数定义以避免生命周期问题
138                                        let function_definition =
139                                            function_definition.clone();
140                                        async move {
141                                            // 调用Rust函数,传入JavaScript参数
142                                            let response = function_definition
143                                                .call(Arguments(&[context.0]))
144                                                .or_throw(&ctx)?;
145                                            // 将Rust函数的返回值序列化为JSON,再转换为JavaScript值
146                                            let k = serde_json::to_value(response)
147                                                .or_throw(&ctx)?
148                                                .into();
149                                            return rquickjs::Result::Ok(JsValue(
150                                                k,
151                                            ));
152                                        }
153                                    },
154                                )),
155                            )
156                            .catch(&ctx)?; // 捕获并处理可能的JavaScript异常
157                        },
158                        2 => {
159                            md_namespace
160                            .set(
161                                function_key, // 函数名作为md对象的属性名
162                                Func::from(Async(
163                                    move |ctx: Ctx<'js>, context: JsValue,context2: JsValue| {
164                                        // 克隆函数定义以避免生命周期问题
165                                        let function_definition =
166                                            function_definition.clone();
167                                        async move {
168                                            // 调用Rust函数,传入JavaScript参数
169                                            let response = function_definition
170                                                .call(Arguments(&[context.0,context2.0]))
171                                                .or_throw(&ctx)?;
172                                            // 将Rust函数的返回值序列化为JSON,再转换为JavaScript值
173                                            let k = serde_json::to_value(response)
174                                                .or_throw(&ctx)?
175                                                .into();
176                                            return rquickjs::Result::Ok(JsValue(
177                                                k,
178                                            ));
179                                        }
180                                    },
181                                )),
182                            )
183                            .catch(&ctx)?; // 捕获并处理可能的JavaScript异常
184                        },
185                        3 => {
186                            md_namespace
187                            .set(
188                                function_key, // 函数名作为md对象的属性名
189                                Func::from(Async(
190                                    move |ctx: Ctx<'js>, context: JsValue,context2: JsValue,context3: JsValue| {
191                                        // 克隆函数定义以避免生命周期问题
192                                        let function_definition =
193                                            function_definition.clone();
194                                        async move {
195                                            // 调用Rust函数,传入JavaScript参数
196                                            let response = function_definition
197                                                .call(Arguments(&[context.0,context2.0,context3.0]))
198                                                .or_throw(&ctx)?;
199                                            // 将Rust函数的返回值序列化为JSON,再转换为JavaScript值
200                                            let k = serde_json::to_value(response)
201                                                .or_throw(&ctx)?
202                                                .into();
203                                            return rquickjs::Result::Ok(JsValue(
204                                                k,
205                                            ));
206                                        }
207                                    },
208                                )),
209                            )
210                            .catch(&ctx)?; // 捕获并处理可能的JavaScript异常
211                        },
212                        _ => {
213                            md_namespace
214                            .set(
215                                function_key, // 函数名作为md对象的属性名
216                                Func::from(Async(
217                                    move |ctx: Ctx<'js>, context: Vec<JsValue>| {
218                                        // 克隆函数定义以避免生命周期问题
219                                        let function_definition =
220                                            function_definition.clone();
221                                        async move {
222                                            // 调用Rust函数,传入JavaScript参数
223                                            let response = function_definition
224                                                .call(Arguments(&context.iter().map(|arg| arg.0.clone()).collect::<Vec<_>>()))
225                                                .or_throw(&ctx)?;
226                                            // 将Rust函数的返回值序列化为JSON,再转换为JavaScript值
227                                            let k = serde_json::to_value(response)
228                                                .or_throw(&ctx)?
229                                                .into();
230                                            return rquickjs::Result::Ok(JsValue(
231                                                k,
232                                            ));
233                                        }
234                                    },
235                                )),
236                            )
237                            .catch(&ctx)?; // 捕获并处理可能的JavaScript异常
238                        },
239                    }
240                }
241            }
242
243            Ok(()) // 成功完成函数注册
244        })
245    }
246}
247
248pub struct ModuforgeModule;
249
250impl ModuleDef for ModuforgeModule {
251    fn declare<'js>(decl: &Declarations<'js>) -> rquickjs::Result<()> {
252        // 声明所有可用的函数
253        for function_key in CustomFunctionRegistry::list_functions() {
254            decl.declare(function_key.as_str())?;
255        }
256        decl.declare("default")?;
257        Ok(())
258    }
259
260    fn evaluate<'js>(
261        ctx: &Ctx<'js>,
262        exports: &Exports<'js>,
263    ) -> rquickjs::Result<()> {
264        export_default(ctx, exports, |default| {
265            // 为每个函数创建对应的异步函数
266            for function_key in CustomFunctionRegistry::list_functions() {
267                if let Some(function_definition) =
268                    CustomFunctionRegistry::get_definition(&function_key)
269                {
270                    let function_definition = function_definition.clone();
271                    let parameters = function_definition.required_parameters();
272                    match parameters {
273                        0 => {
274                            default.set(
275                                &function_key,
276                                Func::from(Async(move |ctx: Ctx<'js>| {
277                                    let function_definition =
278                                        function_definition.clone();
279                                    async move {
280                                        let response = function_definition
281                                            .call(Arguments(&[]))
282                                            .or_throw(&ctx)?;
283
284                                        let result =
285                                            serde_json::to_value(response)
286                                                .or_throw(&ctx)?
287                                                .into();
288
289                                        Ok::<JsValue, rquickjs::Error>(JsValue(
290                                            result,
291                                        ))
292                                    }
293                                })),
294                            )?;
295                        },
296                        1 => {
297                            //只有一个参数
298                            default.set(
299                                &function_key,
300                                Func::from(Async(
301                                    move |ctx: Ctx<'js>, args: JsValue| {
302                                        let function_definition =
303                                            function_definition.clone();
304                                        async move {
305                                            let response = function_definition
306                                                .call(Arguments(&[args.0]))
307                                                .or_throw(&ctx)?;
308
309                                            let result =
310                                                serde_json::to_value(response)
311                                                    .or_throw(&ctx)?
312                                                    .into();
313
314                                            Ok::<JsValue, rquickjs::Error>(
315                                                JsValue(result),
316                                            )
317                                        }
318                                    },
319                                )),
320                            )?;
321                        },
322                        2 => {
323                            //有两个参数
324                            default.set(
325                                &function_key,
326                                Func::from(Async(
327                                    move |ctx: Ctx<'js>, args: JsValue,args2: JsValue| {
328                                        let function_definition =
329                                            function_definition.clone();
330                                        async move {
331                                            let response = function_definition
332                                                .call(Arguments(&[args.0,args2.0]))
333                                                .or_throw(&ctx)?;
334
335                                            let result =
336                                                serde_json::to_value(response)
337                                                    .or_throw(&ctx)?
338                                                    .into();
339
340                                            Ok::<JsValue, rquickjs::Error>(
341                                                JsValue(result),
342                                            )
343                                        }
344                                    },
345                                )),
346                            )?;
347                        },
348                        3 => {
349                            //有三个参数
350                            default.set(
351                                &function_key,
352                                Func::from(Async(
353                                    move |ctx: Ctx<'js>, args: JsValue,args2: JsValue,args3: JsValue| {
354                                        let function_definition =
355                                            function_definition.clone();
356                                        async move {
357                                            let response = function_definition
358                                                .call(Arguments(&[args.0,args2.0,args3.0]))
359                                                .or_throw(&ctx)?;
360
361                                            let result =
362                                                serde_json::to_value(response)
363                                                    .or_throw(&ctx)?
364                                                    .into();
365
366                                            Ok::<JsValue, rquickjs::Error>(
367                                                JsValue(result),
368                                            )
369                                        }
370                                    },
371                                )),
372                            )?;
373                        },
374                        _ => {
375                            //4个以上参数 的参数必须以数组的形式传入
376                            default.set(
377                                &function_key,
378                                Func::from(Async(
379                                    move |ctx: Ctx<'js>, args: Vec<JsValue>| {
380                                        let function_definition =
381                                            function_definition.clone();
382                                        async move {
383                                            let args_vec = args
384                                                .iter()
385                                                .map(|arg| arg.0.clone())
386                                                .collect::<Vec<_>>();
387                                            let response = function_definition
388                                                .call(Arguments(&args_vec))
389                                                .or_throw(&ctx)?;
390
391                                            let result =
392                                                serde_json::to_value(response)
393                                                    .or_throw(&ctx)?
394                                                    .into();
395
396                                            Ok::<JsValue, rquickjs::Error>(
397                                                JsValue(result),
398                                            )
399                                        }
400                                    },
401                                )),
402                            )?;
403                        },
404                    }
405                }
406            }
407
408            Ok(())
409        })
410    }
411}