lintje 0.9.0

Lintje is an opinionated linter for Git.
use core::ops::Range;

use crate::commit::Commit;
use crate::issue::{Context, Issue, Position};
use crate::rule::Rule;
use crate::rule::RuleValidator;

pub struct RebaseCommit {}

impl RebaseCommit {
    pub fn new() -> Self {
        Self {}
    }
}

impl RuleValidator<Commit> for RebaseCommit {
    fn validate(&self, commit: &Commit) -> Option<Vec<Issue>> {
        let subject = &commit.subject;
        let fixup_check = validate_prefix("fixup", subject);
        if fixup_check.is_some() {
            return fixup_check;
        }
        let squash_check = validate_prefix("squash", subject);
        if squash_check.is_some() {
            return squash_check;
        }
        let amend_check = validate_prefix("amend", subject);
        if amend_check.is_some() {
            return amend_check;
        }

        None
    }
}

fn validate_prefix(prefix: &str, subject: &str) -> Option<Vec<Issue>> {
    if subject.starts_with(&format!("{}! ", prefix)) {
        let context = Context::subject_error(
            subject.to_string(),
            Range {
                start: 0,
                end: prefix.len() + 1,
            },
            format!("Rebase {} commits before pushing or merging", prefix),
        );
        Some(vec![Issue::error(
            Rule::RebaseCommit,
            format!("A {} commit was found", prefix),
            Position::Subject { line: 1, column: 1 },
            vec![context],
        )])
    } else {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::test::*;

    fn validate(commit: &Commit) -> Option<Vec<Issue>> {
        RebaseCommit::new().validate(&commit)
    }

    fn assert_subject_as_valid(subject: &str) {
        assert_eq!(validate(&commit(subject, "")), None);
    }

    #[test]
    fn with_valid_subjects() {
        assert_subject_as_valid("I don't need a rebase");
    }

    #[test]
    fn with_fixup_commit() {
        let issue = first_issue(validate(&commit("fixup! I need a rebase", "")));
        assert_eq!(issue.message, "A fixup commit was found");
        assert_eq!(issue.position, subject_position(1));
        assert_contains_issue_output(
            &issue,
            "1 | fixup! I need a rebase\n\
               | ^^^^^^ Rebase fixup commits before pushing or merging",
        );
    }

    #[test]
    fn with_squash_commit() {
        let issue = first_issue(validate(&commit("squash! I need a rebase", "")));
        assert_eq!(issue.message, "A squash commit was found");
        assert_eq!(issue.position, subject_position(1));
        assert_contains_issue_output(
            &issue,
            "1 | squash! I need a rebase\n\
               | ^^^^^^^ Rebase squash commits before pushing or merging",
        );
    }

    #[test]
    fn with_amend_commit() {
        let issue = first_issue(validate(&commit("amend! I need a rebase", "")));
        assert_eq!(issue.message, "A amend commit was found");
        assert_eq!(issue.position, subject_position(1));
        assert_contains_issue_output(
            &issue,
            "1 | amend! I need a rebase\n\
               | ^^^^^^ Rebase amend commits before pushing or merging",
        );
    }
}