use std::{cell::RefCell, collections::HashMap};
use bc_components::DigestProvider;
use bc_envelope::prelude::*;
use crate::pattern::{Matcher, Path, Pattern, vm::Instr};
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct SearchPattern(Box<Pattern>);
impl SearchPattern {
pub fn new(pattern: Pattern) -> Self { SearchPattern(Box::new(pattern)) }
pub fn pattern(&self) -> &Pattern { &self.0 }
}
impl Matcher for SearchPattern {
fn paths_with_captures(
&self,
haystack: &Envelope,
) -> (Vec<Path>, HashMap<String, Vec<Path>>) {
let paths = {
let result_paths = RefCell::new(Vec::new());
let visitor = |current_envelope: &Envelope,
_level: usize,
_incoming_edge: EdgeType,
path_to_current: Vec<Envelope>|
-> (Vec<Envelope>, bool) {
let mut new_path = path_to_current.clone();
new_path.push(current_envelope.clone());
let pattern_paths = self.0.paths(current_envelope);
for pattern_path in pattern_paths {
let mut full_path = new_path.clone();
if pattern_path.len() > 1 {
full_path.extend(pattern_path.into_iter().skip(1));
} else if pattern_path.len() == 1
&& pattern_path[0].digest() != current_envelope.digest()
{
full_path.extend(pattern_path);
}
result_paths.borrow_mut().push(full_path);
}
(new_path, false)
};
haystack.walk(false, Vec::new(), &visitor);
let mut seen = std::collections::HashSet::new();
let mut unique = Vec::new();
for path in result_paths.into_inner() {
let digest_path: Vec<_> =
path.iter().map(|e| e.digest()).collect();
if seen.insert(digest_path) {
unique.push(path);
}
}
unique
};
(paths, HashMap::new())
}
fn compile(
&self,
code: &mut Vec<Instr>,
lits: &mut Vec<Pattern>,
captures: &mut Vec<String>,
) {
let idx = lits.len();
lits.push((*self.0).clone());
let mut inner_names = Vec::new();
self.0.collect_capture_names(&mut inner_names);
let mut map = Vec::new();
for name in inner_names {
let pos = if let Some(i) = captures.iter().position(|n| n == &name)
{
i
} else {
let i = captures.len();
captures.push(name.clone());
i
};
map.push((name, pos));
}
code.push(Instr::Search { pat_idx: idx, capture_map: map });
}
}
impl std::fmt::Display for SearchPattern {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "search({})", self.pattern())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_search_pattern_display() {
let pattern = SearchPattern::new(Pattern::text("test"));
assert_eq!(pattern.to_string(), r#"search("test")"#);
}
}