diff --git a/src/clog.rs b/src/clog.rs index 4ff8170..f27ff7c 100644 --- a/src/clog.rs +++ b/src/clog.rs @@ -3,19 +3,19 @@ use std::convert::AsRef; use std::env; use std::fmt; use std::fs::File; -use std::io::{stdout, BufWriter, Write, Read}; +use std::io::{stdout, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::process::Command; use indexmap::IndexMap; use regex::Regex; -use toml::{Value, Parser}; +use toml::{Parser, Value}; -use git::{Commits, Commit}; -use fmt::{ChangelogFormat, FormatWriter, WriterResult, MarkdownWriter, JsonWriter}; -use sectionmap::SectionMap; use error::Error; +use fmt::{ChangelogFormat, FormatWriter, JsonWriter, MarkdownWriter, WriterResult}; +use git::{Commit, Commits}; use link_style::LinkStyle; +use sectionmap::SectionMap; use CLOG_CONFIG_FILE; @@ -77,12 +77,14 @@ pub struct Clog { pub breaks_regex: Regex, pub breaking_regex: Regex, /// The format to output the changelog in (Defaults to Markdown) - pub out_format: ChangelogFormat + pub out_format: ChangelogFormat, } impl fmt::Debug for Clog { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{{ + write!( + f, + "{{ grep: {:?} format: {:?} repo: {:?} @@ -104,50 +106,56 @@ impl fmt::Debug for Clog { breaking_regex: {:?} out_format: {:?} }}", - self.grep, - self.format, - self.repo, - self.link_style, - self.version, - self.patch_ver, - self.subtitle, - self.from, - self.to, - self.infile, - self.outfile, - self.section_map, - self.component_map, - self.git_dir, - self.git_work_tree, - self.regex, - self.closes_regex, - self.breaks_regex, - self.breaking_regex, - self.out_format, + self.grep, + self.format, + self.repo, + self.link_style, + self.version, + self.patch_ver, + self.subtitle, + self.from, + self.to, + self.infile, + self.outfile, + self.section_map, + self.component_map, + self.git_dir, + self.git_work_tree, + self.regex, + self.closes_regex, + self.breaks_regex, + self.breaking_regex, + self.out_format, ) } } - impl Clog { fn _new() -> Clog { debugln!("Creating default clog with _new()"); let mut sections = IndexMap::new(); - sections.insert("Features".to_owned(), vec!["ft".to_owned(), "feat".to_owned()]); - sections.insert("Bug Fixes".to_owned(), vec!["fx".to_owned(), "fix".to_owned()]); + sections.insert( + "Features".to_owned(), + vec!["ft".to_owned(), "feat".to_owned()], + ); + sections.insert( + "Bug Fixes".to_owned(), + vec!["fx".to_owned(), "fix".to_owned()], + ); sections.insert("Performance".to_owned(), vec!["perf".to_owned()]); sections.insert("Unknown".to_owned(), vec!["unk".to_owned()]); sections.insert("Breaking Changes".to_owned(), vec!["breaks".to_owned()]); Clog { - grep: format!("{}BREAKING'", - sections.values() - .map(|v| v.iter().fold(String::new(), |acc, al| { - acc + &format!("^{}|", al)[..] - })) - .fold(String::new(), |acc, al| { - acc + &format!("^{}|", al)[..] - })), + grep: format!( + "{}BREAKING'", + sections + .values() + .map(|v| v + .iter() + .fold(String::new(), |acc, al| { acc + &format!("^{}|", al)[..] })) + .fold(String::new(), |acc, al| { acc + &format!("^{}|", al)[..] }) + ), format: "%H%n%s%n%b%n==END==".to_owned(), repo: "".to_owned(), link_style: LinkStyle::Github, @@ -166,7 +174,7 @@ impl Clog { regex: regex!(r"^([^:\(]+?)(?:\(([^\)]*?)?\))?:(.*)"), closes_regex: regex!(r"(?:Closes|Fixes|Resolves)\s((?:#(\d+)(?:,\s)?)+)"), breaks_regex: regex!(r"(?:Breaks|Broke)\s((?:#(\d+)(?:,\s)?)+)"), - breaking_regex: regex!(r"(?i:breaking)") + breaking_regex: regex!(r"(?i:breaking)"), } } @@ -202,13 +210,13 @@ impl Clog { /// }); /// ``` pub fn with_all>(git_dir: P, work_tree: P, cfg_file: P) -> BuilderResult { - debugln!("Creating clog with \n\tgit_dir: {:?}\n\twork_tree: {:?}\n\tcfg_file: {:?}", + debugln!( + "Creating clog with \n\tgit_dir: {:?}\n\twork_tree: {:?}\n\tcfg_file: {:?}", git_dir.as_ref(), work_tree.as_ref(), - cfg_file.as_ref()); - let clog = try!(Clog::with_dirs(git_dir, - work_tree)); - clog.try_config_file(cfg_file.as_ref()) + cfg_file.as_ref() + ); + Clog::with_dirs(git_dir, work_tree)?.try_config_file(cfg_file.as_ref()) } /// Creates a `Clog` struct using a specific git working directory OR project directory as @@ -227,11 +235,12 @@ impl Clog { /// }); /// ``` pub fn with_dir_and_file>(dir: P, cfg_file: P) -> BuilderResult { - debugln!("Creating clog with \n\tdir: {:?}\n\tcfg_file: {:?}", + debugln!( + "Creating clog with \n\tdir: {:?}\n\tcfg_file: {:?}", dir.as_ref(), - cfg_file.as_ref()); - let clog = try!(Clog::_with_dir(dir)); - clog.try_config_file(cfg_file.as_ref()) + cfg_file.as_ref() + ); + Clog::_with_dir(dir)?.try_config_file(cfg_file.as_ref()) } fn _with_dir>(dir: P) -> BuilderResult { @@ -271,8 +280,7 @@ impl Clog { /// ``` pub fn with_dir>(dir: P) -> BuilderResult { debugln!("Creating clog with \n\tdir: {:?}", dir.as_ref()); - let clog = try!(Clog::_with_dir(dir)); - clog.try_config_file(Path::new(CLOG_CONFIG_FILE)) + Clog::_with_dir(dir)?.try_config_file(Path::new(CLOG_CONFIG_FILE)) } /// Creates a `Clog` struct using a specific git working directory AND a project directory. @@ -290,9 +298,11 @@ impl Clog { /// }); /// ``` pub fn with_dirs>(git_dir: P, work_tree: P) -> BuilderResult { - debugln!("Creating clog with \n\tgit_dir: {:?}\n\twork_tree: {:?}", + debugln!( + "Creating clog with \n\tgit_dir: {:?}\n\twork_tree: {:?}", git_dir.as_ref(), - work_tree.as_ref()); + work_tree.as_ref() + ); let mut clog = Clog::_new(); clog.git_dir = Some(git_dir.as_ref().to_path_buf()); clog.git_work_tree = Some(work_tree.as_ref().to_path_buf()); @@ -373,8 +383,10 @@ impl Clog { } }; - toml_from_latest = - clog_table.lookup("from-latest-tag").unwrap_or(&Value::Boolean(false)).as_bool(); + toml_from_latest = clog_table + .lookup("from-latest-tag") + .unwrap_or(&Value::Boolean(false)) + .as_bool(); toml_repo = match clog_table.lookup("repository") { Some(val) => Some(val.as_str().unwrap_or("").to_owned()), None => Some("".to_owned()), @@ -392,54 +404,50 @@ impl Clog { }, None => Some(LinkStyle::Github), }; - toml_outfile = match clog_table.lookup("outfile") { - Some(val) => Some(val.as_str().unwrap_or("").to_owned()), - None => None, - }; - toml_infile = match clog_table.lookup("infile") { - Some(val) => Some(val.as_str().unwrap_or("").to_owned()), - None => None, - }; - toml_changelog = match clog_table.lookup("changelog") { - Some(val) => Some(val.as_str().unwrap_or("").to_owned()), - None => None, - }; - toml_format = match clog_table.lookup("output-format") { - Some(val) => Some(val.as_str().unwrap_or("").to_owned()), - None => None, - }; + toml_outfile = clog_table + .lookup("outfile") + .map(|val| val.as_str().unwrap_or("").to_owned()); + toml_infile = clog_table + .lookup("infile") + .map(|val| val.as_str().unwrap_or("").to_owned()); + toml_changelog = clog_table + .lookup("changelog") + .map(|val| val.as_str().unwrap_or("").to_owned()); + toml_format = clog_table + .lookup("output-format") + .map(|val| val.as_str().unwrap_or("").to_owned()); match toml_table.get("sections") { - Some(table) => { - match table.as_table() { - Some(table) => { - for (sec, val) in table.iter() { - if let Some(vec) = val.as_slice() { - let alias_vec = vec.iter().map(|v| v.as_str().unwrap_or("").to_owned()).collect::>(); - self.section_map.insert(sec.to_owned(), alias_vec); - } + Some(table) => match table.as_table() { + Some(table) => { + for (sec, val) in table.iter() { + if let Some(vec) = val.as_slice() { + let alias_vec = vec + .iter() + .map(|v| v.as_str().unwrap_or("").to_owned()) + .collect::>(); + self.section_map.insert(sec.to_owned(), alias_vec); } } - None => (), } - } + None => (), + }, None => (), }; match toml_table.get("components") { - Some(table) => { - match table.as_table() { - Some(table) => { - for (comp, val) in table.iter() { - if let Some(vec) = val.as_slice() { - let alias_vec = vec.iter() - .map(|v| v.as_str().unwrap_or("").to_owned()) - .collect::>(); - self.component_map.insert(comp.to_owned(), alias_vec); - } + Some(table) => match table.as_table() { + Some(table) => { + for (comp, val) in table.iter() { + if let Some(vec) = val.as_slice() { + let alias_vec = vec + .iter() + .map(|v| v.as_str().unwrap_or("").to_owned()) + .collect::>(); + self.component_map.insert(comp.to_owned(), alias_vec); } } - None => (), } - } + None => (), + }, None => (), }; } else { @@ -486,7 +494,6 @@ impl Clog { Ok(self) } - /// Sets the grep search pattern for finding commits. /// /// # Example @@ -791,20 +798,21 @@ impl Clog { }; let output = Command::new("git") - .arg(&self.get_git_dir()[..]) - .arg(&self.get_git_work_tree()[..]) - .arg("log") - .arg("-E") - .arg(&format!("--grep={}", self.grep)) - .arg(&format!("--format={}", self.format)) - .arg(&range) - .output().unwrap_or_else(|e| panic!("Failed to run 'git log' with error: {}", e)); + .arg(&self.get_git_dir()[..]) + .arg(&self.get_git_work_tree()[..]) + .arg("log") + .arg("-E") + .arg(&format!("--grep={}", self.grep)) + .arg(&format!("--format={}", self.format)) + .arg(&range) + .output() + .unwrap_or_else(|e| panic!("Failed to run 'git log' with error: {}", e)); String::from_utf8_lossy(&output.stdout) - .split("\n==END==\n") - .map(|commit_str| { self.parse_raw_commit(commit_str) }) - .filter(| entry| entry.commit_type != "Unknown") - .collect() + .split("\n==END==\n") + .map(|commit_str| self.parse_raw_commit(commit_str)) + .filter(|entry| entry.commit_type != "Unknown") + .collect() } #[doc(hidden)] @@ -813,21 +821,24 @@ impl Clog { let hash = lines.next().unwrap_or("").to_owned(); - let (subject, component, commit_type) = match lines.next().and_then(|s| self.regex.captures(s)) { Some(caps) => { let commit_type = self.section_for(caps.at(1).unwrap_or("")).to_owned(); - let component = caps.at(2).map(|component| - match self.component_for(component) { - Some(alias) => alias.clone(), - None => component.to_owned(), - } - .to_owned()); + let component = + caps.at(2) + .map(|component| match self.component_for(component) { + Some(alias) => alias.clone(), + None => component.to_owned(), + }); let subject = caps.at(3); (subject, component, commit_type) } - None => (Some(""), Some("".to_owned()), self.section_for("unk").clone()), + None => ( + Some(""), + Some("".to_owned()), + self.section_for("unk").clone(), + ), }; let mut closes = vec![]; let mut breaks = vec![]; @@ -847,12 +858,12 @@ impl Clog { } Commit { - hash: hash, + hash, subject: subject.unwrap().to_owned(), - component: component.unwrap_or("".to_string()).to_owned(), - closes: closes, - breaks: breaks, - commit_type: commit_type + component: component.unwrap_or_default(), + closes, + breaks, + commit_type, } } @@ -870,12 +881,13 @@ impl Clog { /// ``` pub fn get_latest_tag(&self) -> String { let output = Command::new("git") - .arg(&self.get_git_dir()[..]) - .arg(&self.get_git_work_tree()[..]) - .arg("rev-list") - .arg("--tags") - .arg("--max-count=1") - .output().unwrap_or_else(|e| panic!("Failed to run 'git rev-list' with error: {}",e)); + .arg(&self.get_git_dir()[..]) + .arg(&self.get_git_work_tree()[..]) + .arg("rev-list") + .arg("--tags") + .arg("--max-count=1") + .output() + .unwrap_or_else(|e| panic!("Failed to run 'git rev-list' with error: {}", e)); let buf = String::from_utf8_lossy(&output.stdout); buf.trim_matches('\n').to_owned() @@ -895,12 +907,13 @@ impl Clog { /// ``` pub fn get_latest_tag_ver(&self) -> String { let output = Command::new("git") - .arg(&self.get_git_dir()[..]) - .arg(&self.get_git_work_tree()[..]) - .arg("describe") - .arg("--tags") - .arg("--abbrev=0") - .output().unwrap_or_else(|e| panic!("Failed to run 'git describe' with error: {}",e)); + .arg(&self.get_git_dir()[..]) + .arg(&self.get_git_work_tree()[..]) + .arg("describe") + .arg("--tags") + .arg("--abbrev=0") + .output() + .unwrap_or_else(|e| panic!("Failed to run 'git describe' with error: {}", e)); String::from_utf8_lossy(&output.stdout).into_owned() } @@ -919,11 +932,12 @@ impl Clog { /// ``` pub fn get_last_commit(&self) -> String { let output = Command::new("git") - .arg(&self.get_git_dir()[..]) - .arg(&self.get_git_work_tree()[..]) - .arg("rev-parse") - .arg("HEAD") - .output().unwrap_or_else(|e| panic!("Failed to run 'git rev-parse' with error: {}", e)); + .arg(&self.get_git_dir()[..]) + .arg(&self.get_git_work_tree()[..]) + .arg("rev-parse") + .arg("HEAD") + .output() + .unwrap_or_else(|e| panic!("Failed to run 'git rev-parse' with error: {}", e)); String::from_utf8_lossy(&output.stdout).into_owned() } @@ -935,14 +949,16 @@ impl Clog { "".to_owned() } else if self.git_dir.is_some() { // user supplied both - format!("--work-tree={}", self.git_work_tree.clone().unwrap().to_str().unwrap()) + format!( + "--work-tree={}", + self.git_work_tree.clone().unwrap().to_str().unwrap() + ) } else { // user only supplied a working tree i.e. /home/user/mycode let mut w = self.git_work_tree.clone().unwrap(); w.pop(); format!("--work-tree={}", w.to_str().unwrap()) } - } fn get_git_dir(&self) -> String { @@ -952,7 +968,10 @@ impl Clog { "".to_owned() } else if self.git_work_tree.is_some() { // user supplied both - format!("--git-dir={}", self.git_dir.clone().unwrap().to_str().unwrap()) + format!( + "--git-dir={}", + self.git_dir.clone().unwrap().to_str().unwrap() + ) } else { // user only supplied a git dir i.e. /home/user/mycode/.git let mut g = self.git_dir.clone().unwrap(); @@ -975,14 +994,11 @@ impl Clog { /// assert_eq!("Features", section); /// ``` pub fn section_for(&self, alias: &str) -> &String { - self.section_map.iter() - .filter(|&(_, v)| v.iter().any(|s| s == alias)) - .map(|(k, _)| k) - .next() - .unwrap_or(self.section_map.keys() - .filter(|&k| k == "Unknown") - .next() - .unwrap()) + self.section_map + .iter() + .find(|&(_, v)| v.iter().any(|s| s == alias)) + .map(|(k, _)| k) + .unwrap_or_else(|| self.section_map.keys().find(|&k| k == "Unknown").unwrap()) } /// Retrieves the full component name for a given alias (if one is defined) @@ -1062,10 +1078,14 @@ impl Clog { let mut contents = String::with_capacity(256); if let Some(ref infile) = self.infile { debugln!("infile set to: {:?}", infile); - File::open(infile).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); + File::open(infile) + .map(|mut f| f.read_to_string(&mut contents).ok()) + .ok(); } else { debugln!("infile not set, trying the outfile"); - File::open(cl.as_ref()).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); + File::open(cl.as_ref()) + .map(|mut f| f.read_to_string(&mut contents).ok()) + .ok(); } contents.shrink_to_fit(); @@ -1074,11 +1094,11 @@ impl Clog { match self.out_format { ChangelogFormat::Markdown => { let mut writer = MarkdownWriter::new(&mut file); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } ChangelogFormat::Json => { let mut writer = JsonWriter::new(&mut file); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } } } @@ -1112,7 +1132,9 @@ impl Clog { pub fn write_changelog_from>(&self, cl: P) -> WriterResult { debugln!("Writing changelog from file: {:?}", cl.as_ref()); let mut contents = String::with_capacity(256); - File::open(cl.as_ref()).map(|mut f| f.read_to_string(&mut contents).ok()).ok(); + File::open(cl.as_ref()) + .map(|mut f| f.read_to_string(&mut contents).ok()) + .ok(); contents.shrink_to_fit(); if let Some(ref ofile) = self.outfile { @@ -1122,20 +1144,20 @@ impl Clog { match self.out_format { ChangelogFormat::Markdown => { let mut writer = MarkdownWriter::new(&mut file); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } ChangelogFormat::Json => { let mut writer = JsonWriter::new(&mut file); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } } } if let Err(..) = file.write(contents.as_bytes()) { - return Err(Error::WriteErr) + return Err(Error::WriteErr); } } else { - return Err(Error::CreateFileErr) + return Err(Error::CreateFileErr); } } else { debugln!("outfile not set, using stdout"); @@ -1145,11 +1167,11 @@ impl Clog { match self.out_format { ChangelogFormat::Markdown => { let mut writer = MarkdownWriter::new(&mut out_buf); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } ChangelogFormat::Json => { let mut writer = JsonWriter::new(&mut out_buf); - try!(self.write_changelog_with(&mut writer)); + self.write_changelog_with(&mut writer)?; } } } @@ -1188,11 +1210,13 @@ impl Clog { /// }); /// ``` pub fn write_changelog_with(&self, writer: &mut W) -> WriterResult - where W: FormatWriter { + where + W: FormatWriter, + { debugln!("Writing changelog from writer"); let sm = SectionMap::from_commits(self.get_commits()); - try!(writer.write_changelog(self, &sm)); + writer.write_changelog(self, &sm)?; Ok(()) } diff --git a/src/error.rs b/src/error.rs index 570bb18..1606e48 100644 --- a/src/error.rs +++ b/src/error.rs @@ -22,7 +22,7 @@ pub enum Error { /// Generic catch all I/O related error IoErr, /// Unknown, but fatal error (a catch all) - UnknownErr + UnknownErr, } // Shamelessly taken and adopted from https://github.com/BurntSushi :) @@ -51,15 +51,7 @@ impl Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - _ => write!(f, "{}", self.description()), - } - } -} - -impl StdError for Error { - fn description(&self) -> &str { - match *self { + f.pad(match *self { Error::ConfigParseErr => "error parsing config file", Error::ConfigFormatErr => "incorrect format for config file", Error::CurrentDirErr => "cannot get current directory", @@ -69,12 +61,12 @@ impl StdError for Error { Error::WriteErr => "cannot write to output file or stream", Error::UnknownErr => "unknown fatal error", Error::IoErr => "fatal i/o error with output file", - } + }) } +} - fn cause(&self) -> Option<&StdError> { - match *self { - _ => None, - } +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + None } } diff --git a/src/fmt/json_writer.rs b/src/fmt/json_writer.rs index 5268d83..5d8eae3 100644 --- a/src/fmt/json_writer.rs +++ b/src/fmt/json_writer.rs @@ -4,12 +4,11 @@ use std::io; use time; use clog::Clog; -use git::Commit; use error::Error; use fmt::{FormatWriter, WriterResult}; +use git::Commit; use sectionmap::SectionMap; - /// Wraps a `std::io::Write` object to write `clog` output in a JSON format /// /// # Example @@ -37,8 +36,7 @@ use sectionmap::SectionMap; /// e.exit(); /// }); /// ``` -pub struct JsonWriter<'a>(&'a mut io::Write); - +pub struct JsonWriter<'a>(&'a mut dyn io::Write); impl<'a> JsonWriter<'a> { /// Creates a new instance of the `JsonWriter` struct using a `std::io::Write` object. @@ -66,42 +64,38 @@ impl<'a> JsonWriter<'a> { impl<'a> JsonWriter<'a> { /// Writes the initial header inforamtion for a release fn write_header(&mut self, options: &Clog) -> io::Result<()> { - try!(write!(self.0, "\"header\":{{\"version\":{:?},\"patch_version\":{:?},\"subtitle\":{},", + write!( + self.0, + "\"header\":{{\"version\":{:?},\"patch_version\":{:?},\"subtitle\":{},", options.version, options.patch_ver, match options.subtitle.len() { 0 => "null".to_owned(), - _ => format!("{:?}", &*options.subtitle) + _ => format!("{:?}", &*options.subtitle), } - )); + )?; let date = time::now_utc(); match date.strftime("%Y-%m-%d") { Ok(date) => { - write!( - self.0, - "\"date\":\"{}\"}},", - date - ) + write!(self.0, "\"date\":\"{}\"}},", date) } Err(_) => { - write!( - self.0, - "\"date\":null}},", - ) + write!(self.0, "\"date\":null}},",) } } } /// Writes a particular section of a changelog - fn write_section(&mut self, - options: &Clog, - section: &BTreeMap<&String, &Vec>) - -> WriterResult { - if section.len() == 0 { + fn write_section( + &mut self, + options: &Clog, + section: &BTreeMap<&String, &Vec>, + ) -> WriterResult { + if section.is_empty() { write!(self.0, "\"commits\":null").unwrap(); - return Ok(()) + return Ok(()); } write!(self.0, "\"commits\":[").unwrap(); @@ -118,21 +112,24 @@ impl<'a> JsonWriter<'a> { write!(self.0, "{:?},", component).unwrap(); } write!( - self.0 , "\"subject\":{:?},\"commit_link\":{:?},\"closes\":", + self.0, + "\"subject\":{:?},\"commit_link\":{:?},\"closes\":", entry.subject, - options.link_style - .commit_link(&*entry.hash, &*options.repo) - ).unwrap(); + options.link_style.commit_link(&*entry.hash, &*options.repo) + ) + .unwrap(); if !entry.closes.is_empty() { write!(self.0, "[").unwrap(); let mut c_it = entry.closes.iter().peekable(); while let Some(issue) = c_it.next() { - write!(self.0, + write!( + self.0, "{{\"issue\":{},\"issue_link\":{:?}}}", issue, options.link_style.issue_link(issue, &options.repo) - ).unwrap(); + ) + .unwrap(); if c_it.peek().is_some() { debugln!("There are more close commits, adding comma"); write!(self.0, ",").unwrap(); @@ -140,21 +137,22 @@ impl<'a> JsonWriter<'a> { debugln!("There are no more close commits, no comma required"); } } - write!(self.0, - "],").unwrap(); - } else { + write!(self.0, "],").unwrap(); + } else { write!(self.0, "null,").unwrap(); } - write!(self.0 , "\"breaks\":").unwrap(); + write!(self.0, "\"breaks\":").unwrap(); if !entry.breaks.is_empty() { write!(self.0, "[").unwrap(); let mut c_it = entry.closes.iter().peekable(); while let Some(issue) = c_it.next() { - write!(self.0, + write!( + self.0, "{{\"issue\":{},\"issue_link\":{:?}}}", issue, options.link_style.issue_link(issue, &options.repo) - ).unwrap(); + ) + .unwrap(); if c_it.peek().is_some() { debugln!("There are more breaks commits, adding comma"); write!(self.0, ",").unwrap(); @@ -162,9 +160,8 @@ impl<'a> JsonWriter<'a> { debugln!("There are no more breaks commits, no comma required"); } } - write!(self.0, - "]}}").unwrap(); - } else { + write!(self.0, "]}}").unwrap(); + } else { write!(self.0, "null}}").unwrap(); } if e_it.peek().is_some() { @@ -202,7 +199,8 @@ impl<'a> FormatWriter for JsonWriter<'a> { } write!(self.0, "\"sections\":").unwrap(); - let mut s_it = options.section_map + let mut s_it = options + .section_map .keys() .filter_map(|sec| sm.sections.get(sec).map(|compmap| (sec, compmap))) .peekable(); @@ -213,10 +211,11 @@ impl<'a> FormatWriter for JsonWriter<'a> { debugln!("Writing section: {}", &*sec); write!(self.0, "{{\"title\":{:?},", &*sec).unwrap(); - try!(self.write_section(options, &compmap.iter().collect::>())); + self.write_section(options, &compmap.iter().collect::>())?; write!(self.0, "}}").unwrap(); - if s_it.peek().is_some() { //&& s_it.peek().unwrap().0.len() > 0 { + if s_it.peek().is_some() { + //&& s_it.peek().unwrap().0.len() > 0 { debugln!("There are more sections, adding comma"); write!(self.0, ",").unwrap(); } else { diff --git a/src/fmt/md_writer.rs b/src/fmt/md_writer.rs index 5cda5c9..dfd88dc 100644 --- a/src/fmt/md_writer.rs +++ b/src/fmt/md_writer.rs @@ -4,9 +4,9 @@ use std::io; use time; use clog::Clog; -use git::Commit; use error::Error; use fmt::{FormatWriter, WriterResult}; +use git::Commit; use sectionmap::SectionMap; /// Wraps a `std::io::Write` object to write `clog` output in a Markdown format @@ -36,8 +36,7 @@ use sectionmap::SectionMap; /// e.exit(); /// }); /// ``` -pub struct MarkdownWriter<'a>(&'a mut io::Write); - +pub struct MarkdownWriter<'a>(&'a mut dyn io::Write); impl<'a> MarkdownWriter<'a> { /// Creates a new instance of the `MarkdownWriter` struct using a `std::io::Write` object. @@ -86,24 +85,25 @@ impl<'a> MarkdownWriter<'a> { Err(_) => { write!( self.0, - "\n{} ({})\n\n", - options.version, version_text, "XXXX-XX-XX" + "\n{} (XXXX-XX-XX)\n\n", + options.version, version_text ) } } } /// Writes a particular section of a changelog - fn write_section(&mut self, - options: &Clog, - title: &str, - section: &BTreeMap<&String, &Vec>) - -> WriterResult { - if section.len() == 0 { - return Ok(()) + fn write_section( + &mut self, + options: &Clog, + title: &str, + section: &BTreeMap<&String, &Vec>, + ) -> WriterResult { + if section.is_empty() { + return Ok(()); } - if let Err(..) = self.0.write(&format!("\n#### {}\n\n", title)[..].as_bytes()) { + if let Err(..) = write!(self.0, "\n#### {}\n\n", title) { return Err(Error::WriteErr); } @@ -111,57 +111,71 @@ impl<'a> MarkdownWriter<'a> { let nested = (entries.len() > 1) && !component.is_empty(); let prefix = if nested { - if let Err(..) = write!(self.0 , "* **{}:**\n", component) { + if let Err(..) = writeln!(self.0, "* **{}:**", component) { return Err(Error::WriteErr); } " *".to_owned() } else if !component.is_empty() { format!("* **{}:**", component) } else { - format!("* ") + String::from("* ") }; for entry in entries.iter() { if let Err(..) = write!( - self.0 , "{} {} ([{}]({})", - prefix, - entry.subject, - &entry.hash[0..8], - options.link_style - .commit_link(&*entry.hash, &options.repo[..]) - ) { + self.0, + "{} {} ([{}]({})", + prefix, + entry.subject, + &entry.hash[0..8], + options + .link_style + .commit_link(&*entry.hash, &options.repo[..]) + ) { return Err(Error::WriteErr); } if !entry.closes.is_empty() { - let closes_string = entry.closes.iter() - .map(|s| format!("[#{}]({})", - &*s, - options.link_style.issue_link(&*s, &options.repo))) - .collect::>() - .join(", "); - - if let Err(..) = write!(self.0 , ", closes {}", closes_string) { + let closes_string = entry + .closes + .iter() + .map(|s| { + format!( + "[#{}]({})", + &*s, + options.link_style.issue_link(&*s, &options.repo) + ) + }) + .collect::>() + .join(", "); + + if let Err(..) = write!(self.0, ", closes {}", closes_string) { return Err(Error::WriteErr); } } if !entry.breaks.is_empty() { - let breaks_string = entry.breaks.iter() - .map(|s| format!("[#{}]({})", - &*s, - options.link_style.issue_link(&*s, &options.repo))) - .collect::>() - .join(", "); + let breaks_string = entry + .breaks + .iter() + .map(|s| { + format!( + "[#{}]({})", + &*s, + options.link_style.issue_link(&*s, &options.repo) + ) + }) + .collect::>() + .join(", "); // 5 = "[#]()" i.e. a commit message that only said "BREAKING" if breaks_string.len() != 5 { - if let Err(..) = write!(self.0 , ", breaks {}", breaks_string) { + if let Err(..) = write!(self.0, ", breaks {}", breaks_string) { return Err(Error::WriteErr); } } } - if let Err(..) = write!(self.0 , ")\n") { + if let Err(..) = writeln!(self.0, ")") { return Err(Error::WriteErr); } } @@ -173,8 +187,8 @@ impl<'a> MarkdownWriter<'a> { /// Writes some contents to the `Write` writer object #[allow(dead_code)] fn write(&mut self, content: &str) -> io::Result<()> { - try!(write!(self.0 , "\n\n\n")); - write!(self.0 , "{}", content) + write!(self.0, "\n\n\n")?; + write!(self.0, "{}", content) } } @@ -185,11 +199,16 @@ impl<'a> FormatWriter for MarkdownWriter<'a> { } // Get the section names ordered from `options.section_map` - let s_it = options.section_map + let s_it = options + .section_map .keys() .filter_map(|sec| sm.sections.get(sec).map(|secmap| (sec, secmap))); for (sec, secmap) in s_it { - try!(self.write_section(options, &sec[..], &secmap.iter().collect::>())); + self.write_section( + options, + &sec[..], + &secmap.iter().collect::>(), + )?; } self.0.flush().unwrap(); diff --git a/src/lib.rs b/src/lib.rs index cdbe6c8..a5bce7e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,21 +2,21 @@ extern crate indexmap; extern crate regex; -extern crate toml; extern crate time; +extern crate toml; #[macro_use] mod macros; -pub mod git; -pub mod fmt; -mod sectionmap; mod clog; pub mod error; +pub mod fmt; +pub mod git; mod link_style; +mod sectionmap; pub use clog::Clog; -pub use sectionmap::SectionMap; pub use link_style::LinkStyle; +pub use sectionmap::SectionMap; // The default config file -const CLOG_CONFIG_FILE: &'static str = ".clog.toml"; +const CLOG_CONFIG_FILE: &str = ".clog.toml"; diff --git a/src/link_style.rs b/src/link_style.rs index 8758318..87f4a43 100644 --- a/src/link_style.rs +++ b/src/link_style.rs @@ -1,13 +1,13 @@ -/// Determines the hyperlink style used in commit and issue links. Defaults to `LinksStyle::Github` -/// -/// # Example -/// -/// ```no_run -/// # use clog::{LinkStyle, Clog}; -/// let mut clog = Clog::new().unwrap(); -/// clog.link_style(LinkStyle::Stash); -/// ``` -clog_enum!{ +clog_enum! { + /// Determines the hyperlink style used in commit and issue links. Defaults to `LinksStyle::Github` + /// + /// # Example + /// + /// ```no_run + /// # use clog::{LinkStyle, Clog}; + /// let mut clog = Clog::new().unwrap(); + /// clog.link_style(LinkStyle::Stash); + /// ``` #[derive(Debug)] pub enum LinkStyle { Github, @@ -31,14 +31,14 @@ impl LinkStyle { /// ``` pub fn issue_link>(&self, issue: S, repo: S) -> String { match repo.as_ref() { - "" => format!("{}", issue.as_ref()), + "" => issue.as_ref().to_owned(), link => { match *self { LinkStyle::Github => format!("{}/issues/{}", link, issue.as_ref()), LinkStyle::Gitlab => format!("{}/issues/{}", link, issue.as_ref()), - LinkStyle::Stash => format!("{}", issue.as_ref()), + LinkStyle::Stash => issue.as_ref().to_owned(), // cgit does not support issues - LinkStyle::Cgit => format!("{}", issue.as_ref()), + LinkStyle::Cgit => issue.as_ref().to_owned(), } } } @@ -56,15 +56,13 @@ impl LinkStyle { /// ``` pub fn commit_link>(&self, hash: S, repo: S) -> String { match repo.as_ref() { - "" => format!("{}", &hash.as_ref()[0..8]), - link => { - match *self { - LinkStyle::Github => format!("{}/commit/{}", link, hash.as_ref()), - LinkStyle::Gitlab => format!("{}/commit/{}", link, hash.as_ref()), - LinkStyle::Stash => format!("{}/commits/{}", link, hash.as_ref()), - LinkStyle::Cgit => format!("{}/commit/?id={}", link, hash.as_ref()), - } - } + "" => hash.as_ref()[0..8].to_string(), + link => match *self { + LinkStyle::Github => format!("{}/commit/{}", link, hash.as_ref()), + LinkStyle::Gitlab => format!("{}/commit/{}", link, hash.as_ref()), + LinkStyle::Stash => format!("{}/commits/{}", link, hash.as_ref()), + LinkStyle::Cgit => format!("{}/commit/?id={}", link, hash.as_ref()), + }, } } } diff --git a/src/macros.rs b/src/macros.rs index c753442..7948571 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -31,14 +31,14 @@ macro_rules! debug { #[cfg(not(feature = "debug"))] macro_rules! debugln { - ($fmt:expr) => (); - ($fmt:expr, $($arg:tt)*) => (); + ($fmt:expr) => {}; + ($fmt:expr, $($arg:tt)*) => {}; } #[cfg(not(feature = "debug"))] macro_rules! debug { - ($fmt:expr) => (); - ($fmt:expr, $($arg:tt)*) => (); + ($fmt:expr) => {}; + ($fmt:expr, $($arg:tt)*) => {}; } /// Convenience macro taken from https://github.com/kbknapp/clap-rs to generate more complete enums @@ -65,7 +65,8 @@ macro_rules! debug { /// } /// ``` macro_rules! clog_enum { - (enum $e:ident { $($v:ident),+ } ) => { + ($(#[$meta:meta])* enum $e:ident { $($v:ident),+ } ) => { + $(#[$meta])* enum $e { $($v),+ } @@ -74,7 +75,6 @@ macro_rules! clog_enum { type Err = String; fn from_str(s: &str) -> Result { - use ::std::ascii::AsciiExt; match s { $(stringify!($v) | _ if s.eq_ignore_ascii_case(stringify!($v)) => Ok($e::$v),)+ @@ -108,7 +108,8 @@ macro_rules! clog_enum { } } }; - (pub enum $e:ident { $($v:ident),+ } ) => { + ($(#[$meta:meta])* pub enum $e:ident { $($v:ident),+ } ) => { + $(#[$meta])* pub enum $e { $($v),+ } @@ -117,95 +118,6 @@ macro_rules! clog_enum { type Err = String; fn from_str(s: &str) -> Result { - use ::std::ascii::AsciiExt; - match s { - $(stringify!($v) | - _ if s.eq_ignore_ascii_case(stringify!($v)) => Ok($e::$v),)+ - _ => Err({ - let v = vec![ - $(stringify!($v),)+ - ]; - format!("valid values:{}", - v.iter().fold(String::new(), |a, i| { - a + &format!(" {}", i)[..] - })) - }) - } - } - } - - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $($e::$v => write!(f, stringify!($v)),)+ - } - } - } - - impl $e { - #[allow(dead_code)] - pub fn variants() -> Vec<&'static str> { - vec![ - $(stringify!($v),)+ - ] - } - } - }; - (#[derive($($d:ident),+)] enum $e:ident { $($v:ident),+ } ) => { - #[derive($($d,)+)] - enum $e { - $($v),+ - } - - impl ::std::str::FromStr for $e { - type Err = String; - - fn from_str(s: &str) -> Result { - use ::std::ascii::AsciiExt; - match s { - $(stringify!($v) | - _ if s.eq_ignore_ascii_case(stringify!($v)) => Ok($e::$v),)+ - _ => Err({ - let v = vec![ - $(stringify!($v),)+ - ]; - format!("valid values:{}", - v.iter().fold(String::new(), |a, i| { - a + &format!(" {}", i)[..] - })) - }) - } - } - } - - impl ::std::fmt::Display for $e { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - $($e::$v => write!(f, stringify!($v)),)+ - } - } - } - - impl $e { - #[allow(dead_code)] - pub fn variants() -> Vec<&'static str> { - vec![ - $(stringify!($v),)+ - ] - } - } - }; - (#[derive($($d:ident),+)] pub enum $e:ident { $($v:ident),+ } ) => { - #[derive($($d,)+)] - pub enum $e { - $($v),+ - } - - impl ::std::str::FromStr for $e { - type Err = String; - - fn from_str(s: &str) -> Result { - use ::std::ascii::AsciiExt; match s { $(stringify!($v) | _ if s.eq_ignore_ascii_case(stringify!($v)) => Ok($e::$v),)+