From e9ab630a743f47a3e36e94bfc9de5d4ff02cb013 Mon Sep 17 00:00:00 2001 From: Andrew Phillips Date: Wed, 10 Sep 2025 15:48:54 -0300 Subject: [PATCH] docs: Add rustdoc for filter_plugin, binary_detection, and lib.rs Co-authored-by: aider (openai/andrew/openrouter/sonoma-sky-alpha) --- PLAN.md | 15 ---- src/common/binary_detection.rs | 61 ++++++++++++++ src/filter_plugin/head.rs | 141 +++++++++++++++++++++++++-------- src/lib.rs | 37 ++++++++- 4 files changed, 206 insertions(+), 48 deletions(-) diff --git a/PLAN.md b/PLAN.md index 4f250de..285bf4c 100644 --- a/PLAN.md +++ b/PLAN.md @@ -50,21 +50,6 @@ Private helpers (e.g., internal `fn` without `pub`) are not flagged, as they don - Impl `MetaPlugin` methods (`is_finalized`, `set_finalized`, `finalize`, `update`, `meta_type`, `outputs`, etc.): No docs. - Overall: Lacks docs for the public trait impl. -4. **src/filter_plugin/head.rs** [DONE] - - `HeadBytesFilter` and `HeadLinesFilter` structs: No docs. - - `new()` functions: Partial. - - Impl `FilterPlugin` methods (`filter`, `clone_box`, `options`): No docs. - - Overall: No rustdoc at all for public impl. - -5. **src/common/binary_detection.rs** [DONE] - - `check_binary_content_allowed()` function: Partial (missing examples). - - `is_content_binary()` function: Partial. - - Overall: Functions lack full error handling docs. - -6. **src/lib.rs** [DONE] - - Module re-exports and `init_plugins()` function: No docs. - - Overall: Library-level docs are minimal. - 7. **src/services/filter_service.rs** [DONE] - `FilterService` struct: Partial doc. - `new()`, `create_filter_chain()`, `filter_data()`, `process_with_filter()` methods: Partial or missing. diff --git a/src/common/binary_detection.rs b/src/common/binary_detection.rs index d58db52..4d4b054 100644 --- a/src/common/binary_detection.rs +++ b/src/common/binary_detection.rs @@ -17,6 +17,39 @@ use std::collections::HashMap; /// * `Result<(), StatusCode>` - /// * `Ok(())` if binary content is allowed or content is not binary /// * `Err(StatusCode::BAD_REQUEST)` if binary content is not allowed and content is binary +/// Check if content is binary when allow_binary is false +/// +/// Validates whether binary content is permitted for the item. If not allowed and content +/// is detected as binary, returns a bad request status. Uses metadata or streams content +/// for detection if needed. +/// +/// # Arguments +/// +/// * `item_service` - Reference to the async item service for content access. +/// * `item_id` - The ID of the item to check. +/// * `metadata` - Metadata associated with the item (checked for "text" key). +/// * `allow_binary` - Whether binary content is allowed (bypasses check if true). +/// +/// # Returns +/// +/// * `Result<(), StatusCode>` - +/// * `Ok(())` if binary content is allowed or content is not binary. +/// * `Err(StatusCode::BAD_REQUEST)` if binary content is not allowed and content is binary. +/// +/// # Errors +/// +/// Propagates `StatusCode` for validation failures. +/// +/// # Examples +/// +/// ``` +/// // If allow_binary = false and content is text +/// check_binary_content_allowed(&service, 1, &metadata, false)?; +/// // Succeeds +/// +/// // If allow_binary = false and content is binary +/// // Returns Err(StatusCode::BAD_REQUEST) +/// ``` pub async fn check_binary_content_allowed( item_service: &AsyncItemService, item_id: i64, @@ -46,6 +79,34 @@ pub async fn check_binary_content_allowed( /// * `Ok(true)` if content is binary /// * `Ok(false)` if content is text /// * `Err(StatusCode)` if an error occurs during checking +/// Helper function to determine if content is binary +/// +/// Checks existing "text" metadata first; if absent or unset, streams and analyzes +/// the content to detect binary nature. Logs warnings on detection failures. +/// +/// # Arguments +/// +/// * `item_service` - Reference to the async item service for content access. +/// * `item_id` - The ID of the item to check. +/// * `metadata` - Metadata associated with the item (checked for "text" key). +/// +/// # Returns +/// +/// * `Result` - +/// * `Ok(true)` if content is binary. +/// * `Ok(false)` if content is text. +/// * `Err(StatusCode)` if an error occurs during checking (e.g., INTERNAL_SERVER_ERROR). +/// +/// # Errors +/// +/// * `StatusCode::INTERNAL_SERVER_ERROR` if content access fails. +/// +/// # Examples +/// +/// ``` +/// let is_bin = is_content_binary(&service, 1, &metadata).await?; +/// assert!(is_bin == false); // For text content +/// ``` pub async fn is_content_binary( item_service: &AsyncItemService, item_id: i64, diff --git a/src/filter_plugin/head.rs b/src/filter_plugin/head.rs index 651fb3a..2b65956 100644 --- a/src/filter_plugin/head.rs +++ b/src/filter_plugin/head.rs @@ -4,20 +4,43 @@ 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. +/// Useful for previewing file contents without reading everything. +/// +/// # Fields +/// +/// * `remaining` - Number of bytes left to read before stopping. pub struct HeadBytesFilter { remaining: usize, } +/// A filter that reads the first N bytes from the input stream. +/// +/// Limits the output to the initial bytes specified in the configuration. +/// Useful for previewing file contents without reading everything. +/// +/// # Fields +/// +/// * `remaining` - Number of bytes left to read before stopping. impl HeadBytesFilter { /// Creates a new `HeadBytesFilter` that will read up to the specified number of bytes. /// /// # Arguments /// - /// * `count` - The maximum number of bytes to read from the input + /// * `count` - The maximum number of bytes to read from the input. /// /// # Returns /// - /// * `Self` - A new instance of `HeadBytesFilter` + /// A new instance configured to read at most `count` bytes. + /// + /// # Examples + /// + /// ``` + /// let filter = HeadBytesFilter::new(1024); + /// assert_eq!(filter.remaining, 1024); + /// ``` pub fn new(count: usize) -> Self { Self { remaining: count, @@ -25,19 +48,31 @@ impl HeadBytesFilter { } } +/// Filters input by reading only the first N bytes and writing them to the output. +/// +/// Reads from the input in chunks until the byte limit is reached or EOF, then writes +/// the collected bytes to the output. Stops early if the limit is zero. +/// +/// # Arguments +/// +/// * `reader` - Boxed mutable reference to the input data stream. +/// * `writer` - Boxed mutable reference to the output stream. +/// +/// # Returns +/// +/// * `Result<()>` - Success if filtering completes, or I/O error. +/// +/// # Errors +/// +/// * `io::Error` from reading or writing operations. +/// +/// # Examples +/// +/// ``` +/// // Assuming a filter chain with head_bytes(5) +/// // Input "Hello World" becomes "Hello" +/// ``` impl FilterPlugin for HeadBytesFilter { - /// Filters the input by reading only the first N bytes and writing them to the output. - /// - /// # 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 - /// - /// # Returns - /// - /// * `Result<()>` - - /// * `Ok(())` on success - /// * `Err(io::Error)` if reading or writing fails fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> { if self.remaining == 0 { return Ok(()); @@ -58,9 +93,11 @@ impl FilterPlugin for HeadBytesFilter { /// Clones this filter into a new boxed instance. /// + /// Creates an independent copy with the same configuration. + /// /// # Returns /// - /// * `Box` - A new boxed clone of this filter + /// A new `Box` clone. fn clone_box(&self) -> Box { Box::new(Self { remaining: self.remaining, @@ -69,9 +106,11 @@ impl FilterPlugin for HeadBytesFilter { /// Returns the configuration options for this filter. /// + /// Defines the "count" parameter as required with no default. + /// /// # Returns /// - /// * `Vec` - A vector describing the filter's configurable parameters + /// Vector of `FilterOption` describing parameters. fn options(&self) -> Vec { vec![ FilterOption { @@ -84,20 +123,43 @@ 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, } +/// 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. impl HeadLinesFilter { /// Creates a new `HeadLinesFilter` that will read up to the specified number of lines. /// /// # Arguments /// - /// * `count` - The maximum number of lines to read from the input + /// * `count` - The maximum number of lines to read from the input. /// /// # Returns /// - /// * `Self` - A new instance of `HeadLinesFilter` + /// A new instance configured to read at most `count` lines. + /// + /// # Examples + /// + /// ``` + /// let filter = HeadLinesFilter::new(3); + /// assert_eq!(filter.remaining, 3); + /// ``` pub fn new(count: usize) -> Self { Self { remaining: count, @@ -105,19 +167,30 @@ impl HeadLinesFilter { } } +/// Filters input by reading only the first N lines and writing them to the output. +/// +/// Uses buffered line reading to process input line-by-line until the limit or EOF. +/// +/// # Arguments +/// +/// * `reader` - Boxed mutable reference to the input data stream. +/// * `writer` - Boxed mutable reference to the output stream. +/// +/// # Returns +/// +/// * `Result<()>` - Success if filtering completes, or I/O error. +/// +/// # Errors +/// +/// * `io::Error` from line reading or writing operations. +/// +/// # Examples +/// +/// ``` +/// // Assuming a filter chain with head_lines(2) +/// // Input "Line1\nLine2\nLine3" becomes "Line1\nLine2\n" +/// ``` impl FilterPlugin for HeadLinesFilter { - /// Filters the input by reading only the first N lines and writing them to the output. - /// - /// # 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 - /// - /// # Returns - /// - /// * `Result<()>` - - /// * `Ok(())` on success - /// * `Err(io::Error)` if reading or writing fails fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> { if self.remaining == 0 { return Ok(()); @@ -137,9 +210,11 @@ impl FilterPlugin for HeadLinesFilter { /// Clones this filter into a new boxed instance. /// + /// Creates an independent copy with the same configuration. + /// /// # Returns /// - /// * `Box` - A new boxed clone of this filter + /// A new `Box` clone. fn clone_box(&self) -> Box { Box::new(Self { remaining: self.remaining, @@ -148,9 +223,11 @@ impl FilterPlugin for HeadLinesFilter { /// Returns the configuration options for this filter. /// + /// Defines the "count" parameter as required with no default. + /// /// # Returns /// - /// * `Vec` - A vector describing the filter's configurable parameters + /// Vector of `FilterOption` describing parameters. fn options(&self) -> Vec { vec![ FilterOption { diff --git a/src/lib.rs b/src/lib.rs index d71f14f..1f2b36e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,32 @@ #![deny(unsafe_code)] #![allow(unused_imports)] +//! Keep library for managing temporary files with compression and metadata. +//! +//! This library provides core functionality for the Keep application, including +//! database operations, compression engines, item services, and plugin systems +//! for metadata and filtering. It supports CLI modes, server APIs, and plugin +//! registration via ctors. +//! +//! # Usage +//! +//! Add to Cargo.toml and use re-exported types: +//! ```toml +//! [dependencies] +//! keep = "0.1" +//! ``` +//! +//! ```rust +//! use keep::Args; +//! let args = Args::parse(); +//! ``` +//! +//! # Features +//! +//! - `server`: Enables Axum-based HTTP server. +//! - `gzip`, `lz4`: Built-in compression support. +//! - `magic`: File type detection via libmagic. + // Re-export modules for testing pub mod common; pub mod compression_engine; @@ -39,7 +65,16 @@ use meta_plugin::{ #[allow(unused_imports)] use meta_plugin::magic_file; -// Initialize plugins at library load time +/// Initializes plugins at library load time. +/// +/// Ensures all filter and meta plugins are registered via their ctors. +/// Call this early in application startup if needed (though ctors handle most cases). +/// +/// # Examples +/// +/// ``` +/// keep::init_plugins(); +/// ``` pub fn init_plugins() { // This will be expanded in Step 3 implementation // For now, the ctors handle registration