use crate::{
error,
handler::Handler,
matcher::MatchMaker,
streamer::{Output, Streamer},
};
use std::sync::{Arc, Mutex};
#[derive(Debug)]
struct StackItem {
idx: usize,
match_idx: usize,
buffering_required: bool,
}
type MatcherItem = (Box<dyn MatchMaker>, Vec<Arc<Mutex<dyn Handler>>>);
pub struct Trigger {
input_start: usize,
buffer_start: usize,
buffer: Vec<u8>,
collecting: bool,
matchers: Vec<MatcherItem>,
streamer: Streamer,
matched_stack: Vec<Vec<StackItem>>,
}
impl Default for Trigger {
fn default() -> Self {
Self {
input_start: 0,
buffer_start: 0,
buffer: vec![],
collecting: false,
matchers: vec![],
streamer: Streamer::new(),
matched_stack: vec![],
}
}
}
impl Trigger {
pub fn new() -> Self {
Self::default()
}
pub fn add_matcher(
&mut self,
matcher: Box<dyn MatchMaker>,
handlers: &[Arc<Mutex<dyn Handler>>],
) {
self.matchers.push((matcher, handlers.to_vec()));
}
pub fn process(&mut self, input: &[u8]) -> Result<(), error::General> {
self.streamer.feed(input);
let mut inner_idx = 0;
loop {
match self.streamer.read()? {
Output::Start(idx) => {
let to = idx - self.input_start;
if self.collecting {
self.buffer.extend(&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) {
let mut buffering_required = false;
for handler in &self.matchers[match_idx].1 {
let mut guard = handler.lock().unwrap();
guard.handle_idx(path, match_idx, Output::Start(idx))?;
if guard.buffering_required() {
buffering_required = true
}
}
matched.push(StackItem {
idx,
match_idx,
buffering_required,
});
if !self.collecting & buffering_required {
self.buffer_start = idx;
self.collecting = true;
}
}
}
self.matched_stack.push(matched);
}
Output::End(idx) => {
let current_path = self.streamer.current_path();
let to = idx - self.input_start;
if self.collecting {
self.buffer.extend(&input[inner_idx..to]);
}
inner_idx = to;
let items = self.matched_stack.pop().unwrap();
for item in items {
for handler in &self.matchers[item.match_idx].1 {
let mut guard = handler.lock().unwrap();
guard.handle_idx(current_path, item.match_idx, Output::End(idx))?;
let buffering_required = guard.buffering_required();
guard.handle(
current_path,
item.match_idx,
if buffering_required {
Some(
&self.buffer
[item.idx - self.buffer_start..idx - self.buffer_start],
)
} else {
None
},
)?;
}
}
if self
.matched_stack
.iter()
.all(|items| items.iter().all(|item| !item.buffering_required))
{
self.collecting = false;
self.buffer.clear();
}
}
Output::Pending => {
self.input_start += input.len();
if self.collecting {
self.buffer.extend(&input[inner_idx..]);
}
return Ok(());
}
Output::Separator(_) => {}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Trigger;
use crate::{error, handler::Handler, matcher::Simple, path::Path};
use std::sync::{Arc, Mutex};
#[derive(Default)]
struct TestHandler {
paths: Vec<String>,
data: Vec<Vec<u8>>,
}
impl Handler for TestHandler {
fn handle(
&mut self,
path: &Path,
_matcher_idx: usize,
data: Option<&[u8]>,
) -> Result<Option<Vec<u8>>, error::Handler> {
self.paths.push(path.to_string());
self.data.push(data.unwrap().to_vec());
Ok(None)
}
}
#[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());
}
#[test]
fn basic_pending() {
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#"{"elem"#).unwrap();
trigger.process(br#"ents": [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());
}
}