use eyre::Report;
use eyre::eyre;
use serde::{Serialize, de::DeserializeOwned};
use std::fs;
use std::path::PathBuf;
use crate::config_dir;
pub fn role_dir(role: &str) -> PathBuf {
let mut base = config_dir();
base.push("profiles");
base.push(role);
base
}
pub fn profile_dir(role: &str, profile: &str) -> PathBuf {
let mut dir = role_dir(role);
dir.push(profile);
dir
}
fn profile_path(role: &str, profile: &str) -> PathBuf {
profile_dir(role, profile).join("profile.toml")
}
pub fn profile_exists(role: &str, profile: &str) -> bool {
profile_dir(role, profile).exists()
}
pub fn load_profile<T: DeserializeOwned>(role: &str, profile: &str) -> Result<Option<T>, Report> {
let path = profile_path(role, profile);
match fs::read_to_string(path) {
Ok(data) => Ok(Some(toml::from_str(&data)?)),
Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
Err(e) => Err(e.into()),
}
}
pub fn save_profile<T: Serialize>(role: &str, profile: &str, cfg: &T) -> Result<(), Report> {
let dir = profile_dir(role, profile);
fs::create_dir_all(&dir)?;
let data = toml::to_string(cfg)?;
fs::write(dir.join("profile.toml"), data)?;
Ok(())
}
pub fn delete_profile(role: &str, profile: &str) -> Result<(), Report> {
let dir = profile_dir(role, profile);
if dir.exists() {
fs::remove_dir_all(dir)?;
}
Ok(())
}
pub fn rename_profile(role: &str, old: &str, new: &str) -> Result<(), Report> {
let src = profile_dir(role, old);
let dst = profile_dir(role, new);
if !src.exists() {
return Err(eyre!("profile not found"));
}
if dst.exists() {
return Err(eyre!("profile exists"));
}
let parent = dst.parent().ok_or_else(|| eyre!("invalid destination"))?;
fs::create_dir_all(parent)?;
fs::rename(src, dst)?;
Ok(())
}
pub fn list_profiles(role: &str) -> Result<Vec<String>, Report> {
let dir = role_dir(role);
let mut out = Vec::new();
if dir.exists() {
for entry in fs::read_dir(dir)? {
let entry = entry?;
if entry.path().is_dir()
&& let Some(name) = entry.file_name().to_str()
{
out.push(name.to_string());
}
}
}
out.sort();
Ok(out)
}