watchers 1.8.0

Git integrated file watcher
use std::{
    fs,
    path::{Path, PathBuf},
};

use anyhow::{Context, Result};
use directories::ProjectDirs;
use serde::{Deserialize, Serialize};

fn default_true() -> bool {
    true
}

#[derive(Debug, Deserialize)]
struct ConfigFile {
    pub watch_dir: PathBuf,
    pub commit_delay_secs: u32,
    #[serde(default = "default_true")]
    pub auto_push: bool,
}

#[derive(Clone, Serialize)]
pub struct Config {
    pub name: String,
    pub watch_dir: PathBuf,
    pub commit_delay_secs: u32,
    pub auto_push: bool,
}

pub fn get_watchers_config_dir() -> PathBuf {
    let proj_dir = ProjectDirs::from("", "", "watchers").unwrap();
    proj_dir.config_dir().to_path_buf()
}

impl Config {
    pub fn new(name: impl Into<String>, path: impl AsRef<Path>) -> Config {
        Config {
            name: name.into(),
            watch_dir: path.as_ref().to_path_buf(),
            commit_delay_secs: 60,
            auto_push: true,
        }
    }

    pub fn get_watcher_config_path(name: &str) -> PathBuf {
        let config_dir = get_watchers_config_dir();
        config_dir.join(format!("{}.yml", name))
    }

    pub fn from_file<P: AsRef<Path>>(config_path: P) -> Result<Config> {
        let path = config_path.as_ref();
        let name = path
            .file_stem()
            .and_then(|s| s.to_str())
            .ok_or_else(|| anyhow::anyhow!("Invalid config filename"))?
            .to_string();

        let content = fs::read_to_string(config_path)?;
        let user_config: ConfigFile =
            serde_yaml::from_str(&content).context("Failed to load config")?;
        Ok(Config {
            name,
            watch_dir: user_config.watch_dir,
            commit_delay_secs: user_config.commit_delay_secs,
            auto_push: user_config.auto_push,
        })
    }

    pub fn dump(&self) -> serde_yaml::Result<String> {
        serde_yaml::to_string(self)
    }
}