use core::ops::Range;
use regex::Regex;
use crate::commit::Commit;
use crate::issue::{Context, Issue, Position};
use crate::rule::Rule;
use crate::rule::RuleValidator;
lazy_static! {
static ref SUBJECT_STARTS_WITH_PREFIX: Regex = Regex::new(r"^([\w\(\)/!]+:)\s.*").unwrap();
}
pub struct SubjectCapitalization {}
impl SubjectCapitalization {
pub fn new() -> Self {
Self {}
}
}
impl RuleValidator<Commit> for SubjectCapitalization {
fn validate(&self, commit: &Commit) -> Option<Vec<Issue>> {
if commit.has_issue(&Rule::SubjectPrefix) {
return None;
}
if commit.subject.chars().count() == 0 && commit.has_issue(&Rule::SubjectLength) {
return None;
}
match commit.subject.chars().next() {
Some(character) => {
if character.is_lowercase() {
let context = vec![Context::subject_error(
commit.subject.to_string(),
Range {
start: 0,
end: character.len_utf8(),
},
"Start the subject with a capital letter".to_string(),
)];
Some(vec![Issue::error(
Rule::SubjectCapitalization,
"The subject does not start with a capital letter".to_string(),
Position::Subject { line: 1, column: 1 },
context,
)])
} else {
None
}
}
None => {
error!("SubjectCapitalization validation failure: No first character found of subject.");
None
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test::*;
fn validate(commit: &Commit) -> Option<Vec<Issue>> {
SubjectCapitalization::new().validate(&commit)
}
fn assert_subject_as_valid(subject: &str) {
assert_eq!(validate(&commit(subject, "")), None);
}
#[test]
fn valid_subjects() {
assert_subject_as_valid("Fix test");
}
#[test]
fn starting_with_lowercase() {
let issue = first_issue(validate(&commit("fix test", "")));
assert_eq!(
issue.message,
"The subject does not start with a capital letter"
);
assert_eq!(issue.position, subject_position(1));
assert_contains_issue_output(
&issue,
"1 | fix test\n\
| ^ Start the subject with a capital letter",
);
}
#[test]
fn skipped_length() {
let mut short_commit = commit("", "");
short_commit.issues.push(Issue::error(
Rule::SubjectLength,
"some message".to_string(),
Position::Subject { line: 1, column: 1 },
vec![],
));
assert!(short_commit.has_issue(&Rule::SubjectLength));
assert!(!short_commit.has_issue(&Rule::SubjectCapitalization));
}
#[test]
fn skipped_prefix() {
let mut short_commit = commit("chore: foo", "");
short_commit.issues.push(Issue::error(
Rule::SubjectPrefix,
"some message".to_string(),
Position::Subject { line: 1, column: 1 },
vec![],
));
assert!(short_commit.has_issue(&Rule::SubjectPrefix));
assert!(!short_commit.has_issue(&Rule::SubjectCapitalization));
}
}