refactor: Unify filter plugin creation and option handling

Co-authored-by: aider (openai/andrew/openrouter/deepseek/deepseek-chat-v3.1) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-03 08:26:44 -03:00
parent bd2a8af186
commit 254ac6359b

View File

@@ -25,9 +25,9 @@ pub trait FilterPlugin: Send {
fn options(&self) -> Vec<FilterOption>; fn options(&self) -> Vec<FilterOption>;
} }
#[derive(Debug, EnumString, strum::VariantNames)] #[derive(Debug, EnumString, strum::VariantNames, strum::Display)]
#[strum(serialize_all = "snake_case")] #[strum(serialize_all = "snake_case")]
enum FilterType { pub enum FilterType {
HeadBytes, HeadBytes,
HeadLines, HeadLines,
TailBytes, TailBytes,
@@ -143,35 +143,7 @@ pub fn parse_filter_string(filter_str: &str) -> Result<FilterChain> {
// Create the appropriate filter plugin // Create the appropriate filter plugin
if let Ok(filter_type) = FilterType::from_str(filter_name) { if let Ok(filter_type) = FilterType::from_str(filter_name) {
let plugin = match filter_type { let plugin = create_filter_with_options(filter_type, &unnamed_params, &options)?;
FilterType::Grep => {
create_filter_with_options::<grep::GrepFilter>(&unnamed_params, &options)?
}
FilterType::HeadBytes => {
create_filter_with_options::<head::HeadBytesFilter>(&unnamed_params, &options)?
}
FilterType::HeadLines => {
create_filter_with_options::<head::HeadLinesFilter>(&unnamed_params, &options)?
}
FilterType::TailBytes => {
create_filter_with_options::<tail::TailBytesFilter>(&unnamed_params, &options)?
}
FilterType::TailLines => {
create_filter_with_options::<tail::TailLinesFilter>(&unnamed_params, &options)?
}
FilterType::SkipBytes => {
create_filter_with_options::<skip::SkipBytesFilter>(&unnamed_params, &options)?
}
FilterType::SkipLines => {
create_filter_with_options::<skip::SkipLinesFilter>(&unnamed_params, &options)?
}
FilterType::StripAnsi => {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"strip_ansi filter doesn't take parameters"
));
}
};
chain.add_plugin(plugin); chain.add_plugin(plugin);
continue; continue;
} }
@@ -205,12 +177,23 @@ pub fn parse_filter_string(filter_str: &str) -> Result<FilterChain> {
} }
// Helper function to create filter with proper option handling // Helper function to create filter with proper option handling
fn create_filter_with_options<T: FilterPlugin + Default>( fn create_filter_with_options(
filter_type: FilterType,
unnamed_params: &[serde_json::Value], unnamed_params: &[serde_json::Value],
named_options: &HashMap<String, serde_json::Value>, named_options: &HashMap<String, serde_json::Value>,
) -> Result<Box<dyn FilterPlugin>> { ) -> Result<Box<dyn FilterPlugin>> {
let mut plugin = T::default(); // Get the default options for this filter type by creating a temporary instance
let option_defs = plugin.options(); // To do this, we need to create a default instance of the appropriate filter
let option_defs = match filter_type {
FilterType::Grep => grep::GrepFilter::new("".to_string())?.options(),
FilterType::HeadBytes => head::HeadBytesFilter::new(0).options(),
FilterType::HeadLines => head::HeadLinesFilter::new(0).options(),
FilterType::TailBytes => tail::TailBytesFilter::new(0).options(),
FilterType::TailLines => tail::TailLinesFilter::new(0).options(),
FilterType::SkipBytes => skip::SkipBytesFilter::new(0).options(),
FilterType::SkipLines => skip::SkipLinesFilter::new(0).options(),
FilterType::StripAnsi => strip_ansi::StripAnsiFilter::new().options(),
};
let mut options = HashMap::new(); let mut options = HashMap::new();
@@ -257,23 +240,95 @@ fn create_filter_with_options<T: FilterPlugin + Default>(
} }
// Create the specific filter with the processed options // Create the specific filter with the processed options
// This part needs to be implemented for each filter type create_specific_filter(filter_type, &options)
// For now, we'll use a match on the type name
// Note: This is a placeholder - you'll need to implement proper constructors for each filter
Ok(create_specific_filter::<T>(&options)?)
} }
// Helper to create specific filter instances based on options // Helper to create specific filter instances based on options
fn create_specific_filter<T: FilterPlugin + Default>( fn create_specific_filter(
filter_type: FilterType,
options: &HashMap<String, serde_json::Value>, options: &HashMap<String, serde_json::Value>,
) -> Result<Box<dyn FilterPlugin>> { ) -> Result<Box<dyn FilterPlugin>> {
// This is a simplified implementation match filter_type {
// In practice, you'd need to handle each filter type specifically FilterType::Grep => {
let mut plugin = T::default(); let pattern = options.get("pattern")
.and_then(|v| v.as_str())
// For now, just return the default plugin .ok_or_else(|| std::io::Error::new(
// You'll need to implement proper initialization based on options std::io::ErrorKind::InvalidInput,
Ok(Box::new(plugin)) "grep filter requires 'pattern' parameter"
))?;
grep::GrepFilter::new(pattern.to_string()).map(|f| Box::new(f) as Box<dyn FilterPlugin>)
}
FilterType::HeadBytes => {
let count = options.get("count")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"head_bytes filter requires 'count' parameter"
))?;
Ok(Box::new(head::HeadBytesFilter::new(count)))
}
FilterType::HeadLines => {
let count = options.get("count")
.and_then(|v| v.as_u64())
.map(|n| n as usize)
.ok_or_else(|| std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"head_lines filter requires 'count' parameter"
))?;
Ok(Box::new(head::HeadLinesFilter::new(count)))
}
FilterType::TailBytes => {
let count = options.get("count")
.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 'count' parameter"
))?;
Ok(Box::new(tail::TailBytesFilter::new(count)))
}
FilterType::TailLines => {
let count = options.get("count")
.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 'count' parameter"
))?;
Ok(Box::new(tail::TailLinesFilter::new(count)))
}
FilterType::SkipBytes => {
let count = options.get("count")
.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 'count' parameter"
))?;
Ok(Box::new(skip::SkipBytesFilter::new(count)))
}
FilterType::SkipLines => {
let count = options.get("count")
.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 'count' parameter"
))?;
Ok(Box::new(skip::SkipLinesFilter::new(count)))
}
FilterType::StripAnsi => {
// StripAnsi doesn't take any parameters
if !options.is_empty() {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"strip_ansi filter doesn't take parameters"
));
}
Ok(Box::new(strip_ansi::StripAnsiFilter::new()))
}
}
} }
// Helper function to parse option values // Helper function to parse option values