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