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 std::io::Result;
|
||||
use std::io::{Result, Read, Write, BufRead};
|
||||
use regex::Regex;
|
||||
|
||||
pub struct GrepFilter {
|
||||
regex: Regex,
|
||||
buffer: Vec<u8>,
|
||||
}
|
||||
|
||||
impl GrepFilter {
|
||||
@@ -13,55 +12,19 @@ impl GrepFilter {
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
Ok(Self {
|
||||
regex,
|
||||
buffer: Vec::new(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl FilterPlugin for GrepFilter {
|
||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
||||
self.buffer.extend_from_slice(data);
|
||||
|
||||
let mut result = Vec::new();
|
||||
let mut lines = Vec::new();
|
||||
let mut start = 0;
|
||||
|
||||
// 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;
|
||||
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||
let buf_reader = std::io::BufReader::new(reader);
|
||||
for line in buf_reader.lines() {
|
||||
let line = line?;
|
||||
if self.regex.is_match(&line) {
|
||||
writeln!(writer, "{}", line)?;
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
use std::io::Result;
|
||||
use std::io::{Result, Read, Write};
|
||||
use std::io::BufRead;
|
||||
|
||||
pub mod head;
|
||||
pub mod tail;
|
||||
@@ -8,8 +9,7 @@ pub mod strip_ansi;
|
||||
pub mod utils;
|
||||
|
||||
pub trait FilterPlugin: Send {
|
||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>>;
|
||||
fn finish(&mut self) -> Result<Vec<u8>>;
|
||||
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()>;
|
||||
}
|
||||
|
||||
pub struct FilterChain {
|
||||
@@ -27,33 +27,32 @@ impl FilterChain {
|
||||
self.plugins.push(plugin);
|
||||
}
|
||||
|
||||
pub fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
||||
let mut current_data = data.to_vec();
|
||||
for plugin in &mut self.plugins {
|
||||
// Process the current data through the plugin
|
||||
let processed = plugin.process(¤t_data)?;
|
||||
current_data = processed;
|
||||
|
||||
// Early exit if no data remains
|
||||
if current_data.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(current_data)
|
||||
pub fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||
if self.plugins.is_empty() {
|
||||
// If no plugins, just copy the input to output
|
||||
std::io::copy(reader, writer)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn finish(&mut self) -> Result<Vec<u8>> {
|
||||
let mut result = Vec::new();
|
||||
// For multiple plugins, we need to chain them together
|
||||
// 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 plugin in &mut self.plugins {
|
||||
let finished_data = plugin.finish()?;
|
||||
if !finished_data.is_empty() {
|
||||
result.extend(finished_data);
|
||||
for (i, plugin) in self.plugins.iter_mut().enumerate() {
|
||||
let mut input = std::io::Cursor::new(¤t_data);
|
||||
|
||||
// For the last plugin, write directly to the output writer
|
||||
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(result)
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use std::io::Result;
|
||||
use std::io::{Result, Read, Write};
|
||||
use strip_ansi_escapes::strip as strip_ansi_escapes;
|
||||
|
||||
use super::FilterPlugin;
|
||||
@@ -12,13 +12,11 @@ impl StripAnsiFilter {
|
||||
}
|
||||
|
||||
impl FilterPlugin for StripAnsiFilter {
|
||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
||||
// Strip ANSI escape sequences from the input data
|
||||
let stripped = strip_ansi_escapes(data);
|
||||
Ok(stripped)
|
||||
}
|
||||
|
||||
fn finish(&mut self) -> Result<Vec<u8>> {
|
||||
Ok(Vec::new())
|
||||
fn filter<R: Read, W: Write>(&mut self, reader: &mut R, writer: &mut W) -> Result<()> {
|
||||
let mut data = Vec::new();
|
||||
reader.read_to_end(&mut data)?;
|
||||
let stripped = strip_ansi_escapes(&data);
|
||||
writer.write_all(&stripped)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::filter_plugin::{FilterChain, parse_filter_string};
|
||||
use std::io::Result;
|
||||
use std::io::{Result, Read, Write};
|
||||
|
||||
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 {
|
||||
chain.process(data)
|
||||
chain.filter(reader, writer)
|
||||
} 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>> {
|
||||
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
|
||||
// Helper method to process data with a filter string in one call
|
||||
pub fn process_with_filter(&self, data: &[u8], filter_str: Option<&str>) -> Result<Vec<u8>> {
|
||||
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