Skip to main content

systemprompt_loader/
services_bootstrap.rs

1//! Process-wide services-config bootstrap.
2//!
3//! Mirrors `systemprompt_config::ProfileBootstrap` for the services tree: the
4//! merged, validated [`ServicesConfig`] — provider catalog, resolved gateway,
5//! agents, MCP servers — is loaded once, right after the profile, and read
6//! through `&'static` accessors for the life of the process. A load failure is
7//! a boot failure: nothing downstream may run against a catalog that did not
8//! parse.
9//!
10//! Copyright (c) systemprompt.io — Business Source License 1.1.
11//! See <https://systemprompt.io> for licensing details.
12
13use std::path::Path;
14use std::sync::OnceLock;
15
16use systemprompt_models::services::{GatewayConfig, ProviderRegistry, ServicesConfig};
17
18use crate::config_loader::ConfigLoader;
19use crate::error::{ConfigLoadError, ConfigLoadResult};
20
21static SERVICES: OnceLock<ServicesConfig> = OnceLock::new();
22
23#[derive(Debug, Clone, Copy)]
24pub struct ServicesBootstrap;
25
26impl ServicesBootstrap {
27    pub fn init() -> ConfigLoadResult<&'static ServicesConfig> {
28        if SERVICES.get().is_some() {
29            return Err(ConfigLoadError::AlreadyInitialized);
30        }
31        let services = ConfigLoader::load()?;
32        Self::install(services)
33    }
34
35    pub fn init_from_path(path: &Path) -> ConfigLoadResult<&'static ServicesConfig> {
36        if SERVICES.get().is_some() {
37            return Err(ConfigLoadError::AlreadyInitialized);
38        }
39        let services = ConfigLoader::load_from_path(path)?;
40        Self::install(services)
41    }
42
43    pub fn try_init() -> ConfigLoadResult<&'static ServicesConfig> {
44        if let Some(services) = SERVICES.get() {
45            return Ok(services);
46        }
47        Self::init()
48    }
49
50    pub fn get() -> ConfigLoadResult<&'static ServicesConfig> {
51        SERVICES.get().ok_or(ConfigLoadError::NotInitialized)
52    }
53
54    pub fn providers() -> ConfigLoadResult<&'static ProviderRegistry> {
55        Self::get().map(|s| &s.providers)
56    }
57
58    pub fn gateway() -> ConfigLoadResult<Option<&'static GatewayConfig>> {
59        Self::get().map(ServicesConfig::gateway_config)
60    }
61
62    #[must_use]
63    pub fn is_initialized() -> bool {
64        SERVICES.get().is_some()
65    }
66
67    fn install(services: ServicesConfig) -> ConfigLoadResult<&'static ServicesConfig> {
68        SERVICES
69            .set(services)
70            .map_err(|_already| ConfigLoadError::AlreadyInitialized)?;
71        SERVICES.get().ok_or(ConfigLoadError::NotInitialized)
72    }
73}