systemprompt_loader/
profile_loader.rs1use std::path::Path;
14use systemprompt_models::Profile;
15use systemprompt_models::profile::ProfileError;
16
17use crate::error::{ProfileLoadError, ProfileLoadResult};
18
19#[derive(Debug, Clone, Copy)]
20pub struct ProfileLoader;
21
22impl ProfileLoader {
23 pub fn load_from_path(profile_path: &Path) -> ProfileLoadResult<Profile> {
24 let content =
25 std::fs::read_to_string(profile_path).map_err(|source| ProfileError::ReadFile {
26 path: profile_path.to_path_buf(),
27 source,
28 })?;
29 Profile::from_yaml(&content, profile_path).map_err(ProfileLoadError::from)
30 }
31
32 pub fn load(services_path: &Path, profile_name: &str) -> ProfileLoadResult<Profile> {
33 let profile_path = services_path
34 .join("profiles")
35 .join(format!("{profile_name}.secrets.profile.yaml"));
36
37 Self::load_from_path(&profile_path)
38 }
39
40 pub fn load_from_path_and_validate(profile_path: &Path) -> ProfileLoadResult<Profile> {
41 let profile = Self::load_from_path(profile_path)?;
42 profile.validate().map_err(ProfileLoadError::from)?;
43 Ok(profile)
44 }
45
46 pub fn load_and_validate(
47 services_path: &Path,
48 profile_name: &str,
49 ) -> ProfileLoadResult<Profile> {
50 let profile = Self::load(services_path, profile_name)?;
51 profile.validate().map_err(ProfileLoadError::from)?;
52 Ok(profile)
53 }
54
55 pub fn save(profile: &Profile, services_path: &Path) -> ProfileLoadResult<()> {
56 let profiles_dir = services_path.join("profiles");
57 std::fs::create_dir_all(&profiles_dir).map_err(|e| ProfileLoadError::Io {
58 path: profiles_dir.clone(),
59 source: e,
60 })?;
61
62 let profile_path = profiles_dir.join(format!("{}.secrets.profile.yaml", profile.name));
63 let content = profile.to_yaml().map_err(ProfileLoadError::from)?;
64
65 let content_with_header = format!(
66 "# systemprompt.io Profile: {}\n# \n# WARNING: This file contains secrets.\n# DO NOT \
67 commit to version control.\n\n{content}",
68 profile.display_name
69 );
70
71 std::fs::write(&profile_path, content_with_header).map_err(|e| ProfileLoadError::Io {
72 path: profile_path,
73 source: e,
74 })
75 }
76
77 #[must_use]
78 pub fn list_available(services_path: &Path) -> Vec<String> {
79 let profiles_dir = services_path.join("profiles");
80
81 if !profiles_dir.exists() {
82 return Vec::new();
83 }
84
85 match std::fs::read_dir(&profiles_dir) {
86 Ok(entries) => entries
87 .filter_map(Result::ok)
88 .filter_map(|e| {
89 let name = e.file_name().to_string_lossy().to_string();
90 name.strip_suffix(".secrets.profile.yaml")
91 .map(str::to_owned)
92 })
93 .collect(),
94 Err(e) => {
95 tracing::warn!(
96 error = %e,
97 path = %profiles_dir.display(),
98 "Failed to read profiles directory"
99 );
100 Vec::new()
101 },
102 }
103 }
104}