tytanic_filter/ast/
pat.rs1use 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#[derive(Clone, PartialEq, Eq, Hash)]
21pub enum Pat {
22 Glob(Glob),
24
25 Regex(Regex),
27
28 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 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}