1use std::sync::Arc;
9
10use harn_vm::{Vm, VmError, VmValue};
11
12use crate::error::HostlibError;
13
14pub type SyncHandler = Arc<dyn Fn(&[VmValue]) -> Result<VmValue, HostlibError> + Send + Sync>;
18
19#[derive(Clone)]
23pub struct RegisteredBuiltin {
24 pub name: &'static str,
26 pub module: &'static str,
28 pub method: &'static str,
30 pub handler: SyncHandler,
32}
33
34impl std::fmt::Debug for RegisteredBuiltin {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 f.debug_struct("RegisteredBuiltin")
37 .field("name", &self.name)
38 .field("module", &self.module)
39 .field("method", &self.method)
40 .finish()
41 }
42}
43
44#[derive(Default)]
46pub struct BuiltinRegistry {
47 builtins: Vec<RegisteredBuiltin>,
48}
49
50impl BuiltinRegistry {
51 pub fn new() -> Self {
53 Self::default()
54 }
55
56 pub fn register(&mut self, builtin: RegisteredBuiltin) {
58 self.builtins.push(builtin);
59 }
60
61 pub fn register_unimplemented(
64 &mut self,
65 name: &'static str,
66 module: &'static str,
67 method: &'static str,
68 ) {
69 let handler: SyncHandler =
70 Arc::new(move |_args| Err(HostlibError::Unimplemented { builtin: name }));
71 self.register(RegisteredBuiltin {
72 name,
73 module,
74 method,
75 handler,
76 });
77 }
78
79 pub(crate) fn register_fn(
83 &mut self,
84 module: &'static str,
85 name: &'static str,
86 method: &'static str,
87 runner: fn(&[VmValue]) -> Result<VmValue, HostlibError>,
88 ) {
89 let handler: SyncHandler = Arc::new(runner);
90 self.register(RegisteredBuiltin {
91 name,
92 module,
93 method,
94 handler,
95 });
96 }
97
98 pub(crate) fn register_gated_fn(
102 &mut self,
103 module: &'static str,
104 name: &'static str,
105 method: &'static str,
106 runner: fn(&[VmValue]) -> Result<VmValue, HostlibError>,
107 ) {
108 self.register(RegisteredBuiltin {
109 name,
110 module,
111 method,
112 handler: crate::tools::permissions::gated_handler(name, runner),
113 });
114 }
115
116 pub fn iter(&self) -> impl Iterator<Item = &RegisteredBuiltin> {
118 self.builtins.iter()
119 }
120
121 pub fn len(&self) -> usize {
123 self.builtins.len()
124 }
125
126 pub fn is_empty(&self) -> bool {
128 self.builtins.is_empty()
129 }
130
131 pub fn find(&self, name: &str) -> Option<&RegisteredBuiltin> {
133 self.builtins.iter().find(|b| b.name == name)
134 }
135}
136
137pub trait HostlibCapability: 'static {
141 fn module_name(&self) -> &'static str;
143
144 fn register_builtins(&self, registry: &mut BuiltinRegistry);
146}
147
148pub struct HostlibRegistry {
154 builtins: BuiltinRegistry,
155 modules: Vec<&'static str>,
156}
157
158impl Default for HostlibRegistry {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl HostlibRegistry {
165 pub fn new() -> Self {
168 Self {
169 builtins: BuiltinRegistry::new(),
170 modules: Vec::new(),
171 }
172 }
173
174 #[must_use]
176 pub fn with<C: HostlibCapability>(mut self, capability: C) -> Self {
177 let module = capability.module_name();
178 capability.register_builtins(&mut self.builtins);
179 self.modules.push(module);
180 self
181 }
182
183 pub fn register_into_vm(&mut self, vm: &mut Vm) {
185 for builtin in self.builtins.iter().cloned() {
186 let handler = builtin.handler.clone();
187 vm.register_builtin(
188 builtin.name,
189 move |args, _out| -> Result<VmValue, VmError> {
190 handler(args).map_err(VmError::from)
191 },
192 );
193 }
194 }
195
196 pub fn builtins(&self) -> &BuiltinRegistry {
199 &self.builtins
200 }
201
202 pub fn modules(&self) -> &[&'static str] {
204 &self.modules
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn unimplemented_builtins_route_through_error() {
214 let mut registry = BuiltinRegistry::new();
215 registry.register_unimplemented("hostlib_demo", "demo", "ping");
216 let entry = registry.find("hostlib_demo").expect("registered");
217 let err = (entry.handler)(&[]).expect_err("should be unimplemented");
218 assert!(
219 matches!(err, HostlibError::Unimplemented { builtin } if builtin == "hostlib_demo")
220 );
221 }
222
223 #[test]
224 fn registry_records_modules_in_order() {
225 struct First;
226 impl HostlibCapability for First {
227 fn module_name(&self) -> &'static str {
228 "first"
229 }
230 fn register_builtins(&self, _registry: &mut BuiltinRegistry) {}
231 }
232 struct Second;
233 impl HostlibCapability for Second {
234 fn module_name(&self) -> &'static str {
235 "second"
236 }
237 fn register_builtins(&self, _registry: &mut BuiltinRegistry) {}
238 }
239
240 let registry = HostlibRegistry::new().with(First).with(Second);
241 assert_eq!(registry.modules(), &["first", "second"]);
242 }
243}