1use std::pin::Pin;
11use std::sync::Arc;
12
13use tracing::{debug, warn};
14
15use apcore::context::Context;
16use apcore::errors::ModuleError;
17use apcore::Registry;
18
19use crate::output::types::{Verifier, WriteResult};
20use crate::output::verifiers::{run_verifier_chain, RegistryVerifier};
21use crate::types::ScannedModule;
22
23pub type HandlerFn = Arc<
30 dyn for<'a> Fn(
31 serde_json::Value,
32 &'a Context<serde_json::Value>,
33 ) -> Pin<
34 Box<
35 dyn std::future::Future<Output = Result<serde_json::Value, ModuleError>>
36 + Send
37 + 'a,
38 >,
39 > + Send
40 + Sync,
41>;
42
43pub type HandlerFactory = Arc<dyn Fn(&str) -> Option<HandlerFn> + Send + Sync>;
60
61pub struct RegistryWriter {
75 handler_factory: Option<HandlerFactory>,
76}
77
78impl Default for RegistryWriter {
79 fn default() -> Self {
80 Self::new()
81 }
82}
83
84impl RegistryWriter {
85 pub fn new() -> Self {
95 Self {
96 handler_factory: None,
97 }
98 }
99
100 pub fn with_handler_factory(factory: HandlerFactory) -> Self {
102 Self {
103 handler_factory: Some(factory),
104 }
105 }
106}
107
108impl RegistryWriter {
109 pub fn write(
124 &self,
125 modules: &[ScannedModule],
126 registry: &mut Registry,
127 dry_run: bool,
128 verify: bool,
129 verifiers: Option<&[&dyn Verifier]>,
130 ) -> Vec<WriteResult> {
131 let mut results: Vec<WriteResult> = Vec::new();
132
133 for module in modules {
134 if dry_run {
135 results.push(WriteResult::new(module.module_id.clone()));
136 continue;
137 }
138
139 let fm = self.to_function_module(module);
140 let descriptor = apcore::registry::registry::ModuleDescriptor {
142 module_id: module.module_id.clone(),
143 name: Some(module.module_id.clone()),
144 description: module.description.clone(),
145 documentation: module.documentation.clone(),
146 input_schema: module.input_schema.clone(),
147 output_schema: module.output_schema.clone(),
148 version: module.version.clone(),
149 tags: module.tags.clone(),
150 annotations: module.annotations.clone(),
151 examples: module.examples.clone(),
152 metadata: module.metadata.clone(),
153 display: module.display.clone(),
154 sunset_date: None,
155 dependencies: vec![],
156 enabled: true,
157 };
158 if let Err(e) = registry.register(&module.module_id, Box::new(fm), descriptor) {
162 warn!(
163 module_id = %module.module_id,
164 error = %e,
165 "RegistryWriter registration failed"
166 );
167 results.push(WriteResult::failed(
168 module.module_id.clone(),
169 None,
170 format!("Registration failed: {e}"),
171 ));
172 continue;
173 }
174 debug!("Registered module: {}", module.module_id);
175
176 let mut result = WriteResult::new(module.module_id.clone());
177 if verify {
178 result = verify_registry(&result, &module.module_id, registry);
179 }
180 if result.verified {
181 if let Some(vs) = verifiers {
182 let chain_result = run_verifier_chain(vs, "", &module.module_id);
183 if !chain_result.ok {
184 result = WriteResult::failed(
185 result.module_id,
186 result.path,
187 chain_result.error.unwrap_or_default(),
188 );
189 }
190 }
191 }
192 results.push(result);
193 }
194
195 results
196 }
197}
198
199impl RegistryWriter {
200 fn to_function_module(&self, module: &ScannedModule) -> apcore::decorator::FunctionModule {
206 let annotations = module.annotations.clone().unwrap_or_default();
207 let input_schema = module.input_schema.clone();
208 let output_schema = module.output_schema.clone();
209
210 if let Some(factory) = &self.handler_factory {
212 if let Some(handler) = factory(&module.target) {
213 return apcore::decorator::FunctionModule::new::<_, ()>(
214 annotations,
215 input_schema,
216 output_schema,
217 move |inputs: serde_json::Value,
218 ctx: &Context<serde_json::Value>|
219 -> Pin<
220 Box<
221 dyn std::future::Future<Output = Result<serde_json::Value, ModuleError>>
222 + Send
223 + '_,
224 >,
225 > { handler(inputs, ctx) },
226 );
227 }
228 }
229
230 debug!(
232 module_id = %module.module_id,
233 "RegistryWriter using passthrough handler (no HandlerFactory configured)",
234 );
235 fn passthrough<'a>(
236 inputs: serde_json::Value,
237 _ctx: &'a Context<serde_json::Value>,
238 ) -> Pin<
239 Box<
240 dyn std::future::Future<Output = Result<serde_json::Value, ModuleError>>
241 + Send
242 + 'a,
243 >,
244 > {
245 Box::pin(async move { Ok(inputs) })
246 }
247
248 apcore::decorator::FunctionModule::new::<_, ()>(
249 annotations,
250 input_schema,
251 output_schema,
252 passthrough,
253 )
254 }
255}
256
257fn verify_registry(result: &WriteResult, module_id: &str, registry: &Registry) -> WriteResult {
259 let verifier = RegistryVerifier::new(registry);
260 let vr = verifier.verify("", module_id);
261 if vr.ok {
262 result.clone()
263 } else {
264 WriteResult::failed(module_id.into(), None, vr.error.unwrap_or_default())
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271 use serde_json::json;
272
273 fn sample_module() -> ScannedModule {
274 ScannedModule::new(
275 "users.get".into(),
276 "Get user".into(),
277 json!({"type": "object"}),
278 json!({"type": "object"}),
279 vec!["users".into()],
280 "app:get_user".into(),
281 )
282 }
283
284 #[test]
285 fn test_write_dry_run() {
286 let writer = RegistryWriter::new();
287 let mut registry = Registry::new();
288 let modules = vec![sample_module()];
289 let results = writer.write(&modules, &mut registry, true, false, None);
290 assert_eq!(results.len(), 1);
291 assert_eq!(results[0].module_id, "users.get");
292 assert!(!registry.has("users.get"));
293 }
294
295 #[test]
296 fn test_write_registers_module() {
297 let writer = RegistryWriter::new();
298 let mut registry = Registry::new();
299 let modules = vec![sample_module()];
300 let results = writer.write(&modules, &mut registry, false, false, None);
301 assert_eq!(results.len(), 1);
302 assert!(registry.has("users.get"));
303 }
304
305 #[test]
306 fn test_write_with_verify() {
307 let writer = RegistryWriter::new();
308 let mut registry = Registry::new();
309 let modules = vec![sample_module()];
310 let results = writer.write(&modules, &mut registry, false, true, None);
311 assert_eq!(results.len(), 1);
312 assert!(results[0].verified);
313 }
314
315 #[test]
316 fn test_write_empty_list() {
317 let writer = RegistryWriter::new();
318 let mut registry = Registry::new();
319 let results = writer.write(&[], &mut registry, false, false, None);
320 assert!(results.is_empty());
321 }
322
323 #[test]
324 fn test_write_multiple_modules() {
325 let writer = RegistryWriter::new();
326 let mut registry = Registry::new();
327 let modules = vec![
328 ScannedModule::new(
329 "mod.a".into(),
330 "A".into(),
331 json!({"type": "object"}),
332 json!({"type": "object"}),
333 vec![],
334 "app:a".into(),
335 ),
336 ScannedModule::new(
337 "mod.b".into(),
338 "B".into(),
339 json!({"type": "object"}),
340 json!({"type": "object"}),
341 vec![],
342 "app:b".into(),
343 ),
344 ];
345 let results = writer.write(&modules, &mut registry, false, false, None);
346 assert_eq!(results.len(), 2);
347 assert!(registry.has("mod.a"));
348 assert!(registry.has("mod.b"));
349 assert!(results[0].verified);
350 assert!(results[1].verified);
351 }
352}