systemprompt_cli/commands/web/
paths.rs1use 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_owned()),
39 paths
40 .assets
41 .unwrap_or_else(|| DEFAULT_ASSETS_PATH.to_owned()),
42 ),
43 None => (
44 DEFAULT_TEMPLATES_PATH.to_owned(),
45 DEFAULT_ASSETS_PATH.to_owned(),
46 ),
47 }
48 },
49 Err(e) if e.kind() == std::io::ErrorKind::NotFound => (
50 DEFAULT_TEMPLATES_PATH.to_owned(),
51 DEFAULT_ASSETS_PATH.to_owned(),
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}