use crate::{
error,
handler::Handler,
matcher::MatchMaker,
path::Path,
streamer::{Output, Streamer},
};
use std::{
mem,
sync::{Arc, Mutex},
};
type MatcherItem = (Box<dyn MatchMaker>, Vec<Arc<Mutex<dyn Handler>>>);
pub struct Convert {
input_start: usize,
buffer_start: usize,
buffer: Vec<u8>,
matched: Option<(Path, usize)>,
matchers: Vec<MatcherItem>,
streamer: Streamer,
}
impl Default for Convert {
fn default() -> Self {
Self {
input_start: 0,
buffer_start: 0,
buffer: vec![],
matched: None,
matchers: vec![],
streamer: Streamer::new(),
}
}
}
impl Convert {
pub fn new() -> Self {
Self::default()
}
pub fn add_matcher(
&mut self,
matcher: Box<dyn MatchMaker>,
handlers: Vec<Arc<Mutex<dyn Handler>>>,
) {
self.matchers.push((matcher, handlers));
}
pub fn process(&mut self, input: &[u8]) -> Result<Vec<Vec<u8>>, error::General> {
self.streamer.feed(input);
let mut inner_idx = 0;
let mut result: Vec<Vec<u8>> = vec![];
loop {
match self.streamer.read()? {
Output::Start(idx) => {
if self.matched.is_none() {
let path = self.streamer.current_path();
for (matcher_idx, (matcher, _)) in self.matchers.iter().enumerate() {
if matcher.match_path(path) {
self.buffer_start = idx;
self.matched = Some((path.clone(), matcher_idx));
let to = idx - self.input_start;
result.push(input[inner_idx..to].to_vec());
inner_idx = to;
break;
}
}
}
}
Output::End(idx) => {
let current_path = self.streamer.current_path();
let mut clear = false;
if let Some((matched_path, matcher_idx)) = self.matched.as_ref() {
if current_path == matched_path {
clear = true;
let to = idx - self.input_start;
self.buffer.extend(&input[inner_idx..to]);
inner_idx = to;
let mut buffer = vec![];
mem::swap(&mut buffer, &mut self.buffer);
let start_idx = idx - buffer.len();
for handler in self.matchers[*matcher_idx].1.iter() {
let mut guard = handler.lock().unwrap();
guard.handle_idx(current_path, start_idx, Output::End(idx))?;
if let Some(converted) =
guard.handle(current_path, *matcher_idx, Some(&buffer))?
{
buffer = converted;
}
}
result.push(buffer);
}
}
if clear {
self.matched = None;
}
}
Output::Pending => {
self.input_start += input.len();
if self.matched.is_some() {
self.buffer.extend(&input[inner_idx..]);
} else {
result.push(input[inner_idx..].to_vec())
}
return Ok(result);
}
Output::Separator(_) => {}
}
}
}
}
#[cfg(test)]
mod tests {
use super::Convert;
use crate::{handler::Replace, matcher::Simple};
use std::sync::{Arc, Mutex};
fn make_replace_handler() -> Arc<Mutex<Replace>> {
return Arc::new(Mutex::new(Replace::new(vec![b'"', b'*', b'*', b'*', b'"'])));
}
#[test]
fn basic() {
let mut convert = Convert::new();
let matcher = Simple::new(r#"[]{"password"}"#).unwrap();
convert.add_matcher(Box::new(matcher), vec![make_replace_handler()]);
let mut output = convert
.process(br#"[{"id": 1, "password": "secret1"}, {"id": 2, "password": "secret2"}]"#)
.unwrap();
assert_eq!(output.len(), 5);
assert_eq!(
String::from_utf8(output.remove(0)).unwrap(),
r#"[{"id": 1, "password": "#
);
assert_eq!(String::from_utf8(output.remove(0)).unwrap(), r#""***""#);
assert_eq!(
String::from_utf8(output.remove(0)).unwrap(),
r#"}, {"id": 2, "password": "#
);
assert_eq!(String::from_utf8(output.remove(0)).unwrap(), r#""***""#);
assert_eq!(String::from_utf8(output.remove(0)).unwrap(), "}]");
}
#[test]
fn basic_pending() {
let mut convert = Convert::new();
let matcher = Simple::new(r#"[]{"password"}"#).unwrap();
convert.add_matcher(Box::new(matcher), vec![make_replace_handler()]);
let mut result = vec![];
let output = convert.process(br#"[{"id": 1, "password": "s"#).unwrap();
result.extend(output);
let output = convert
.process(br#"ecret1"}, {"id": 2, "password": "secret2"}]"#)
.unwrap();
result.extend(output);
assert_eq!(
String::from_utf8(result.into_iter().flatten().collect()).unwrap(),
r#"[{"id": 1, "password": "***"}, {"id": 2, "password": "***"}]"#
);
}
}