coveralls 0.1.5

Send job to Coveralls (coveralls.io)
Documentation
use super::LogValueKind;
use regex::Regex;
use git2::{Oid, Repository};
use simple_error::SimpleError;
use std::io::{Result, Error, ErrorKind};

macro_rules! checked {
    ($r:expr) => {match $r {
        Ok(v) => v,
        Err(err) => { return Err(Error::new(ErrorKind::Other, err)); }
    }};
}

struct BranchInfos {
    head_id: Oid,
    branch_name: Option<String>,
}

impl BranchInfos {
    fn from_repo(repo: &Repository) -> Result<Self> {
        let head = checked! { repo.head() };
        let head_id = match head.target() {
            Some(id) => id,
            None => {
                let msg = if head.is_branch() {
                    match head.name() {
                        None => String::from("The head of this repository is a branch that has no ID"),
                        Some(name) => {
                            format!("The head of this repository is a branch that has no ID (name: {})", name)
                        }
                    }
                } else {
                    String::from("The head of this repository is not a branch and has no ID")
                };

                return Err(Error::new(ErrorKind::Other, SimpleError::new(msg)));
            }
        };

        let branch_name = if head.is_branch() {
            head.name().map(String::from)
        } else {
            None
        };

        Ok(Self { head_id, branch_name })
    }
}

pub(in super::super) struct GitFetcher {
    repo: Repository,
    head_id: Oid,
    branch_name: Option<String>,
    branch_re: Regex,
}

impl GitFetcher {
    pub(in super::super) fn new() -> Result<Self> {
        let repo = checked! { Repository::open(".") };
        let infos = BranchInfos::from_repo(&repo)?;
        let branch_re = Regex::new(r"^refs/heads/(.+)$").expect("Bad regex");

        Ok(GitFetcher {
            repo,
            head_id: infos.head_id,
            branch_name: infos.branch_name,
            branch_re,
        })
    }

    pub(in super::super) fn get_branch(&self) -> Result<Option<String>> {
        if let Some(name) = self.branch_name.as_ref() {
            if let Some(caps) = self.branch_re.captures(name) {
                return Ok(Some(caps[1].to_string()));
            }
        }

        Ok(None)
    }

    pub(in super::super) fn get_log(&self, kind: LogValueKind) -> Result<Option<String>> {
        let commit = checked! { self.repo.find_commit(self.head_id) };

        Ok(match kind {
            LogValueKind::Id => Some(commit.id().to_string()),
            LogValueKind::AuthorName => commit.author().name().map(String::from),
            LogValueKind::AuthorEmail => commit.author().email().map(String::from),
            LogValueKind::CommitterName => commit.committer().name().map(String::from),
            LogValueKind::CommitterEmail => commit.committer().email().map(String::from),
            LogValueKind::Message => commit.message().map(String::from),
        })
    }

    pub(in super::super) fn get_remotes(&self) -> Result<Option<Vec<(String, String)>>> {
        let mut res = vec![];
        let remotes = checked! { self.repo.remotes() };

        for name in remotes.iter().filter_map(|x| x) {
            let remote = checked! { self.repo.find_remote(name) };

            if let Some(url) = remote.url() {
                res.push((name.to_string(), url.to_string()));
            }
        }

        if res.is_empty() {
            Ok(None)
        } else {
            Ok(Some(res))
        }
    }
}