harper_core/expr/
duration_expr.rs

1use crate::patterns::WordSet;
2use crate::{Span, Token};
3
4use super::{Expr, SequenceExpr, SpelledNumberExpr};
5
6#[derive(Default)]
7pub struct DurationExpr;
8
9impl Expr for DurationExpr {
10    fn run(&self, cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
11        if tokens.is_empty() {
12            return None;
13        }
14
15        let units = WordSet::new(&[
16            "minute", "minutes", "hour", "hours", "day", "days", "week", "weeks", "month",
17            "months", "year", "years",
18        ]);
19
20        let expr = SequenceExpr::default()
21            .then_longest_of(vec![
22                Box::new(SpelledNumberExpr),
23                Box::new(SequenceExpr::default().then_number()),
24            ])
25            .then_whitespace()
26            .then(units);
27
28        expr.run(cursor, tokens, source)
29    }
30}
31
32#[cfg(test)]
33pub mod tests {
34    use super::DurationExpr;
35    use crate::Document;
36    use crate::expr::ExprExt;
37    use crate::linting::tests::SpanVecExt;
38
39    #[test]
40    fn detect_10_days() {
41        let doc = Document::new_markdown_default_curated("Is 10 days a long time?");
42        let matches = DurationExpr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
43        assert_eq!(matches.to_strings(&doc), vec!["10 days"]);
44    }
45
46    #[test]
47    fn detect_ten_days() {
48        let doc = Document::new_markdown_default_curated("I think ten days is a long time.");
49        let matches = DurationExpr.iter_matches_in_doc(&doc).collect::<Vec<_>>();
50        assert_eq!(matches.to_strings(&doc), vec!["ten days"]);
51    }
52}