Skip to main content

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