moduforge_rules_engine/handler/function/module/
mod.rs1use std::cell::RefCell;
2use std::collections::HashSet;
3use std::ops::DerefMut;
4use std::rc::Rc;
5
6use rquickjs::loader::{Bundle, Loader, ModuleLoader as MDLoader, Resolver};
7use rquickjs::module::{Declared, Exports};
8use rquickjs::{embed, Ctx, Error, Module, Object};
9
10use crate::handler::function::module::custom::ModuforgeModule;
11use crate::handler::function::module::http::HttpModule;
12use crate::handler::function::module::zen::ZenModule;
13
14pub mod console;
15pub mod custom;
16pub mod http;
17pub mod zen;
18
19static JS_BUNDLE: Bundle = embed! {
20 "dayjs": "js/dayjs.mjs",
21 "big.js": "js/big.mjs",
22 "zod": "js/zod.mjs"
23};
24
25#[derive(Clone)]
26pub struct ModuleLoader(Rc<RefCell<BaseModuleLoader>>);
27
28impl ModuleLoader {
29 pub fn new() -> Self {
30 Self(Rc::new(RefCell::new(BaseModuleLoader::new())))
31 }
32
33 pub fn add_module(
34 &self,
35 module: String,
36 ) {
37 let reference = self.0.borrow_mut();
38 reference.add_module(module);
39 }
40
41 pub fn has_module(
42 &self,
43 module: &str,
44 ) -> bool {
45 let reference = self.0.borrow();
46 reference.has_module(module)
47 }
48}
49
50impl Resolver for ModuleLoader {
51 fn resolve<'js>(
52 &mut self,
53 ctx: &Ctx<'js>,
54 base: &str,
55 name: &str,
56 ) -> rquickjs::Result<String> {
57 let mut inner = self.0.borrow_mut();
58 inner.deref_mut().resolve(ctx, base, name)
59 }
60}
61
62impl Loader for ModuleLoader {
63 fn load<'js>(
64 &mut self,
65 ctx: &Ctx<'js>,
66 name: &str,
67 ) -> rquickjs::Result<Module<'js, Declared>> {
68 let mut inner = self.0.borrow_mut();
69 inner.deref_mut().load(ctx, name)
70 }
71}
72
73struct BaseModuleLoader {
74 bundle: Bundle,
75 defined_modules: RefCell<HashSet<String>>,
76 md_loader: MDLoader,
77}
78
79impl BaseModuleLoader {
80 pub fn new() -> Self {
81 let mut hs = HashSet::from([
82 "zen".to_string(),
83 "http".to_string(),
84 "moduforge".to_string(),
85 ]);
86
87 JS_BUNDLE.iter().for_each(|(key, _)| {
88 hs.insert(key.to_string());
89 });
90
91 Self {
92 bundle: JS_BUNDLE,
93 defined_modules: RefCell::new(hs),
94 md_loader: MDLoader::default()
95 .with_module("zen", ZenModule)
96 .with_module("http", HttpModule)
97 .with_module("moduforge", ModuforgeModule),
98 }
99 }
100
101 pub fn add_module(
102 &self,
103 value: String,
104 ) {
105 let mut modules = self.defined_modules.borrow_mut();
106 modules.insert(value);
107 }
108
109 pub fn has_module(
110 &self,
111 value: &str,
112 ) -> bool {
113 let modules = self.defined_modules.borrow();
114 modules.contains(value)
115 }
116}
117
118impl Resolver for &mut BaseModuleLoader {
119 fn resolve<'js>(
120 &mut self,
121 ctx: &Ctx<'js>,
122 base: &str,
123 name: &str,
124 ) -> rquickjs::Result<String> {
125 if let Ok(b) = self.bundle.resolve(ctx, base, name) {
126 return Ok(b);
127 }
128
129 let defined_modules = self.defined_modules.borrow();
130 if defined_modules.contains(name) {
131 return Ok(name.to_string());
132 }
133
134 Err(Error::new_resolving(base, name))
135 }
136}
137
138impl Loader for &mut BaseModuleLoader {
139 fn load<'js>(
140 &mut self,
141 ctx: &Ctx<'js>,
142 name: &str,
143 ) -> rquickjs::Result<Module<'js, Declared>> {
144 self.bundle.load(ctx, name).or_else(|_| self.md_loader.load(ctx, name))
145 }
146}
147
148pub(crate) fn export_default<'js, F>(
149 ctx: &Ctx<'js>,
150 exports: &Exports<'js>,
151 f: F,
152) -> rquickjs::Result<()>
153where
154 F: FnOnce(&Object<'js>) -> rquickjs::Result<()>,
155{
156 let default = Object::new(ctx.clone())?;
157 f(&default)?;
158
159 for name in default.keys::<String>() {
160 let name = name?;
161 let value: rquickjs::Value = default.get(&name)?;
162 exports.export(name, value)?;
163 }
164
165 exports.export("default", default)?;
166
167 Ok(())
168}