ipfrs_core/
config.rs

1//! Centralized configuration management for IPFRS
2//!
3//! This module provides a unified configuration system for all IPFRS operations,
4//! including environment variable support, validation, and preset configurations
5//! for common use cases.
6//!
7//! # Example
8//!
9//! ```rust
10//! use ipfrs_core::config::{Config, ConfigBuilder};
11//! use ipfrs_core::HashAlgorithm;
12//!
13//! // Use default configuration
14//! let config = Config::default();
15//!
16//! // Build custom configuration
17//! let config = ConfigBuilder::new()
18//!     .chunk_size(512 * 1024)
19//!     .hash_algorithm(HashAlgorithm::Sha3_256)
20//!     .enable_metrics(true)
21//!     .build()
22//!     .unwrap();
23//!
24//! // Use preset for high-performance scenarios
25//! let config = Config::high_performance();
26//!
27//! println!("Chunk size: {}", config.chunk_size);
28//! println!("Hash algorithm: {:?}", config.hash_algorithm);
29//! ```
30
31use crate::chunking::{ChunkingStrategy, DEFAULT_CHUNK_SIZE, MAX_CHUNK_SIZE, MIN_CHUNK_SIZE};
32use crate::cid::HashAlgorithm;
33use crate::error::{Error, Result};
34use once_cell::sync::Lazy;
35use std::sync::{Arc, RwLock};
36
37/// Global configuration instance
38pub static GLOBAL_CONFIG: Lazy<Arc<RwLock<Config>>> =
39    Lazy::new(|| Arc::new(RwLock::new(Config::default())));
40
41/// Get the global configuration
42///
43/// # Example
44///
45/// ```rust
46/// use ipfrs_core::config::global_config;
47///
48/// let config = global_config();
49/// let chunk_size = config.read().unwrap().chunk_size;
50/// ```
51pub fn global_config() -> Arc<RwLock<Config>> {
52    Arc::clone(&GLOBAL_CONFIG)
53}
54
55/// Set the global configuration
56///
57/// # Example
58///
59/// ```rust
60/// use ipfrs_core::config::{set_global_config, Config};
61///
62/// let config = Config::high_performance();
63/// set_global_config(config);
64/// ```
65pub fn set_global_config(config: Config) {
66    *GLOBAL_CONFIG.write().unwrap() = config;
67}
68
69/// Main configuration for IPFRS operations
70#[derive(Debug, Clone)]
71pub struct Config {
72    // Chunking settings
73    /// Size of each chunk in bytes
74    pub chunk_size: usize,
75    /// Chunking strategy to use
76    pub chunking_strategy: ChunkingStrategy,
77    /// Maximum links per DAG node
78    pub max_links_per_node: usize,
79
80    // Hashing settings
81    /// Hash algorithm for CID generation
82    pub hash_algorithm: HashAlgorithm,
83
84    // Performance settings
85    /// Number of threads for parallel operations (None = use all available)
86    pub num_threads: Option<usize>,
87    /// Enable parallel chunking for large files
88    pub enable_parallel_chunking: bool,
89    /// Threshold for switching to parallel chunking (bytes)
90    pub parallel_threshold: usize,
91
92    // Memory settings
93    /// Enable memory pooling for allocations
94    pub enable_pooling: bool,
95    /// Maximum pool size in bytes
96    pub pool_max_size: usize,
97
98    // Metrics and observability
99    /// Enable metrics collection
100    pub enable_metrics: bool,
101    /// Maximum metrics samples to keep
102    pub metrics_max_samples: usize,
103
104    // Validation settings
105    /// Enable block verification on read
106    pub verify_blocks: bool,
107    /// Enable CID validation on parse
108    pub validate_cids: bool,
109
110    // Storage settings
111    /// Enable compression for storage
112    pub enable_compression: bool,
113    /// Compression level (0-9, where 0 is no compression)
114    pub compression_level: u8,
115}
116
117impl Default for Config {
118    fn default() -> Self {
119        Self {
120            // Chunking defaults
121            chunk_size: DEFAULT_CHUNK_SIZE,
122            chunking_strategy: ChunkingStrategy::FixedSize,
123            max_links_per_node: 174,
124
125            // Hashing defaults
126            hash_algorithm: HashAlgorithm::Sha256,
127
128            // Performance defaults
129            num_threads: None,
130            enable_parallel_chunking: true,
131            parallel_threshold: 1_000_000, // 1MB
132
133            // Memory defaults
134            enable_pooling: true,
135            pool_max_size: 100 * 1024 * 1024, // 100MB
136
137            // Metrics defaults
138            enable_metrics: true,
139            metrics_max_samples: 10_000,
140
141            // Validation defaults
142            verify_blocks: true,
143            validate_cids: true,
144
145            // Storage defaults
146            enable_compression: false,
147            compression_level: 3,
148        }
149    }
150}
151
152impl Config {
153    /// Create a new configuration with default values
154    pub fn new() -> Self {
155        Self::default()
156    }
157
158    /// Validate the configuration
159    pub fn validate(&self) -> Result<()> {
160        // Validate chunk size
161        if self.chunk_size < MIN_CHUNK_SIZE {
162            return Err(Error::InvalidInput(format!(
163                "Chunk size {} is below minimum {}",
164                self.chunk_size, MIN_CHUNK_SIZE
165            )));
166        }
167        if self.chunk_size > MAX_CHUNK_SIZE {
168            return Err(Error::InvalidInput(format!(
169                "Chunk size {} exceeds maximum {}",
170                self.chunk_size, MAX_CHUNK_SIZE
171            )));
172        }
173
174        // Validate compression level
175        if self.compression_level > 9 {
176            return Err(Error::InvalidInput(format!(
177                "Compression level {} exceeds maximum 9",
178                self.compression_level
179            )));
180        }
181
182        // Validate pool size
183        if self.pool_max_size == 0 && self.enable_pooling {
184            return Err(Error::InvalidInput(
185                "Pool max size cannot be zero when pooling is enabled".to_string(),
186            ));
187        }
188
189        Ok(())
190    }
191
192    // === Preset Configurations ===
193
194    /// Configuration optimized for high performance
195    ///
196    /// Uses SHA3-256 hashing, parallel processing, and larger chunk sizes.
197    pub fn high_performance() -> Self {
198        Self {
199            hash_algorithm: HashAlgorithm::Sha3_256,
200            chunk_size: 512 * 1024, // 512KB
201            enable_parallel_chunking: true,
202            parallel_threshold: 100_000, // 100KB
203            num_threads: None,           // Use all available
204            chunking_strategy: ChunkingStrategy::FixedSize,
205            enable_pooling: true,
206            pool_max_size: 200 * 1024 * 1024, // 200MB
207            enable_metrics: true,
208            ..Default::default()
209        }
210    }
211
212    /// Configuration optimized for storage efficiency
213    ///
214    /// Uses content-defined chunking for better deduplication.
215    pub fn storage_optimized() -> Self {
216        Self {
217            chunking_strategy: ChunkingStrategy::ContentDefined,
218            chunk_size: 128 * 1024, // 128KB for better deduplication
219            enable_compression: true,
220            compression_level: 6,
221            hash_algorithm: HashAlgorithm::Sha256,
222            ..Default::default()
223        }
224    }
225
226    /// Configuration for embedded/resource-constrained systems
227    ///
228    /// Uses smaller chunks, less parallelism, and minimal memory.
229    pub fn embedded() -> Self {
230        Self {
231            chunk_size: 64 * 1024, // 64KB
232            enable_parallel_chunking: false,
233            num_threads: Some(1),
234            enable_pooling: false,
235            pool_max_size: 10 * 1024 * 1024, // 10MB
236            enable_metrics: false,
237            hash_algorithm: HashAlgorithm::Sha256,
238            ..Default::default()
239        }
240    }
241
242    /// Configuration for testing and development
243    ///
244    /// Enables all validations and uses smaller sizes for faster tests.
245    pub fn testing() -> Self {
246        Self {
247            chunk_size: 16 * 1024, // 16KB
248            verify_blocks: true,
249            validate_cids: true,
250            enable_metrics: true,
251            enable_parallel_chunking: false, // More deterministic
252            hash_algorithm: HashAlgorithm::Sha256,
253            ..Default::default()
254        }
255    }
256}
257
258/// Builder for creating custom configurations
259#[derive(Debug, Default)]
260pub struct ConfigBuilder {
261    config: Config,
262}
263
264impl ConfigBuilder {
265    /// Create a new configuration builder
266    pub fn new() -> Self {
267        Self {
268            config: Config::default(),
269        }
270    }
271
272    /// Set chunk size
273    pub fn chunk_size(mut self, size: usize) -> Self {
274        self.config.chunk_size = size;
275        self
276    }
277
278    /// Set chunking strategy
279    pub fn chunking_strategy(mut self, strategy: ChunkingStrategy) -> Self {
280        self.config.chunking_strategy = strategy;
281        self
282    }
283
284    /// Set hash algorithm
285    pub fn hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
286        self.config.hash_algorithm = algorithm;
287        self
288    }
289
290    /// Set number of threads
291    pub fn num_threads(mut self, threads: usize) -> Self {
292        self.config.num_threads = Some(threads);
293        self
294    }
295
296    /// Enable or disable parallel chunking
297    pub fn enable_parallel_chunking(mut self, enable: bool) -> Self {
298        self.config.enable_parallel_chunking = enable;
299        self
300    }
301
302    /// Set parallel chunking threshold
303    pub fn parallel_threshold(mut self, threshold: usize) -> Self {
304        self.config.parallel_threshold = threshold;
305        self
306    }
307
308    /// Enable or disable memory pooling
309    pub fn enable_pooling(mut self, enable: bool) -> Self {
310        self.config.enable_pooling = enable;
311        self
312    }
313
314    /// Set maximum pool size
315    pub fn pool_max_size(mut self, size: usize) -> Self {
316        self.config.pool_max_size = size;
317        self
318    }
319
320    /// Enable or disable metrics collection
321    pub fn enable_metrics(mut self, enable: bool) -> Self {
322        self.config.enable_metrics = enable;
323        self
324    }
325
326    /// Set maximum metrics samples
327    pub fn metrics_max_samples(mut self, samples: usize) -> Self {
328        self.config.metrics_max_samples = samples;
329        self
330    }
331
332    /// Enable or disable block verification
333    pub fn verify_blocks(mut self, verify: bool) -> Self {
334        self.config.verify_blocks = verify;
335        self
336    }
337
338    /// Enable or disable CID validation
339    pub fn validate_cids(mut self, validate: bool) -> Self {
340        self.config.validate_cids = validate;
341        self
342    }
343
344    /// Enable or disable compression
345    pub fn enable_compression(mut self, enable: bool) -> Self {
346        self.config.enable_compression = enable;
347        self
348    }
349
350    /// Set compression level
351    pub fn compression_level(mut self, level: u8) -> Self {
352        self.config.compression_level = level;
353        self
354    }
355
356    /// Build the configuration
357    pub fn build(self) -> Result<Config> {
358        self.config.validate()?;
359        Ok(self.config)
360    }
361}
362
363#[cfg(test)]
364mod tests {
365    use super::*;
366
367    #[test]
368    fn test_default_config() {
369        let config = Config::default();
370        assert_eq!(config.chunk_size, DEFAULT_CHUNK_SIZE);
371        assert!(config.enable_metrics);
372        assert!(config.verify_blocks);
373    }
374
375    #[test]
376    fn test_config_validation() {
377        let mut config = Config::default();
378        assert!(config.validate().is_ok());
379
380        // Invalid chunk size
381        config.chunk_size = 100;
382        assert!(config.validate().is_err());
383
384        // Valid chunk size
385        config.chunk_size = 128 * 1024;
386        assert!(config.validate().is_ok());
387
388        // Invalid compression level
389        config.compression_level = 10;
390        assert!(config.validate().is_err());
391    }
392
393    #[test]
394    fn test_high_performance_preset() {
395        let config = Config::high_performance();
396        assert_eq!(config.hash_algorithm, HashAlgorithm::Sha3_256);
397        assert_eq!(config.chunk_size, 512 * 1024);
398        assert!(config.enable_parallel_chunking);
399    }
400
401    #[test]
402    fn test_storage_optimized_preset() {
403        let config = Config::storage_optimized();
404        assert_eq!(config.chunking_strategy, ChunkingStrategy::ContentDefined);
405        assert!(config.enable_compression);
406        assert_eq!(config.compression_level, 6);
407    }
408
409    #[test]
410    fn test_embedded_preset() {
411        let config = Config::embedded();
412        assert_eq!(config.chunk_size, 64 * 1024);
413        assert!(!config.enable_parallel_chunking);
414        assert_eq!(config.num_threads, Some(1));
415        assert!(!config.enable_pooling);
416    }
417
418    #[test]
419    fn test_testing_preset() {
420        let config = Config::testing();
421        assert_eq!(config.chunk_size, 16 * 1024);
422        assert!(config.verify_blocks);
423        assert!(config.validate_cids);
424        assert!(!config.enable_parallel_chunking);
425    }
426
427    #[test]
428    fn test_config_builder() {
429        let config = ConfigBuilder::new()
430            .chunk_size(256 * 1024)
431            .hash_algorithm(HashAlgorithm::Sha3_256)
432            .enable_metrics(true)
433            .num_threads(4)
434            .build()
435            .unwrap();
436
437        assert_eq!(config.chunk_size, 256 * 1024);
438        assert_eq!(config.hash_algorithm, HashAlgorithm::Sha3_256);
439        assert!(config.enable_metrics);
440        assert_eq!(config.num_threads, Some(4));
441    }
442
443    #[test]
444    fn test_config_builder_validation() {
445        let result = ConfigBuilder::new().chunk_size(100).build();
446        assert!(result.is_err());
447
448        let result = ConfigBuilder::new().chunk_size(128 * 1024).build();
449        assert!(result.is_ok());
450    }
451
452    #[test]
453    fn test_global_config() {
454        let config = global_config();
455        {
456            let cfg = config.read().unwrap();
457            assert_eq!(cfg.chunk_size, DEFAULT_CHUNK_SIZE);
458        }
459
460        // Set new global config
461        set_global_config(Config::high_performance());
462
463        {
464            let cfg = config.read().unwrap();
465            assert_eq!(cfg.hash_algorithm, HashAlgorithm::Sha3_256);
466        }
467
468        // Reset to default for other tests
469        set_global_config(Config::default());
470    }
471
472    #[test]
473    fn test_builder_fluent_interface() {
474        let config = ConfigBuilder::new()
475            .chunk_size(128 * 1024)
476            .enable_parallel_chunking(true)
477            .parallel_threshold(500_000)
478            .enable_compression(true)
479            .compression_level(5)
480            .build()
481            .unwrap();
482
483        assert_eq!(config.chunk_size, 128 * 1024);
484        assert!(config.enable_parallel_chunking);
485        assert_eq!(config.parallel_threshold, 500_000);
486        assert!(config.enable_compression);
487        assert_eq!(config.compression_level, 5);
488    }
489}