Skip to main content

systemprompt_config/paths/
system.rs

1//! System path resolution with canonicalisation and defaults discovery.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use std::path::{Path, PathBuf};
7
8use super::PathError;
9use systemprompt_manifest::paths::PathResolution;
10use systemprompt_manifest::profile::PathsConfig;
11
12#[derive(Debug, Clone)]
13pub struct SystemPaths {
14    root: PathBuf,
15    services: PathBuf,
16    skills: PathBuf,
17    settings: PathBuf,
18    content_config: PathBuf,
19    geoip_database: Option<PathBuf>,
20    defaults: PathBuf,
21}
22
23impl SystemPaths {
24    const LOGS_DIR: &'static str = "logs";
25
26    pub fn from_profile(
27        paths: &PathsConfig,
28        resolution: PathResolution,
29    ) -> Result<Self, PathError> {
30        let root = match resolution {
31            PathResolution::Canonicalize => Self::canonicalize(&paths.system, "system")?,
32            PathResolution::Lexical => Self::lexical(&paths.system, "system")?,
33        };
34        let defaults = Self::resolve_defaults_dir(&root);
35
36        Ok(Self {
37            root,
38            services: PathBuf::from(&paths.services),
39            skills: PathBuf::from(paths.skills()),
40            settings: PathBuf::from(paths.config()),
41            content_config: PathBuf::from(paths.content_config()),
42            geoip_database: paths.geoip_database.as_ref().map(PathBuf::from),
43            defaults,
44        })
45    }
46
47    fn canonicalize(path: &str, field: &'static str) -> Result<PathBuf, PathError> {
48        std::fs::canonicalize(path).map_err(|source| PathError::CanonicalizeFailed {
49            path: PathBuf::from(path),
50            field,
51            source,
52        })
53    }
54
55    fn lexical(path: &str, field: &'static str) -> Result<PathBuf, PathError> {
56        let path = PathBuf::from(path);
57        if path.is_absolute() {
58            Ok(path)
59        } else {
60            Err(PathError::NotAbsolute { path, field })
61        }
62    }
63
64    fn resolve_defaults_dir(root: &Path) -> PathBuf {
65        root.join("defaults")
66    }
67
68    pub fn root(&self) -> &Path {
69        &self.root
70    }
71
72    pub fn services(&self) -> &Path {
73        &self.services
74    }
75
76    pub fn skills(&self) -> &Path {
77        &self.skills
78    }
79
80    pub fn settings(&self) -> &Path {
81        &self.settings
82    }
83
84    pub fn content_config(&self) -> &Path {
85        &self.content_config
86    }
87
88    pub fn geoip_database(&self) -> Option<&Path> {
89        self.geoip_database.as_deref()
90    }
91
92    pub fn logs(&self) -> PathBuf {
93        self.root.join(Self::LOGS_DIR)
94    }
95
96    pub fn defaults(&self) -> &Path {
97        &self.defaults
98    }
99}