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