use super::Result;
use std::iter::FromIterator;
use std::process::{Command, Output};
pub fn in_git_repository() -> Result<bool> {
git(&["rev-parse", "--is-inside-work-tree"]).map(|o| o.status.success())
}
pub fn last_tag() -> Result<Option<String>> {
last_tags(1).map(|mut v| v.pop())
}
pub fn commits_in_log(args: &[String]) -> Result<Vec<String>> {
let mut log_args = vec!["log", "--format=format:%H"];
log_args.extend(args.iter().map(String::as_str));
git(&log_args).map(|o| read_lines(&o))
}
pub fn get_commit_message(sha: &str) -> Result<Vec<String>> {
git(&[
"log",
"--format=format:%H%n%an%n%aD%n%s%n%b",
"--max-count=1",
sha,
]).map(|o| read_lines(&o))
}
pub fn get_remote_url(name: &str) -> Result<Option<String>> {
git(&["remote", "get-url", name])
.map(|o| read_lines(&o))
.map(|mut v: Vec<String>| v.pop().and_then(usable_url))
}
fn usable_url(raw: String) -> Option<String> {
if raw.to_lowercase().starts_with("http") {
if let Some(index) = raw.rfind(".git") {
return Some(raw[0..index].to_string());
} else {
return Some(raw);
}
}
None
}
fn last_tags(n: i32) -> Result<Vec<String>> {
git(&[
"for-each-ref",
&format!("--count={}", n),
"--sort=-taggerdate",
"--format=%(refname:short)",
"refs/tags/*",
]).map(|o| read_lines(&o))
}
fn git(args: &[&str]) -> Result<Output> {
trace!("git {}", args.join(" "));
let output = Command::new("git").args(args).output()?;
if output.status.success() {
Ok(output)
} else {
Err(format_err!("{}", String::from_utf8_lossy(&output.stderr)))
}
}
fn read_lines<T: FromIterator<String>>(o: &Output) -> T {
String::from_utf8_lossy(&o.stdout)
.lines()
.map(String::from)
.collect::<T>()
}
#[cfg(test)]
mod tests {
#[test]
fn in_git_repository() {
assert!(super::in_git_repository().unwrap());
}
#[test]
fn last_tag() {
assert!(super::last_tag().is_ok());
}
#[test]
fn commits_in_log() {
use super::commits_in_log;
let mut range = vec![String::from("v0.1.1..v0.2.0")];
let commits = commits_in_log(&range);
assert!(commits.is_ok(), "{:?}", commits);
let forward = commits.unwrap();
assert_eq!(forward.len(), 2);
range.push(String::from("--reverse"));
let commits = commits_in_log(&range);
assert!(commits.is_ok(), "{:?}", commits);
let mut backward = commits.unwrap();
backward.reverse();
assert_eq!(backward, forward);
}
#[test]
fn get_commit_message() {
use super::get_commit_message;
assert!(get_commit_message("v0.1.1").is_ok());
assert!(get_commit_message("bad").is_err());
}
#[test]
fn get_usable_url() {
use super::usable_url;
let ssh = String::from("git@github.com:aldrin/git-changelog.git");
let raw = String::from("https://github.com/aldrin/git-changelog.git");
let usable = "https://github.com/aldrin/git-changelog";
assert_eq!(usable_url(usable.to_string()), Some(usable.to_string()));
assert_eq!(usable_url(raw), Some(usable.to_string()));
assert_eq!(usable_url(ssh), None);
}
#[test]
fn get_remote_url() {
use super::get_remote_url;
let expected = Some(String::from("https://github.com/aldrin/git-changelog"));
let found = get_remote_url("origin").unwrap();
assert!(get_remote_url("bad").is_err());
assert_eq!(found, expected);
}
}