Skip to main content

systemprompt_cli/commands/web/
paths.rs

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