dyd 0.4.0

CLI for daily diffing of git repos
Documentation
use crate::app::AppResult;
use crate::event::Event;
use crate::git;
use crate::manifest::Remote;
use crate::time;

use std::path::{Path, PathBuf};
use std::sync::mpsc;

#[derive(Clone, Debug)]
pub enum RepoStatus {
    Checking,
    Cloning,
    Pulling,
    Failed,
    Log,
    Finished,
}

impl Default for RepoStatus {
    fn default() -> Self {
        RepoStatus::Checking
    }
}

#[derive(Debug, Default)]
pub struct Repo {
    pub(crate) logs: Vec<Log>,
    pub(crate) name: String,
    pub(crate) origin: String,
    pub(crate) status: RepoStatus,
}

impl From<Remote> for Repo {
    fn from(remote: Remote) -> Self {
        Repo {
            name: remote.name,
            origin: remote.origin,
            ..Default::default()
        }
    }
}

#[derive(Clone, Debug)]
pub struct Log {
    pub(crate) age: String,
    pub(crate) author: String,
    pub commit_datetime: chrono::DateTime<chrono::Utc>,
    pub(crate) message: String,
    pub(crate) sha: String,
}

impl From<&str> for Log {
    fn from(log_str: &str) -> Self {
        let values: Vec<&str> = log_str.split('\x0B').collect();
        let sha = values[0].to_owned();
        let commit_datetime = time::parse_unix(values[1]).unwrap();
        let age = values[2].to_owned();
        let author = values[3].to_owned();
        let message = values[4].to_owned();
        Self {
            age,
            author,
            commit_datetime,
            message,
            sha,
        }
    }
}

impl Repo {
    pub fn update(&self, id: String, root_path: &Path, sender: mpsc::Sender<Event>) -> AppResult<()> {
        let path = self.path(root_path)?;
        let origin = self.origin.clone();

        std::thread::spawn(move || {
            if path.is_dir() {
                sender
                    .send(Event::RepoStatusChange(id.clone(), RepoStatus::Pulling))
                    .unwrap();

                git::pull(&path);
            } else {
                sender
                    .send(Event::RepoStatusChange(id.clone(), RepoStatus::Cloning))
                    .unwrap();
                git::clone(&origin, &path);
            }
            sender
                .send(Event::RepoStatusChange(id.clone(), RepoStatus::Log))
                .unwrap();

            let logs = Repo::logs(&path);
            sender
                .send(Event::RepoStatusComplete(id.clone(), logs))
                .unwrap();
        });
        Ok(())
    }

    pub fn path(&self, root: &Path) -> AppResult<PathBuf> {
        if let Some(path) = Path::new(&self.origin).file_name() {
            Ok(root.join(path))
        } else {
            Err(format!("Unable to determine local path for {}", self.name).into())
        }
    }

    fn logs(path: &PathBuf) -> Vec<Log> {
        let logs = git::logs(path);

        std::str::from_utf8(&logs)
            .unwrap()
            .trim()
            .split('\n')
            .into_iter()
            .map(|l| l.into())
            .collect()
    }
}