harper_core/expr/
expr_map.rs

1use crate::LSend;
2use crate::Span;
3use crate::Token;
4
5use super::Expr;
6
7/// A map from an [`Expr`] to arbitrary data.
8///
9/// It has been a common pattern for rule authors to build a list of expressions that match a
10/// grammatical error.
11/// Then, depending on which expression was matched, a suggestion is chosen from another list.
12///
13/// The [`ExprMap`] unifies these two lists into one.
14///
15/// When used as a [`Expr`] in and of itself, it simply iterates through
16/// all contained expressions, returning the first match found.
17/// You should not assume this search is deterministic.
18pub struct ExprMap<T>
19where
20    T: LSend,
21{
22    rows: Vec<Row<T>>,
23}
24
25struct Row<T>
26where
27    T: LSend,
28{
29    pub key: Box<dyn Expr>,
30    pub element: T,
31}
32
33impl<T> Default for ExprMap<T>
34where
35    T: LSend,
36{
37    fn default() -> Self {
38        Self {
39            rows: Default::default(),
40        }
41    }
42}
43
44impl<T> ExprMap<T>
45where
46    T: LSend,
47{
48    pub fn insert(&mut self, expr: impl Expr + 'static, value: T) {
49        self.rows.push(Row {
50            key: Box::new(expr),
51            element: value,
52        });
53    }
54
55    /// Look up the corresponding value for the given map.
56    pub fn lookup(&self, cursor: usize, tokens: &[Token], source: &[char]) -> Option<&T> {
57        for row in &self.rows {
58            let len = row.key.run(cursor, tokens, source);
59
60            if len.is_some() {
61                return Some(&row.element);
62            }
63        }
64
65        None
66    }
67}
68
69impl<T> Expr for ExprMap<T>
70where
71    T: LSend,
72{
73    fn run(&self, cursor: usize, tokens: &[Token], source: &[char]) -> Option<Span<Token>> {
74        self.rows
75            .iter()
76            .filter_map(|row| row.key.run(cursor, tokens, source))
77            .next()
78    }
79}