genome-sh 0.1.0

The jq of genomics. Fast, local, human-readable variant analysis.
use std::collections::BTreeMap;
use std::path::PathBuf;

use anyhow::Result;

/// Configuration stored at ~/.genome/config.toml.
pub struct Config {
    path: PathBuf,
    values: BTreeMap<String, String>,
}

impl Config {
    pub fn load() -> Result<Self> {
        let path = config_path();

        let values = if path.exists() {
            let content = std::fs::read_to_string(&path)?;
            let table: toml::Table = content.parse().unwrap_or_default();
            table
                .into_iter()
                .filter_map(|(k, v)| v.as_str().map(|s| (k, s.to_string())))
                .collect()
        } else {
            BTreeMap::new()
        };

        Ok(Self { path, values })
    }

    pub fn get(&self, key: &str) -> Option<&str> {
        self.values.get(key).map(String::as_str)
    }

    pub fn set(&mut self, key: &str, value: &str) -> Result<()> {
        self.values.insert(key.to_string(), value.to_string());
        Ok(())
    }

    pub fn entries(&self) -> impl Iterator<Item = (&str, &str)> {
        self.values.iter().map(|(k, v)| (k.as_str(), v.as_str()))
    }

    pub fn save(&self) -> Result<()> {
        if let Some(parent) = self.path.parent() {
            std::fs::create_dir_all(parent)?;
        }

        let mut table = toml::Table::new();
        for (k, v) in &self.values {
            table.insert(k.clone(), toml::Value::String(v.clone()));
        }

        let content = toml::to_string_pretty(&table)?;
        std::fs::write(&self.path, content)?;

        Ok(())
    }
}

fn config_path() -> PathBuf {
    dirs::config_dir()
        .unwrap_or_else(|| PathBuf::from("."))
        .join("genome-sh")
        .join("config.toml")
}