Skip to main content

systemprompt_runtime/
config_planes.rs

1//! The registered [`ConfigPlane`]s and the authority each one runs under.
2//!
3//! [`ConfigPlaneSet`] is the write guard console and API writers consult:
4//! [`ConfigPlaneSet::ensure_db_writable`] refuses a write to a plane whose
5//! declaration file exists and whose profile authority is `file`, because
6//! the reconciler would overwrite the edit on its next pass. A plane whose
7//! file is absent is never file-managed, whatever the profile says.
8//!
9//! Copyright (c) systemprompt.io — Business Source License 1.1.
10//! See <https://systemprompt.io> for licensing details.
11
12use std::collections::HashSet;
13use std::path::{Path, PathBuf};
14use std::sync::Arc;
15
16use serde::Serialize;
17use systemprompt_manifest::profile::{ConfigPlanesConfig, PlaneAuthority};
18use systemprompt_provider_contracts::{ConfigPlane, ConfigPlaneError, ConfigPlaneResult};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
21#[serde(rename_all = "snake_case")]
22pub enum EffectiveAuthority {
23    File,
24    Db,
25    Undeclared,
26}
27
28impl EffectiveAuthority {
29    #[must_use]
30    pub const fn as_str(self) -> &'static str {
31        match self {
32            Self::File => "file",
33            Self::Db => "db",
34            Self::Undeclared => "undeclared",
35        }
36    }
37
38    #[must_use]
39    pub const fn is_file_managed(self) -> bool {
40        matches!(self, Self::File)
41    }
42}
43
44#[derive(Clone)]
45pub struct ConfigPlaneSet {
46    planes: Vec<Arc<dyn ConfigPlane>>,
47    config: ConfigPlanesConfig,
48    services_root: PathBuf,
49}
50
51impl std::fmt::Debug for ConfigPlaneSet {
52    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
53        f.debug_struct("ConfigPlaneSet")
54            .field("planes", &self.names())
55            .field("config", &self.config)
56            .field("services_root", &self.services_root)
57            .finish()
58    }
59}
60
61impl ConfigPlaneSet {
62    pub fn new(
63        planes: Vec<Arc<dyn ConfigPlane>>,
64        config: ConfigPlanesConfig,
65        services_root: PathBuf,
66    ) -> ConfigPlaneResult<Self> {
67        let mut seen = HashSet::new();
68        for plane in &planes {
69            if !seen.insert(plane.name()) {
70                return Err(ConfigPlaneError::DuplicatePlane(plane.name().to_owned()));
71            }
72        }
73        Ok(Self {
74            planes,
75            config,
76            services_root,
77        })
78    }
79
80    #[must_use]
81    pub fn planes(&self) -> &[Arc<dyn ConfigPlane>] {
82        &self.planes
83    }
84
85    #[must_use]
86    pub fn names(&self) -> Vec<&'static str> {
87        self.planes.iter().map(|p| p.name()).collect()
88    }
89
90    #[must_use]
91    pub fn services_root(&self) -> &Path {
92        &self.services_root
93    }
94
95    #[must_use]
96    pub const fn config(&self) -> &ConfigPlanesConfig {
97        &self.config
98    }
99
100    pub fn find(&self, name: &str) -> ConfigPlaneResult<&Arc<dyn ConfigPlane>> {
101        self.planes
102            .iter()
103            .find(|p| p.name() == name)
104            .ok_or_else(|| ConfigPlaneError::UnknownPlane(name.to_owned()))
105    }
106
107    #[must_use]
108    pub fn declared_exists(&self, plane: &dyn ConfigPlane) -> bool {
109        self.services_root.join(plane.declared_path()).exists()
110    }
111
112    #[must_use]
113    pub fn authority_of(&self, plane: &dyn ConfigPlane) -> EffectiveAuthority {
114        match self.config.authority_for(plane.name()) {
115            PlaneAuthority::Db => EffectiveAuthority::Db,
116            PlaneAuthority::File if self.declared_exists(plane) => EffectiveAuthority::File,
117            PlaneAuthority::File => EffectiveAuthority::Undeclared,
118        }
119    }
120
121    pub fn authority(&self, name: &str) -> ConfigPlaneResult<EffectiveAuthority> {
122        Ok(self.authority_of(self.find(name)?.as_ref()))
123    }
124
125    pub fn is_file_managed(&self, name: &str) -> ConfigPlaneResult<bool> {
126        Ok(self.authority(name)?.is_file_managed())
127    }
128
129    pub fn ensure_db_writable(&self, name: &str) -> ConfigPlaneResult<()> {
130        let plane = self.find(name)?;
131        if self.authority_of(plane.as_ref()).is_file_managed() {
132            return Err(ConfigPlaneError::FileManaged {
133                plane: name.to_owned(),
134                path: plane.declared_path().to_owned(),
135            });
136        }
137        Ok(())
138    }
139
140    #[must_use]
141    pub fn unregistered_authority_keys(&self) -> Vec<&str> {
142        self.config
143            .authority
144            .keys()
145            .map(String::as_str)
146            .filter(|key| !self.planes.iter().any(|p| p.name() == *key))
147            .collect()
148    }
149}