diff --git a/src/services/filter_service.rs b/src/services/filter_service.rs index 5212d2e..aa57b90 100644 --- a/src/services/filter_service.rs +++ b/src/services/filter_service.rs @@ -4,13 +4,63 @@ use std::io::{Result, Read, Write}; use once_cell::sync::Lazy; use std::sync::Mutex; +/// Service for managing filter chains and plugin registration. +/// +/// The `FilterService` provides functionality to parse filter strings, create filter chains, +/// and apply them to input/output streams. It integrates with the global filter plugin +/// registry to support dynamic loading of filter implementations like `head`, `tail`, +/// `grep`, and custom plugins. +/// +/// # Usage +/// +/// ```rust +/// let service = FilterService::new(); +/// let chain = service.create_filter_chain(Some("head_lines(10)")).unwrap(); +/// service.filter_data(&mut chain, &mut reader, &mut writer)?; +/// ``` pub struct FilterService; impl FilterService { + /// Creates a new `FilterService` instance. + /// + /// # Returns + /// + /// A new `FilterService`. + /// + /// # Examples + /// + /// ``` + /// let service = FilterService::new(); + /// ``` pub fn new() -> Self { Self } + /// Creates a filter chain from a filter string specification. + /// + /// Parses the filter string using the filter parser and constructs a `FilterChain` + /// with the appropriate plugins. Returns `None` if no filter string is provided. + /// + /// # Arguments + /// + /// * `filter_str` - Optional filter string, e.g., "head_lines(10),grep(pattern=error)". + /// + /// # Returns + /// + /// * `Result, io::Error>` - The parsed chain or an error if parsing fails. + /// + /// # Errors + /// + /// * `io::Error` - If the filter string is invalid or parsing fails. + /// + /// # Examples + /// + /// ``` + /// let chain = service.create_filter_chain(Some("head_lines(10)"))?; + /// assert!(chain.is_some()); + /// let empty = service.create_filter_chain(None)?; + /// assert!(empty.is_none()); + /// ``` pub fn create_filter_chain(&self, filter_str: Option<&str>) -> Result> { if let Some(filter_str) = filter_str { parse_filter_string(filter_str).map(Some) @@ -19,6 +69,32 @@ impl FilterService { } } + /// Applies a filter chain to input data and writes to output. + /// + /// If a filter chain is provided, it processes the data through each filter in sequence. + /// If no chain is provided, it copies the input directly to the output. + /// + /// # Type Parameters + /// + /// * `R` - Type implementing `Read` for the input source. + /// * `W` - Type implementing `Write` for the output destination. + /// + /// # Arguments + /// + /// * `chain` - Mutable reference to an optional filter chain. + /// * `reader` - Mutable reference to the input reader. + /// * `writer` - Mutable reference to the output writer. + /// + /// # Returns + /// + /// * `Result<(), io::Error>` - Success or I/O error if filtering fails. + /// + /// # Examples + /// + /// ``` + /// let mut chain = parse_filter_string("head_lines(5)")?; + /// service.filter_data(&mut chain, &mut reader, &mut writer)?; + /// ``` pub fn filter_data( &self, chain: &mut Option, @@ -34,7 +110,26 @@ impl FilterService { } } - // Helper method to process data with a filter string in one call + /// Processes data with an optional filter string in a single call. + /// + /// This is a convenience method that creates a filter chain, applies it to the data, + /// and returns the filtered result as a byte vector. Useful for simple one-off filtering. + /// + /// # Arguments + /// + /// * `data` - Input byte slice to process. + /// * `filter_str` - Optional filter string to apply. + /// + /// # Returns + /// + /// * `Result, io::Error>` - Filtered data or error if filtering fails. + /// + /// # Examples + /// + /// ``` + /// let filtered = service.process_with_filter(b"Hello\nWorld\n", Some("head_lines(1)"))?; + /// assert_eq!(filtered, b"Hello\n"); + /// ``` pub fn process_with_filter(&self, data: &[u8], filter_str: Option<&str>) -> Result> { let mut chain = self.create_filter_chain(filter_str)?; let mut reader = std::io::Cursor::new(data); @@ -50,16 +145,65 @@ impl FilterService { } } -/// Global registry for filter plugins +/// Global registry for filter plugins. +/// +/// This static variable holds a thread-safe map of filter plugin names to their +/// constructors. Plugins register themselves at initialization time using +/// `register_filter_plugin`. The registry is lazily initialized on first access. +/// +/// # Usage +/// +/// Plugins use this registry for dynamic loading: +/// +/// ```rust +/// static FILTER_PLUGIN_REGISTRY: Lazy Box>>> = +/// Lazy::new(|| Mutex::new(HashMap::new())); +/// ``` static FILTER_PLUGIN_REGISTRY: Lazy Box>>> = Lazy::new(|| Mutex::new(HashMap::new())); -/// Register a filter plugin with the global registry +/// Register a filter plugin with the global registry. +/// +/// Adds a filter plugin to the registry so it can be dynamically loaded by name. +/// This function is typically called at module initialization time using `#[ctor::ctor]`. +/// +/// # Arguments +/// +/// * `name` - The name of the filter plugin (e.g., "head_lines"). +/// * `constructor` - A function that returns a new `Box` instance. +/// +/// # Panics +/// +/// Panics if the registry lock cannot be acquired (unlikely in normal use). +/// +/// # Examples +/// +/// ```rust +/// register_filter_plugin("my_filter", || Box::new(MyFilter::new())); +/// ``` pub fn register_filter_plugin(name: &str, constructor: fn() -> Box) { FILTER_PLUGIN_REGISTRY.lock().unwrap().insert(name.to_string(), constructor); } -/// Get a map of available filter plugins +/// Get a map of available filter plugins. +/// +/// Returns a copy of the current registry contents, mapping plugin names to their constructors. +/// This is useful for status reporting or plugin discovery. +/// +/// # Returns +/// +/// `HashMap Box>` - A clone of the registry map. +/// +/// # Panics +/// +/// Panics if the registry lock cannot be acquired. +/// +/// # Examples +/// +/// ```rust +/// let available = get_available_filter_plugins(); +/// assert!(available.contains_key("head_lines")); +/// ``` pub fn get_available_filter_plugins() -> HashMap Box> { FILTER_PLUGIN_REGISTRY.lock().unwrap().clone() }