Skip to main content

harper_core/expr/
not.rs

1use crate::{Span, Token};
2
3use super::Expr;
4
5/// A zero-width assertion that matches when its inner expression does not.
6pub struct Not {
7    inner: Box<dyn Expr>,
8}
9
10impl Not {
11    pub fn new(inner: impl Expr + 'static) -> Self {
12        Self {
13            inner: Box::new(inner),
14        }
15    }
16}
17
18impl Expr for Not {
19    fn run(&self, cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
20        self.inner
21            .run(cursor, tokens, source)
22            .is_none()
23            .then(|| Span::empty(cursor))
24    }
25}
26
27#[cfg(test)]
28mod tests {
29    use crate::{
30        Document,
31        expr::{AnchorStart, ExprExt, SequenceExpr},
32        linting::tests::SpanVecExt,
33    };
34
35    use super::Not;
36
37    #[test]
38    fn rejects_expression_at_start() {
39        let document = Document::new_plain_english_curated("Give the rise to power.");
40        let expression = SequenceExpr::with(Not::new(AnchorStart))
41            .then_any_capitalization_of("give")
42            .then_whitespace()
43            .then_any_capitalization_of("the")
44            .then_whitespace()
45            .then_any_capitalization_of("rise")
46            .then_whitespace()
47            .then_any_capitalization_of("to");
48
49        let matches = expression
50            .iter_matches_in_doc(&document)
51            .collect::<Vec<_>>();
52
53        assert!(matches.is_empty());
54    }
55
56    #[test]
57    fn matches_expression_after_start() {
58        let document = Document::new_plain_english_curated("They give the rise to power.");
59        let expression = SequenceExpr::with(Not::new(AnchorStart))
60            .then_any_capitalization_of("give")
61            .then_whitespace()
62            .then_any_capitalization_of("the")
63            .then_whitespace()
64            .then_any_capitalization_of("rise")
65            .then_whitespace()
66            .then_any_capitalization_of("to");
67
68        let matches = expression
69            .iter_matches_in_doc(&document)
70            .collect::<Vec<_>>();
71
72        assert_eq!(matches.to_strings(&document), ["give the rise to"]);
73    }
74}