streamson_lib/matcher/
combinator.rs

1//! Combinator path matcher
2
3use std::{ops, sync::Arc};
4
5use super::Matcher;
6use crate::{path::Path, streamer::ParsedKind};
7
8#[derive(Debug, Clone)]
9/// Combines several matches together
10///
11/// It implements normal boolean algebra
12/// * `! comb`  will negate the combinator
13/// * `comb1 & comb2` both should pass
14/// * `comb1 | comb2` at least one should pass
15pub enum Combinator {
16    /// Represents the actual underlying matcher
17    Matcher(Arc<dyn Matcher + Sync>),
18    /// Negates the expression
19    Not(Box<Combinator>),
20    /// Both expressions should be valid
21    And(Box<Combinator>, Box<Combinator>),
22    /// At least one of the expressions should be valid
23    Or(Box<Combinator>, Box<Combinator>),
24}
25
26impl Matcher for Combinator {
27    fn match_path(&self, path: &Path, kind: ParsedKind) -> bool {
28        match self {
29            Self::Matcher(matcher) => matcher.match_path(path, kind),
30            Self::Not(combinator) => !combinator.match_path(path, kind),
31            Self::Or(first, second) => {
32                first.match_path(path, kind) || second.match_path(path, kind)
33            }
34            Self::And(first, second) => {
35                first.match_path(path, kind) && second.match_path(path, kind)
36            }
37        }
38    }
39}
40
41impl Combinator {
42    /// Creates a new matcher combinator
43    ///
44    /// # Arguments
45    /// * `matcher` - matcher to be wrapped
46    pub fn new(matcher: impl Matcher + 'static + Sync) -> Self {
47        Self::Matcher(Arc::new(matcher))
48    }
49}
50
51impl ops::Not for Combinator {
52    type Output = Self;
53
54    fn not(self) -> Self {
55        Self::Not(Box::new(self))
56    }
57}
58
59impl ops::BitAnd for Combinator {
60    type Output = Self;
61    fn bitand(self, rhs: Self) -> Self {
62        Self::And(Box::new(self), Box::new(rhs))
63    }
64}
65
66impl ops::BitOr for Combinator {
67    type Output = Self;
68    fn bitor(self, rhs: Self) -> Self {
69        Self::Or(Box::new(self), Box::new(rhs))
70    }
71}
72
73#[cfg(test)]
74mod tests {
75    use super::{Combinator, Matcher};
76    use crate::{
77        matcher::{Depth, Simple},
78        path::Path,
79        streamer::ParsedKind,
80    };
81    use std::convert::TryFrom;
82
83    #[test]
84    fn wrapper() {
85        let comb = Combinator::new(Depth::new(1, Some(1)));
86        assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
87        assert!(comb.match_path(&Path::try_from(r#"[0]"#).unwrap(), ParsedKind::Obj));
88    }
89
90    #[test]
91    fn not() {
92        let comb = !Combinator::new(Depth::new(1, None));
93        assert!(!comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
94        assert!(!comb.match_path(&Path::try_from(r#"[0]"#).unwrap(), ParsedKind::Obj));
95        assert!(comb.match_path(&Path::try_from(r#""#).unwrap(), ParsedKind::Obj));
96        assert!(!comb.match_path(
97            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
98            ParsedKind::Obj
99        ));
100    }
101
102    #[test]
103    fn and() {
104        let comb = Combinator::new(Depth::new(1, Some(1)))
105            & Combinator::new(Simple::new(r#"{}"#).unwrap());
106        assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
107        assert!(!comb.match_path(&Path::try_from(r#"[0]"#).unwrap(), ParsedKind::Obj));
108        assert!(!comb.match_path(&Path::try_from(r#""#).unwrap(), ParsedKind::Obj));
109        assert!(!comb.match_path(
110            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
111            ParsedKind::Obj
112        ));
113    }
114
115    #[test]
116    fn or() {
117        let comb = Combinator::new(Depth::new(1, Some(1)))
118            | Combinator::new(Simple::new(r#"{}[0]"#).unwrap());
119        assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
120        assert!(comb.match_path(&Path::try_from(r#"[0]"#).unwrap(), ParsedKind::Obj));
121        assert!(!comb.match_path(&Path::try_from(r#""#).unwrap(), ParsedKind::Obj));
122        assert!(comb.match_path(
123            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
124            ParsedKind::Obj
125        ));
126        assert!(!comb.match_path(
127            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
128            ParsedKind::Obj
129        ));
130    }
131
132    #[test]
133    fn complex() {
134        let comb1 = Combinator::new(Depth::new(1, Some(1)))
135            | Combinator::new(Simple::new(r#"{}[0]"#).unwrap());
136        let comb2 = Combinator::new(Depth::new(2, Some(2)))
137            | Combinator::new(Simple::new(r#"{}"#).unwrap());
138        let comb3 = !comb1 & comb2;
139
140        assert!(!comb3.match_path(&Path::try_from(r#"{"People"}"#).unwrap(), ParsedKind::Arr));
141        assert!(!comb3.match_path(&Path::try_from(r#"[0]"#).unwrap(), ParsedKind::Obj));
142        assert!(!comb3.match_path(&Path::try_from(r#""#).unwrap(), ParsedKind::Obj));
143        assert!(!comb3.match_path(
144            &Path::try_from(r#"{"People"}[0]"#).unwrap(),
145            ParsedKind::Obj
146        ));
147        assert!(comb3.match_path(
148            &Path::try_from(r#"{"People"}[1]"#).unwrap(),
149            ParsedKind::Obj
150        ));
151    }
152}