Skip to main content

systemprompt_runtime/
registry.rs

1//! Module API and well-known route registries built from `inventory`.
2//!
3//! Modules register HTTP routers with
4//! [`register_module_api!`](crate::register_module_api) and well-known
5//! endpoints with
6//! [`register_wellknown_route!`](crate::register_wellknown_route). Both submit
7//! to `inventory` collectors that this module materialises into runtime maps.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use axum::Router;
13use serde::{Deserialize, Serialize};
14use std::collections::HashMap;
15
16use systemprompt_models::modules::ServiceCategory;
17
18use crate::AppContext;
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
21pub enum ModuleType {
22    Regular,
23    Proxy,
24}
25
26#[derive(Debug)]
27pub struct ModuleApiRegistry {
28    registry: HashMap<String, ModuleApiImpl>,
29}
30
31#[derive(Debug)]
32struct ModuleApiImpl {
33    category: ServiceCategory,
34    module_type: ModuleType,
35    router_fn: fn(&AppContext) -> Router,
36    auth_required: bool,
37}
38
39#[derive(Debug, Copy, Clone)]
40pub struct ModuleApiRegistration {
41    pub module_name: &'static str,
42    pub category: ServiceCategory,
43    pub module_type: ModuleType,
44    pub router_fn: fn(&AppContext) -> Router,
45    pub auth_required: bool,
46}
47
48inventory::collect!(ModuleApiRegistration);
49
50#[derive(Debug, Clone, Copy)]
51pub struct WellKnownRoute {
52    pub path: &'static str,
53    pub handler_fn: fn(&AppContext) -> Router,
54    pub methods: &'static [axum::http::Method],
55}
56
57inventory::collect!(WellKnownRoute);
58
59impl Default for ModuleApiRegistry {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl ModuleApiRegistry {
66    pub fn new() -> Self {
67        let mut registry = HashMap::new();
68
69        for registration in inventory::iter::<ModuleApiRegistration> {
70            let api_impl = ModuleApiImpl {
71                category: registration.category,
72                module_type: registration.module_type,
73                router_fn: registration.router_fn,
74                auth_required: registration.auth_required,
75            };
76            registry.insert(registration.module_name.to_owned(), api_impl);
77        }
78
79        Self { registry }
80    }
81
82    pub fn get_routes(&self, module_name: &str, ctx: &AppContext) -> Option<Router> {
83        self.registry
84            .get(module_name)
85            .map(|impl_| (impl_.router_fn)(ctx))
86    }
87
88    pub fn get_category(&self, module_name: &str) -> Option<ServiceCategory> {
89        self.registry.get(module_name).map(|impl_| impl_.category)
90    }
91
92    pub fn get_module_type(&self, module_name: &str) -> Option<ModuleType> {
93        self.registry
94            .get(module_name)
95            .map(|impl_| impl_.module_type)
96    }
97
98    pub fn get_auth_required(&self, module_name: &str) -> Option<bool> {
99        self.registry
100            .get(module_name)
101            .map(|impl_| impl_.auth_required)
102    }
103
104    pub fn modules_by_category(&self, category: ServiceCategory) -> Vec<String> {
105        self.registry
106            .iter()
107            .filter(|(_, impl_)| matches!(impl_.category, c if c as u8 == category as u8))
108            .map(|(name, _)| name.clone())
109            .collect()
110    }
111}