use std::{ops, sync::Arc};
use super::MatchMaker;
use crate::path::Path;
#[derive(Debug, Clone)]
pub enum Combinator {
Matcher(Arc<dyn MatchMaker + Sync>),
Not(Box<Combinator>),
And(Box<Combinator>, Box<Combinator>),
Or(Box<Combinator>, Box<Combinator>),
}
impl MatchMaker for Combinator {
fn match_path(&self, path: &Path) -> bool {
match self {
Self::Matcher(matcher) => matcher.match_path(path),
Self::Not(combinator) => !combinator.match_path(path),
Self::Or(first, second) => first.match_path(path) || second.match_path(path),
Self::And(first, second) => first.match_path(path) && second.match_path(path),
}
}
}
impl Combinator {
pub fn new(matcher: impl MatchMaker + 'static + Sync) -> Self {
Self::Matcher(Arc::new(matcher))
}
}
impl ops::Not for Combinator {
type Output = Self;
fn not(self) -> Self {
Self::Not(Box::new(self))
}
}
impl ops::BitAnd for Combinator {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self::And(Box::new(self), Box::new(rhs))
}
}
impl ops::BitOr for Combinator {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self::Or(Box::new(self), Box::new(rhs))
}
}
#[cfg(test)]
mod tests {
use super::{Combinator, MatchMaker};
use crate::{
matcher::{Depth, Simple},
path::Path,
};
use std::convert::TryFrom;
#[test]
fn wrapper() {
let comb = Combinator::new(Depth::new(1, Some(1)));
assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap()));
assert!(comb.match_path(&Path::try_from(r#"[0]"#).unwrap()));
}
#[test]
fn not() {
let comb = !Combinator::new(Depth::new(1, None));
assert!(!comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#"[0]"#).unwrap()));
assert!(comb.match_path(&Path::try_from(r#""#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#"{"People"}[0]"#).unwrap()));
}
#[test]
fn and() {
let comb = Combinator::new(Depth::new(1, Some(1)))
& Combinator::new(Simple::new(r#"{}"#).unwrap());
assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#"[0]"#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#""#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#"{"People"}[0]"#).unwrap()));
}
#[test]
fn or() {
let comb = Combinator::new(Depth::new(1, Some(1)))
| Combinator::new(Simple::new(r#"{}[0]"#).unwrap());
assert!(comb.match_path(&Path::try_from(r#"{"People"}"#).unwrap()));
assert!(comb.match_path(&Path::try_from(r#"[0]"#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#""#).unwrap()));
assert!(comb.match_path(&Path::try_from(r#"{"People"}[0]"#).unwrap()));
assert!(!comb.match_path(&Path::try_from(r#"{"People"}[1]"#).unwrap()));
}
#[test]
fn complex() {
let comb1 = Combinator::new(Depth::new(1, Some(1)))
| Combinator::new(Simple::new(r#"{}[0]"#).unwrap());
let comb2 = Combinator::new(Depth::new(2, Some(2)))
| Combinator::new(Simple::new(r#"{}"#).unwrap());
let comb3 = !comb1 & comb2;
assert!(!comb3.match_path(&Path::try_from(r#"{"People"}"#).unwrap()));
assert!(!comb3.match_path(&Path::try_from(r#"[0]"#).unwrap()));
assert!(!comb3.match_path(&Path::try_from(r#""#).unwrap()));
assert!(!comb3.match_path(&Path::try_from(r#"{"People"}[0]"#).unwrap()));
assert!(comb3.match_path(&Path::try_from(r#"{"People"}[1]"#).unwrap()));
}
}