Skip to main content

systemprompt_loader/
profile_loader.rs

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