hermes_agent_cli_core/
profiles.rs1use crate::config::Config;
4use anyhow::{Context, Result};
5use std::fs;
6use std::path::PathBuf;
7
8pub fn profiles_dir() -> PathBuf {
10 Config::hermes_home().join("profiles")
11}
12
13pub fn profile_path(name: &str) -> PathBuf {
15 profiles_dir().join(format!("{}.yaml", name))
16}
17
18pub fn ensure_profiles_dir() -> Result<()> {
20 let dir = profiles_dir();
21 if !dir.exists() {
22 fs::create_dir_all(&dir)
23 .with_context(|| format!("failed to create profiles directory at {:?}", dir))?;
24 }
25 Ok(())
26}
27
28pub fn list_profiles() -> Result<Vec<String>> {
30 ensure_profiles_dir()?;
31 let dir = profiles_dir();
32 let mut profiles = Vec::new();
33
34 let entries = fs::read_dir(&dir)
35 .with_context(|| format!("failed to read profiles directory {:?}", dir))?;
36
37 for entry in entries {
38 let entry = entry?;
39 let path = entry.path();
40 if path.extension().map(|e| e == "yaml").unwrap_or(false) {
41 if let Some(stem) = path.file_stem() {
42 profiles.push(stem.to_string_lossy().to_string());
43 }
44 }
45 }
46
47 profiles.sort();
48 Ok(profiles)
49}
50
51pub fn load_profile(name: &str) -> Result<Config> {
53 let path = profile_path(name);
54 if !path.exists() {
55 anyhow::bail!("profile '{}' not found at {:?}", name, path);
56 }
57 let content = fs::read_to_string(&path)
58 .with_context(|| format!("failed to read profile from {:?}", path))?;
59 let config: Config = serde_yaml::from_str(&content)
60 .with_context(|| format!("failed to parse profile from {:?}", path))?;
61 Ok(config)
62}
63
64pub fn save_profile(name: &str, config: &Config) -> Result<()> {
66 ensure_profiles_dir()?;
67 let path = profile_path(name);
68 let content = serde_yaml::to_string(config).context("failed to serialize profile")?;
69 fs::write(&path, content).with_context(|| format!("failed to write profile to {:?}", path))?;
70 Ok(())
71}
72
73pub fn clone_profile(from_name: &str, to_name: &str) -> Result<()> {
75 let config = load_profile(from_name)?;
76 save_profile(to_name, &config)?;
77 Ok(())
78}
79
80pub fn delete_profile(name: &str) -> Result<()> {
82 let path = profile_path(name);
83 if !path.exists() {
84 anyhow::bail!("profile '{}' not found", name);
85 }
86 fs::remove_file(&path).with_context(|| format!("failed to delete profile at {:?}", path))?;
87 Ok(())
88}
89
90pub fn rename_profile(old_name: &str, new_name: &str) -> Result<()> {
92 let old_path = profile_path(old_name);
93 let new_path = profile_path(new_name);
94
95 if !old_path.exists() {
96 anyhow::bail!("profile '{}' not found", old_name);
97 }
98 if new_path.exists() {
99 anyhow::bail!("profile '{}' already exists", new_name);
100 }
101
102 fs::rename(&old_path, &new_path).with_context(|| {
103 format!("failed to rename profile from {:?} to {:?}", old_path, new_path)
104 })?;
105 Ok(())
106}
107
108pub fn profile_exists(name: &str) -> bool {
110 profile_path(name).exists()
111}
112
113pub fn get_active_profile() -> String {
115 std::env::var("HERMES_PROFILE").unwrap_or_else(|_| "default".to_string())
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121
122 #[test]
123 fn test_profiles_dir() {
124 let dir = profiles_dir();
125 assert!(dir.ends_with("profiles"));
126 }
127
128 #[test]
129 fn test_profile_path() {
130 let path = profile_path("myprofile");
131 assert!(path.ends_with("myprofile.yaml"));
132 }
133}