1use crate::ParseError;
2
3pub fn filters(directives: &str) -> Filters<'_> {
8 Filters { directives }
9}
10
11#[derive(Debug, Clone)]
13pub struct Filters<'a> {
14 directives: &'a str,
15}
16
17#[derive(Debug, Clone)]
21pub struct Filter<'a> {
22 pub target: &'a str,
23 pub span: Option<SpanFilters<'a>>,
24 pub level: Option<&'a str>,
25}
26
27#[derive(Debug, Clone)]
29pub struct SpanFilters<'a> {
30 directives: &'a str,
31}
32
33#[derive(Debug, Clone)]
37pub struct SpanFilter<'a> {
38 pub name: &'a str,
39 pub fields: Option<FieldFilters<'a>>,
40}
41
42#[derive(Debug, Clone)]
44pub struct FieldFilters<'a> {
45 directives: &'a str,
46}
47
48#[derive(Debug, Clone, PartialEq, Eq)]
50pub struct FieldFilter<'a> {
51 pub name: &'a str,
52 pub value: Option<&'a str>,
53}
54
55#[repr(u8)]
56#[derive(Clone, Copy)]
57enum Syntax {
58 LBrack = b'[',
59 RBrack = b']',
60 LBrace = b'{',
61 RBrace = b'}',
62 Equal = b'=',
63 Comma = b',',
64}
65
66fn find_any_syntax(haystack: &str) -> (usize, Option<Syntax>) {
67 use Syntax::*;
68 haystack
69 .bytes()
70 .enumerate()
71 .find_map(|(i, b)| match b {
72 b'[' => Some((i, LBrack)),
73 b']' => Some((i, RBrack)),
74 b'{' => Some((i, LBrace)),
75 b'}' => Some((i, RBrace)),
76 b'=' => Some((i, Equal)),
77 b',' => Some((i, Comma)),
78 _ => None,
79 })
80 .map_or_else(|| (haystack.len(), None), |(i, c)| (i, Some(c)))
81}
82
83macro_rules! switch_syntax {
85 ($haystack:expr => |$i:ident| {
86 $($($syntax:tt)|+ => $expr:expr),* $(,)?
87 }) => {
88 #[allow(unused_variables)]
89 match find_any_syntax($haystack) {
90 $(($i, $(switch_syntax!(@syntax $syntax))|+) => $expr,)*
91 }
92 };
93
94 (@syntax '[') => (Some(Syntax::LBrack));
95 (@syntax ']') => (Some(Syntax::RBrack));
96 (@syntax '{') => (Some(Syntax::LBrace));
97 (@syntax '}') => (Some(Syntax::RBrace));
98 (@syntax '=') => (Some(Syntax::Equal));
99 (@syntax ',') => (Some(Syntax::Comma));
100 (@syntax % ) => (None);
101}
102
103fn find_syntax(haystack: &str, syntax: Syntax) -> Option<usize> {
104 haystack.bytes().position(move |b| b == syntax as u8)
105}
106
107impl<'a> Filters<'a> {
108 fn err<T>(&mut self) -> Result<T, ParseError> {
109 self.directives = "";
110 Err(ParseError::BadSyntax)
111 }
112
113 fn target(&mut self) -> Result<&'a str, ParseError> {
114 switch_syntax!(self.directives => |i| {
115 ']' | '{' | '}' => self.err(),
120
121 '[' | '=' | ',' | % => {
127 let target = &self.directives[..i];
128 self.directives = &self.directives[i..];
129 Ok(target)
130 },
131 })
132 }
133
134 fn span(&mut self) -> Result<Option<SpanFilters<'a>>, ParseError> {
135 if let Some(stripped) = self.directives.strip_prefix('[') {
137 self.directives = stripped;
138 match find_syntax(self.directives, Syntax::RBrack) {
139 None => self.err(),
140 Some(i) => {
143 let directives = &self.directives[..i];
144 self.directives = &self.directives[i + 1..];
145 Ok(Some(SpanFilters { directives }))
146 }
147 }
148 } else {
149 Ok(None)
150 }
151 }
152
153 fn level(&mut self) -> Result<Option<&'a str>, ParseError> {
154 if self.directives.is_empty() || self.directives.starts_with(',') {
156 return Ok(None);
157 }
158 if let Some(stripped) = self.directives.strip_prefix('=') {
159 self.directives = stripped;
160 } else {
161 return self.err();
162 }
163 switch_syntax!(self.directives => |i| {
164 '[' | ']' | '{' | '}' | '=' => self.err(),
171
172 ',' | % => {
176 let level = &self.directives[..i];
177 self.directives = &self.directives[i..];
178 Ok(Some(level))
179 },
180 })
181 }
182
183 fn comma(&mut self) -> Result<(), ParseError> {
184 if let Some(stripped) = self.directives.strip_prefix(',') {
185 self.directives = stripped;
186 Ok(())
187 } else if self.directives.is_empty() {
188 Ok(())
189 } else {
190 self.err()
191 }
192 }
193}
194
195impl<'a> Iterator for Filters<'a> {
196 type Item = Result<Filter<'a>, ParseError>;
197
198 fn next(&mut self) -> Option<Self::Item> {
199 if self.directives.is_empty() {
200 return None;
201 }
202
203 if self.directives.contains('"') || self.directives.contains('/') {
205 let _ = self.err::<()>();
206 return Some(Err(ParseError::ReservedSyntax));
207 }
208
209 Some((|| {
210 let target = self.target()?;
211 let span = self.span()?;
212 let level = self.level()?;
213 self.comma()?;
214 Ok(Filter {
215 target,
216 span,
217 level,
218 })
219 })())
220 }
221
222 fn size_hint(&self) -> (usize, Option<usize>) {
223 (
224 0,
225 Some(
226 self.directives
227 .as_bytes()
228 .iter()
229 .filter(|&&b| b == b',')
230 .count()
231 + !self.directives.is_empty() as usize,
232 ),
233 )
234 }
235}
236
237impl<'a> SpanFilters<'a> {
238 fn err<T>(&mut self) -> Result<T, ParseError> {
239 self.directives = "";
240 Err(ParseError::BadSyntax)
241 }
242
243 fn name(&mut self) -> Result<&'a str, ParseError> {
244 switch_syntax!(self.directives => |i| {
245 '[' | ']' | '}' | '=' => self.err(),
251
252 '{' | ',' | % => {
257 let name = &self.directives[..i];
258 self.directives = &self.directives[i..];
259 Ok(name)
260 },
261 })
262 }
263
264 fn fields(&mut self) -> Result<Option<FieldFilters<'a>>, ParseError> {
265 if let Some(stripped) = self.directives.strip_prefix('{') {
267 self.directives = stripped;
268 match find_syntax(self.directives, Syntax::RBrace) {
269 None => self.err(),
270 Some(i) => {
273 let directives = &self.directives[..i];
274 self.directives = &self.directives[i + 1..];
275 Ok(Some(FieldFilters { directives }))
276 }
277 }
278 } else {
279 Ok(None)
280 }
281 }
282
283 fn comma(&mut self) -> Result<(), ParseError> {
284 if let Some(stripped) = self.directives.strip_prefix(',') {
285 self.directives = stripped;
286 Ok(())
287 } else if self.directives.is_empty() {
288 Ok(())
289 } else {
290 self.err()
291 }
292 }
293}
294
295impl<'a> Iterator for SpanFilters<'a> {
296 type Item = Result<SpanFilter<'a>, ParseError>;
297
298 fn next(&mut self) -> Option<Self::Item> {
299 if self.directives.is_empty() {
300 return None;
301 }
302
303 if self.directives.contains('"') || self.directives.contains('/') {
305 let _ = self.err::<()>();
306 return Some(Err(ParseError::ReservedSyntax));
307 }
308
309 Some((|| {
310 let name = self.name()?;
311 let fields = self.fields()?;
312 self.comma()?;
313 Ok(SpanFilter { name, fields })
314 })())
315 }
316
317 fn size_hint(&self) -> (usize, Option<usize>) {
318 (
319 0,
320 Some(
321 self.directives
322 .as_bytes()
323 .iter()
324 .filter(|&&b| b == b',')
325 .count()
326 + !self.directives.is_empty() as usize,
327 ),
328 )
329 }
330}
331
332impl<'a> FieldFilters<'a> {
333 fn err<T>(&mut self) -> Result<T, ParseError> {
334 self.directives = "";
335 Err(ParseError::BadSyntax)
336 }
337
338 fn name(&mut self) -> Result<&'a str, ParseError> {
339 switch_syntax!(self.directives => |i| {
340 '[' | ']' | '{' | '}' => self.err(),
346
347 '=' | ',' | % => {
352 let name = &self.directives[..i];
353 self.directives = &self.directives[i..];
354 Ok(name)
355 },
356 })
357 }
358
359 fn value(&mut self) -> Result<Option<&'a str>, ParseError> {
360 if let Some(stripped) = self.directives.strip_prefix('=') {
362 self.directives = stripped;
363 } else {
364 return Ok(None);
365 }
366 switch_syntax!(self.directives => |i| {
367 '[' | ']' | '{' | '}' | '=' => self.err(),
374
375 ',' | % => {
379 let value = &self.directives[..i];
380 self.directives = &self.directives[i..];
381 Ok(Some(value))
382 },
383 })
384 }
385
386 fn comma(&mut self) -> Result<(), ParseError> {
387 if let Some(stripped) = self.directives.strip_prefix(',') {
388 self.directives = stripped;
389 Ok(())
390 } else if self.directives.is_empty() {
391 Ok(())
392 } else {
393 self.err()
394 }
395 }
396}
397
398impl<'a> Iterator for FieldFilters<'a> {
399 type Item = Result<FieldFilter<'a>, ParseError>;
400
401 fn next(&mut self) -> Option<Self::Item> {
402 if self.directives.is_empty() {
403 return None;
404 }
405
406 if self.directives.contains('"') || self.directives.contains('/') {
408 let _ = self.err::<()>();
409 return Some(Err(ParseError::ReservedSyntax));
410 }
411
412 Some((|| {
413 let name = self.name()?;
414 let value = self.value()?;
415 self.comma()?;
416 Ok(FieldFilter { name, value })
417 })())
418 }
419
420 fn size_hint(&self) -> (usize, Option<usize>) {
421 (
422 0,
423 Some(
424 self.directives
425 .as_bytes()
426 .iter()
427 .filter(|&&b| b == b',')
428 .count()
429 + !self.directives.is_empty() as usize,
430 ),
431 )
432 }
433}