use crate::{
error,
handler::Handler,
matcher::Matcher,
streamer::{Streamer, Token},
};
use std::{
collections::HashSet,
sync::{Arc, Mutex},
};
use super::{Output, Strategy};
#[derive(Debug)]
struct StackItem {
idx: usize,
match_idx: usize,
}
type MatcherItem = (Box<dyn Matcher>, Arc<Mutex<dyn Handler>>);
pub struct Trigger {
input_start: usize,
matchers: Vec<MatcherItem>,
streamer: Streamer,
matched_stack: Vec<Vec<StackItem>>,
level: usize,
}
impl Default for Trigger {
fn default() -> Self {
Self {
input_start: 0,
matchers: vec![],
streamer: Streamer::new(),
matched_stack: vec![],
level: 0,
}
}
}
impl Strategy for Trigger {
fn process(&mut self, input: &[u8]) -> Result<Vec<Output>, error::General> {
self.streamer.feed(input);
let mut inner_idx = 0;
loop {
match self.streamer.read()? {
Token::Start(idx, kind) => {
self.level += 1;
let to = idx - self.input_start;
self.feed(&input[inner_idx..to])?;
inner_idx = to;
let mut matched = vec![];
let path = self.streamer.current_path();
for (match_idx, (matcher, _)) in self.matchers.iter().enumerate() {
if matcher.match_path(path, kind) {
let mut guard = self.matchers[match_idx].1.lock().unwrap();
guard.start(path, match_idx, Token::Start(idx, kind))?;
matched.push(StackItem { idx, match_idx });
}
}
self.matched_stack.push(matched);
}
Token::End(idx, kind) => {
self.level -= 1;
let to = idx - self.input_start;
self.feed(&input[inner_idx..to])?;
inner_idx = to;
let current_path = self.streamer.current_path();
let items = self.matched_stack.pop().unwrap();
for item in items {
let mut guard = self.matchers[item.match_idx].1.lock().unwrap();
guard.end(current_path, item.match_idx, Token::End(idx, kind))?;
}
}
Token::Pending => {
self.input_start += input.len();
self.feed(&input[inner_idx..])?;
return Ok(vec![]);
}
Token::Separator(_) => {}
}
}
}
fn terminate(&mut self) -> Result<Vec<Output>, error::General> {
if self.level == 0 {
Ok(vec![])
} else {
Err(error::InputTerminated::new(self.input_start).into())
}
}
}
impl Trigger {
pub fn new() -> Self {
Self::default()
}
pub fn add_matcher(&mut self, matcher: Box<dyn Matcher>, handler: Arc<Mutex<dyn Handler>>) {
self.matchers.push((matcher, handler));
}
fn feed(&mut self, data: &[u8]) -> Result<(), error::Handler> {
let mut seen_match_idx = HashSet::<usize>::new();
for matched_items in &self.matched_stack {
for matched_item in matched_items {
if seen_match_idx.insert(matched_item.match_idx) {
let mut guard = self.matchers[matched_item.match_idx].1.lock().unwrap();
guard.feed(data, matched_item.match_idx)?;
}
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::{Strategy, Trigger};
use crate::{
error,
handler::Handler,
matcher::Simple,
path::Path,
streamer::Token,
test::{Single, Splitter, Window},
};
use rstest::*;
use std::{
any::Any,
sync::{Arc, Mutex},
};
#[derive(Default)]
struct TestHandler {
paths: Vec<String>,
data: Vec<Vec<u8>>,
current: Vec<u8>,
}
impl Handler for TestHandler {
fn start(
&mut self,
path: &Path,
_matcher_idx: usize,
_kind: Token,
) -> Result<Option<Vec<u8>>, error::Handler> {
self.paths.push(path.to_string());
Ok(None)
}
fn feed(
&mut self,
data: &[u8],
_matcher_idx: usize,
) -> Result<Option<Vec<u8>>, error::Handler> {
self.current.extend(data.to_vec());
Ok(None)
}
fn end(
&mut self,
_path: &Path,
_matcher_idx: usize,
_kind: Token,
) -> Result<Option<Vec<u8>>, error::Handler> {
self.data.push(self.current.clone());
self.current.clear();
Ok(None)
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[test]
fn basic() {
let mut trigger = Trigger::new();
let handler = Arc::new(Mutex::new(TestHandler::default()));
let matcher = Simple::new(r#"{"elements"}[]"#).unwrap();
trigger.add_matcher(Box::new(matcher), handler.clone());
trigger.process(br#"{"elements": [1, 2, 3, 4]}"#).unwrap();
let guard = handler.lock().unwrap();
assert_eq!(guard.paths[0], r#"{"elements"}[0]"#);
assert_eq!(guard.data[0], br#"1"#.to_vec());
assert_eq!(guard.paths[1], r#"{"elements"}[1]"#);
assert_eq!(guard.data[1], br#"2"#.to_vec());
assert_eq!(guard.paths[2], r#"{"elements"}[2]"#);
assert_eq!(guard.data[2], br#"3"#.to_vec());
assert_eq!(guard.paths[3], r#"{"elements"}[3]"#);
assert_eq!(guard.data[3], br#"4"#.to_vec());
}
#[rstest(
splitter,
case::single(Box::new(Single::new())),
case::window1(Box::new(Window::new(1))),
case::window5(Box::new(Window::new(5))),
case::window100(Box::new(Window::new(100)))
)]
fn splitted(splitter: Box<dyn Splitter>) {
for parts in splitter.split(br#"{"elements": [1, 2, 3, 4]}"#.to_vec()) {
let mut trigger = Trigger::new();
let handler = Arc::new(Mutex::new(TestHandler::default()));
let matcher = Simple::new(r#"{"elements"}[]"#).unwrap();
trigger.add_matcher(Box::new(matcher), handler.clone());
for part in parts {
trigger.process(&part).unwrap();
}
let guard = handler.lock().unwrap();
assert_eq!(guard.paths.len(), 4);
assert_eq!(guard.paths[0], r#"{"elements"}[0]"#);
assert_eq!(guard.data[0], br#"1"#.to_vec());
assert_eq!(guard.paths[1], r#"{"elements"}[1]"#);
assert_eq!(guard.data[1], br#"2"#.to_vec());
assert_eq!(guard.paths[2], r#"{"elements"}[2]"#);
assert_eq!(guard.data[2], br#"3"#.to_vec());
assert_eq!(guard.paths[3], r#"{"elements"}[3]"#);
assert_eq!(guard.data[3], br#"4"#.to_vec());
}
}
}