use std::collections::HashMap;
use bc_envelope::prelude::*;
use known_values::KnownValue;
use crate::{
Pattern,
pattern::{Matcher, Path, compile_as_atomic, leaf::LeafPattern, vm::Instr},
};
#[derive(Debug, Clone)]
pub struct KnownValuePattern(dcbor_pattern::KnownValuePattern);
impl KnownValuePattern {
pub fn any() -> Self { Self(dcbor_pattern::KnownValuePattern::any()) }
pub fn value(value: KnownValue) -> Self {
Self(dcbor_pattern::KnownValuePattern::value(value))
}
pub fn named(name: impl Into<String>) -> Self {
Self(dcbor_pattern::KnownValuePattern::named(name))
}
pub fn regex(regex: regex::Regex) -> Self {
Self(dcbor_pattern::KnownValuePattern::regex(regex))
}
pub fn from_dcbor_pattern(
dcbor_pattern: dcbor_pattern::KnownValuePattern,
) -> Self {
Self(dcbor_pattern)
}
}
impl PartialEq for KnownValuePattern {
fn eq(&self, other: &Self) -> bool { self.0 == other.0 }
}
impl Eq for KnownValuePattern {}
impl std::hash::Hash for KnownValuePattern {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.0.hash(state); }
}
impl Matcher for KnownValuePattern {
fn paths(&self, haystack: &Envelope) -> Vec<Path> {
let subject = haystack.subject();
if let Some(known_value) = subject.as_known_value() {
let known_value_cbor = known_value.to_cbor();
let dcbor_paths =
dcbor_pattern::Matcher::paths(&self.0, &known_value_cbor);
dcbor_paths
.into_iter()
.map(|_dcbor_path| {
vec![haystack.clone()]
})
.collect()
} else {
vec![]
}
}
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
(self.paths(haystack), HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
literals: &mut Vec<Pattern>,
captures: &mut Vec<String>,
) {
compile_as_atomic(
&Pattern::Leaf(LeafPattern::KnownValue(self.clone())),
code,
literals,
captures,
);
}
}
impl std::fmt::Display for KnownValuePattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_known_value_pattern_any() {
use known_values::KnownValue;
let value = KnownValue::new(1);
let envelope = Envelope::new(value.clone());
let pattern = KnownValuePattern::any();
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![envelope.clone()]);
let text_envelope = Envelope::new("test");
let paths = pattern.paths(&text_envelope);
assert!(paths.is_empty());
}
#[test]
fn test_known_value_pattern_specific() {
let value = known_values::DATE;
let envelope = Envelope::new(value.clone());
let pattern = KnownValuePattern::value(value.clone());
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![envelope.clone()]);
let different_value = known_values::LANGUAGE;
let pattern = KnownValuePattern::value(different_value);
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
}
#[test]
fn test_known_value_pattern_named() {
let value = known_values::DATE;
let envelope = Envelope::new(value.clone());
let pattern = KnownValuePattern::named("date");
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![envelope.clone()]);
let pattern = KnownValuePattern::named("language");
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
let pattern = KnownValuePattern::named("unknown_name");
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
let text_envelope = Envelope::new("test");
let pattern = KnownValuePattern::named("date");
let paths = pattern.paths(&text_envelope);
assert!(paths.is_empty());
}
#[test]
fn test_known_value_pattern_regex() {
let value = known_values::DATE;
let envelope = Envelope::new(value.clone());
let regex = regex::Regex::new(r"^da.*").unwrap();
let pattern = KnownValuePattern::regex(regex);
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![envelope.clone()]);
let regex = regex::Regex::new(r".*te$").unwrap();
let pattern = KnownValuePattern::regex(regex);
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![envelope.clone()]);
let regex = regex::Regex::new(r"^lang.*").unwrap();
let pattern = KnownValuePattern::regex(regex);
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
let text_envelope = Envelope::new("test");
let regex = regex::Regex::new(r".*").unwrap();
let pattern = KnownValuePattern::regex(regex);
let paths = pattern.paths(&text_envelope);
assert!(paths.is_empty());
}
#[test]
fn test_known_value_pattern_display() {
bc_envelope::register_tags();
let pattern = KnownValuePattern::any();
assert_eq!(pattern.to_string(), "known");
let pattern = KnownValuePattern::value(known_values::DATE);
assert_eq!(pattern.to_string(), "'date'");
let pattern = KnownValuePattern::named("date");
assert_eq!(pattern.to_string(), "'date'");
let regex = regex::Regex::new(r"^da.*").unwrap();
let pattern = KnownValuePattern::regex(regex);
assert_eq!(pattern.to_string(), "'/^da.*/'");
}
#[test]
fn test_known_value_pattern_dcbor_integration() {
let date_envelope = Envelope::new(known_values::DATE);
let language_envelope = Envelope::new(known_values::LANGUAGE);
let text_envelope = Envelope::new("test");
let any_pattern = KnownValuePattern::any();
assert!(any_pattern.matches(&date_envelope));
assert!(any_pattern.matches(&language_envelope));
assert!(!any_pattern.matches(&text_envelope));
let date_pattern = KnownValuePattern::value(known_values::DATE);
assert!(date_pattern.matches(&date_envelope));
assert!(!date_pattern.matches(&language_envelope));
assert!(!date_pattern.matches(&text_envelope));
let named_date_pattern = KnownValuePattern::named("date");
assert!(named_date_pattern.matches(&date_envelope));
assert!(!named_date_pattern.matches(&language_envelope));
assert!(!named_date_pattern.matches(&text_envelope));
let date_regex = regex::Regex::new(r"^da.*").unwrap();
let regex_pattern = KnownValuePattern::regex(date_regex);
assert!(regex_pattern.matches(&date_envelope));
assert!(!regex_pattern.matches(&language_envelope));
assert!(!regex_pattern.matches(&text_envelope));
let paths = date_pattern.paths(&date_envelope);
assert_eq!(paths.len(), 1);
assert_eq!(paths[0], vec![date_envelope.clone()]);
let no_paths = date_pattern.paths(&language_envelope);
assert_eq!(no_paths.len(), 0);
}
}