diff --git a/Cargo.toml b/Cargo.toml index 79aa36f..b1c609a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,6 @@ sha2 = "0.10.0" md5 = "0.7.0" stderrlog = "0.6.0" strum = { version = "0.27.2", features = ["derive"] } -strum_macros = "0.27.2" term = "1.1.0" termsize = "0.1.9" tokio = { version = "1.0", features = ["full"] } diff --git a/src/filter_plugin/mod.rs b/src/filter_plugin/mod.rs index 84575b7..c62eca9 100644 --- a/src/filter_plugin/mod.rs +++ b/src/filter_plugin/mod.rs @@ -1,4 +1,6 @@ use std::io::{Result, Read, Write}; +use std::str::FromStr; +use strum::{EnumString, EnumVariantNames}; pub mod head; pub mod tail; @@ -11,6 +13,19 @@ pub trait FilterPlugin: Send { fn filter(&mut self, reader: Box<&mut dyn Read>, writer: Box<&mut dyn Write>) -> Result<()>; } +#[derive(Debug, EnumString, EnumVariantNames)] +#[strum(serialize_all = "snake_case")] +enum FilterType { + HeadBytes, + HeadLines, + TailBytes, + TailLines, + SkipBytes, + SkipLines, + Grep, + StripAnsi, +} + pub struct FilterChain { plugins: Vec>, } @@ -72,39 +87,70 @@ pub fn parse_filter_string(filter_str: &str) -> Result { 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(Box::new($constructor(count))); + // Parse the filter type + if let Ok(filter_type) = FilterType::from_str(part) { + match filter_type { + FilterType::StripAnsi => { + chain.add_plugin(Box::new(strip_ansi::StripAnsiFilter::new())); continue; } - }}; + _ => { + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + format!("Filter '{}' requires parameters", part) + )); + } + } } - // Handle strip_ansi filter - if part == "strip_ansi" { - chain.add_plugin(Box::new(strip_ansi::StripAnsiFilter::new())); - continue; + // Handle filters with parameters + // Extract the filter name and parameters + if let Some((filter_name, params)) = part.split_once('(') { + if let Some(params) = params.strip_suffix(')') { + if let Ok(filter_type) = FilterType::from_str(filter_name) { + match filter_type { + FilterType::Grep => { + // Remove quotes if present + let pattern = params.trim_matches(|c| c == '\'' || c == '"'); + chain.add_plugin(Box::new(grep::GrepFilter::new(pattern.to_string())?)); + } + FilterType::HeadBytes => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(head::HeadBytesFilter::new(count))); + } + FilterType::HeadLines => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(head::HeadLinesFilter::new(count))); + } + FilterType::TailBytes => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(tail::TailBytesFilter::new(count))); + } + FilterType::TailLines => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(tail::TailLinesFilter::new(count))); + } + FilterType::SkipBytes => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(skip::SkipBytesFilter::new(count))); + } + FilterType::SkipLines => { + let count = utils::parse_number(params)?; + chain.add_plugin(Box::new(skip::SkipLinesFilter::new(count))); + } + FilterType::StripAnsi => { + // This should not happen as strip_ansi doesn't take parameters + return Err(std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "strip_ansi filter doesn't take parameters" + )); + } + } + 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())?)); - continue; - } - - // Handle other filters using the macro - parse_filter!("head_bytes(", ")", head::HeadBytesFilter::new); - parse_filter!("head_lines(", ")", head::HeadLinesFilter::new); - parse_filter!("tail_bytes(", ")", tail::TailBytesFilter::new); - parse_filter!("tail_lines(", ")", tail::TailLinesFilter::new); - parse_filter!("skip_bytes(", ")", skip::SkipBytesFilter::new); - parse_filter!("skip_lines(", ")", skip::SkipLinesFilter::new); - // If we get here, the filter wasn't recognized return Err(std::io::Error::new( std::io::ErrorKind::InvalidInput,