volli-core 0.1.11

Shared types for volli
Documentation
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)
}