use std::{collections::VecDeque, mem::swap};
use crate::{
error,
matcher::MatchMaker,
path::Path,
streamer::{Output, Streamer},
};
pub struct Filter {
buffer_idx: usize,
buffer: VecDeque<u8>,
streamer: Streamer,
matchers: Vec<Box<dyn MatchMaker>>,
matched_path: Option<Path>,
last_output_level: usize,
last_output_idx: Option<usize>,
delayed_discard: bool,
}
impl Default for Filter {
fn default() -> Self {
Self {
buffer_idx: 0,
buffer: VecDeque::new(),
matchers: vec![],
streamer: Streamer::new(),
matched_path: None,
last_output_idx: None,
delayed_discard: false,
last_output_level: 0,
}
}
}
impl Filter {
pub fn new() -> Self {
Self::default()
}
fn move_forward(&mut self, idx: usize) -> VecDeque<u8> {
let mut splitted = self.buffer.split_off(idx - self.buffer_idx);
swap(&mut self.buffer, &mut splitted);
self.buffer_idx = idx;
splitted
}
pub fn add_matcher(&mut self, matcher: Box<dyn MatchMaker>) {
self.matchers.push(matcher);
}
pub fn process(&mut self, input: &[u8]) -> Result<Vec<u8>, error::General> {
self.streamer.feed(input);
self.buffer.extend(input);
let mut result = Vec::new();
loop {
match self.streamer.read()? {
Output::Pending => {
if self.matched_path.is_none() {
if let Some(final_idx) = self.last_output_idx {
result.extend(self.move_forward(final_idx));
}
}
return Ok(result);
}
Output::Start(idx) => {
if self.matched_path.is_none() {
if self.delayed_discard {
self.move_forward(idx);
self.delayed_discard = false;
}
let current_path = self.streamer.current_path().clone();
if self.matchers.iter().any(|e| e.match_path(¤t_path)) {
self.matched_path = Some(current_path);
let move_to_idx = if let Some(last_idx) = self.last_output_idx.take() {
last_idx
} else {
idx
};
result.extend(self.move_forward(move_to_idx));
if self.last_output_level < self.streamer.current_path().depth() {
self.delayed_discard = true;
}
} else {
self.last_output_idx = Some(idx + 1); self.last_output_level = self.streamer.current_path().depth();
}
}
}
Output::End(idx) => {
if let Some(path) = self.matched_path.as_ref() {
if path == self.streamer.current_path() {
self.matched_path = None;
if !self.delayed_discard {
self.move_forward(idx);
}
}
} else {
if self.delayed_discard {
self.move_forward(idx - 1); self.delayed_discard = false;
}
self.last_output_idx = Some(idx);
self.last_output_level = self.streamer.current_path().depth();
}
}
Output::Separator(idx) => {
if self.matched_path.is_none() {
if self.delayed_discard {
self.move_forward(idx + 1); self.delayed_discard = false;
self.last_output_idx = Some(idx + 1);
} else {
self.last_output_idx = Some(idx);
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Filter;
use crate::matcher::{Combinator, Simple};
fn get_input() -> Vec<Vec<u8>> {
vec![
br#"{"users": [{"uid": 1}, {"uid": 2}, {"uid": 3}], "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
.iter()
.map(|e| *e)
.collect(),
]
}
#[test]
fn single_matcher_no_match() {
let input = get_input();
let matcher = Simple::new(r#"{"no-existing"}[]{"uid"}"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(filter.process(&input[0]).unwrap(), input[0].clone());
}
#[test]
fn single_matcher_array_first() {
let input = get_input();
let matcher = Simple::new(r#"{"users"}[0]"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [ {"uid": 2}, {"uid": 3}], "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
);
}
#[test]
fn single_matcher_array_last() {
let input = get_input();
let matcher = Simple::new(r#"{"users"}[2]"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [{"uid": 1}, {"uid": 2}], "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
);
}
#[test]
fn single_matcher_array_middle() {
let input = get_input();
let matcher = Simple::new(r#"{"users"}[1]"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [{"uid": 1}, {"uid": 3}], "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
);
}
#[test]
fn single_matcher_array_all() {
let input = get_input();
let matcher = Simple::new(r#"{"users"}[]"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [], "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
);
}
#[test]
fn single_matcher_object_first() {
let input = get_input();
let matcher = Simple::new(r#"{"users"}"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{ "groups": [{"gid": 1}, {"gid": 2}], "void": {}}"#
);
}
#[test]
fn single_matcher_object_last() {
let input = get_input();
let matcher = Simple::new(r#"{"void"}"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [{"uid": 1}, {"uid": 2}, {"uid": 3}], "groups": [{"gid": 1}, {"gid": 2}]}"#
);
}
#[test]
fn single_matcher_object_middle() {
let input = get_input();
let matcher = Simple::new(r#"{"groups"}"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{"users": [{"uid": 1}, {"uid": 2}, {"uid": 3}], "void": {}}"#
);
}
#[test]
fn single_matcher_object_all() {
let input = get_input();
let matcher = Simple::new(r#"{}"#).unwrap();
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
assert_eq!(
String::from_utf8(filter.process(&input[0]).unwrap()).unwrap(),
r#"{}"#
);
}
#[test]
fn combinator_slices() {
let input = get_input();
for i in 0..input.len() {
let start_input = &input[0][0..i];
let end_input = &input[0][i..];
let matcher = Combinator::new(Simple::new(r#"{"users"}"#).unwrap())
| Combinator::new(Simple::new(r#"{"void"}"#).unwrap());
let mut filter = Filter::new();
filter.add_matcher(Box::new(matcher));
let mut result: Vec<u8> = Vec::new();
result.extend(filter.process(&start_input).unwrap());
result.extend(filter.process(&end_input).unwrap());
assert_eq!(
String::from_utf8(result).unwrap(),
r#"{ "groups": [{"gid": 1}, {"gid": 2}]}"#
)
}
}
}