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