Skip to main content

systemprompt_models/profile/
paths.rs

1use serde::{Deserialize, Serialize};
2use std::path::{Path, PathBuf};
3
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct PathsConfig {
6    pub system: String,
7    pub services: String,
8    pub bin: String,
9
10    #[serde(default)]
11    pub web_path: Option<String>,
12
13    #[serde(default)]
14    pub storage: Option<String>,
15
16    #[serde(default)]
17    pub geoip_database: Option<String>,
18}
19
20impl PathsConfig {
21    pub fn resolve_relative_to(&mut self, base: &Path) {
22        self.system = resolve_path(base, &self.system);
23        self.services = resolve_path(base, &self.services);
24        self.bin = resolve_path(base, &self.bin);
25        self.storage = self.storage.as_ref().map(|p| resolve_path(base, p));
26        self.geoip_database = self.geoip_database.as_ref().map(|p| resolve_path(base, p));
27        self.web_path = self.web_path.as_ref().map(|p| resolve_path(base, p));
28    }
29
30    pub fn skills(&self) -> String {
31        format!("{}/skills", self.services)
32    }
33
34    pub fn config(&self) -> String {
35        format!("{}/config/config.yaml", self.services)
36    }
37
38    pub fn ai_config(&self) -> String {
39        format!("{}/ai/config.yaml", self.services)
40    }
41
42    pub fn content_config(&self) -> String {
43        format!("{}/content/config.yaml", self.services)
44    }
45
46    pub fn web_config(&self) -> String {
47        format!("{}/web/config.yaml", self.services)
48    }
49
50    pub fn web_metadata(&self) -> String {
51        format!("{}/web/metadata.yaml", self.services)
52    }
53
54    pub fn plugins(&self) -> String {
55        format!("{}/plugins", self.services)
56    }
57
58    pub fn marketplaces(&self) -> String {
59        format!("{}/marketplaces", self.services)
60    }
61
62    pub fn hooks(&self) -> String {
63        format!("{}/hooks", self.services)
64    }
65
66    pub fn agents(&self) -> String {
67        format!("{}/agents", self.services)
68    }
69
70    pub fn web_path_resolved(&self) -> String {
71        self.web_path
72            .clone()
73            .unwrap_or_else(|| format!("{}/web", self.system))
74    }
75
76    pub fn storage_resolved(&self) -> Option<&str> {
77        self.storage.as_deref()
78    }
79
80    pub fn geoip_database_resolved(&self) -> Option<&str> {
81        self.geoip_database.as_deref()
82    }
83}
84
85pub fn resolve_path(base: &Path, path: &str) -> String {
86    let p = Path::new(path);
87    if p.is_absolute() {
88        path.to_string()
89    } else {
90        let resolved = base.join(p);
91        resolved.canonicalize().map_or_else(
92            |_| resolved.to_string_lossy().to_string(),
93            |canonical| canonical.to_string_lossy().to_string(),
94        )
95    }
96}
97
98pub fn expand_home(path_str: &str) -> PathBuf {
99    path_str.strip_prefix("~/").map_or_else(
100        || PathBuf::from(path_str),
101        |stripped| {
102            let home = std::env::var("HOME")
103                .or_else(|_| std::env::var("USERPROFILE"))
104                .unwrap_or_else(|_| {
105                    tracing::warn!(
106                        path = %path_str,
107                        "Cannot expand ~/ path: neither HOME nor USERPROFILE is set"
108                    );
109                    String::new()
110                });
111            PathBuf::from(home).join(stripped)
112        },
113    )
114}
115
116pub fn resolve_with_home(base: &Path, path_str: &str) -> PathBuf {
117    let path = expand_home(path_str);
118
119    if path.is_absolute() {
120        path
121    } else {
122        base.join(path)
123    }
124}