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:
Andrew Phillips
2025-09-02 11:12:50 -03:00
parent fc413738b7
commit 099f3cde69
4 changed files with 63 additions and 114 deletions

View File

@@ -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(())
}
}

View File

@@ -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(&current_data)?;
current_data = processed;
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(());
}
// 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)?;
for (i, plugin) in self.plugins.iter_mut().enumerate() {
let mut input = std::io::Cursor::new(&current_data);
// Early exit if no data remains
if current_data.is_empty() {
break;
// 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(current_data)
}
pub fn finish(&mut self) -> Result<Vec<u8>> {
let mut result = Vec::new();
// 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);
}
}
Ok(result)
Ok(())
}
}

View File

@@ -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(())
}
}