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}