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