systemprompt_models/profile/
paths.rs1use 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 hooks(&self) -> String {
59 format!("{}/hooks", self.services)
60 }
61
62 pub fn agents(&self) -> String {
63 format!("{}/agents", self.services)
64 }
65
66 pub fn web_path_resolved(&self) -> String {
67 self.web_path
68 .clone()
69 .unwrap_or_else(|| format!("{}/web", self.system))
70 }
71
72 pub fn storage_resolved(&self) -> Option<&str> {
73 self.storage.as_deref()
74 }
75
76 pub fn geoip_database_resolved(&self) -> Option<&str> {
77 self.geoip_database.as_deref()
78 }
79}
80
81pub fn resolve_path(base: &Path, path: &str) -> String {
82 let p = Path::new(path);
83 if p.is_absolute() {
84 path.to_string()
85 } else {
86 let resolved = base.join(p);
87 resolved.canonicalize().map_or_else(
88 |_| resolved.to_string_lossy().to_string(),
89 |canonical| canonical.to_string_lossy().to_string(),
90 )
91 }
92}
93
94pub fn expand_home(path_str: &str) -> PathBuf {
95 path_str.strip_prefix("~/").map_or_else(
96 || PathBuf::from(path_str),
97 |stripped| {
98 let home = std::env::var("HOME")
99 .or_else(|_| std::env::var("USERPROFILE"))
100 .unwrap_or_else(|_| {
101 tracing::warn!(
102 path = %path_str,
103 "Cannot expand ~/ path: neither HOME nor USERPROFILE is set"
104 );
105 String::new()
106 });
107 PathBuf::from(home).join(stripped)
108 },
109 )
110}
111
112pub fn resolve_with_home(base: &Path, path_str: &str) -> PathBuf {
113 let path = expand_home(path_str);
114
115 if path.is_absolute() {
116 path
117 } else {
118 base.join(path)
119 }
120}