Skip to main content

systemprompt_loader/
profile_loader.rs

1use anyhow::{Context, Result};
2use std::path::Path;
3use systemprompt_models::Profile;
4
5#[derive(Debug, Clone, Copy)]
6pub struct ProfileLoader;
7
8impl ProfileLoader {
9    pub fn load_from_path(profile_path: &Path) -> Result<Profile> {
10        let content = std::fs::read_to_string(profile_path)
11            .with_context(|| format!("Failed to read profile: {}", profile_path.display()))?;
12
13        Profile::parse(&content, profile_path)
14    }
15
16    pub fn load(services_path: &Path, profile_name: &str) -> Result<Profile> {
17        let profile_path = services_path
18            .join("profiles")
19            .join(format!("{profile_name}.secrets.profile.yaml"));
20
21        Self::load_from_path(&profile_path)
22    }
23
24    pub fn load_from_path_and_validate(profile_path: &Path) -> Result<Profile> {
25        let profile = Self::load_from_path(profile_path)?;
26        profile.validate()?;
27        Ok(profile)
28    }
29
30    pub fn load_and_validate(services_path: &Path, profile_name: &str) -> Result<Profile> {
31        let profile = Self::load(services_path, profile_name)?;
32        profile.validate()?;
33        Ok(profile)
34    }
35
36    pub fn save(profile: &Profile, services_path: &Path) -> Result<()> {
37        let profiles_dir = services_path.join("profiles");
38        std::fs::create_dir_all(&profiles_dir).context("Failed to create profiles directory")?;
39
40        let profile_path = profiles_dir.join(format!("{}.secrets.profile.yaml", profile.name));
41        let content = profile.to_yaml()?;
42
43        let content_with_header = format!(
44            "# systemprompt.io Profile: {}\n# \n# WARNING: This file contains secrets.\n# DO NOT \
45             commit to version control.\n\n{content}",
46            profile.display_name
47        );
48
49        std::fs::write(&profile_path, content_with_header)
50            .with_context(|| format!("Failed to write profile: {}", profile_path.display()))
51    }
52
53    pub fn list_available(services_path: &Path) -> Vec<String> {
54        let profiles_dir = services_path.join("profiles");
55
56        if !profiles_dir.exists() {
57            return Vec::new();
58        }
59
60        match std::fs::read_dir(&profiles_dir) {
61            Ok(entries) => entries
62                .filter_map(std::result::Result::ok)
63                .filter_map(|e| {
64                    let name = e.file_name().to_string_lossy().to_string();
65                    name.strip_suffix(".secrets.profile.yaml")
66                        .map(ToString::to_string)
67                })
68                .collect(),
69            Err(e) => {
70                tracing::warn!(
71                    error = %e,
72                    path = %profiles_dir.display(),
73                    "Failed to read profiles directory"
74                );
75                Vec::new()
76            },
77        }
78    }
79}