refactor: Update filter API to use Read/Write traits
Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
@@ -1,10 +1,9 @@
|
|||||||
use super::FilterPlugin;
|
use super::FilterPlugin;
|
||||||
use std::io::Result;
|
use std::io::{Result, Read, Write, BufRead};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
|
|
||||||
pub struct GrepFilter {
|
pub struct GrepFilter {
|
||||||
regex: Regex,
|
regex: Regex,
|
||||||
buffer: Vec<u8>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GrepFilter {
|
impl GrepFilter {
|
||||||
@@ -13,55 +12,19 @@ impl GrepFilter {
|
|||||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
regex,
|
regex,
|
||||||
buffer: Vec::new(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilterPlugin for GrepFilter {
|
impl FilterPlugin for GrepFilter {
|
||||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||||
self.buffer.extend_from_slice(data);
|
let buf_reader = std::io::BufReader::new(reader);
|
||||||
|
for line in buf_reader.lines() {
|
||||||
let mut result = Vec::new();
|
let line = line?;
|
||||||
let mut lines = Vec::new();
|
if self.regex.is_match(&line) {
|
||||||
let mut start = 0;
|
writeln!(writer, "{}", line)?;
|
||||||
|
|
||||||
// Split into lines
|
|
||||||
for (i, &byte) in self.buffer.iter().enumerate() {
|
|
||||||
if byte == b'\n' {
|
|
||||||
let line = &self.buffer[start..=i];
|
|
||||||
lines.push(line.to_vec());
|
|
||||||
start = i + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
// Keep the remaining data in buffer
|
|
||||||
let remaining = self.buffer.split_off(start);
|
|
||||||
self.buffer = remaining;
|
|
||||||
|
|
||||||
// Filter lines that match the regex
|
|
||||||
for line in lines {
|
|
||||||
if let Ok(line_str) = std::str::from_utf8(&line) {
|
|
||||||
if self.regex.is_match(line_str) {
|
|
||||||
result.extend_from_slice(&line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn finish(&mut self) -> Result<Vec<u8>> {
|
|
||||||
// Process any remaining data in buffer
|
|
||||||
let mut result = Vec::new();
|
|
||||||
if !self.buffer.is_empty() {
|
|
||||||
if let Ok(line_str) = std::str::from_utf8(&self.buffer) {
|
|
||||||
if self.regex.is_match(line_str) {
|
|
||||||
result.extend_from_slice(&self.buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.buffer.clear();
|
|
||||||
}
|
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
use std::io::Result;
|
use std::io::{Result, Read, Write};
|
||||||
|
use std::io::BufRead;
|
||||||
|
|
||||||
pub mod head;
|
pub mod head;
|
||||||
pub mod tail;
|
pub mod tail;
|
||||||
@@ -8,8 +9,7 @@ pub mod strip_ansi;
|
|||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|
||||||
pub trait FilterPlugin: Send {
|
pub trait FilterPlugin: Send {
|
||||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>>;
|
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()>;
|
||||||
fn finish(&mut self) -> Result<Vec<u8>>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FilterChain {
|
pub struct FilterChain {
|
||||||
@@ -27,33 +27,32 @@ impl FilterChain {
|
|||||||
self.plugins.push(plugin);
|
self.plugins.push(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
pub fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||||
let mut current_data = data.to_vec();
|
if self.plugins.is_empty() {
|
||||||
for plugin in &mut self.plugins {
|
// If no plugins, just copy the input to output
|
||||||
// Process the current data through the plugin
|
std::io::copy(reader, writer)?;
|
||||||
let processed = plugin.process(¤t_data)?;
|
return Ok(());
|
||||||
current_data = processed;
|
|
||||||
|
|
||||||
// Early exit if no data remains
|
|
||||||
if current_data.is_empty() {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(current_data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish(&mut self) -> Result<Vec<u8>> {
|
// For multiple plugins, we need to chain them together
|
||||||
let mut result = Vec::new();
|
// We'll use a temporary buffer to hold intermediate results
|
||||||
|
let mut current_data = Vec::new();
|
||||||
|
std::io::copy(reader, &mut current_data)?;
|
||||||
|
|
||||||
// Process each plugin's finish method and collect results
|
for (i, plugin) in self.plugins.iter_mut().enumerate() {
|
||||||
for plugin in &mut self.plugins {
|
let mut input = std::io::Cursor::new(¤t_data);
|
||||||
let finished_data = plugin.finish()?;
|
|
||||||
if !finished_data.is_empty() {
|
// For the last plugin, write directly to the output writer
|
||||||
result.extend(finished_data);
|
if i == self.plugins.len() - 1 {
|
||||||
|
plugin.filter(&mut input, writer)?;
|
||||||
|
} else {
|
||||||
|
// For intermediate plugins, write to a buffer
|
||||||
|
let mut output = Vec::new();
|
||||||
|
plugin.filter(&mut input, &mut output)?;
|
||||||
|
current_data = output;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
Ok(result)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use std::io::Result;
|
use std::io::{Result, Read, Write};
|
||||||
use strip_ansi_escapes::strip as strip_ansi_escapes;
|
use strip_ansi_escapes::strip as strip_ansi_escapes;
|
||||||
|
|
||||||
use super::FilterPlugin;
|
use super::FilterPlugin;
|
||||||
@@ -12,13 +12,11 @@ impl StripAnsiFilter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FilterPlugin for StripAnsiFilter {
|
impl FilterPlugin for StripAnsiFilter {
|
||||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||||
// Strip ANSI escape sequences from the input data
|
let mut data = Vec::new();
|
||||||
let stripped = strip_ansi_escapes(data);
|
reader.read_to_end(&mut data)?;
|
||||||
Ok(stripped)
|
let stripped = strip_ansi_escapes(&data);
|
||||||
}
|
writer.write_all(&stripped)?;
|
||||||
|
Ok(())
|
||||||
fn finish(&mut self) -> Result<Vec<u8>> {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use crate::filter_plugin::{FilterChain, parse_filter_string};
|
use crate::filter_plugin::{FilterChain, parse_filter_string};
|
||||||
use std::io::Result;
|
use std::io::{Result, Read, Write};
|
||||||
|
|
||||||
pub struct FilterService;
|
pub struct FilterService;
|
||||||
|
|
||||||
@@ -16,44 +16,33 @@ impl FilterService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_data(&self, chain: &mut Option<FilterChain>, data: &[u8]) -> Result<Vec<u8>> {
|
pub fn filter_data<R: Read, W: Write>(
|
||||||
|
&self,
|
||||||
|
chain: &mut Option<FilterChain>,
|
||||||
|
reader: &mut R,
|
||||||
|
writer: &mut W
|
||||||
|
) -> Result<()> {
|
||||||
if let Some(chain) = chain {
|
if let Some(chain) = chain {
|
||||||
chain.process(data)
|
chain.filter(reader, writer)
|
||||||
} else {
|
} else {
|
||||||
Ok(data.to_vec())
|
// If no filter chain, just copy the input to output
|
||||||
|
std::io::copy(reader, writer)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finish_processing(&self, chain: &mut Option<FilterChain>) -> Result<Vec<u8>> {
|
// Helper method to process data with a filter string in one call
|
||||||
if let Some(chain) = chain {
|
|
||||||
chain.finish()
|
|
||||||
} else {
|
|
||||||
Ok(Vec::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a method to process data through the filter chain and handle finish automatically
|
|
||||||
pub fn process_all_data(&self, chain: &mut Option<FilterChain>, data: &[u8]) -> Result<Vec<u8>> {
|
|
||||||
let mut processed = if let Some(chain) = chain {
|
|
||||||
chain.process(data)?
|
|
||||||
} else {
|
|
||||||
data.to_vec()
|
|
||||||
};
|
|
||||||
|
|
||||||
// If we have a chain, also get any remaining data from finish()
|
|
||||||
if chain.is_some() {
|
|
||||||
let finished = self.finish_processing(chain)?;
|
|
||||||
if !finished.is_empty() {
|
|
||||||
processed.extend(finished);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(processed)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper method to create and process data with a filter string in one call
|
|
||||||
pub fn process_with_filter(&self, data: &[u8], filter_str: Option<&str>) -> Result<Vec<u8>> {
|
pub fn process_with_filter(&self, data: &[u8], filter_str: Option<&str>) -> Result<Vec<u8>> {
|
||||||
let mut chain = self.create_filter_chain(filter_str)?;
|
let mut chain = self.create_filter_chain(filter_str)?;
|
||||||
self.process_all_data(&mut chain, data)
|
let mut reader = std::io::Cursor::new(data);
|
||||||
|
let mut writer = Vec::new();
|
||||||
|
|
||||||
|
if let Some(ref mut chain) = chain {
|
||||||
|
chain.filter(&mut reader, &mut writer)?;
|
||||||
|
} else {
|
||||||
|
std::io::copy(&mut reader, &mut writer)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user