systemprompt_runtime/
registry.rs1use axum::Router;
10use std::collections::HashMap;
11
12pub use systemprompt_models::modules::{Module, ModuleType, Modules, ServiceCategory};
13
14use crate::AppContext;
15
16#[derive(Debug)]
17pub struct ModuleApiRegistry {
18 registry: HashMap<String, ModuleApiImpl>,
19}
20
21#[derive(Debug)]
22struct ModuleApiImpl {
23 category: ServiceCategory,
24 module_type: ModuleType,
25 router_fn: fn(&AppContext) -> Router,
26 auth_required: bool,
27}
28
29#[derive(Debug, Copy, Clone)]
30pub struct ModuleApiRegistration {
31 pub module_name: &'static str,
32 pub category: ServiceCategory,
33 pub module_type: ModuleType,
34 pub router_fn: fn(&AppContext) -> Router,
35 pub auth_required: bool,
36}
37
38inventory::collect!(ModuleApiRegistration);
39
40#[derive(Debug, Clone, Copy)]
41pub struct WellKnownRoute {
42 pub path: &'static str,
43 pub handler_fn: fn(&AppContext) -> Router,
44 pub methods: &'static [axum::http::Method],
45}
46
47inventory::collect!(WellKnownRoute);
48
49impl Default for ModuleApiRegistry {
50 fn default() -> Self {
51 Self::new()
52 }
53}
54
55impl ModuleApiRegistry {
56 pub fn new() -> Self {
57 let mut registry = HashMap::new();
58
59 for registration in inventory::iter::<ModuleApiRegistration> {
60 let api_impl = ModuleApiImpl {
61 category: registration.category,
62 module_type: registration.module_type,
63 router_fn: registration.router_fn,
64 auth_required: registration.auth_required,
65 };
66 registry.insert(registration.module_name.to_string(), api_impl);
67 }
68
69 Self { registry }
70 }
71
72 pub fn get_routes(&self, module_name: &str, ctx: &AppContext) -> Option<Router> {
73 self.registry
74 .get(module_name)
75 .map(|impl_| (impl_.router_fn)(ctx))
76 }
77
78 pub fn get_category(&self, module_name: &str) -> Option<ServiceCategory> {
79 self.registry.get(module_name).map(|impl_| impl_.category)
80 }
81
82 pub fn get_module_type(&self, module_name: &str) -> Option<ModuleType> {
83 self.registry
84 .get(module_name)
85 .map(|impl_| impl_.module_type)
86 }
87
88 pub fn get_auth_required(&self, module_name: &str) -> Option<bool> {
89 self.registry
90 .get(module_name)
91 .map(|impl_| impl_.auth_required)
92 }
93
94 pub fn modules_by_category(&self, category: ServiceCategory) -> Vec<String> {
95 self.registry
96 .iter()
97 .filter(|(_, impl_)| matches!(impl_.category, c if c as u8 == category as u8))
98 .map(|(name, _)| name.clone())
99 .collect()
100 }
101}
102
103pub trait ModuleRuntime {
104 fn routes(&self, ctx: &AppContext, registry: &ModuleApiRegistry) -> Option<Router>;
105 fn create_api_registry(&self) -> ModuleApiRegistry;
106}
107
108impl ModuleRuntime for Module {
109 fn routes(&self, ctx: &AppContext, registry: &ModuleApiRegistry) -> Option<Router> {
110 if let Some(api) = &self.api {
111 if api.enabled {
112 return registry.get_routes(&self.name, ctx);
113 }
114 }
115 None
116 }
117
118 fn create_api_registry(&self) -> ModuleApiRegistry {
119 ModuleApiRegistry::new()
120 }
121}
122
123impl ModuleRuntime for Modules {
124 fn routes(&self, _ctx: &AppContext, _registry: &ModuleApiRegistry) -> Option<Router> {
125 None
126 }
127
128 fn create_api_registry(&self) -> ModuleApiRegistry {
129 ModuleApiRegistry::new()
130 }
131}