#![doc(html_root_url = "https://saschagrunert.github.io/git-journal/")]
#![deny(missing_docs)]
//! # The Git Commit Message and Changelog Generation Framework
//!
//! This crate contains the library for the
//! [`git-journal`](https://github.com/saschagrunert/git-journal) executable. It handles all the
//! parsing and commit message modification stuff which is provided by the executable.
//!
//! ### Example usage
//!
//! ```
//! use gitjournal::GitJournal;
//! let mut journal = GitJournal::new(".").unwrap();
//! journal.parse_log("HEAD", "rc", &1, &false, &true);
//! journal.print_log(true, None, None).expect("Could not print short log.");
//! ```
//!
//! Simply create a new git-journal struct from a given path (`.` in this example). Then parse the
//! log between a given commit range or a single commit. In this example we want to retrieve
//! everything included in the last git tag, which does not represent a release candidate (contains
//! `"rc"`). After that parsing the log will be printed in the shortest possible format.
//!
extern crate chrono;
extern crate git2;
extern crate rayon;
extern crate regex;
extern crate rustc_serialize;
extern crate term;
extern crate toml;
#[macro_use]
extern crate nom;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
use chrono::{UTC, TimeZone};
use git2::{ObjectType, Oid, Repository};
use log::LogLevelFilter;
use rayon::prelude::*;
use toml::Value;
use logger::Logger;
use parser::{Parser, ParsedTag, Tags};
pub use config::Config;
use std::{fmt, fs};
use std::collections::BTreeMap;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use std::io::prelude::*;
mod logger;
mod parser;
pub mod config;
/// An enumeration of possible errors that can happen when working with git-journal.
#[derive(Debug)]
pub enum Error {
/// Erros related to the git repository.
Git(git2::Error),
/// Erros related to the system IO, like parsing of the configuration file.
Io(std::io::Error),
/// Errors related to the parsing and printing of the log.
Parser(parser::Error),
/// Errors related to the setup process.
Setup(config::Error),
/// Errors related to the commit message verification.
Verify(String),
}
impl From<git2::Error> for Error {
fn from(err: git2::Error) -> Error {
Error::Git(err)
}
}
impl From<std::io::Error> for Error {
fn from(err: std::io::Error) -> Error {
Error::Io(err)
}
}
impl From<config::Error> for Error {
fn from(err: config::Error) -> Error {
Error::Setup(err)
}
}
impl From<parser::Error> for Error {
fn from(err: parser::Error) -> Error {
Error::Parser(err)
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
Error::Git(ref err) => write!(f, "Git: {}", err),
Error::Io(ref err) => write!(f, "Io: {}", err),
Error::Parser(ref err) => write!(f, "Parser: {}", err),
Error::Setup(ref err) => write!(f, "Setup: {}", err),
Error::Verify(ref err) => write!(f, "Verify: {}", err),
}
}
}
/// The main structure of git-journal.
pub struct GitJournal {
/// The configuration structure
pub config: Config,
parser: Parser,
path: String,
tags: Vec<(Oid, String)>,
}
impl GitJournal {
/// Constructs a new `GitJournal<Result<GitJournal, Error>>`. Searches upwards if the given
/// path does not contain the `.git` directory.
///
/// # Examples
///
/// ```
/// use gitjournal::GitJournal;
///
/// let journal = GitJournal::new(".").unwrap();
/// ```
///
/// # Errors
/// When not providing a path with a valid git repository ('.git' folder or the initial parsing
/// of the git tags failed.
///
pub fn new(path: &str) -> Result<Self, Error> {
// Search upwards for the .git directory
let mut path_buf = if path != "." {
PathBuf::from(path)
} else {
try!(std::env::current_dir())
};
'git_search: loop {
for dir in try!(fs::read_dir(&path_buf)) {
let dir_path = try!(dir).path();
if dir_path.ends_with(".git") {
break 'git_search;
}
}
if !path_buf.pop() {
break;
}
}
// Open the repository
let repo = try!(Repository::open(&path_buf));
// Get all available tags in some vector of tuples
let mut new_tags = vec![];
for name in try!(repo.tag_names(None)).iter() {
let name = try!(name.ok_or(git2::Error::from_str("Could not receive tag name")));
let obj = try