refactor: reduce code duplication in filter and item services
Co-authored-by: aider (openai/andrew/openrouter/mistralai/mistral-medium-3.1) <aider@aider.chat>
This commit is contained in:
@@ -6,6 +6,7 @@ pub mod head;
|
||||
pub mod tail;
|
||||
pub mod grep;
|
||||
pub mod skip;
|
||||
pub mod utils;
|
||||
|
||||
pub trait FilterPlugin: Send {
|
||||
fn process(&mut self, data: &[u8]) -> Result<Vec<u8>>;
|
||||
@@ -31,58 +32,77 @@ impl FilterChain {
|
||||
let mut current_data = data.to_vec();
|
||||
for plugin in &mut self.plugins {
|
||||
current_data = plugin.process(¤t_data)?;
|
||||
// Early exit if no data remains
|
||||
if current_data.is_empty() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
Ok(current_data)
|
||||
}
|
||||
|
||||
pub fn finish(&mut self) -> Result<Vec<u8>> {
|
||||
let mut current_data = Vec::new();
|
||||
let mut result = Vec::new();
|
||||
let mut all_data = Vec::new();
|
||||
|
||||
for plugin in &mut self.plugins {
|
||||
let processed = plugin.finish()?;
|
||||
if !processed.is_empty() {
|
||||
current_data = processed;
|
||||
all_data.extend(processed);
|
||||
}
|
||||
}
|
||||
Ok(current_data)
|
||||
|
||||
// If we have any data from finish, use it
|
||||
if !all_data.is_empty() {
|
||||
result = all_data;
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function to parse filter string and create appropriate plugins
|
||||
pub fn parse_filter_string(filter_str: &str) -> Result<FilterChain> {
|
||||
let mut chain = FilterChain::new();
|
||||
|
||||
|
||||
for part in filter_str.split('|') {
|
||||
let part = part.trim();
|
||||
if part.is_empty() {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// Define a macro to reduce duplication in filter parsing
|
||||
macro_rules! parse_filter {
|
||||
($prefix:expr, $suffix:expr, $constructor:expr) => {{
|
||||
if let Some(stripped) = part.strip_prefix($prefix).and_then(|s| s.strip_suffix($suffix)) {
|
||||
let count = utils::parse_number(stripped)?;
|
||||
chain.add_plugin($constructor(count));
|
||||
continue;
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
// Handle grep filter
|
||||
if let Some(stripped) = part.strip_prefix("grep(").and_then(|s| s.strip_suffix(')')) {
|
||||
// Remove quotes if present
|
||||
let pattern = stripped.trim_matches(|c| c == '\'' || c == '"');
|
||||
chain.add_plugin(Box::new(grep::GrepFilter::new(pattern.to_string())?));
|
||||
} else if let Some(stripped) = part.strip_prefix("head_bytes(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(head::HeadBytesFilter::new(count)));
|
||||
} else if let Some(stripped) = part.strip_prefix("head_lines(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(head::HeadLinesFilter::new(count)));
|
||||
} else if let Some(stripped) = part.strip_prefix("tail_bytes(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(tail::TailBytesFilter::new(count)?));
|
||||
} else if let Some(stripped) = part.strip_prefix("tail_lines(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(tail::TailLinesFilter::new(count)?));
|
||||
} else if let Some(stripped) = part.strip_prefix("skip_bytes(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(skip::SkipBytesFilter::new(count)));
|
||||
} else if let Some(stripped) = part.strip_prefix("skip_lines(").and_then(|s| s.strip_suffix(')')) {
|
||||
let count: usize = stripped.parse().map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))?;
|
||||
chain.add_plugin(Box::new(skip::SkipLinesFilter::new(count)));
|
||||
} else {
|
||||
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("Unknown filter: {}", part)));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle other filters using the macro
|
||||
parse_filter!("head_bytes(", ")", |count| Box::new(head::HeadBytesFilter::new(count)));
|
||||
parse_filter!("head_lines(", ")", |count| Box::new(head::HeadLinesFilter::new(count)));
|
||||
parse_filter!("tail_bytes(", ")", |count| Box::new(tail::TailBytesFilter::new(count)));
|
||||
parse_filter!("tail_lines(", ")", |count| Box::new(tail::TailLinesFilter::new(count)));
|
||||
parse_filter!("skip_bytes(", ")", |count| Box::new(skip::SkipBytesFilter::new(count)));
|
||||
parse_filter!("skip_lines(", ")", |count| Box::new(skip::SkipLinesFilter::new(count)));
|
||||
|
||||
// If we get here, the filter wasn't recognized
|
||||
return Err(std::io::Error::new(
|
||||
std::io::ErrorKind::InvalidInput,
|
||||
format!("Unknown filter: {}", part)
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
Ok(chain)
|
||||
}
|
||||
|
||||
34
src/filter_plugin/utils.rs
Normal file
34
src/filter_plugin/utils.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use std::io::Result;
|
||||
|
||||
/// Helper trait for common filter operations
|
||||
pub trait FilterUtils {
|
||||
/// Process data through a filter, handling empty results
|
||||
fn process_data(&mut self, data: &[u8]) -> Result<Vec<u8>>;
|
||||
|
||||
/// Process data and check if we should continue processing
|
||||
fn process_and_check_continue(&mut self, data: &[u8]) -> Result<(Vec<u8>, bool)>;
|
||||
}
|
||||
|
||||
impl<T: FilterPlugin> FilterUtils for T {
|
||||
fn process_data(&mut self, data: &[u8]) -> Result<Vec<u8>> {
|
||||
let result = self.process(data)?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn process_and_check_continue(&mut self, data: &[u8]) -> Result<(Vec<u8>, bool)> {
|
||||
let result = self.process(data)?;
|
||||
let should_continue = !result.is_empty();
|
||||
Ok((result, should_continue))
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function to create a filter chain from a string
|
||||
pub fn create_filter_chain(filter_str: &str) -> Result<Option<super::FilterChain>> {
|
||||
super::parse_filter_string(filter_str).map(Some)
|
||||
}
|
||||
|
||||
/// Helper function to parse a number from a string with error handling
|
||||
pub fn parse_number<T: std::str::FromStr>(s: &str) -> Result<T> {
|
||||
s.parse::<T>()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidInput, e))
|
||||
}
|
||||
Reference in New Issue
Block a user