chronicle 0.1.0

write your chronicle from command line
Documentation
use anyhow::{Result, bail};
use serde::{Serialize, Deserialize};

use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::path::PathBuf;

#[derive(Default, Serialize, Deserialize)]
pub struct ChronicleConfig {
    pub stores: Vec<String>,
    pub reverse: Option<bool>,
    pub date: Option<String>,
    pub time: Option<String>,
}

impl ChronicleConfig {
    pub fn new(store: &str) -> Self {
        Self {
            stores: vec![store.to_string()],
            ..Default::default()
        }
    }
}

fn reverse_default() -> bool { true }
fn date_default() -> String { String::from("%Y-%m-%d") }
fn time_default() -> String { String::from("%H:%M") }

#[derive(Default, Serialize, Deserialize)]
pub struct Config {
    pub editor: String,
    #[serde(default = "reverse_default")]
    pub reverse: bool,
    #[serde(default = "date_default")]
    pub date: String,
    #[serde(default = "time_default")]
    pub time: String,
    pub chronicle: HashMap<String, ChronicleConfig>,
}

impl Config {
    pub fn new() -> Self {
        Self {
            reverse: reverse_default(),
            date: date_default(),
            time: time_default(),
            ..Default::default()
        }
    }

    pub fn exists(&self, name: &str) -> bool {
        self.chronicle.contains_key(name)
    }
}

pub fn dir() -> PathBuf {
    home::home_dir().unwrap().join(".chronicle")
}

pub fn backup_dir() -> PathBuf {
    dir().join("backup~")
}

pub fn config_path() -> PathBuf {
    dir().join("config.toml")
}

pub fn draft_path(name: &str) -> PathBuf {
    dir().join(name)
}

fn init_config() -> Result<()> {
    let d = dir();
    fs::create_dir_all(&d)?;
    let p = config_path();
    let mut f = File::create(&p)?;

    let c = Config::new();
    let b = toml::to_vec(&c)?;
    f.write_all(&b)?;
    Ok(())
}

pub fn read_config() -> Result<Config> {
    let p = config_path();
    let f = File::open(&p).or_else(|err| {
        match err.kind() {
            io::ErrorKind::NotFound => {
                init_config().and_then(|()| {
                    File::open(&p)
                        .map_err(|e| e.into())
                })
            },
            _ => Err(anyhow::Error::from(err))
        }
    });

    if let Err(e) = f {
        bail!(e);
    }

    let mut f = f?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;

    toml::from_str(&s)
        .map_err(|e| e.into())
}

pub fn write_config(cfg: &mut Config) -> Result<()> {
    let p = config_path();
    let b = toml::to_vec(cfg)?;
    fs::write(&p, &b)?;
    Ok(())
}