cedar_policy_core/ast/
pattern.rs1use std::sync::Arc;
18
19use serde::{Deserialize, Serialize};
20
21#[derive(Deserialize, Hash, Debug, Clone, Copy, PartialEq, Eq)]
23#[cfg_attr(not(fuzzing), derive(Serialize))]
29#[cfg_attr(fuzzing, derive(arbitrary::Arbitrary))]
30pub enum PatternElem {
31 Char(char),
33 Wildcard,
35}
36
37#[cfg(fuzzing)]
38impl Serialize for PatternElem {
39 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
40 where
41 S: serde::Serializer,
42 {
43 #[derive(Debug, Serialize)]
45 enum PatternElemForDafny {
46 Char(u32),
47 Wildcard,
48 }
49 match self {
50 Self::Char(c) => PatternElemForDafny::Char(*c as u32).serialize(serializer),
51 Self::Wildcard => PatternElemForDafny::Wildcard.serialize(serializer),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Hash, Eq, PartialEq, Serialize, Deserialize)]
59#[serde(transparent)]
60pub struct Pattern {
61 elems: Arc<Vec<PatternElem>>,
63}
64
65impl Pattern {
66 pub fn new(elems: impl IntoIterator<Item = PatternElem>) -> Self {
68 Self {
69 elems: Arc::new(elems.into_iter().collect()),
70 }
71 }
72
73 pub fn get_elems(&self) -> &[PatternElem] {
75 &self.elems
76 }
77
78 pub fn iter(&self) -> impl Iterator<Item = &PatternElem> {
80 self.elems.iter()
81 }
82}
83
84impl std::fmt::Display for Pattern {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 for pc in self.elems.as_ref() {
87 match pc {
88 PatternElem::Char(c) if c == &'*' => write!(f, r#"\*"#)?,
89 PatternElem::Char(c) => write!(f, "{}", c.escape_debug())?,
90 PatternElem::Wildcard => write!(f, r#"*"#)?,
91 }
92 }
93 Ok(())
94 }
95}
96
97impl PatternElem {
98 fn match_char(&self, text_char: &char) -> bool {
99 match self {
100 PatternElem::Char(c) => text_char == c,
101 PatternElem::Wildcard => true,
102 }
103 }
104 fn is_wildcard(&self) -> bool {
105 matches!(self, PatternElem::Wildcard)
106 }
107}
108
109impl Pattern {
110 pub fn wildcard_match(&self, text: &str) -> bool {
112 let pattern = self.get_elems();
113 if pattern.is_empty() {
114 return text.is_empty();
115 }
116
117 let text: Vec<char> = text.chars().collect();
126
127 let mut i: usize = 0; let mut j: usize = 0; let mut star_idx: usize = 0; let mut tmp_idx: usize = 0; let mut contains_star: bool = false; let text_len = text.len();
134 let pattern_len = pattern.len();
135
136 while i < text_len && (!contains_star || star_idx != pattern_len - 1) {
137 if j < pattern_len && pattern[j].is_wildcard() {
138 contains_star = true;
139 star_idx = j;
140 tmp_idx = i;
141 j += 1;
142 } else if j < pattern_len && pattern[j].match_char(&text[i]) {
143 i += 1;
144 j += 1;
145 } else if contains_star {
146 j = star_idx + 1;
147 i = tmp_idx + 1;
148 tmp_idx = i;
149 } else {
150 return false;
151 }
152 }
153
154 while j < pattern_len && pattern[j].is_wildcard() {
155 j += 1;
156 }
157
158 j == pattern_len
159 }
160}
161
162#[cfg(test)]
163pub mod test {
164 use super::*;
165
166 impl std::ops::Add for Pattern {
167 type Output = Pattern;
168 fn add(self, rhs: Self) -> Self::Output {
169 let elems = [self.get_elems(), rhs.get_elems()].concat();
170 Pattern::new(elems)
171 }
172 }
173
174 fn string_map(text: &str) -> Pattern {
176 Pattern::new(text.chars().map(PatternElem::Char))
177 }
178
179 fn star() -> Pattern {
181 Pattern::new(vec![PatternElem::Wildcard])
182 }
183
184 fn empty() -> Pattern {
186 Pattern::new(vec![])
187 }
188
189 #[test]
190 fn test_wildcard_match_basic() {
191 assert!((string_map("foo") + star()).wildcard_match("foo bar"));
193 assert!((star() + string_map("bar")).wildcard_match("foo bar"));
194 assert!((star() + string_map("o b") + star()).wildcard_match("foo bar"));
195 assert!((string_map("f") + star() + string_map(" bar")).wildcard_match("foo bar"));
196 assert!((string_map("f") + star() + star() + string_map("r")).wildcard_match("foo bar"));
197 assert!((star() + string_map("f") + star() + star() + star()).wildcard_match("foo bar"));
198
199 assert!(!(star() + string_map("foo")).wildcard_match("foo bar"));
201 assert!(!(string_map("bar") + star()).wildcard_match("foo bar"));
202 assert!(!(star() + string_map("bo") + star()).wildcard_match("foo bar"));
203 assert!(!(string_map("f") + star() + string_map("br")).wildcard_match("foo bar"));
204 assert!(!(star() + string_map("x") + star() + star() + star()).wildcard_match("foo bar"));
205 assert!(!empty().wildcard_match("foo bar"));
206
207 assert!(empty().wildcard_match(""));
209 assert!(star().wildcard_match(""));
210
211 assert!(!string_map("foo bar").wildcard_match(""));
213
214 assert!(string_map("*").wildcard_match("*"));
216 assert!(star().wildcard_match("*"));
217
218 assert!(!string_map("\u{0000}").wildcard_match("*"));
220 assert!(!string_map(r#"\u{0000}"#).wildcard_match("*"));
221 }
222
223 #[test]
224 fn test_wildcard_match_unicode() {
225 assert!((string_map("y") + star()).wildcard_match("y̆"));
227 assert!(string_map("y̆").wildcard_match("y̆"));
228
229 assert!(!(star() + string_map("p") + star()).wildcard_match("y̆"));
231
232 assert!((star() + string_map("p") + star()).wildcard_match("ḛ̶͑͝x̶͔͛a̵̰̯͛m̴͉̋́p̷̠͂l̵͇̍̔ȩ̶̣͝"));
234 assert!((star() + string_map("a̵̰̯͛m̴͉̋́") + star()).wildcard_match("ḛ̶͑͝x̶͔͛a̵̰̯͛m̴͉̋́p̷̠͂l̵͇̍̔ȩ̶̣͝"));
235
236 assert!(!(string_map("y") + star()).wildcard_match("ḛ̶͑͝x̶͔͛a̵̰̯͛m̴͉̋́p̷̠͂l̵͇̍̔ȩ̶̣͝"));
238 }
239}