just_engine/runner/plugin/registry.rs
1//! Built-in registry for managing built-in objects and plugins.
2
3use std::collections::HashMap;
4use std::path::Path;
5
6use super::types::{BuiltInFn, BuiltInObject, PluginInfo};
7use super::config::PluginConfig;
8use crate::runner::std_lib::register_core_builtins;
9
10/// Error type for plugin operations.
11#[derive(Debug)]
12pub enum PluginError {
13 /// Plugin not found at the specified path.
14 NotFound(String),
15 /// Plugin failed to load.
16 LoadError(String),
17 /// Plugin registration failed.
18 RegistrationError(String),
19 /// Configuration error.
20 ConfigError(String),
21 /// Object not found in registry.
22 ObjectNotFound(String),
23 /// Method not found on object.
24 MethodNotFound(String, String),
25}
26
27impl std::fmt::Display for PluginError {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 PluginError::NotFound(path) => write!(f, "Plugin not found: {}", path),
31 PluginError::LoadError(msg) => write!(f, "Plugin load error: {}", msg),
32 PluginError::RegistrationError(msg) => write!(f, "Plugin registration error: {}", msg),
33 PluginError::ConfigError(msg) => write!(f, "Plugin config error: {}", msg),
34 PluginError::ObjectNotFound(name) => write!(f, "Object not found: {}", name),
35 PluginError::MethodNotFound(obj, method) => {
36 write!(f, "Method not found: {}.{}", obj, method)
37 }
38 }
39 }
40}
41
42impl std::error::Error for PluginError {}
43
44/// Loaded plugin information.
45pub struct LoadedPlugin {
46 pub info: PluginInfo,
47 pub source: PluginSource,
48}
49
50/// Source of a loaded plugin.
51pub enum PluginSource {
52 /// Built-in (compiled with the runtime).
53 BuiltIn,
54 /// Native dynamic library.
55 Native(String),
56 /// JavaScript file.
57 JavaScript(String),
58}
59
60/// Registry for built-in objects and methods.
61///
62/// Manages all built-in JavaScript objects (Math, console, Array, etc.)
63/// and their methods. Supports plugin loading and method overrides.
64///
65/// # Examples
66///
67/// ```
68/// use just::runner::plugin::registry::BuiltInRegistry;
69/// use just::runner::plugin::types::EvalContext;
70///
71/// // Create registry with core built-ins
72/// let registry = BuiltInRegistry::with_core();
73///
74/// // Use with evaluation context
75/// let mut ctx = EvalContext::new();
76/// ctx.install_core_builtins(registry);
77/// ```
78pub struct BuiltInRegistry {
79 /// All registered built-in objects.
80 objects: HashMap<String, BuiltInObject>,
81
82 /// Loaded plugins.
83 plugins: Vec<LoadedPlugin>,
84
85 /// Override chain (object.method -> list of override sources).
86 overrides: HashMap<String, Vec<String>>,
87}
88
89impl BuiltInRegistry {
90 /// Create an empty registry.
91 ///
92 /// Use [`with_core`](Self::with_core) instead to get a registry
93 /// with standard built-in objects.
94 ///
95 /// # Examples
96 ///
97 /// ```
98 /// use just::runner::plugin::registry::BuiltInRegistry;
99 ///
100 /// let registry = BuiltInRegistry::new();
101 /// // Registry is empty, no built-ins registered
102 /// ```
103 pub fn new() -> Self {
104 BuiltInRegistry {
105 objects: HashMap::new(),
106 plugins: Vec::new(),
107 overrides: HashMap::new(),
108 }
109 }
110
111 /// Create a registry with core built-in objects.
112 ///
113 /// Includes: Math, console, JSON, String, Number, Array, Object, Error types.
114 /// This is the most common way to create a registry.
115 ///
116 /// # Examples
117 ///
118 /// ```
119 /// use just::parser::JsParser;
120 /// use just::runner::plugin::registry::BuiltInRegistry;
121 /// use just::runner::plugin::types::EvalContext;
122 /// use just::runner::eval::statement::execute_statement;
123 ///
124 /// let mut ctx = EvalContext::new();
125 /// ctx.install_core_builtins(BuiltInRegistry::with_core());
126 ///
127 /// // Now you can use Math, console, etc.
128 /// let code = "var x = Math.abs(-42);";
129 /// let ast = JsParser::parse_to_ast_from_str(code).unwrap();
130 /// for stmt in &ast.body {
131 /// execute_statement(stmt, &mut ctx).unwrap();
132 /// }
133 /// ```
134 pub fn with_core() -> Self {
135 let mut registry = Self::new();
136
137 // Register core built-ins from std_lib
138 register_core_builtins(&mut registry);
139
140 // Mark that we have core built-ins loaded
141 registry.plugins.push(LoadedPlugin {
142 info: PluginInfo::new("core", "0.1.0")
143 .with_provides(vec![
144 "console".to_string(),
145 "Object".to_string(),
146 "Array".to_string(),
147 "String".to_string(),
148 "Number".to_string(),
149 "Math".to_string(),
150 "JSON".to_string(),
151 "Error".to_string(),
152 ]),
153 source: PluginSource::BuiltIn,
154 });
155
156 registry
157 }
158
159 /// Register a custom built-in object.
160 ///
161 /// Use this to add your own built-in objects to the registry.
162 ///
163 /// # Examples
164 ///
165 /// ```
166 /// use just::runner::plugin::registry::BuiltInRegistry;
167 /// use just::runner::plugin::types::BuiltInObject;
168 ///
169 /// let mut registry = BuiltInRegistry::new();
170 /// let my_object = BuiltInObject::new("MyObject");
171 /// registry.register_object(my_object);
172 /// ```
173 pub fn register_object(&mut self, obj: BuiltInObject) {
174 self.objects.insert(obj.name.clone(), obj);
175 }
176
177 /// Get a registered object by name.
178 ///
179 /// Returns `None` if the object is not registered.
180 ///
181 /// # Examples
182 ///
183 /// ```
184 /// use just::runner::plugin::registry::BuiltInRegistry;
185 ///
186 /// let registry = BuiltInRegistry::with_core();
187 /// let math = registry.get_object("Math");
188 /// assert!(math.is_some());
189 /// ```
190 pub fn get_object(&self, name: &str) -> Option<&BuiltInObject> {
191 self.objects.get(name)
192 }
193
194 /// Get a mutable reference to a registered object.
195 pub fn get_object_mut(&mut self, name: &str) -> Option<&mut BuiltInObject> {
196 self.objects.get_mut(name)
197 }
198
199 /// Override an existing built-in method.
200 pub fn override_method(
201 &mut self,
202 object: &str,
203 method: &str,
204 func: BuiltInFn,
205 ) -> Result<(), PluginError> {
206 let obj = self
207 .objects
208 .get_mut(object)
209 .ok_or_else(|| PluginError::ObjectNotFound(object.to_string()))?;
210
211 obj.methods.insert(method.to_string(), func);
212
213 // Track the override
214 let key = format!("{}.{}", object, method);
215 self.overrides
216 .entry(key)
217 .or_insert_with(Vec::new)
218 .push("manual".to_string());
219
220 Ok(())
221 }
222
223 /// Get a built-in function for execution.
224 pub fn get_method(&self, object: &str, method: &str) -> Option<&BuiltInFn> {
225 self.objects
226 .get(object)
227 .and_then(|obj| obj.methods.get(method))
228 }
229
230 /// Get a constructor function for an object.
231 pub fn get_constructor(&self, object: &str) -> Option<&BuiltInFn> {
232 self.objects
233 .get(object)
234 .and_then(|obj| obj.constructor.as_ref())
235 }
236
237 /// Check if an object exists in the registry.
238 pub fn has_object(&self, name: &str) -> bool {
239 self.objects.contains_key(name)
240 }
241
242 /// Check if a method exists on an object.
243 pub fn has_method(&self, object: &str, method: &str) -> bool {
244 self.objects
245 .get(object)
246 .map(|obj| obj.methods.contains_key(method))
247 .unwrap_or(false)
248 }
249
250 /// Load native plugin from dynamic library.
251 /// Note: This is a placeholder - actual implementation requires unsafe FFI code
252 /// and the `libloading` crate for cross-platform support.
253 pub fn load_native_plugin(&mut self, path: &Path) -> Result<PluginInfo, PluginError> {
254 // TODO: Implement using libloading crate
255 // The plugin must export:
256 // - plugin_info() -> PluginInfo
257 // - plugin_register(&mut BuiltInRegistry)
258 Err(PluginError::LoadError(format!(
259 "Native plugin loading not yet implemented: {}",
260 path.display()
261 )))
262 }
263
264 /// Load JavaScript plugin from file.
265 /// Note: This requires the runtime to be functional to execute the JS code.
266 pub fn load_js_plugin(&mut self, path: &Path) -> Result<PluginInfo, PluginError> {
267 // TODO: Implement after runtime is complete
268 // The JS file should export __plugin__ metadata and object definitions
269 Err(PluginError::LoadError(format!(
270 "JavaScript plugin loading not yet implemented: {}",
271 path.display()
272 )))
273 }
274
275 /// Load plugins from config file.
276 pub fn load_from_config(&mut self, config_path: &Path) -> Result<(), PluginError> {
277 let config = PluginConfig::load(config_path)?;
278
279 for native_plugin in &config.native_plugins {
280 if native_plugin.enabled {
281 self.load_native_plugin(Path::new(&native_plugin.path))?;
282 }
283 }
284
285 for js_plugin in &config.js_plugins {
286 if js_plugin.enabled {
287 self.load_js_plugin(Path::new(&js_plugin.path))?;
288 }
289 }
290
291 Ok(())
292 }
293
294 /// Get list of all registered object names.
295 pub fn object_names(&self) -> Vec<&String> {
296 self.objects.keys().collect()
297 }
298
299 /// Get list of all loaded plugins.
300 pub fn loaded_plugins(&self) -> &[LoadedPlugin] {
301 &self.plugins
302 }
303}
304
305impl Default for BuiltInRegistry {
306 fn default() -> Self {
307 Self::with_core()
308 }
309}