harper_core/expr/
duration_expr.rs1use 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}