tytanic_filter/ast/
pat.rs

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