moduforge_rules_engine/handler/function/module/
mod.rs

1use 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}