systemprompt_loader/
secrets_loader.rs1use 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}