repo-cli 0.1.3

A sane way to manage all of your git repositories
Documentation
use crate::{
    config::{ConfigData, RawConfigData},
    query::Scheme,
    util,
};
use anyhow::{Context, Result};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};
use std::{
    collections::HashSet,
    convert::{TryFrom, TryInto},
    path::{Path, PathBuf},
};

lazy_static! {
    pub static ref DEFAULT_ROOT: PathBuf = dirs_next::home_dir()
        .map(|path| path.join("repo"))
        .unwrap_or_else(|| {
            util::make_path_buf("~/repo").expect("failed to determine default root directory")
        });
}

impl Default for ConfigData {
    fn default() -> Self {
        let mut shell = Vec::with_capacity(2);
        shell.push(String::from("sh"));
        shell.push(String::from("-c"));

        Self {
            root: Some((*DEFAULT_ROOT).to_path_buf()),
            root_str: Some(format!("{}", (*DEFAULT_ROOT).display())),
            cli: Some(false),
            host: Some("github.com".to_owned()),
            ssh_user: Some("git".to_owned()),
            scheme: Some(Scheme::Https),
            shell: Some(shell),
            include: HashSet::new(),
            exclude: HashSet::new(),
            path: None,
        }
    }
}

impl ConfigData {
    pub fn new() -> Self {
        Self {
            root: None,
            root_str: None,
            cli: None,
            host: None,
            ssh_user: None,
            scheme: None,
            shell: None,
            include: HashSet::new(),
            exclude: HashSet::new(),
            path: None,
        }
    }
    fn from_raw(raw: RawConfigData) -> Self {
        let root_str = raw.root.clone();
        let root = raw.root.and_then(|path| util::make_path_buf(path).ok());

        Self {
            root,
            root_str,
            cli: raw.cli,
            host: raw.default_host,
            ssh_user: raw.default_ssh_user,
            scheme: raw.default_scheme,
            shell: raw.shell,
            include: raw.include.unwrap_or_default(),
            exclude: raw.exclude.unwrap_or_default(),
            path: Some(raw.path),
        }
    }

    pub fn to_string_pretty(&self) -> Result<String> {
        let raw = self.to_raw();
        toml::to_string_pretty(&raw)
            .context(format!("failed to serialize RawConfigData:\n\n{:#?}", raw))
    }

    fn to_raw(&self) -> RawConfigData {
        let include = if self.include.is_empty() {
            None
        } else {
            Some(self.include.iter().cloned().collect())
        };

        let exclude = if self.exclude.is_empty() {
            None
        } else {
            Some(self.exclude.iter().cloned().collect())
        };

        let shell = self.shell.as_ref().map(|shell| shell.to_vec());

        RawConfigData {
            root: self.root_str.clone(),
            cli: self.cli,
            default_host: self.host.clone(),
            default_ssh_user: self.ssh_user.clone(),
            default_scheme: self.scheme,
            shell,
            include,
            exclude,
            path: self.path.clone().unwrap(),
        }
    }

    pub fn from_path<P>(path: P) -> Result<ConfigData>
    where
        P: AsRef<Path> + std::fmt::Debug,
    {
        let content = util::read_content(&path)?;
        let mut raw: RawConfigData = toml::from_str(&content).context(format!(
            "could not serialize content into Config:\n\n{}",
            content
        ))?;
        raw.path = PathBuf::from(path.as_ref().parent().unwrap());
        Ok(raw.into())
    }
}

impl From<RawConfigData> for ConfigData {
    fn from(raw: RawConfigData) -> Self {
        Self::from_raw(raw)
    }
}