systemprompt_cli/commands/web/
paths.rs1use anyhow::{Context, Result};
7use std::fs;
8use std::path::{Path, PathBuf};
9use systemprompt_config::ProfileBootstrap;
10use systemprompt_models::Profile;
11use systemprompt_models::validators::WebConfigRaw;
12
13const DEFAULT_TEMPLATES_PATH: &str = "web/templates";
14const DEFAULT_ASSETS_PATH: &str = "web/assets";
15
16#[derive(Debug)]
17pub struct WebPaths {
18 pub templates: PathBuf,
19 pub assets: PathBuf,
20}
21
22impl WebPaths {
23 pub fn resolve() -> Result<Self> {
24 let profile = ProfileBootstrap::get().context("Failed to get profile")?;
25 Self::resolve_from_profile(profile)
26 }
27
28 pub fn resolve_from_profile(profile: &Profile) -> Result<Self> {
29 let web_config_path = profile.paths.web_config();
30 let services_path = &profile.paths.services;
31
32 let (templates_path, assets_path) = match fs::read_to_string(&web_config_path) {
33 Ok(content) => {
34 let web_config: WebConfigRaw =
35 serde_yaml::from_str(&content).with_context(|| {
36 format!("Failed to parse web config at {}", web_config_path)
37 })?;
38
39 match web_config.paths {
40 Some(paths) => (
41 paths
42 .templates
43 .unwrap_or_else(|| DEFAULT_TEMPLATES_PATH.to_owned()),
44 paths
45 .assets
46 .unwrap_or_else(|| DEFAULT_ASSETS_PATH.to_owned()),
47 ),
48 None => (
49 DEFAULT_TEMPLATES_PATH.to_owned(),
50 DEFAULT_ASSETS_PATH.to_owned(),
51 ),
52 }
53 },
54 Err(e) if e.kind() == std::io::ErrorKind::NotFound => (
55 DEFAULT_TEMPLATES_PATH.to_owned(),
56 DEFAULT_ASSETS_PATH.to_owned(),
57 ),
58 Err(e) => {
59 return Err(e)
60 .with_context(|| format!("Failed to read web config at {}", web_config_path));
61 },
62 };
63
64 let base = Path::new(services_path);
65 let templates = normalize_under_services(&templates_path, base);
66 let assets = normalize_under_services(&assets_path, base);
67
68 Ok(Self { templates, assets })
69 }
70}
71
72fn normalize_under_services(raw: &str, base: &Path) -> PathBuf {
73 let candidate = Path::new(raw);
74 if candidate.is_absolute() {
75 return candidate.to_path_buf();
76 }
77 let stripped = candidate.strip_prefix("services").unwrap_or(candidate);
78 base.join(stripped)
79}