use crate::config::Config;
use anyhow::{Context, Result};
use std::fs;
use std::path::PathBuf;
pub fn profiles_dir() -> PathBuf {
Config::hermes_home().join("profiles")
}
pub fn profile_path(name: &str) -> PathBuf {
profiles_dir().join(format!("{}.yaml", name))
}
pub fn ensure_profiles_dir() -> Result<()> {
let dir = profiles_dir();
if !dir.exists() {
fs::create_dir_all(&dir)
.with_context(|| format!("failed to create profiles directory at {:?}", dir))?;
}
Ok(())
}
pub fn list_profiles() -> Result<Vec<String>> {
ensure_profiles_dir()?;
let dir = profiles_dir();
let mut profiles = Vec::new();
let entries = fs::read_dir(&dir)
.with_context(|| format!("failed to read profiles directory {:?}", dir))?;
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.extension().map(|e| e == "yaml").unwrap_or(false) {
if let Some(stem) = path.file_stem() {
profiles.push(stem.to_string_lossy().to_string());
}
}
}
profiles.sort();
Ok(profiles)
}
pub fn load_profile(name: &str) -> Result<Config> {
let path = profile_path(name);
if !path.exists() {
anyhow::bail!("profile '{}' not found at {:?}", name, path);
}
let content = fs::read_to_string(&path)
.with_context(|| format!("failed to read profile from {:?}", path))?;
let config: Config = serde_yaml::from_str(&content)
.with_context(|| format!("failed to parse profile from {:?}", path))?;
Ok(config)
}
pub fn save_profile(name: &str, config: &Config) -> Result<()> {
ensure_profiles_dir()?;
let path = profile_path(name);
let content = serde_yaml::to_string(config).context("failed to serialize profile")?;
fs::write(&path, content).with_context(|| format!("failed to write profile to {:?}", path))?;
Ok(())
}
pub fn clone_profile(from_name: &str, to_name: &str) -> Result<()> {
let config = load_profile(from_name)?;
save_profile(to_name, &config)?;
Ok(())
}
pub fn delete_profile(name: &str) -> Result<()> {
let path = profile_path(name);
if !path.exists() {
anyhow::bail!("profile '{}' not found", name);
}
fs::remove_file(&path).with_context(|| format!("failed to delete profile at {:?}", path))?;
Ok(())
}
pub fn rename_profile(old_name: &str, new_name: &str) -> Result<()> {
let old_path = profile_path(old_name);
let new_path = profile_path(new_name);
if !old_path.exists() {
anyhow::bail!("profile '{}' not found", old_name);
}
if new_path.exists() {
anyhow::bail!("profile '{}' already exists", new_name);
}
fs::rename(&old_path, &new_path).with_context(|| {
format!("failed to rename profile from {:?} to {:?}", old_path, new_path)
})?;
Ok(())
}
pub fn profile_exists(name: &str) -> bool {
profile_path(name).exists()
}
pub fn get_active_profile() -> String {
std::env::var("HERMES_PROFILE").unwrap_or_else(|_| "default".to_string())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_profiles_dir() {
let dir = profiles_dir();
assert!(dir.ends_with("profiles"));
}
#[test]
fn test_profile_path() {
let path = profile_path("myprofile");
assert!(path.ends_with("myprofile.yaml"));
}
}