rquickjs_extension/loader/
mod.rs1use std::collections::{HashMap, HashSet};
2
3use rquickjs::{
4 module::{Module, ModuleDef},
5 Ctx, JsLifetime, Object, Result,
6};
7
8pub use self::global::GlobalInitializer;
9pub use self::loader::ModuleLoader;
10pub use self::resolver::ModuleResolver;
11use crate::wrapper::{IntoModule, ModuleMeta};
12
13mod global;
14#[allow(clippy::module_inception)]
15mod loader;
16mod resolver;
17
18type GlobalLoadFn = Box<dyn for<'js> FnOnce(&Ctx<'js>, &Object<'js>) -> Result<()> + Send + Sync>;
19type ModuleLoadFn = for<'js> fn(Ctx<'js>, Vec<u8>) -> Result<Module<'js>>;
20
21fn load_module_func<D: ModuleDef>(ctx: Ctx<'_>, name: Vec<u8>) -> Result<Module<'_>> {
22 Module::declare_def::<D, _>(ctx, name)
23}
24
25#[derive(Default)]
55pub struct ExtensionBuilder {
56 modules: HashMap<&'static str, ModuleLoadFn>,
57 globals: Vec<GlobalLoadFn>,
58 names: HashSet<&'static str>,
59}
60
61impl ExtensionBuilder {
62 pub fn new() -> Self {
63 Self::default()
64 }
65
66 #[must_use]
67 pub fn with_extension<O, M, R>(mut self, extension: M) -> Self
68 where
69 for<'js> O: JsLifetime<'js> + Send + Sync + 'static,
70 R: ModuleDef + ModuleMeta,
71 M: IntoModule<O, R>,
72 {
73 self.process_extension(extension, None);
74 self
75 }
76
77 #[must_use]
78 pub fn with_extension_named<O, M, R>(mut self, extension: M, name: &'static str) -> Self
79 where
80 for<'js> O: JsLifetime<'js> + Send + Sync + 'static,
81 R: ModuleDef + ModuleMeta,
82 M: IntoModule<O, R>,
83 {
84 self.process_extension(extension, Some(name));
85 self
86 }
87
88 pub fn add_extension<O, M, R>(&mut self, extension: M) -> &mut Self
89 where
90 for<'js> O: JsLifetime<'js> + Send + Sync + 'static,
91 R: ModuleDef + ModuleMeta,
92 M: IntoModule<O, R>,
93 {
94 self.process_extension(extension, None)
95 }
96
97 pub fn add_extension_named<O, M, R>(&mut self, extension: M, name: &'static str) -> &mut Self
98 where
99 for<'js> O: JsLifetime<'js> + Send + Sync + 'static,
100 R: ModuleDef + ModuleMeta,
101 M: IntoModule<O, R>,
102 {
103 self.process_extension(extension, Some(name))
104 }
105
106 fn process_extension<O, M, R>(&mut self, extension: M, name: Option<&'static str>) -> &mut Self
107 where
108 for<'js> O: JsLifetime<'js> + Send + Sync + 'static,
109 R: ModuleDef + ModuleMeta,
110 M: IntoModule<O, R>,
111 {
112 let o = extension.options();
113
114 let globals_fn = move |ctx: &Ctx<'_>, globals: &Object<'_>| {
116 let globals_fn = M::globals;
117 globals_fn(globals, &o)?;
118 let _ = ctx.store_userdata(o);
119 Ok(())
120 };
121
122 let boxed_globals: GlobalLoadFn = Box::new(globals_fn);
124
125 if R::is_module() {
126 let name = name.unwrap_or(R::name());
127 self.names.insert(name);
128 self.modules.insert(name, load_module_func::<R>);
129 }
130
131 self.globals.push(boxed_globals);
132 self
133 }
134
135 pub fn build(self) -> (ModuleLoader, ModuleResolver, GlobalInitializer) {
136 (
137 ModuleLoader::new(self.modules),
138 ModuleResolver::new(self.names),
139 GlobalInitializer::new(self.globals),
140 )
141 }
142}