Skip to main content

crush_core/plugin/
contract.rs

1//! Plugin contract trait definition
2//!
3//! This module defines the `CompressionAlgorithm` trait that all plugins must implement.
4//! Plugins register themselves at compile-time using the `linkme` distributed slice pattern.
5
6use crate::error::Result;
7use crate::plugin::PluginMetadata;
8use std::sync::atomic::AtomicBool;
9use std::sync::Arc;
10
11/// Trait that all compression plugins must implement
12///
13/// Plugins provide compress/decompress operations with cooperative cancellation support.
14/// The `cancel_flag` parameter allows the library to signal timeout or user cancellation,
15/// and plugins SHOULD check this flag periodically (e.g., every block) and return early if set.
16///
17/// # Safety and Error Handling
18///
19/// - All methods return `Result<T>` to enable proper error propagation
20/// - Plugins MUST NOT panic - all errors should be returned via `Result`
21/// - Plugins MUST validate input data before processing
22/// - If `cancel_flag` is set to `true`, plugins SHOULD return `Err(PluginError::Cancelled)`
23///
24/// # Example Implementation
25///
26/// ```no_run
27/// use crush_core::plugin::{CompressionAlgorithm, PluginMetadata, COMPRESSION_ALGORITHMS};
28/// use crush_core::error::Result;
29/// use std::sync::Arc;
30/// use std::sync::atomic::{AtomicBool, Ordering};
31/// use linkme::distributed_slice;
32///
33/// struct MyPlugin;
34///
35/// impl CompressionAlgorithm for MyPlugin {
36///     fn name(&self) -> &'static str { "my_plugin" }
37///
38///     fn metadata(&self) -> PluginMetadata {
39///         PluginMetadata {
40///             name: "my_plugin",
41///             version: "1.0.0",
42///             magic_number: [0x43, 0x52, 0x01, 0x10],
43///             throughput: 500.0,
44///             compression_ratio: 0.65,
45///             description: "My compression algorithm",
46///         }
47///     }
48///
49///     fn compress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>> {
50///         let mut output = Vec::new();
51///         for chunk in input.chunks(4096) {
52///             // Check cancellation flag periodically
53///             if cancel_flag.load(Ordering::Acquire) {
54///                 return Err(crush_core::error::PluginError::Cancelled.into());
55///             }
56///             // Compress chunk...
57///         }
58///         Ok(output)
59///     }
60///
61///     fn decompress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>> {
62///         // Similar implementation with cancellation checking
63///         Ok(vec![])
64///     }
65///
66///     fn detect(&self, file_header: &[u8]) -> bool {
67///         file_header.len() >= 4 && file_header[0..4] == self.metadata().magic_number
68///     }
69/// }
70///
71/// // Register plugin at compile-time
72/// #[distributed_slice(COMPRESSION_ALGORITHMS)]
73/// static MY_PLUGIN: &dyn CompressionAlgorithm = &MyPlugin;
74/// ```
75pub trait CompressionAlgorithm: Send + Sync {
76    /// Plugin name (must be unique, used for manual selection)
77    fn name(&self) -> &'static str;
78
79    /// Plugin metadata (performance characteristics and identification)
80    fn metadata(&self) -> PluginMetadata;
81
82    /// Compress input data
83    ///
84    /// # Parameters
85    ///
86    /// - `input`: Raw uncompressed data to compress
87    /// - `cancel_flag`: Atomic flag for cooperative cancellation
88    ///   Plugins SHOULD check `cancel_flag.load(Ordering::Acquire)` periodically
89    ///   and return `Err(PluginError::Cancelled)` if set to `true`
90    ///
91    /// # Returns
92    ///
93    /// Compressed data without any header (header is added by the library)
94    ///
95    /// # Errors
96    ///
97    /// - `PluginError::Cancelled` if `cancel_flag` is set
98    /// - `PluginError::OperationFailed` for compression errors
99    /// - `ValidationError::CorruptedData` if input is invalid
100    fn compress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>>;
101
102    /// Decompress compressed data
103    ///
104    /// # Parameters
105    ///
106    /// - `input`: Compressed data (without Crush header, header already removed by library)
107    /// - `cancel_flag`: Atomic flag for cooperative cancellation
108    ///
109    /// # Returns
110    ///
111    /// Original uncompressed data
112    ///
113    /// # Errors
114    ///
115    /// - `PluginError::Cancelled` if `cancel_flag` is set
116    /// - `PluginError::OperationFailed` for decompression errors
117    /// - `ValidationError::CorruptedData` if compressed data is invalid
118    fn decompress(&self, input: &[u8], cancel_flag: Arc<AtomicBool>) -> Result<Vec<u8>>;
119
120    /// Detect if this plugin can handle the given file header
121    ///
122    /// This method determines if the plugin supports compressing a particular file type.
123    /// Implementation is plugin-specific and may check:
124    /// - File extension (if passed as metadata)
125    /// - Magic bytes at the start of the file
126    /// - Content analysis
127    ///
128    /// For decompression routing, the library uses the magic number in the Crush header,
129    /// so this method is primarily for compression-time file type detection.
130    ///
131    /// # Parameters
132    ///
133    /// - `file_header`: First bytes of the file (typically 512-4096 bytes)
134    ///
135    /// # Returns
136    ///
137    /// `true` if this plugin can compress this file type, `false` otherwise
138    ///
139    /// # Performance Note
140    ///
141    /// This method should execute in sub-millisecond time as it may be called
142    /// for every plugin during file type detection.
143    fn detect(&self, file_header: &[u8]) -> bool;
144}