docs: Add rustdoc for filter_plugin, binary_detection, and lib.rs

Co-authored-by: aider (openai/andrew/openrouter/sonoma-sky-alpha) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-09-10 15:48:54 -03:00
parent b257a74162
commit e9ab630a74
4 changed files with 206 additions and 48 deletions

15
PLAN.md
View File

@@ -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. - Impl `MetaPlugin` methods (`is_finalized`, `set_finalized`, `finalize`, `update`, `meta_type`, `outputs`, etc.): No docs.
- Overall: Lacks docs for the public trait impl. - 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] 7. **src/services/filter_service.rs** [DONE]
- `FilterService` struct: Partial doc. - `FilterService` struct: Partial doc.
- `new()`, `create_filter_chain()`, `filter_data()`, `process_with_filter()` methods: Partial or missing. - `new()`, `create_filter_chain()`, `filter_data()`, `process_with_filter()` methods: Partial or missing.

View File

@@ -17,6 +17,39 @@ use std::collections::HashMap;
/// * `Result<(), StatusCode>` - /// * `Result<(), StatusCode>` -
/// * `Ok(())` if binary content is allowed or content is not binary /// * `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 /// * `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( pub async fn check_binary_content_allowed(
item_service: &AsyncItemService, item_service: &AsyncItemService,
item_id: i64, item_id: i64,
@@ -46,6 +79,34 @@ pub async fn check_binary_content_allowed(
/// * `Ok(true)` if content is binary /// * `Ok(true)` if content is binary
/// * `Ok(false)` if content is text /// * `Ok(false)` if content is text
/// * `Err(StatusCode)` if an error occurs during checking /// * `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<bool, StatusCode>` -
/// * `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( pub async fn is_content_binary(
item_service: &AsyncItemService, item_service: &AsyncItemService,
item_id: i64, item_id: i64,

View File

@@ -4,20 +4,43 @@ use crate::common::PIPESIZE;
use crate::services::filter_service::register_filter_plugin; 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.
/// 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 { pub struct HeadBytesFilter {
remaining: usize, 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 { impl HeadBytesFilter {
/// Creates a new `HeadBytesFilter` that will read up to the specified number of bytes. /// Creates a new `HeadBytesFilter` that will read up to the specified number of bytes.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `count` - The maximum number of bytes to read from the input /// * `count` - The maximum number of bytes to read from the input.
/// ///
/// # Returns /// # 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 { pub fn new(count: usize) -> Self {
Self { Self {
remaining: count, remaining: count,
@@ -25,19 +48,31 @@ impl HeadBytesFilter {
} }
} }
impl FilterPlugin for HeadBytesFilter { /// Filters input by reading only the first N bytes and writing them to the output.
/// Filters the 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 /// # Arguments
/// ///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream /// * `reader` - Boxed mutable reference to the input data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent /// * `writer` - Boxed mutable reference to the output stream.
/// ///
/// # Returns /// # Returns
/// ///
/// * `Result<()>` - /// * `Result<()>` - Success if filtering completes, or I/O error.
/// * `Ok(())` on success ///
/// * `Err(io::Error)` if reading or writing fails /// # 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 {
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> { fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
if self.remaining == 0 { if self.remaining == 0 {
return Ok(()); return Ok(());
@@ -58,9 +93,11 @@ impl FilterPlugin for HeadBytesFilter {
/// Clones this filter into a new boxed instance. /// Clones this filter into a new boxed instance.
/// ///
/// Creates an independent copy with the same configuration.
///
/// # Returns /// # Returns
/// ///
/// * `Box<dyn FilterPlugin>` - A new boxed clone of this filter /// A new `Box<dyn FilterPlugin>` clone.
fn clone_box(&self) -> Box<dyn FilterPlugin> { fn clone_box(&self) -> Box<dyn FilterPlugin> {
Box::new(Self { Box::new(Self {
remaining: self.remaining, remaining: self.remaining,
@@ -69,9 +106,11 @@ impl FilterPlugin for HeadBytesFilter {
/// Returns the configuration options for this filter. /// Returns the configuration options for this filter.
/// ///
/// Defines the "count" parameter as required with no default.
///
/// # Returns /// # Returns
/// ///
/// * `Vec<FilterOption>` - A vector describing the filter's configurable parameters /// Vector of `FilterOption` describing parameters.
fn options(&self) -> Vec<FilterOption> { fn options(&self) -> Vec<FilterOption> {
vec![ vec![
FilterOption { 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.
/// 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 { pub struct HeadLinesFilter {
remaining: usize, 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 { impl HeadLinesFilter {
/// Creates a new `HeadLinesFilter` that will read up to the specified number of lines. /// Creates a new `HeadLinesFilter` that will read up to the specified number of lines.
/// ///
/// # Arguments /// # Arguments
/// ///
/// * `count` - The maximum number of lines to read from the input /// * `count` - The maximum number of lines to read from the input.
/// ///
/// # Returns /// # 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 { pub fn new(count: usize) -> Self {
Self { Self {
remaining: count, remaining: count,
@@ -105,19 +167,30 @@ impl HeadLinesFilter {
} }
} }
impl FilterPlugin for HeadLinesFilter { /// Filters input by reading only the first N lines and writing them to the output.
/// Filters the 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 /// # Arguments
/// ///
/// * `reader` - A boxed mutable reference to the input reader providing the data stream /// * `reader` - Boxed mutable reference to the input data stream.
/// * `writer` - A boxed mutable reference to the output writer where filtered data is sent /// * `writer` - Boxed mutable reference to the output stream.
/// ///
/// # Returns /// # Returns
/// ///
/// * `Result<()>` - /// * `Result<()>` - Success if filtering completes, or I/O error.
/// * `Ok(())` on success ///
/// * `Err(io::Error)` if reading or writing fails /// # 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 {
fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> { fn filter(&mut self, mut reader: Box<&mut dyn Read>, mut writer: Box<&mut dyn Write>) -> Result<()> {
if self.remaining == 0 { if self.remaining == 0 {
return Ok(()); return Ok(());
@@ -137,9 +210,11 @@ impl FilterPlugin for HeadLinesFilter {
/// Clones this filter into a new boxed instance. /// Clones this filter into a new boxed instance.
/// ///
/// Creates an independent copy with the same configuration.
///
/// # Returns /// # Returns
/// ///
/// * `Box<dyn FilterPlugin>` - A new boxed clone of this filter /// A new `Box<dyn FilterPlugin>` clone.
fn clone_box(&self) -> Box<dyn FilterPlugin> { fn clone_box(&self) -> Box<dyn FilterPlugin> {
Box::new(Self { Box::new(Self {
remaining: self.remaining, remaining: self.remaining,
@@ -148,9 +223,11 @@ impl FilterPlugin for HeadLinesFilter {
/// Returns the configuration options for this filter. /// Returns the configuration options for this filter.
/// ///
/// Defines the "count" parameter as required with no default.
///
/// # Returns /// # Returns
/// ///
/// * `Vec<FilterOption>` - A vector describing the filter's configurable parameters /// Vector of `FilterOption` describing parameters.
fn options(&self) -> Vec<FilterOption> { fn options(&self) -> Vec<FilterOption> {
vec![ vec![
FilterOption { FilterOption {

View File

@@ -2,6 +2,32 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
#![allow(unused_imports)] #![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 // Re-export modules for testing
pub mod common; pub mod common;
pub mod compression_engine; pub mod compression_engine;
@@ -39,7 +65,16 @@ use meta_plugin::{
#[allow(unused_imports)] #[allow(unused_imports)]
use meta_plugin::magic_file; 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() { pub fn init_plugins() {
// This will be expanded in Step 3 implementation // This will be expanded in Step 3 implementation
// For now, the ctors handle registration // For now, the ctors handle registration