Skip to content

Commit

Permalink
feat: Report commit ID in failure message
Browse files Browse the repository at this point in the history
This is a part of #9
  • Loading branch information
epage committed Aug 2, 2019
1 parent 6021ea3 commit 205e489
Show file tree
Hide file tree
Showing 5 changed files with 425 additions and 5 deletions.
40 changes: 40 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ once_cell = "0.2.4"
unicase = "2.4.0"
failure = "0.1"
git2 = "0.9"
clap = "2"
structopt = "0.2.18"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
toml = "0.4"
unicode-segmentation = "1.3.0"
log = "0.4"
env_logger = "0.5"
clap-verbosity-flag = "0.2.0"
grep-cli = "0.1"
imperative = "1.0"
derive_more = "0.15.0"
202 changes: 202 additions & 0 deletions src/checks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
use crate::report;

pub fn check_all(
source: report::Source,
message: &str,
config: &crate::config::Config,
report: report::Report,
) -> Result<(), failure::Error> {
if !config.no_wip() {
check_wip(source, message, report)?;
}
if !config.no_fixup() {
check_fixup(source, message, report)?;
}
match config.style() {
crate::config::Style::Conventional => {
let parsed = committed::conventional::Message::parse(message).unwrap();
if config.imperative_subject() {
check_imperative_subject(source, parsed.subject, report)?;
}
if config.subject_capitalized() {
check_capitalized_subject(source, parsed.subject, report)?;
}
if config.subject_not_punctuated() {
check_subject_not_punctuated(source, parsed.subject, report)?;
}
}
crate::config::Style::None => {
let parsed = committed::no_style::Message::parse(message).unwrap();
if config.imperative_subject() {
check_imperative_subject(source, parsed.raw_subject, report)?;
}
if config.subject_capitalized() {
check_capitalized_subject(source, parsed.raw_subject, report)?;
}
if config.subject_not_punctuated() {
check_subject_not_punctuated(source, parsed.raw_subject, report)?;
}
}
}
if config.subject_length() != 0 {
check_subject_length(source, message, config.subject_length(), report)?;
}
if config.line_length() != 0 {
check_line_length(source, message, config.line_length(), report)?;
}

Ok(())
}

pub fn check_subject_length(
source: report::Source,
message: &str,
max_length: usize,
report: report::Report,
) -> Result<(), failure::Error> {
let subject = message
.split('\n')
.next()
.ok_or_else(|| failure::Context::new("Commit cannot be empty"))?;
let subject = subject.trim_end();
let count = unicode_segmentation::UnicodeSegmentation::graphemes(subject, true).count();
if max_length < count {
report(report::Message::error(
source,
report::SubjectTooLong {
max_length,
actual_length: count,
},
));
failure::bail!(
"Commit subject is {}, exceeding the max length of {}",
count,
max_length
);
}
Ok(())
}

pub fn check_line_length(
source: report::Source,
message: &str,
max_length: usize,
report: report::Report,
) -> Result<(), failure::Error> {
for line in message.split('\n') {
let line = line.trim_end();
let count = unicode_segmentation::UnicodeSegmentation::graphemes(line, true).count();
if max_length < count {
report(report::Message::error(
source,
report::LineTooLong {
max_length,
actual_length: count,
},
));
failure::bail!(
"Commit line is {}, exceeding the max length of {}",
count,
max_length
);
}
}
Ok(())
}

pub fn check_capitalized_subject(
source: report::Source,
subject: &str,
report: report::Report,
) -> Result<(), failure::Error> {
let first_word = subject
.split_whitespace()
.next()
.ok_or_else(|| failure::Context::new("Subject cannot be empty"))?;
let first = first_word
.chars()
.next()
.ok_or_else(|| failure::Context::new("Subject cannot be empty"))?;
if !first.is_uppercase() {
report(report::Message::error(
source,
report::CapitalizeSubject { first_word },
));
failure::bail!("Subject must be capitalized: `{}`", subject);
}
Ok(())
}

pub fn check_subject_not_punctuated(
source: report::Source,
subject: &str,
report: report::Report,
) -> Result<(), failure::Error> {
let last = subject
.chars()
.last()
.ok_or_else(|| failure::Context::new("Subject cannot be empty"))?;
if last.is_ascii_punctuation() {
report(report::Message::error(
source,
report::NoPunctuation { punctuation: last },
));
failure::bail!("Subject must not be punctuated: `{}`", last);
}
Ok(())
}

pub fn check_imperative_subject(
source: report::Source,
subject: &str,
report: report::Report,
) -> Result<(), failure::Error> {
let first_word = subject
.split_whitespace()
.next()
.expect("Subject should have at least one word");
if !imperative::Mood::new()
.is_imperative(first_word)
.unwrap_or(true)
{
report(report::Message::error(
source,
report::Imperative { first_word },
));
failure::bail!(
"Subject does not start with imperative verb: {}",
first_word
);
}
Ok(())
}

static WIP_RE: once_cell::sync::Lazy<regex::Regex> =
once_cell::sync::Lazy::new(|| regex::Regex::new("^(wip|WIP)\\b").unwrap());

pub fn check_wip(
source: report::Source,
message: &str,
report: report::Report,
) -> Result<(), failure::Error> {
if WIP_RE.is_match(message) {
report(report::Message::error(source, report::Wip {}));
failure::bail!("Work-in-progress commits must be cleaned up");
}
Ok(())
}

static FIXUP_RE: once_cell::sync::Lazy<regex::Regex> =
once_cell::sync::Lazy::new(|| regex::Regex::new("^fixup! ").unwrap());

pub fn check_fixup(
source: report::Source,
message: &str,
report: report::Report,
) -> Result<(), failure::Error> {
if FIXUP_RE.is_match(message) {
report(report::Message::error(source, report::Fixup {}));
failure::bail!("Fixup commits must be squashed");
}
Ok(())
}

0 comments on commit 205e489

Please sign in to comment.