harper-core 2.0.0

The language checker for developers.
Documentation
use crate::Token;
use crate::expr::{DurationExpr, Expr, SequenceExpr};
use crate::linting::expr_linter::Chunk;
use crate::linting::{ExprLinter, Lint, LintKind, Suggestion};
use crate::token_string_ext::TokenStringExt;

pub struct AfterLater {
    expr: SequenceExpr,
}

impl Default for AfterLater {
    fn default() -> Self {
        Self {
            expr: SequenceExpr::aco("after")
                .t_ws()
                .then_optional(
                    SequenceExpr::word_set(&[
                        "about",
                        "almost",
                        "approximately",
                        "around",
                        "circa",
                        "exactly",
                        "just",
                        "maybe",
                        "nearly",
                        "only",
                        "perhaps",
                        "precisely",
                        "probably",
                        "roughly",
                    ])
                    .t_ws(),
                )
                .then(DurationExpr)
                .t_ws()
                .t_aco("later"),
        }
    }
}

impl ExprLinter for AfterLater {
    type Unit = Chunk;

    fn expr(&self) -> &dyn Expr {
        &self.expr
    }

    fn match_to_lint(&self, toks: &[Token], src: &[char]) -> Option<Lint> {
        let without_after: Vec<char> = toks[2..].span()?.get_content(src).to_vec();
        let without_later: Vec<char> = toks[..toks.len() - 2].span()?.get_content(src).to_vec();

        let template_chars = toks.span()?.get_content(src);

        Some(Lint {
            span: toks.span()?,
            lint_kind: LintKind::Redundancy,
            message: "Don't use `later` following `after [a period of time]`".to_string(),
            suggestions: vec![
                Suggestion::replace_with_match_case(without_after, template_chars),
                Suggestion::replace_with_match_case(without_later, template_chars),
            ],
            ..Default::default()
        })
    }

    fn description(&self) -> &str {
        "Checks for the word `later` following `after [a period of time]`."
    }
}

#[cfg(test)]
mod tests {
    use super::AfterLater;
    use crate::linting::tests::assert_suggestion_result;

    #[test]
    fn after_90_days_later() {
        assert_suggestion_result(
            "Try to rename your organization after 90 days later because of GitHub official documentation it said.",
            AfterLater::default(),
            "Try to rename your organization after 90 days because of GitHub official documentation it said.",
        );
    }

    #[test]
    fn after_about_30_minutes_later() {
        assert_suggestion_result(
            "It plays like 1 minute of the song and then stops, and after about 30 minutes later, the bot disconnects an throws DisTubeError",
            AfterLater::default(),
            "It plays like 1 minute of the song and then stops, and about 30 minutes later, the bot disconnects an throws DisTubeError",
        );
    }

    #[test]
    fn after_14_days_later() {
        assert_suggestion_result(
            "After 14 days later, the cache expired.",
            AfterLater::default(),
            "After 14 days, the cache expired.",
        );
    }

    #[test]
    fn after_exactly_5_minutes_later() {
        assert_suggestion_result(
            "After exactly 5 minutes later, they try again and the cluster is formed then.",
            AfterLater::default(),
            "Exactly 5 minutes later, they try again and the cluster is formed then.",
        );
    }

    #[test]
    fn after_22_years_later_1() {
        assert_suggestion_result(
            "Completed YR campaign for 2nd time after 22 years later.",
            AfterLater::default(),
            "Completed YR campaign for 2nd time after 22 years.",
        );
    }

    #[test]
    fn after_almost_2_years_later() {
        assert_suggestion_result(
            "This buyer contacted me after almost 2 years later.",
            AfterLater::default(),
            "This buyer contacted me almost 2 years later.",
        );
    }

    #[test]
    fn after_2_years_later() {
        assert_suggestion_result(
            "Is Jedi Survivor better now after 2 years later?",
            AfterLater::default(),
            "Is Jedi Survivor better now after 2 years?",
        );
    }

    #[test]
    fn after_a_year_later() {
        assert_suggestion_result(
            "Even after a year later, I don’t know how to get my self-love back.",
            AfterLater::default(),
            "Even a year later, I don’t know how to get my self-love back.",
        );
    }

    #[test]
    fn after_22_years_later_2() {
        assert_suggestion_result(
            "After 22 years later, my top 1 game was Zeroed",
            AfterLater::default(),
            "After 22 years, my top 1 game was Zeroed",
        );
    }
}