vomit-config 0.4.0

Shared configuration library for all Vomit project tools
Documentation
use std::{fs, path::Path};

use derpscfg::indexmap::IndexMap;
use derpscfg::prelude::*;

use crate::ConfigError;

#[derive(Debug, Derpscfg)]
pub(crate) struct ScfgSend {
    pub(crate) remote: Option<String>,
    pub(crate) from: Option<String>,
    pub(crate) user: Option<String>,
    pub(crate) password: Option<String>,
    #[scfg(name = "pass-cmd")]
    pub(crate) pass_cmd: Option<String>,
}

#[derive(Debug, Derpscfg)]
pub(crate) struct ScfgAccount {
    pub(crate) local: Option<String>,
    pub(crate) remote: Option<String>,
    pub(crate) user: Option<String>,
    pub(crate) password: Option<String>,
    #[scfg(name = "pass-cmd")]
    pub(crate) pass_cmd: Option<String>,

    pub(crate) send: Option<ScfgSend>,
}

#[derive(Debug, Derpscfg)]
pub(crate) struct ScfgConfig {
    #[scfg(name = "account")]
    accounts: IndexMap<String, ScfgAccount>,
}

impl ScfgConfig {
    pub(crate) fn for_account(&self, name: Option<&str>) -> Option<(&String, &ScfgAccount)> {
        if let Some(name) = name {
            self.accounts.get_key_value(name)
        } else {
            self.accounts.iter().next()
        }
    }
}

pub(crate) fn load_scfg(doc: &str) -> Result<ScfgConfig, ConfigError> {
    let cfg: ScfgConfig = derpscfg::parse(doc)?;
    if cfg.accounts.is_empty() {
        Err(ConfigError::Error("no accounts found"))
    } else {
        Ok(cfg)
    }
}

pub(crate) fn load_scfg_file<P: AsRef<Path>>(path: P) -> Result<ScfgConfig, ConfigError> {
    let contents = fs::read_to_string(path)?;
    load_scfg(&contents)
}

#[cfg(test)]
mod tests {
    use super::*;

    static TEST: &str = "
account example {
    local '/home/test/.maildir'
    remote 'mx.example.com'
    user 'johndoe'
    password 'hunter1'
}";

    #[test]
    fn test_load() {
        let cfg = load_scfg(TEST).unwrap();
        assert_eq!(cfg.accounts.len(), 1);
    }

    #[test]
    fn test_load_empty() {
        let cfg = load_scfg("");
        assert!(matches!(cfg, Err(ConfigError::Error(_))))
    }

    static TEST_DUP: &str = "
account example {
    local '/home/test/.maildir'
    remote 'mx.example.com'
    user 'johndoe'
    password 'hunter1'
}
account example {
    local '/home/test/.maildir'
    remote 'mx.example.com'
    user 'johndoe'
    password 'hunter1'
}";
    #[test]
    fn test_load_dup() {
        let cfg = load_scfg(TEST_DUP);
        assert!(matches!(
            cfg,
            Err(ConfigError::ScfgError(derpscfg::Error::DuplicateKey(_)))
        ));
    }
}