refactor: simplify filter plugin signatures by removing boxed parameters

This commit is contained in:
Andrew Phillips
2025-09-12 10:36:09 -03:00
committed by Andrew Phillips (aider)
parent 9c354d5ef4
commit 059bde09e4
10 changed files with 158 additions and 301 deletions

View File

@@ -53,8 +53,8 @@ impl GrepFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream.
/// * `writer` - A boxed mutable reference to the output writer where matching lines are sent.
/// * `reader` - Mutable reference to the input data stream.
/// * `writer` - Mutable reference to the output writer where matching lines are sent.
///
/// # Returns
///
@@ -67,33 +67,33 @@ impl GrepFilter {
/// # Examples
///
/// ```
/// filter.filter(Box::new(&mut input), Box::new(&mut output))?;
/// filter.filter(&mut input, &mut output)?;
/// ```
impl FilterPlugin for GrepFilter {
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(&mut *reader);
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(reader);
for line in buf_reader.by_ref().lines() {
let line = line?;
if self.regex.is_match(&line) {
writeln!(&mut *writer, "{}", line)?;
writeln!(writer, "{}", line)?;
}
}
Ok(())
}
/// Clones this filter into a new boxed instance.
///
/// Creates a new GrepFilter with the same regex pattern.
///
/// # Returns
///
/// A new `Box<dyn FilterPlugin>` representing a clone of this filter.
///
/// # Examples
///
/// ```
/// let cloned = filter.clone_box();
/// ```
///
/// Creates a new GrepFilter with the same regex pattern.
///
/// # Returns
///
/// A new `Box<dyn FilterPlugin>` representing a clone of this filter.
///
/// # Examples
///
/// ```
/// let cloned = filter.clone_box();
/// ```
fn clone_box(&self) -> Box<dyn FilterPlugin> {
Box::new(Self {
regex: self.regex.clone(),
@@ -101,20 +101,20 @@ impl FilterPlugin for GrepFilter {
}
/// Returns the configuration options for this filter.
///
/// The only option is the required "pattern" for the regex.
///
/// # Returns
///
/// A vector containing one `FilterOption` for "pattern" (required, no default).
///
/// # Examples
///
/// ```
/// let opts = filter.options();
/// assert_eq!(opts.len(), 1);
/// assert!(opts[0].required);
/// ```
///
/// The only option is the required "pattern" for the regex.
///
/// # Returns
///
/// A vector containing one `FilterOption` for "pattern" (required, no default).
///
/// # Examples
///
/// ```
/// let opts = filter.options();
/// assert_eq!(opts.len(), 1);
/// assert!(opts[0].required);
/// ```
fn options(&self) -> Vec<FilterOption> {
vec![
FilterOption {
@@ -124,4 +124,4 @@ impl FilterPlugin for GrepFilter {
}
]
}
}
}

View File

@@ -3,7 +3,6 @@ use std::io::{Result, Read, Write, BufRead};
use crate::common::PIPESIZE;
use crate::services::filter_service::register_filter_plugin;
/// A filter that reads the first N bytes from the input stream.
/// A filter that reads the first N bytes from the input stream.
///
/// Limits the output to the initial bytes specified in the configuration.
@@ -55,8 +54,8 @@ impl HeadBytesFilter {
///
/// # Arguments
///
/// * `reader` - Boxed mutable reference to the input data stream.
/// * `writer` - Boxed mutable reference to the output stream.
/// * `reader` - Mutable reference to the input data stream.
/// * `writer` - Mutable reference to the output stream.
///
/// # Returns
///
@@ -73,7 +72,7 @@ impl HeadBytesFilter {
/// // Input "Hello World" becomes "Hello"
/// ```
impl FilterPlugin for HeadBytesFilter {
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
if self.remaining == 0 {
return Ok(());
}
@@ -81,11 +80,11 @@ impl FilterPlugin for HeadBytesFilter {
let mut buffer = vec![0; PIPESIZE];
while self.remaining > 0 {
let to_read = std::cmp::min(self.remaining, PIPESIZE);
let bytes_read = (&mut *reader).read(&mut buffer[..to_read])?;
let bytes_read = reader.read(&mut buffer[..to_read])?;
if bytes_read == 0 {
break;
}
(&mut *writer).write_all(&buffer[..bytes_read])?;
writer.write_all(&buffer[..bytes_read])?;
self.remaining -= bytes_read;
}
Ok(())
@@ -123,14 +122,6 @@ impl FilterPlugin for HeadBytesFilter {
}
/// A filter that reads the first N lines from the input stream.
/// A filter that reads the first N lines from the input stream.
///
/// Limits output to the initial lines specified, writing each full line to output.
/// Handles line endings properly using buffered reading.
///
/// # Fields
///
/// * `remaining` - Number of lines left to read before stopping.
pub struct HeadLinesFilter {
remaining: usize,
}
@@ -173,8 +164,8 @@ impl HeadLinesFilter {
///
/// # Arguments
///
/// * `reader` - Boxed mutable reference to the input data stream.
/// * `writer` - Boxed mutable reference to the output stream.
/// * `reader` - Mutable reference to the input data stream.
/// * `writer` - Mutable reference to the output stream.
///
/// # Returns
///
@@ -191,15 +182,15 @@ impl HeadLinesFilter {
/// // Input "Line1\nLine2\nLine3" becomes "Line1\nLine2\n"
/// ```
impl FilterPlugin for HeadLinesFilter {
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
if self.remaining == 0 {
return Ok(());
}
let mut buf_reader = std::io::BufReader::new(&mut *reader);
let mut buf_reader = std::io::BufReader::new(reader);
for line in buf_reader.by_ref().lines() {
let line = line?;
writeln!(&mut *writer, "{}", line)?;
writeln!(writer, "{}", line)?;
self.remaining -= 1;
if self.remaining == 0 {
break;

View File

@@ -19,36 +19,6 @@ impl SkipBytesFilter {
remaining: count,
}
}
/// Creates a new instance from options.
///
/// # Arguments
///
/// * `options` - An optional JSON value containing configuration options for the filter.
///
/// # Returns
///
/// A `Result` containing a boxed `FilterPlugin` on success, or an `io::Error` if options are invalid.
pub fn create(options: Option<serde_json::Value>) -> Result<Box<dyn FilterPlugin>> {
let options = options.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"skip_bytes filter requires options"
)
})?;
let count = options.get("n")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"skip_bytes filter requires 'n' parameter"
)
})?;
Ok(Box::new(Self::new(count)))
}
}
impl FilterPlugin for SkipBytesFilter {
@@ -56,19 +26,19 @@ impl FilterPlugin for SkipBytesFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent.
/// * `reader` - Mutable reference to the input reader providing the data stream.
/// * `writer` - Mutable reference to the output writer where filtered data is sent.
///
/// # Returns
///
/// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
// Skip bytes in chunks
if self.remaining > 0 {
let mut buffer = vec![0; PIPESIZE];
while self.remaining > 0 {
let to_read = std::cmp::min(self.remaining, PIPESIZE);
let bytes_read = (&mut *reader).read(&mut buffer[..to_read])?;
let bytes_read = reader.read(&mut buffer[..to_read])?;
if bytes_read == 0 {
break;
}
@@ -77,7 +47,7 @@ impl FilterPlugin for SkipBytesFilter {
}
// Copy the remaining data using io::copy for efficiency
std::io::copy(&mut *reader, &mut *writer)?;
std::io::copy(reader, writer)?;
Ok(())
}
@@ -124,36 +94,6 @@ impl SkipLinesFilter {
remaining: count,
}
}
/// Creates a new instance from options.
///
/// # Arguments
///
/// * `options` - An optional JSON value containing configuration options for the filter.
///
/// # Returns
///
/// A `Result` containing a boxed `FilterPlugin` on success, or an `io::Error` if options are invalid.
pub fn create(options: Option<serde_json::Value>) -> Result<Box<dyn FilterPlugin>> {
let options = options.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"skip_lines filter requires options"
)
})?;
let count = options.get("n")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"skip_lines filter requires 'n' parameter"
)
})?;
Ok(Box::new(Self::new(count)))
}
}
impl FilterPlugin for SkipLinesFilter {
@@ -161,20 +101,20 @@ impl FilterPlugin for SkipLinesFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent.
/// * `reader` - Mutable reference to the input reader providing the data stream.
/// * `writer` - Mutable reference to the output writer where filtered data is sent.
///
/// # Returns
///
/// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(&mut *reader);
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(reader);
for line in buf_reader.by_ref().lines() {
let line = line?;
if self.remaining > 0 {
self.remaining -= 1;
} else {
writeln!(&mut *writer, "{}", line)?;
writeln!(writer, "{}", line)?;
}
}
Ok(())
@@ -212,4 +152,4 @@ impl FilterPlugin for SkipLinesFilter {
fn register_skip_filters() {
register_filter_plugin("skip_bytes", || Box::new(SkipBytesFilter::new(0)));
register_filter_plugin("skip_lines", || Box::new(SkipLinesFilter::new(0)));
}
}

View File

@@ -3,6 +3,11 @@ use strip_ansi_escapes::Writer;
use super::{FilterPlugin, FilterOption};
/// A filter that removes ANSI escape sequences from the input.
///
/// # Fields
///
/// None, stateless filter.
#[derive(Default)]
pub struct StripAnsiFilter;
impl StripAnsiFilter {
@@ -21,16 +26,17 @@ impl FilterPlugin for StripAnsiFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream with potential ANSI codes.
/// * `writer` - A boxed mutable reference to the output writer where plain text is sent.
/// * `reader` - Mutable reference to the input reader providing the data stream with potential ANSI codes.
/// * `writer` - Mutable reference to the output writer where plain text is sent.
///
/// # Returns
///
/// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
let mut ansi_writer = Writer::new(&mut *writer);
std::io::copy(&mut *reader, &mut ansi_writer)?;
ansi_writer.flush()
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
let mut ansi_writer = Writer::new(writer);
std::io::copy(reader, &mut ansi_writer)?;
ansi_writer.flush()?;
Ok(())
}
/// Clones this filter into a new boxed instance.

View File

@@ -22,36 +22,6 @@ impl TailBytesFilter {
count,
}
}
/// Creates a new instance from options.
///
/// # Arguments
///
/// * `options` - An optional JSON value containing configuration options for the filter.
///
/// # Returns
///
/// A `Result` containing a boxed `FilterPlugin` on success, or an `io::Error` if options are invalid.
pub fn create(options: Option<serde_json::Value>) -> Result<Box<dyn FilterPlugin>> {
let options = options.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"tail_bytes filter requires options"
)
})?;
let count = options.get("n")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"tail_bytes filter requires 'n' parameter"
)
})?;
Ok(Box::new(Self::new(count)))
}
}
impl FilterPlugin for TailBytesFilter {
@@ -59,16 +29,16 @@ impl FilterPlugin for TailBytesFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent.
/// * `reader` - Mutable reference to the input reader providing the data stream.
/// * `writer` - Mutable reference to the output writer where filtered data is sent.
///
/// # Returns
///
/// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
let mut temp_buffer = vec![0; PIPESIZE];
loop {
let bytes_read = (&mut *reader).read(&mut temp_buffer)?;
let bytes_read = reader.read(&mut temp_buffer)?;
if bytes_read == 0 {
break;
}
@@ -84,7 +54,7 @@ impl FilterPlugin for TailBytesFilter {
// Write the buffered data at the end
let result: Vec<u8> = self.buffer.iter().cloned().collect();
(&mut *writer).write_all(&result)?;
writer.write_all(&result)?;
Ok(())
}
@@ -134,36 +104,6 @@ impl TailLinesFilter {
count,
}
}
/// Creates a new instance from options.
///
/// # Arguments
///
/// * `options` - An optional JSON value containing configuration options for the filter.
///
/// # Returns
///
/// A `Result` containing a boxed `FilterPlugin` on success, or an `io::Error` if options are invalid.
pub fn create(options: Option<serde_json::Value>) -> Result<Box<dyn FilterPlugin>> {
let options = options.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"tail_lines filter requires options"
)
})?;
let count = options.get("n")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"tail_lines filter requires 'n' parameter"
)
})?;
Ok(Box::new(Self::new(count)))
}
}
impl FilterPlugin for TailLinesFilter {
@@ -171,14 +111,14 @@ impl FilterPlugin for TailLinesFilter {
///
/// # Arguments
///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent.
/// * `reader` - Mutable reference to the input reader providing the data stream.
/// * `writer` - Mutable reference to the output writer where filtered data is sent.
///
/// # Returns
///
/// Returns `Ok(())` on success, or an `io::Error` if reading or writing fails.
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(&mut *reader);
fn filter(&mut self, reader: &mut dyn Read, writer: &mut dyn Write) -> Result<()> {
let mut buf_reader = std::io::BufReader::new(reader);
for line in buf_reader.by_ref().lines() {
let line = line?;
if self.lines.len() == self.count {
@@ -189,7 +129,7 @@ impl FilterPlugin for TailLinesFilter {
// Write the buffered lines
for line in &self.lines {
writeln!(&mut *writer, "{}", line)?;
writeln!(writer, "{}", line)?;
}
Ok(())
}