use dcbor::prelude::*;
use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
#[derive(Debug, Clone, 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(&self, haystack: &CBOR) -> Vec<Path> {
if !self.pattern().matches(haystack) {
vec![vec![haystack.clone()]]
} else {
vec![]
}
}
fn paths_with_captures(
&self,
haystack: &CBOR,
) -> (Vec<Path>, std::collections::HashMap<String, Vec<Path>>) {
let (inner_paths, _inner_captures) =
self.pattern().paths_with_captures(haystack);
if inner_paths.is_empty() {
(
vec![vec![haystack.clone()]],
std::collections::HashMap::new(),
)
} else {
(vec![], std::collections::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 });
}
fn collect_capture_names(&self, names: &mut Vec<String>) {
let _ = names; }
fn is_complex(&self) -> bool {
true
}
}
impl std::fmt::Display for NotPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.pattern().is_complex() {
write!(f, "!({})", self.pattern())
} else {
write!(f, "!{}", self.pattern())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_not_pattern_display() {
let not_pattern = NotPattern::new(Pattern::number(5));
assert_eq!(not_pattern.to_string(), "!5");
}
#[test]
fn test_not_pattern_display_complex() {
let and_pattern =
Pattern::Meta(crate::pattern::meta::MetaPattern::And(
crate::pattern::meta::AndPattern::new(vec![
Pattern::number(5),
Pattern::text("hello"),
]),
));
let not_pattern = NotPattern::new(and_pattern);
assert_eq!(not_pattern.to_string(), r#"!(5 & "hello")"#);
}
#[test]
fn test_not_pattern_matches_when_inner_fails() {
let pattern = NotPattern::new(Pattern::number(5));
let cbor_42 = CBOR::from(42); assert!(pattern.matches(&cbor_42));
let cbor_text = CBOR::from("hello"); assert!(pattern.matches(&cbor_text));
}
#[test]
fn test_not_pattern_fails_when_inner_matches() {
let pattern = NotPattern::new(Pattern::number(5));
let cbor_5 = CBOR::from(5); assert!(!pattern.matches(&cbor_5));
}
}