streamson_lib/matcher/
simple.rs

1//! Simple path matcher
2
3use super::Matcher;
4use crate::{
5    error,
6    path::{Element, Path},
7    streamer::ParsedKind,
8};
9use std::str::FromStr;
10
11/// StringMatch to match array elements
12type StringMatch = Option<String>;
13
14/// IndexMatch to match array elements
15#[derive(Debug, Clone, PartialEq)]
16struct IndexMatch(Vec<(Option<usize>, Option<usize>)>);
17
18impl FromStr for IndexMatch {
19    type Err = error::Matcher;
20
21    fn from_str(path: &str) -> Result<Self, Self::Err> {
22        let splitted = path.split(',');
23        let mut result = vec![];
24
25        for item_str in splitted {
26            let inner_splitted: Vec<_> = item_str.split('-').collect();
27            match inner_splitted.len() {
28                1 => {
29                    let index: usize = inner_splitted[0]
30                        .parse()
31                        .map_err(|_| error::Matcher::Parse(inner_splitted[0].to_string()))?;
32                    result.push((Some(index), Some(index + 1)));
33                }
34                2 => {
35                    let start_opt: Option<usize> =
36                        if inner_splitted[0].is_empty() {
37                            None
38                        } else {
39                            Some(inner_splitted[0].parse().map_err(|_| {
40                                error::Matcher::Parse(inner_splitted[0].to_string())
41                            })?)
42                        };
43                    let end_opt: Option<usize> =
44                        if inner_splitted[1].is_empty() {
45                            None
46                        } else {
47                            Some(inner_splitted[1].parse().map_err(|_| {
48                                error::Matcher::Parse(inner_splitted[1].to_string())
49                            })?)
50                        };
51                    match (start_opt, end_opt) {
52                        (Some(start), Some(end)) => {
53                            if start >= end {
54                                return Err(error::Matcher::Parse(item_str.to_string()));
55                            }
56                        }
57                        (None, None) => return Err(error::Matcher::Parse(item_str.to_string())),
58                        _ => {}
59                    }
60                    result.push((start_opt, end_opt));
61                }
62                _ => return Err(error::Matcher::Parse(item_str.to_string())),
63            }
64        }
65
66        Ok(Self(result))
67    }
68}
69
70/// SimplePath path matcher
71#[derive(Debug, Clone, PartialEq)]
72enum SimplePathElement {
73    Key(StringMatch),
74    Index(IndexMatch),
75    WildCardSingle,
76    WildCardAny,
77}
78
79impl PartialEq<Element> for SimplePathElement {
80    fn eq(&self, other: &Element) -> bool {
81        match &self {
82            SimplePathElement::Key(None) => other.is_key(),
83            SimplePathElement::Key(Some(key)) => {
84                if let Element::Key(pkey) = other {
85                    key == pkey
86                } else {
87                    false
88                }
89            }
90            SimplePathElement::Index(idx_matches) => {
91                if let Element::Index(idx) = other {
92                    if idx_matches.0.is_empty() {
93                        true
94                    } else {
95                        idx_matches.0.iter().any(|(min_opt, max_opt)| {
96                            if let Some(max) = max_opt {
97                                if idx >= max {
98                                    return false;
99                                }
100                            }
101                            if let Some(min) = min_opt {
102                                if idx < min {
103                                    return false;
104                                }
105                            }
106                            true
107                        })
108                    }
109                } else {
110                    false
111                }
112            }
113            SimplePathElement::WildCardAny => true,
114            SimplePathElement::WildCardSingle => true,
115        }
116    }
117}
118
119/// Based on orignal path format {"People"}[0]{"Height"}
120///
121/// It matches {"People"}[0]{"Height"} - height of the first person
122/// It matches {"People"}[]{"Height"} - matches the height of all people
123/// It matches {"People"}[0]{} - matches all attributes of the first person
124#[derive(Default, Debug, Clone)]
125pub struct Simple {
126    path: Vec<SimplePathElement>,
127}
128
129#[derive(Debug, PartialEq)]
130enum SimpleMatcherStates {
131    ElementStart,
132    Array,
133    ObjectStart,
134    Object(bool),
135    ObjectEnd,
136}
137
138impl Matcher for Simple {
139    fn match_path(&self, path: &Path, _kind: ParsedKind) -> bool {
140        // If no AnyWildcard present and length differs
141        // return false right away
142        if !self
143            .path
144            .iter()
145            .any(|e| matches!(e, SimplePathElement::WildCardAny))
146            && path.depth() != self.path.len()
147        {
148            return false;
149        }
150
151        let path = path.get_path();
152
153        // first is element idx, second path index
154        // starting at the beginning
155        let mut indexes = vec![(0, 0)];
156
157        while !indexes.is_empty() {
158            let (spath_idx, path_idx) = indexes.pop().unwrap();
159
160            if spath_idx == self.path.len() && path_idx == path.len() {
161                // all matched
162                return true;
163            }
164
165            if spath_idx >= self.path.len() {
166                // matcher lenght reached => fallback
167                continue;
168            }
169
170            // match indexes
171            match self.path[spath_idx] {
172                SimplePathElement::WildCardAny => {
173                    indexes.push((spath_idx + 1, path_idx)); // wildcard over
174                    if path_idx < path.len() {
175                        indexes.push((spath_idx, path_idx + 1)); // wildcard matched
176                    }
177                }
178                _ => {
179                    if path_idx >= path.len() {
180                        continue;
181                    } else if self.path[spath_idx] == path[path_idx] {
182                        indexes.push((spath_idx + 1, path_idx + 1));
183                    } else {
184                        continue;
185                    }
186                }
187            }
188        }
189
190        false
191    }
192}
193
194impl FromStr for Simple {
195    type Err = error::Matcher;
196
197    fn from_str(path: &str) -> Result<Self, Self::Err> {
198        let mut state = SimpleMatcherStates::ElementStart;
199        let mut buffer = vec![];
200        let mut result = vec![];
201
202        for chr in path.chars() {
203            state = match state {
204                SimpleMatcherStates::ElementStart => match chr {
205                    '[' => SimpleMatcherStates::Array,
206                    '{' => SimpleMatcherStates::ObjectStart,
207                    '?' => {
208                        result.push(SimplePathElement::WildCardSingle);
209                        SimpleMatcherStates::ElementStart
210                    }
211                    '*' => {
212                        result.push(SimplePathElement::WildCardAny);
213                        SimpleMatcherStates::ElementStart
214                    }
215                    _ => {
216                        return Err(error::Matcher::Parse(path.to_string()));
217                    }
218                },
219                SimpleMatcherStates::Array => match chr {
220                    ']' => {
221                        let new_element = if buffer.is_empty() {
222                            SimplePathElement::Index(IndexMatch(vec![]))
223                        } else {
224                            SimplePathElement::Index(
225                                buffer
226                                    .drain(..)
227                                    .collect::<String>()
228                                    .parse()
229                                    .map_err(|_| error::Matcher::Parse(path.to_string()))?,
230                            )
231                        };
232                        result.push(new_element);
233                        SimpleMatcherStates::ElementStart
234                    }
235                    '0'..='9' | '-' | ',' => {
236                        buffer.push(chr);
237                        SimpleMatcherStates::Array
238                    }
239                    _ => {
240                        return Err(error::Matcher::Parse(path.to_string()));
241                    }
242                },
243                SimpleMatcherStates::ObjectStart => match chr {
244                    '}' => {
245                        result.push(SimplePathElement::Key(None));
246                        SimpleMatcherStates::ElementStart
247                    }
248                    '"' => SimpleMatcherStates::Object(false),
249                    _ => {
250                        return Err(error::Matcher::Parse(path.to_string()));
251                    }
252                },
253                SimpleMatcherStates::Object(false) => match chr {
254                    '"' => SimpleMatcherStates::ObjectEnd,
255                    '\\' => {
256                        buffer.push(chr);
257                        SimpleMatcherStates::Object(true)
258                    }
259                    _ => {
260                        buffer.push(chr);
261                        SimpleMatcherStates::Object(false)
262                    }
263                },
264                SimpleMatcherStates::Object(true) => {
265                    buffer.push(chr);
266                    SimpleMatcherStates::Object(false)
267                }
268                SimpleMatcherStates::ObjectEnd => match chr {
269                    '}' => {
270                        result.push(SimplePathElement::Key(Some(buffer.drain(..).collect())));
271                        SimpleMatcherStates::ElementStart
272                    }
273                    _ => {
274                        return Err(error::Matcher::Parse(path.to_string()));
275                    }
276                },
277            }
278        }
279        if state == SimpleMatcherStates::ElementStart {
280            Ok(Self { path: result })
281        } else {
282            Err(error::Matcher::Parse(path.to_string()))
283        }
284    }
285}
286
287impl Simple {
288    /// Creates new simple matcher
289    ///
290    /// # Arguments
291    /// * `path_expr` - path expression (e.g. `{"users"}[0]{"addresses"}{}`)
292    pub fn new(path_expr: &str) -> Result<Self, error::Matcher> {
293        Self::from_str(path_expr)
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::{Matcher, Simple};
300    use crate::{path::Path, streamer::ParsedKind};
301    use std::{convert::TryFrom, str::FromStr};
302
303    #[test]
304    fn exact() {
305        let simple = Simple::from_str(r#"{"People"}[0]{"Height"}"#).unwrap();
306
307        assert!(!simple.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
308        assert!(!simple.match_path(
309            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
310            ParsedKind::Obj
311        ));
312        assert!(!simple.match_path(
313            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
314            ParsedKind::Num
315        ));
316        assert!(simple.match_path(
317            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
318            ParsedKind::Num
319        ));
320        assert!(!simple.match_path(
321            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
322            ParsedKind::Obj
323        ));
324        assert!(!simple.match_path(
325            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
326            ParsedKind::Num
327        ));
328        assert!(!simple.match_path(
329            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
330            ParsedKind::Num
331        ));
332    }
333
334    #[test]
335    fn wild_array() {
336        let simple = Simple::from_str(r#"{"People"}[]{"Height"}"#).unwrap();
337
338        assert!(!simple.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
339        assert!(!simple.match_path(
340            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
341            ParsedKind::Obj
342        ));
343        assert!(!simple.match_path(
344            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
345            ParsedKind::Num
346        ));
347        assert!(simple.match_path(
348            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
349            ParsedKind::Num
350        ));
351        assert!(!simple.match_path(
352            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
353            ParsedKind::Obj
354        ));
355        assert!(!simple.match_path(
356            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
357            ParsedKind::Num
358        ));
359        assert!(simple.match_path(
360            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
361            ParsedKind::Num
362        ));
363    }
364
365    #[test]
366    fn ranges_array() {
367        let simple = Simple::from_str(r#"{"People"}[3,4-5,5,-3,6-]{"Height"}"#).unwrap();
368
369        assert!(simple.match_path(
370            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
371            ParsedKind::Num
372        ));
373        assert!(simple.match_path(
374            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
375            ParsedKind::Num
376        ));
377        assert!(simple.match_path(
378            &Path::try_from(r#"{"People"}[2]{"Height"}"#).unwrap(),
379            ParsedKind::Num
380        ));
381        assert!(simple.match_path(
382            &Path::try_from(r#"{"People"}[3]{"Height"}"#).unwrap(),
383            ParsedKind::Num
384        ));
385        assert!(simple.match_path(
386            &Path::try_from(r#"{"People"}[4]{"Height"}"#).unwrap(),
387            ParsedKind::Num
388        ));
389        assert!(simple.match_path(
390            &Path::try_from(r#"{"People"}[5]{"Height"}"#).unwrap(),
391            ParsedKind::Num
392        ));
393        assert!(simple.match_path(
394            &Path::try_from(r#"{"People"}[6]{"Height"}"#).unwrap(),
395            ParsedKind::Num
396        ));
397    }
398
399    #[test]
400    fn wild_object() {
401        let simple = Simple::from_str(r#"{"People"}[0]{}"#).unwrap();
402
403        assert!(!simple.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
404        assert!(!simple.match_path(
405            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
406            ParsedKind::Obj
407        ));
408        assert!(simple.match_path(
409            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
410            ParsedKind::Num
411        ));
412        assert!(simple.match_path(
413            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
414            ParsedKind::Num
415        ));
416        assert!(!simple.match_path(
417            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
418            ParsedKind::Obj
419        ));
420        assert!(!simple.match_path(
421            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
422            ParsedKind::Num
423        ));
424        assert!(!simple.match_path(
425            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
426            ParsedKind::Num
427        ));
428    }
429
430    #[test]
431    fn object_escapes() {
432        let simple = Simple::from_str(r#"{"People"}[0]{"\""}"#).unwrap();
433        assert!(simple.match_path(
434            &Path::try_from(r#"{"People"}[0]{"\""}"#).unwrap(),
435            ParsedKind::Num
436        ));
437        assert!(!simple.match_path(
438            &Path::try_from(r#"{"People"}[0]{""}"#).unwrap(),
439            ParsedKind::Num
440        ));
441        assert!(!simple.match_path(
442            &Path::try_from(r#"{"People"}[0]{"\"x"}"#).unwrap(),
443            ParsedKind::Num
444        ));
445        assert!(!simple.match_path(
446            &Path::try_from(r#"{"People"}[0]{"y\""}"#).unwrap(),
447            ParsedKind::Num
448        ));
449    }
450
451    #[test]
452    fn wild_object_escapes() {
453        let simple = Simple::from_str(r#"{"People"}[0]{}"#).unwrap();
454        assert!(simple.match_path(
455            &Path::try_from(r#"{"People"}[0]{"O\"ll"}"#).unwrap(),
456            ParsedKind::Num
457        ));
458        assert!(simple.match_path(
459            &Path::try_from(r#"{"People"}[0]{"O\\\"ll"}"#).unwrap(),
460            ParsedKind::Num
461        ));
462    }
463
464    #[test]
465    fn parse() {
466        assert!(Simple::from_str(r#""#).is_ok());
467        assert!(Simple::from_str(r#"{}"#).is_ok());
468        assert!(Simple::from_str(r#"{}[3]"#).is_ok());
469        assert!(Simple::from_str(r#"{"xx"}[]"#).is_ok());
470        assert!(Simple::from_str(r#"{}[]"#).is_ok());
471        assert!(Simple::from_str(r#"{"š𐍈€"}"#).is_ok());
472        assert!(Simple::from_str(r#"{"\""}"#).is_ok());
473        assert!(Simple::from_str(r#"[1,2,8,3-,-2,2-3]"#).is_ok());
474        assert!(Simple::from_str(r#"?"#).is_ok());
475        assert!(Simple::from_str(r#"????"#).is_ok());
476        assert!(Simple::from_str(r#"?{}[1]?{"xx"}"#).is_ok());
477        assert!(Simple::from_str(r#"*"#).is_ok());
478        assert!(Simple::from_str(r#"****"#).is_ok());
479        assert!(Simple::from_str(r#"*{}[1]**{"xx"}*"#).is_ok());
480    }
481
482    #[test]
483    fn parse_error() {
484        assert!(Simple::from_str(r#"{"People""#).is_err());
485        assert!(Simple::from_str(r#"[}"#).is_err());
486        assert!(Simple::from_str(r#"{"People}"#).is_err());
487        assert!(Simple::from_str(r#"{"š𐍈€""#).is_err());
488        assert!(Simple::from_str(r#"[1,2,8,3-,-2,-]"#).is_err());
489        assert!(Simple::from_str(r#"[3-3]"#).is_err());
490        assert!(Simple::from_str(r#"[,2,8]"#).is_err());
491        assert!(Simple::from_str(r#"[2,8,]"#).is_err());
492    }
493
494    #[test]
495    fn single_wild() {
496        let simple = Simple::from_str(r#"?[0]{"range"}?"#).unwrap();
497
498        assert!(simple.match_path(
499            &Path::try_from(r#"[1][0]{"range"}{"from_home"}"#).unwrap(),
500            ParsedKind::Num
501        ));
502        assert!(simple.match_path(
503            &Path::try_from(r#"{"People"}[0]{"range"}[1]"#).unwrap(),
504            ParsedKind::Num
505        ));
506        assert!(!simple.match_path(
507            &Path::try_from(r#"[0]{"range"}{"from_home"}"#).unwrap(),
508            ParsedKind::Num
509        ));
510        assert!(!simple.match_path(
511            &Path::try_from(r#"{"People"}[0]{"range"}"#).unwrap(),
512            ParsedKind::Arr
513        ));
514        assert!(!simple.match_path(
515            &Path::try_from(r#"{"People"}[1]{"range"}[1]"#).unwrap(),
516            ParsedKind::Num
517        ));
518        assert!(!simple.match_path(
519            &Path::try_from(r#"[1][0]{"other"}{"from_home"}"#).unwrap(),
520            ParsedKind::Num
521        ));
522    }
523
524    #[test]
525    fn any_wild() {
526        let simple = Simple::from_str(r#"*[0]*{"range"}**"#).unwrap();
527
528        assert!(simple.match_path(&Path::try_from(r#"[0]{"range"}"#).unwrap(), ParsedKind::Obj));
529        assert!(simple.match_path(
530            &Path::try_from(r#"[1][0]{"range"}{"from_home"}"#).unwrap(),
531            ParsedKind::Obj
532        ));
533        assert!(simple.match_path(
534            &Path::try_from(r#"{"another"}[1][0]{"range"}{"from_home"}[2]"#).unwrap(),
535            ParsedKind::Obj
536        ));
537        assert!(simple.match_path(
538            &Path::try_from(r#"[0][2]{"range"}"#).unwrap(),
539            ParsedKind::Obj
540        ));
541        assert!(simple.match_path(
542            &Path::try_from(r#"[0]{"middle"}{"range"}"#).unwrap(),
543            ParsedKind::Obj
544        ));
545        assert!(!simple.match_path(&Path::try_from(r#"[1]{"range"}"#).unwrap(), ParsedKind::Obj));
546        assert!(!simple.match_path(&Path::try_from(r#"[0]{"other"}"#).unwrap(), ParsedKind::Obj));
547    }
548}