use std::{collections::HashMap, ops::RangeBounds};
use bc_envelope::prelude::*;
use crate::{
DCBORMatcher, DCBORPattern, Pattern,
pattern::{Matcher, Path, compile_as_atomic, leaf::LeafPattern, vm::Instr},
};
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct ArrayPattern(dcbor_pattern::ArrayPattern);
impl ArrayPattern {
pub fn any() -> Self { ArrayPattern(dcbor_pattern::ArrayPattern::any()) }
pub fn interval(interval: impl RangeBounds<usize>) -> Self {
ArrayPattern(dcbor_pattern::ArrayPattern::with_length_range(interval))
}
pub fn count(n: usize) -> Self {
ArrayPattern(dcbor_pattern::ArrayPattern::with_length_range(n..=n))
}
pub fn from_dcbor_pattern(pattern: DCBORPattern) -> Self {
ArrayPattern(dcbor_pattern::ArrayPattern::with_elements(pattern))
}
pub fn from_dcbor_array_pattern(
array_pattern: dcbor_pattern::ArrayPattern,
) -> Self {
ArrayPattern(array_pattern)
}
}
impl std::hash::Hash for ArrayPattern {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.to_string().hash(state);
}
}
impl Matcher for ArrayPattern {
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
let paths = if let Some(cbor_value) = haystack.subject().as_leaf() {
if self.0.matches(&cbor_value) {
vec![vec![haystack.clone()]]
} else {
vec![]
}
} else {
vec![]
};
(paths, HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
literals: &mut Vec<Pattern>,
captures: &mut Vec<String>,
) {
compile_as_atomic(
&Pattern::Leaf(LeafPattern::Array(self.clone())),
code,
literals,
captures,
);
}
}
impl std::fmt::Display for ArrayPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_pattern_any() {
let cbor_array = vec![1, 2, 3].to_cbor();
let envelope = Envelope::new(cbor_array);
let pattern = ArrayPattern::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_array_pattern_count() {
let cbor_array = vec![1, 2, 3].to_cbor();
let envelope = Envelope::new(cbor_array);
let pattern = ArrayPattern::count(3);
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
let pattern = ArrayPattern::count(5);
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
}
#[test]
fn test_array_pattern_range() {
let cbor_array = vec![1, 2, 3].to_cbor();
let envelope = Envelope::new(cbor_array);
let pattern = ArrayPattern::interval(2..=4);
let paths = pattern.paths(&envelope);
assert_eq!(paths.len(), 1);
let pattern = ArrayPattern::interval(5..=10);
let paths = pattern.paths(&envelope);
assert!(paths.is_empty());
}
#[test]
fn test_array_pattern_display() {
assert_eq!(ArrayPattern::any().to_string(), "array");
assert_eq!(ArrayPattern::count(3).to_string(), "[{3}]");
assert_eq!(ArrayPattern::interval(2..=5).to_string(), "[{2,5}]");
assert_eq!(ArrayPattern::interval(3..).to_string(), "[{3,}]");
}
}