streamson_lib/matcher/
depth.rs

1//! Depth path matcher
2
3use std::str::FromStr;
4
5use super::Matcher;
6use crate::{error, path::Path, streamer::ParsedKind};
7
8/// Based on actual path depth
9///
10/// Path is matched when path depth is higher or equal min and lower or equal max (optional)
11#[derive(Default, Debug, Clone)]
12pub struct Depth {
13    min: usize,
14    max: Option<usize>,
15}
16
17impl Depth {
18    /// Creates new depth matcher
19    ///
20    /// # Arguments
21    /// * `min` - minimal depth (lower won't be matched)
22    /// * `max` - maximal depth - optional (higher won't be matched)
23    pub fn new(min: usize, max: Option<usize>) -> Self {
24        Self { min, max }
25    }
26}
27
28impl Matcher for Depth {
29    fn match_path(&self, path: &Path, _kind: ParsedKind) -> bool {
30        let depth = path.depth();
31        if let Some(max) = self.max {
32            self.min <= depth && depth <= max
33        } else {
34            self.min <= depth
35        }
36    }
37}
38
39impl FromStr for Depth {
40    type Err = error::Matcher;
41    fn from_str(s: &str) -> Result<Self, Self::Err> {
42        let splitted: Vec<&str> = s.splitn(2, '-').collect();
43        match splitted.len() {
44            1 => match splitted[0].parse() {
45                Ok(start) => Ok(Self::new(start, Some(start))),
46                Err(_) => Err(error::Matcher::Parse(s.into())),
47            },
48            2 => match (splitted[0].parse(), splitted[1].parse()) {
49                (Ok(start), Ok(end)) => {
50                    if start > end {
51                        Err(error::Matcher::Parse(s.into()))
52                    } else {
53                        Ok(Self::new(start, Some(end)))
54                    }
55                }
56                (Ok(start), _) if splitted[1].is_empty() => Ok(Self::new(start, None)),
57                _ => Err(error::Matcher::Parse(s.into())),
58            },
59            _ => Err(error::Matcher::Parse(s.into())),
60        }
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::{Depth, Matcher};
67    use crate::{path::Path, streamer::ParsedKind};
68    use std::{convert::TryFrom, str::FromStr};
69
70    #[test]
71    fn match_path() {
72        let depth = Depth::from_str("2-").unwrap();
73
74        assert!(!depth.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
75        assert!(depth.match_path(
76            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
77            ParsedKind::Obj
78        ));
79        assert!(depth.match_path(
80            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
81            ParsedKind::Num
82        ));
83        assert!(depth.match_path(
84            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
85            ParsedKind::Num
86        ));
87        assert!(depth.match_path(
88            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
89            ParsedKind::Obj
90        ));
91        assert!(depth.match_path(
92            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
93            ParsedKind::Num
94        ));
95        assert!(depth.match_path(
96            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
97            ParsedKind::Num
98        ));
99
100        let depth: Depth = "2-2".parse().unwrap();
101        assert!(!depth.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
102        assert!(depth.match_path(
103            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
104            ParsedKind::Obj
105        ));
106        assert!(!depth.match_path(
107            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
108            ParsedKind::Num
109        ));
110        assert!(!depth.match_path(
111            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
112            ParsedKind::Num
113        ));
114        assert!(depth.match_path(
115            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
116            ParsedKind::Obj
117        ));
118        assert!(!depth.match_path(
119            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
120            ParsedKind::Num
121        ));
122        assert!(!depth.match_path(
123            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
124            ParsedKind::Num
125        ));
126
127        let depth: Depth = "2".parse().unwrap();
128        assert!(!depth.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
129        assert!(depth.match_path(
130            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
131            ParsedKind::Obj
132        ));
133        assert!(!depth.match_path(
134            &Path::try_from(r#"{"People"}[0]{"Age"}"#).unwrap(),
135            ParsedKind::Num
136        ));
137        assert!(!depth.match_path(
138            &Path::try_from(r#"{"People"}[0]{"Height"}"#).unwrap(),
139            ParsedKind::Num
140        ));
141        assert!(depth.match_path(
142            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
143            ParsedKind::Obj
144        ));
145        assert!(!depth.match_path(
146            &Path::try_from(r#"{"People"}[1]{"Age"}"#).unwrap(),
147            ParsedKind::Num
148        ));
149        assert!(!depth.match_path(
150            &Path::try_from(r#"{"People"}[1]{"Height"}"#).unwrap(),
151            ParsedKind::Num
152        ));
153    }
154
155    #[test]
156    fn depth_parse() {
157        assert!(Depth::from_str("").is_err());
158        assert!(Depth::from_str("-").is_err());
159        assert!(Depth::from_str("4").is_ok());
160        assert!(Depth::from_str("4-").is_ok());
161        assert!(Depth::from_str("4-5").is_ok());
162        assert!(Depth::from_str("4-4").is_ok());
163        assert!(Depth::from_str("4-3").is_err());
164        assert!(Depth::from_str("4-3x").is_err());
165    }
166}