tytanic_filter/ast/
pat.rs

1use std::hash::Hash;
2
3use pest::iterators::Pair;
4
5use super::{Error, Glob, PairExt, PairsExt, Regex, Rule, Str};
6use crate::eval::{self, Context, Eval, Set, Test, Value};
7
8/// A pattern literal node.
9#[derive(Clone, PartialEq, Eq, Hash)]
10pub enum Pat {
11    /// A glob pattern literal.
12    Glob(Glob),
13
14    /// A regex pattern literal.
15    Regex(Regex),
16
17    /// An exact pattern literal.
18    Exact(Str),
19}
20
21impl std::fmt::Debug for Pat {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        let (prefix, pat) = match self {
24            Pat::Glob(glob) => ("glob", glob.as_str()),
25            Pat::Regex(regex) => ("regex", regex.as_str()),
26            Pat::Exact(pat) => ("exact", pat.as_str()),
27        };
28
29        write!(f, "{prefix}:{pat:?}")
30    }
31}
32
33impl Pat {
34    /// Returns true if the id matches this pattern.
35    pub fn is_match<S: AsRef<str>>(&self, id: S) -> bool {
36        match self {
37            Self::Glob(pat) => pat.is_match(id),
38            Self::Regex(regex) => regex.is_match(id),
39            Self::Exact(pat) => id.as_ref() == pat.as_str(),
40        }
41    }
42}
43
44impl<T: Test> Eval<T> for Pat {
45    fn eval(&self, _ctx: &Context<T>) -> Result<Value<T>, eval::Error> {
46        Ok(Value::Set(Set::coerce_pat(self.clone())))
47    }
48}
49
50impl Pat {
51    pub(super) fn parse(pair: Pair<'_, Rule>) -> Result<Self, Error> {
52        pair.expect_rules(&[Rule::pat_inner])?;
53        let mut pairs = pair.into_inner();
54
55        let kind = pairs.expect_pair(&[Rule::pat_kind])?.as_str();
56        let _ = pairs.expect_pair(&[Rule::pat_sep])?;
57        let inner = pairs.expect_pair(&[Rule::pat_raw_lit, Rule::str_double, Rule::str_single])?;
58        pairs.expect_end()?;
59
60        let pat: Str = if inner.as_rule() == Rule::pat_raw_lit {
61            Str(inner.as_str().into())
62        } else {
63            Str::parse(inner)?
64        };
65
66        Ok(match kind {
67            "g" | "glob" => Self::Glob(Glob::new(&pat)?),
68            "r" | "regex" => Self::Regex(Regex::new(&pat)?),
69            "e" | "exact" => Self::Exact(pat),
70            _ => unreachable!("unhandled kind: {kind:?}"),
71        })
72    }
73}