diff options
author | Sascha Grunert <mail@saschagrunert.de> | 2016-11-16 14:30:54 +0100 |
---|---|---|
committer | Sascha Grunert <mail@saschagrunert.de> | 2016-11-16 14:30:54 +0100 |
commit | 0adc9580f67cfac66b5b1a66b643cb8fefa66a99 (patch) | |
tree | fe6d58fe5189081ad53856c7f1de3a7df36fd9aa | |
parent | 415071cb9c98084cb9aaa104e328e3994d614035 (diff) |
Improved main error handling
-rw-r--r-- | .gitjournal.toml | 2 | ||||
-rw-r--r-- | src/config.rs | 43 | ||||
-rw-r--r-- | src/errors.rs | 127 | ||||
-rw-r--r-- | src/lib.rs | 126 | ||||
-rw-r--r-- | src/logger.rs | 18 | ||||
-rw-r--r-- | src/main.rs | 59 | ||||
-rw-r--r-- | src/parser.rs | 145 |
7 files changed, 250 insertions, 270 deletions
diff --git a/.gitjournal.toml b/.gitjournal.toml index 0f34c51..508a97e 100644 --- a/.gitjournal.toml +++ b/.gitjournal.toml @@ -7,4 +7,4 @@ excluded_commit_tags = [] show_commit_hash = false show_prefix = false sort_by = "date" -template_prefix = "JIRA-1234" +template_prefix = "" diff --git a/src/config.rs b/src/config.rs index fd7b95b..da641de 100644 --- a/src/config.rs +++ b/src/config.rs @@ -3,45 +3,13 @@ //! use rustc_serialize::Encodable; -use toml::{Encoder, Value, Parser, encode_str, decode}; -use toml; +use toml::{self, Encoder, Value, Parser, encode_str, decode}; -use std::io; -use std::fmt; use std::fs::File; use std::path::PathBuf; use std::io::prelude::*; -/// An enumeration of possible errors that can happen when working with the configuration. -#[derive(Debug)] -pub enum Error { - /// Erros related to the toml parsing. - Toml(toml::Error), - - /// Erros related to the system IO, like saving the configuration file. - Io(io::Error), -} - -impl From<toml::Error> for Error { - fn from(err: toml::Error) -> Error { - Error::Toml(err) - } -} - -impl From<io::Error> for Error { - fn from(err: io::Error) -> Error { - Error::Io(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Toml(ref err) => write!(f, "Toml: {}", err), - Error::Io(ref err) => write!(f, "Io: {}", err), - } - } -} +use errors::{GitJournalResult, internal_error}; /// The configuration structure for git-journal. #[derive(Default, Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)] @@ -122,14 +90,13 @@ impl Config { /// # Errors /// When toml encoding or file creation failed. /// - pub fn save_default_config(&self, path: &str) -> Result<String, Error> { + pub fn save_default_config(&self, path: &str) -> GitJournalResult<String> { let mut encoder = Encoder::new(); self.encode(&mut encoder)?; let toml_string = encode_str(&Value::Table(encoder.toml)); let path_buf = self.get_path_with_filename(path); - let path_string = path_buf.to_str() - .ok_or(io::Error::new(io::ErrorKind::Other, "Cannot convert path to string"))?; + let path_string = path_buf.to_str().ok_or(internal_error("IO", "Cannot convert path to string"))?; let mut file = File::create(&path_buf)?; file.write_all(toml_string.as_bytes())?; @@ -148,7 +115,7 @@ impl Config { /// # Errors /// When toml decoding or file opening failed. /// - pub fn load(&mut self, path: &str) -> Result<(), Error> { + pub fn load(&mut self, path: &str) -> GitJournalResult<()> { let path_buf = self.get_path_with_filename(path); let mut file = File::open(path_buf)?; let mut toml_string = String::new(); diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..3074656 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,127 @@ +//! Basic error handling mechanisms + +use std::error::Error; +use std::{fmt, io, num}; + +use git2; +use log; +use term; +use toml; + +/// The result type for GitJournal +pub type GitJournalResult<T> = Result<T, Box<GitJournalError>>; + +/// GitJournal error trait +pub trait GitJournalError: Error + Send + 'static { + /// The internal cause for the error + fn gitjournal_cause(&self) -> Option<&GitJournalError> { + None + } + + /// Convert the `GitJournalError` to an `Error` + fn as_error(&self) -> &Error + where Self: Sized + { + self as &Error + } +} + +impl Error for Box<GitJournalError> { + fn description(&self) -> &str { + (**self).description() + } + fn cause(&self) -> Option<&Error> { + (**self).cause() + } +} + +impl GitJournalError for Box<GitJournalError> { + fn gitjournal_cause(&self) -> Option<&GitJournalError> { + (**self).gitjournal_cause() + } +} + +/// Concrete errors +struct ConcreteGitJournalError { + description: String, + detail: Option<String>, + cause: Option<Box<Error + Send>>, +} + +impl fmt::Display for ConcreteGitJournalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description)?; + if let Some(ref s) = self.detail { + write!(f, " ({})", s)?; + } + Ok(()) + } +} + +impl fmt::Debug for ConcreteGitJournalError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(self, f) + } +} + +impl Error for ConcreteGitJournalError { + fn description(&self) -> &str { + &self.description + } + fn cause(&self) -> Option<&Error> { + self.cause.as_ref().map(|c| { + let e: &Error = &**c; + e + }) + } +} + +impl GitJournalError for ConcreteGitJournalError {} + +/// Various error implementors +macro_rules! from_error { + ($($p:ty,)*) => ( + $(impl From<$p> for Box<GitJournalError> { + fn from(t: $p) -> Box<GitJournalError> { Box::new(t) } + })* + ) +} + +from_error! { + git2::Error, + io::Error, + log::ShutdownLoggerError, + num::ParseIntError, + term::Error, + toml::Error, +} + +impl GitJournalError for git2::Error {} +impl GitJournalError for io::Error {} +impl GitJournalError for log::ShutdownLoggerError {} +impl GitJournalError for num::ParseIntError {} +impl GitJournalError for term::Error {} +impl GitJournalError for toml::Error {} + +/// Raise and internal error +pub fn internal_error(error: &str, detail: &str) -> Box<GitJournalError> { + Box::new(ConcreteGitJournalError { + description: error.to_string(), + detail: Some(detail.to_string()), + cause: None, + }) +} + +pub fn internal(error: &fmt::Display) -> Box<GitJournalError> { + Box::new(ConcreteGitJournalError { + description: error.to_string(), + detail: None, + cause: None, + }) +} + +macro_rules! bail { + ($($fmt:tt)*) => ( + return Err(::errors::internal(&format_args!($($fmt)*))) + ) +} @@ -39,81 +39,29 @@ extern crate lazy_static; #[macro_use] extern crate log; +use std::collections::BTreeMap; +use std::fs::{File, OpenOptions}; +use std::io::prelude::*; +use std::path::{Path, PathBuf}; +use std::{env, fs}; + use chrono::{UTC, TimeZone}; use git2::{ObjectType, Oid, Repository}; use log::LogLevelFilter; use rayon::prelude::*; use toml::Value; +pub use config::Config; +pub use errors::{GitJournalResult, GitJournalError, internal_error}; 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::*; +#[macro_use] +mod errors; 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 @@ -124,8 +72,8 @@ pub struct GitJournal { } impl GitJournal { - /// Constructs a new `GitJournal<Result<GitJournal, Error>>`. Searches upwards if the given - /// path does not contain the `.git` directory. + /// Constructs a new `GitJournal`. Searches upwards if the given path does not contain the + /// `.git` directory. /// /// # Examples /// @@ -139,12 +87,21 @@ impl GitJournal { /// 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> { + pub fn new(path: &str) -> GitJournalResult<Self> { + // Setup the logger if not already set + if log::set_logger(|max_log_level| { + max_log_level.set(LogLevelFilter::Info); + Box::new(Logger) + }) + .is_err() { + warn!("Logger already set."); + }; + // Search upwards for the .git directory let mut path_buf = if path != "." { PathBuf::from(path) } else { - std::env::current_dir()? + env::current_dir()? }; 'git_search: loop { for dir in fs::read_dir(&path_buf)? { @@ -184,15 +141,9 @@ impl GitJournal { result: vec![], }; - // Setup the logger if not already set - if new_config.enable_debug { - if log::set_logger(|max_log_level| { - max_log_level.set(LogLevelFilter::Info); - Box::new(Logger) - }) - .is_err() { - warn!("Logger already set."); - }; + // Shut down the logger if the user does not want debug output + if !new_config.enable_debug { + log::shutdown_logger()?; } // Return the git journal object @@ -261,7 +212,7 @@ impl GitJournal { /// - When the writing of the default configuration fails. /// - When installation of the commit message (preparation) hook fails. /// - pub fn setup(&self) -> Result<(), Error> { + pub fn setup(&self) -> GitJournalResult<()> { // Save the default config let output_file = Config::new().save_default_config(&self.path)?; info!("Defaults written to '{}' file.", output_file); @@ -275,11 +226,12 @@ impl GitJournal { Ok(()) } - fn install_git_hook(&self, name: &str, content: &str) -> Result<(), Error> { + fn install_git_hook(&self, name: &str, content: &str) -> GitJournalResult<()> { let mut hook_path = PathBuf::from(&self.path); hook_path.push(".git/hooks"); hook_path.push(name); let mut hook_file: File; + if hook_path.exists() { warn!("There is already a hook available in '{}'. Please verifiy \ the hook by hand after the installation.", @@ -303,14 +255,14 @@ impl GitJournal { } #[cfg(unix)] - fn chmod(&self, path: &Path, perms: u32) -> Result<(), Error> { + fn chmod(&self, path: &Path, perms: u32) -> GitJournalResult<()> { use std::os::unix::prelude::PermissionsExt; fs::set_permissions(path, fs::Permissions::from_mode(perms))?; Ok(()) } #[cfg(windows)] - fn chmod(&self, _path: &Path, _perms: u32) -> Result<(), Error> { + fn chmod(&self, _path: &Path, _perms: u32) -> GitJournalResult<()> { Ok(()) } @@ -330,7 +282,7 @@ impl GitJournal { /// # Errors /// When the path is not available or writing the commit message fails. /// - pub fn prepare(&self, path: &str, commit_type: Option<&str>) -> Result<(), Error> { + pub fn prepare(&self, path: &str, commit_type: Option<&str>) -> GitJournalResult<()> { // If the message is not valid, assume a new commit and provide the template. if let Err(error) = self.verify(path) { // But if the message is provided via the cli with `-m`, then abort since @@ -392,7 +344,7 @@ impl GitJournal { /// # Errors /// When the commit message is not valid due to RFC0001 or opening of the given file failed. /// - pub fn verify(&self, path: &str) -> Result<(), Error> { + pub fn verify(&self, path: &str) -> GitJournalResult<()> { // Open the file and read to string let mut file = File::open(path)?; let mut commit_message = String::new(); @@ -411,14 +363,14 @@ impl GitJournal { file.read_to_string(&mut toml_string)?; let toml = toml::Parser::new(&toml_string).parse() - .ok_or(Error::Verify("Could not parse default toml template.".to_owned()))?; + .ok_or(internal_error("Verify", "Could not parse default toml template."))?; let toml_tags = self.parser.get_tags_from_toml(&toml, vec![]); let invalid_tags = tags.into_iter().filter(|tag| !toml_tags.contains(tag)).collect::<Vec<String>>(); if !invalid_tags.is_empty() { warn!("These tags are not part of the default template: '{}'.", invalid_tags.join(", ")); - return Err(Error::Verify("Not all tags exists in the default template.".to_owned())); + bail!("Not all tags exists in the default template."); } } Ok(()) @@ -444,7 +396,7 @@ impl GitJournal { max_tags_count: &u32, all: &bool, skip_unreleased: &bool) - -> Result<(), Error> { + -> GitJournalResult<()> { let repo = Repository::open(&self.path)?; let mut revwalk = repo.revwalk()?; @@ -533,7 +485,7 @@ impl GitJournal { Ok(parsed_message) => { *result = Some(parsed_message); } - Err(e) => warn!("Skipping commit: {}.", e), + Err(e) => warn!("Skipping commit: {}", e), } }); @@ -579,7 +531,7 @@ impl GitJournal { /// # Errors /// If the generation of the template was impossible. /// - pub fn generate_template(&self) -> Result<(), Error> { + pub fn generate_template(&self) -> GitJournalResult<()> { let mut tags = vec![parser::TOML_DEFAULT_KEY.to_owned()]; // Get all the tags @@ -646,7 +598,7 @@ impl GitJournal { /// # Errors /// If some commit message could not be print. /// - pub fn print_log(&self, compact: bool, template: Option<&str>, output: Option<&str>) -> Result<(), Error> { + pub fn print_log(&self, compact: bool, template: Option<&str>, output: Option<&str>) -> GitJournalResult<()> { // Choose the template let mut default_template = PathBuf::from(&self.path); diff --git a/src/logger.rs b/src/logger.rs index eed0a04..931e449 100644 --- a/src/logger.rs +++ b/src/logger.rs @@ -1,6 +1,10 @@ //! The logger implementation use log::{Log, LogRecord, LogLevel, LogMetadata}; -use term; + +use term::stderr; +use term::color::{BRIGHT_BLUE, GREEN, BRIGHT_YELLOW, RED}; + +use errors::{GitJournalResult, internal_error}; /// The logging structure pub struct Logger; @@ -20,28 +24,28 @@ impl Log for Logger { } impl Logger { - fn log_result(&self, record: &LogRecord) -> Result<(), term::Error> { + fn log_result(&self, record: &LogRecord) -> GitJournalResult<()> { // We have to create a new terminal on each log because // `term::Terminal<Output=std::io::Stderr> + Send + 'static` cannot be shared between // threads safely' - let mut t = term::stderr().ok_or(term::Error::NotSupported)?; - t.fg(term::color::BRIGHT_BLUE)?; + let mut t = stderr().ok_or(internal_error("Term", "Could not create terminal"))?; + t.fg(BRIGHT_BLUE)?; write!(t, "[git-journal] ")?; match record.level() { LogLevel::Info => { - t.fg(term::color::GREEN)?; + t.fg(GREEN)?; write!(t, "[OKAY] ")?; t.reset()?; writeln!(t, "{}", record.args())?; } LogLevel::Warn => { - t.fg(term::color::BRIGHT_YELLOW)?; + t.fg(BRIGHT_YELLOW)?; write!(t, "[WARN] ")?; t.reset()?; writeln!(t, "{}", record.args())?; } LogLevel::Error => { - t.fg(term::color::RED)?; + t.fg(RED)?; write!(t, "[ERROR] ")?; t.reset()?; writeln!(t, "{}", record.args())?; diff --git a/src/main.rs b/src/main.rs index 23e37df..ee8171c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,44 +7,16 @@ extern crate clap; extern crate log; use std::process::exit; -use std::{env, fmt, fs}; +use std::{env, fs}; use clap::{App, Shell}; -use gitjournal::GitJournal; +use gitjournal::{GitJournal, GitJournalResult, GitJournalError, internal_error}; -fn error_and_exit(string: &str, error: Error) { +fn error_and_exit(string: &str, error: Box<GitJournalError>) { error!("{}: {}", string, error); exit(1); } -enum Error { - Cli, - ParseInt(std::num::ParseIntError), - GitJournal(gitjournal::Error), -} - -impl From<gitjournal::Error> for Error { - fn from(err: gitjournal::Error) -> Error { - Error::GitJournal(err) - } -} - -impl From<std::num::ParseIntError> for Error { - fn from(err: std::num::ParseIntError) -> Error { - Error::ParseInt(err) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::Cli => write!(f, "Cli argument parsing"), - Error::ParseInt(ref err) => write!(f, "ParseInt: {}", err), - Error::GitJournal(ref err) => write!(f, "GitJournal: {}", err), - } - } -} - fn is_program_in_path(program: &str) -> bool { if let Ok(path) = env::var("PATH") { for p in path.split(':') { @@ -63,12 +35,12 @@ fn main() { } } -fn run() -> Result<(), Error> { +fn run() -> GitJournalResult<()> { // Load the CLI parameters from the yaml file let yaml = load_yaml!("cli.yaml"); let mut app = App::from_yaml(yaml).version(crate_version!()); let matches = app.clone().get_matches(); - let path = matches.value_of("path").ok_or(Error::Cli)?; + let path = matches.value_of("path").ok_or(internal_error("Cli", "No 'path' provided"))?; // Create the journal let mut journal = GitJournal::new(path)?; @@ -78,13 +50,10 @@ fn run() -> Result<(), Error> { Some("prepare") => { // Prepare a commit message before editing by the user if let Some(sub_matches) = matches.subcommand_matches("prepare") { - match journal.prepare(sub_matches.value_of("message").ok_or(Error::Cli)?, + match journal.prepare(sub_matches.value_of("message").ok_or(internal_error("Cli", "No 'message' provided"))?, sub_matches.value_of("type")) { Ok(()) => info!("Commit message prepared."), - Err(error) => { - error_and_exit("Commit message preparation failed", - Error::GitJournal(error)) - } + Err(error) => error_and_exit("Commit message preparation failed", error) } } } @@ -109,17 +78,19 @@ fn run() -> Result<(), Error> { Some("verify") => { // Verify a commit message if let Some(sub_matches) = matches.subcommand_matches("verify") { - match journal.verify(sub_matches.value_of("message").ok_or(Error::Cli)?) { + match journal.verify(sub_matches.value_of("message").ok_or(internal_error("Cli", "No 'message' provided"))?) { Ok(()) => info!("Commit message valid."), - Err(error) => error_and_exit("Commit message invalid", Error::GitJournal(error)), + Err(error) => error_and_exit("Commit message invalid", error), } } } _ => { // Get all values of the given CLI parameters with default values - let revision_range = matches.value_of("revision_range").ok_or(Error::Cli)?; - let tag_skip_pattern = matches.value_of("tag_skip_pattern").ok_or(Error::Cli)?; - let tags_count = matches.value_of("tags_count").ok_or(Error::Cli)?; + let revision_range = matches.value_of("revision_range") + .ok_or(internal_error("Cli", "No 'revision_range' provided"))?; + let tag_skip_pattern = matches.value_of("tag_skip_pattern") + .ok_or(internal_error("Cli", "No 'task_skip_pattern' provided"))?; + let tags_count = matches.value_of("tags_count").ok_or(internal_error("Cli", "No 'tags_count' provided"))?; let max_tags = tags_count.parse::<u32>()?; // Parse the log @@ -128,7 +99,7 @@ fn run() -> Result<(), Error> { &max_tags, &matches.is_present("all"), &matches.is_present("skip_unreleased")) { - error_and_exit("Log parsing error", Error::GitJournal(error)); + error_and_exit("Log parsing error", error); } // Generate the template or print the log diff --git a/src/parser.rs b/src/parser.rs index 31cf2f2..bc79d30 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3,18 +3,15 @@ use git2::Oid; use nom::{IResult, alpha, digit, space, rest}; use regex::{Regex, RegexBuilder}; use term; -use toml; -use toml::Value; +use toml::{self, Value}; use std::collections::BTreeMap; -use std::fmt; use std::fs::File; use std::io::prelude::*; -use std::io; -use std::iter; -use std::str; +use std::{iter, str}; use config::Config; +use errors::{GitJournalResult, internal_error}; pub static TOML_DEFAULT_KEY: &'static str = "default"; pub static TOML_FOOTERS_KEY: &'static str = "footers"; @@ -26,49 +23,6 @@ pub static TOML_ONCE_KEY: &'static str = "once"; pub static TOML_HEADER_KEY: &'static str = "header"; pub static TOML_FOOTER_KEY: &'static str = "footer"; -#[derive(Debug)] -pub enum Error { - CommitMessageLength, - FooterParsing(String), - Io(io::Error), - ParagraphParsing(String), - SummaryParsing(String), - Terminal, - Toml(toml::Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Error::CommitMessageLength => write!(f, "Commit message length too small."), - Error::FooterParsing(ref line) => write!(f, "Footer parsing: '{}'", line), - Error::Io(ref e) => write!(f, "Io: {}", e), - Error::ParagraphParsing(ref line) => write!(f, "Paragraph parsing: '{}'", line), - Error::SummaryParsing(ref line) => write!(f, "Summary parsing: '{}'", line), - Error::Terminal => write!(f, "Could not print to terminal."), - Error::Toml(ref e) => write!(f, "Toml: {}", e), - } - } -} - -impl From<term::Error> for Error { - fn from(_err: term::Error) -> Error { - Error::Terminal - } -} - -impl From<io::Error> for Error { - fn from(err: io::Error) -> Error { - Error::Io(err) - } -} - -impl From<toml::Error> for Error { - fn from(err: toml::Error) -> Error { - Error::Toml(err) - } -} - #[derive(PartialEq)] pub enum Printed { Nothing, @@ -83,12 +37,12 @@ pub trait Print { c1: &F, c2: &G, c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error>; + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()>; - fn print_default<T: Write>(&self, t: &mut T, config: &Config, tag: Option<&str>) -> Result<(), Error> { + fn print_default<T: Write>(&self, t: &mut T, config: &Config, tag: Option<&str>) -> GitJournalResult<()> { self.print(t, config, tag, &|_| Ok(()), &|_| Ok(()), &|_| Ok(()))?; Ok(()) } @@ -97,7 +51,7 @@ pub trait Print { mut t: &mut Box<term::StdoutTerminal>, config: &Config, tag: Option<&str>) - -> Result<(), Error> { + -> GitJournalResult<()> { self.print(&mut t, config, tag, @@ -136,7 +90,7 @@ pub trait Print { mut vec: &mut Vec<u8>, config: &Config, tag: Option<&str>) - -> Result<(), Error> { + -> GitJournalResult<()> { self.print_default_term(&mut term, config, tag)?; self.print_default(&mut vec, config, tag)?; Ok(()) @@ -167,10 +121,10 @@ pub struct ParsedTag { } impl ParsedTag { - fn print<T: Write, F, G, H>(&self, t: &mut T, config: &Config, c1: &F, c2: &G, c3: &H) -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + fn print<T: Write, F, G, H>(&self, t: &mut T, config: &Config, c1: &F, c2: &G, c3: &H) -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { if config.colored_output { c1(t)?; @@ -190,12 +144,12 @@ impl ParsedTag { Ok(Printed::Something) } - fn print_default<T: Write>(&self, t: &mut T, config: &Config) -> Result<(), Error> { + fn print_default<T: Write>(&self, t: &mut T, config: &Config) -> GitJournalResult<()> { self.print(t, config, &|_| Ok(()), &|_| Ok(()), &|_| Ok(()))?; Ok(()) } - fn print_default_term(&self, mut t: &mut Box<term::StdoutTerminal>, config: &Config) -> Result<(), Error> { + fn print_default_term(&self, mut t: &mut Box<term::StdoutTerminal>, config: &Config) -> GitJournalResult<()> { self.print(&mut t, config, &|t| { @@ -220,7 +174,7 @@ impl ParsedTag { config: &Config, template: Option<&str>, index_len: (usize, usize)) - -> Result<(), Error> { + -> GitJournalResult<()> { match template { Some(template) => { // Try to parse the template @@ -294,7 +248,7 @@ impl ParsedTag { level: &mut usize, config: &Config, compact: &bool) - -> Result<(), Error> { + -> GitJournalResult<()> { for value in table { if let Value::Array(ref array) = *value.1 { for item in array { @@ -367,7 +321,7 @@ impl ParsedTag { mut vec: &mut Vec<u8>, footer_keys: Option<&[Value]>, config: &Config) - -> Result<(), Error> { + -> GitJournalResult<()> { let mut footer_tree: BTreeMap<String, Vec<String>> = BTreeMap::new(); @@ -458,10 +412,10 @@ impl Print for ParsedCommit { c1: &F, c2: &G, c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { // If summary is already filtered out then do not print at all if self.summary.print(t, config, tag, c1, c2, c3)? == Printed::Nothing { @@ -510,10 +464,10 @@ impl Print for SummaryElement { c1: &F, c2: &G, c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { // Filter out excluded tags if self.tags.iter().filter(|x| config.excluded_commit_tags.contains(x)).count() > 0usize { @@ -589,10 +543,10 @@ impl Print for BodyElement { c1: &F, c2: &G, c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { match *self { BodyElement::List(ref vec) => { @@ -644,10 +598,10 @@ impl Print for ListElement { c1: &F, c2: &G, c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { // Check if list item contains excluded tag if self.tags.iter().filter(|x| config.excluded_commit_tags.contains(x)).count() > 0usize { @@ -713,10 +667,10 @@ impl Print for ParagraphElement { _c1: &F, _c2: &G, _c3: &H) - -> Result<Printed, Error> - where F: Fn(&mut T) -> Result<(), Error>, - G: Fn(&mut T) -> Result<(), Error>, - H: Fn(&mut T) -> Result<(), Error> + -> GitJournalResult<Printed> + where F: Fn(&mut T) -> GitJournalResult<()>, + G: Fn(&mut T) -> GitJournalResult<()>, + H: Fn(&mut T) -> GitJournalResult<()> { // Check if paragraph contains excluded tag if self.tags.iter().filter(|x| config.excluded_commit_tags.contains(x)).coun |