use std::collections::HashMap;
use bc_envelope::prelude::*;
use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct NotPattern(Box<Pattern>);
impl NotPattern {
pub fn new(pattern: Pattern) -> Self { NotPattern(Box::new(pattern)) }
pub fn pattern(&self) -> &Pattern { &self.0 }
}
impl Matcher for NotPattern {
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
let paths = if !self.pattern().matches(haystack) {
vec![vec![haystack.clone()]]
} else {
vec![]
};
(paths, HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
literals: &mut Vec<Pattern>,
_captures: &mut Vec<String>,
) {
let idx = literals.len();
literals.push(self.pattern().clone());
code.push(Instr::NotMatch { pat_idx: idx });
}
}
impl std::fmt::Display for NotPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "!{}", self.pattern())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pattern::Pattern;
#[test]
fn test_not_pattern() {
let envelope = Envelope::new("test");
let text_pattern = Pattern::text("test");
assert!(text_pattern.matches(&envelope));
let not_pattern = NotPattern::new(text_pattern);
assert!(!not_pattern.matches(&envelope));
assert!(
!Pattern::not_matching(Pattern::text("test")).matches(&envelope)
);
let not_matching_pattern = Pattern::text("different");
assert!(!not_matching_pattern.matches(&envelope));
let double_not = NotPattern::new(not_matching_pattern);
assert!(double_not.matches(&envelope));
assert!(
Pattern::not_matching(Pattern::text("different"))
.matches(&envelope)
);
}
#[test]
fn test_not_pattern_paths() {
let envelope = Envelope::new("test");
let not_matching_pattern = Pattern::text("different");
let not_pattern = NotPattern::new(not_matching_pattern);
let paths = not_pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0].len(), 1);
assert_eq!(paths[0][0].extract_subject::<String>().unwrap(), "test");
let matching_pattern = Pattern::text("test");
let not_pattern = NotPattern::new(matching_pattern);
let paths = not_pattern.paths(&envelope);
assert_eq!(paths.len(), 0);
}
#[test]
fn test_not_pattern_display() {
let pattern = NotPattern::new(Pattern::text("test"));
assert_eq!(pattern.to_string(), r#"!"test""#);
let pattern = NotPattern::new(Pattern::any());
assert_eq!(pattern.to_string(), "!*");
}
}