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 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}