Skip to main content

systemprompt_loader/
secrets_loader.rs

1use anyhow::{Context, Result};
2use std::path::{Path, PathBuf};
3use systemprompt_models::Secrets;
4
5#[derive(Debug, Clone, Copy)]
6pub struct SecretsLoader;
7
8impl SecretsLoader {
9    pub fn load_from_file(path: &Path) -> Result<Secrets> {
10        if !path.exists() {
11            anyhow::bail!("Secrets file not found: {}", path.display());
12        }
13
14        let content = std::fs::read_to_string(path)
15            .with_context(|| format!("Failed to read secrets file: {}", path.display()))?;
16
17        Secrets::parse(&content)
18            .with_context(|| format!("Failed to parse secrets file: {}", path.display()))
19    }
20
21    pub fn resolve_and_load(path_str: &str, base_dir: Option<&Path>) -> Result<Secrets> {
22        let path = Self::resolve_path(path_str, base_dir)?;
23        Self::load_from_file(&path)
24    }
25
26    pub fn resolve_path(path_str: &str, base_dir: Option<&Path>) -> Result<PathBuf> {
27        let path = if let Some(stripped) = path_str.strip_prefix("~/") {
28            let home = std::env::var("HOME")
29                .or_else(|_| std::env::var("USERPROFILE"))
30                .map_err(|_| {
31                    anyhow::anyhow!(
32                        "Cannot resolve path '{}': neither HOME nor USERPROFILE environment \
33                         variable is set",
34                        path_str
35                    )
36                })?;
37            PathBuf::from(home).join(stripped)
38        } else {
39            PathBuf::from(path_str)
40        };
41
42        if path.is_relative() {
43            Ok(base_dir.map_or_else(|| path.clone(), |base| base.join(&path)))
44        } else {
45            Ok(path)
46        }
47    }
48
49    pub fn exists(path: &Path) -> bool {
50        path.exists()
51    }
52}