1use 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
37pub static GLOBAL_CONFIG: Lazy<Arc<RwLock<Config>>> =
39 Lazy::new(|| Arc::new(RwLock::new(Config::default())));
40
41pub fn global_config() -> Arc<RwLock<Config>> {
52 Arc::clone(&GLOBAL_CONFIG)
53}
54
55pub fn set_global_config(config: Config) {
66 *GLOBAL_CONFIG.write().unwrap() = config;
67}
68
69#[derive(Debug, Clone)]
71pub struct Config {
72 pub chunk_size: usize,
75 pub chunking_strategy: ChunkingStrategy,
77 pub max_links_per_node: usize,
79
80 pub hash_algorithm: HashAlgorithm,
83
84 pub num_threads: Option<usize>,
87 pub enable_parallel_chunking: bool,
89 pub parallel_threshold: usize,
91
92 pub enable_pooling: bool,
95 pub pool_max_size: usize,
97
98 pub enable_metrics: bool,
101 pub metrics_max_samples: usize,
103
104 pub verify_blocks: bool,
107 pub validate_cids: bool,
109
110 pub enable_compression: bool,
113 pub compression_level: u8,
115}
116
117impl Default for Config {
118 fn default() -> Self {
119 Self {
120 chunk_size: DEFAULT_CHUNK_SIZE,
122 chunking_strategy: ChunkingStrategy::FixedSize,
123 max_links_per_node: 174,
124
125 hash_algorithm: HashAlgorithm::Sha256,
127
128 num_threads: None,
130 enable_parallel_chunking: true,
131 parallel_threshold: 1_000_000, enable_pooling: true,
135 pool_max_size: 100 * 1024 * 1024, enable_metrics: true,
139 metrics_max_samples: 10_000,
140
141 verify_blocks: true,
143 validate_cids: true,
144
145 enable_compression: false,
147 compression_level: 3,
148 }
149 }
150}
151
152impl Config {
153 pub fn new() -> Self {
155 Self::default()
156 }
157
158 pub fn validate(&self) -> Result<()> {
160 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 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 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 pub fn high_performance() -> Self {
198 Self {
199 hash_algorithm: HashAlgorithm::Sha3_256,
200 chunk_size: 512 * 1024, enable_parallel_chunking: true,
202 parallel_threshold: 100_000, num_threads: None, chunking_strategy: ChunkingStrategy::FixedSize,
205 enable_pooling: true,
206 pool_max_size: 200 * 1024 * 1024, enable_metrics: true,
208 ..Default::default()
209 }
210 }
211
212 pub fn storage_optimized() -> Self {
216 Self {
217 chunking_strategy: ChunkingStrategy::ContentDefined,
218 chunk_size: 128 * 1024, enable_compression: true,
220 compression_level: 6,
221 hash_algorithm: HashAlgorithm::Sha256,
222 ..Default::default()
223 }
224 }
225
226 pub fn embedded() -> Self {
230 Self {
231 chunk_size: 64 * 1024, enable_parallel_chunking: false,
233 num_threads: Some(1),
234 enable_pooling: false,
235 pool_max_size: 10 * 1024 * 1024, enable_metrics: false,
237 hash_algorithm: HashAlgorithm::Sha256,
238 ..Default::default()
239 }
240 }
241
242 pub fn testing() -> Self {
246 Self {
247 chunk_size: 16 * 1024, verify_blocks: true,
249 validate_cids: true,
250 enable_metrics: true,
251 enable_parallel_chunking: false, hash_algorithm: HashAlgorithm::Sha256,
253 ..Default::default()
254 }
255 }
256}
257
258#[derive(Debug, Default)]
260pub struct ConfigBuilder {
261 config: Config,
262}
263
264impl ConfigBuilder {
265 pub fn new() -> Self {
267 Self {
268 config: Config::default(),
269 }
270 }
271
272 pub fn chunk_size(mut self, size: usize) -> Self {
274 self.config.chunk_size = size;
275 self
276 }
277
278 pub fn chunking_strategy(mut self, strategy: ChunkingStrategy) -> Self {
280 self.config.chunking_strategy = strategy;
281 self
282 }
283
284 pub fn hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
286 self.config.hash_algorithm = algorithm;
287 self
288 }
289
290 pub fn num_threads(mut self, threads: usize) -> Self {
292 self.config.num_threads = Some(threads);
293 self
294 }
295
296 pub fn enable_parallel_chunking(mut self, enable: bool) -> Self {
298 self.config.enable_parallel_chunking = enable;
299 self
300 }
301
302 pub fn parallel_threshold(mut self, threshold: usize) -> Self {
304 self.config.parallel_threshold = threshold;
305 self
306 }
307
308 pub fn enable_pooling(mut self, enable: bool) -> Self {
310 self.config.enable_pooling = enable;
311 self
312 }
313
314 pub fn pool_max_size(mut self, size: usize) -> Self {
316 self.config.pool_max_size = size;
317 self
318 }
319
320 pub fn enable_metrics(mut self, enable: bool) -> Self {
322 self.config.enable_metrics = enable;
323 self
324 }
325
326 pub fn metrics_max_samples(mut self, samples: usize) -> Self {
328 self.config.metrics_max_samples = samples;
329 self
330 }
331
332 pub fn verify_blocks(mut self, verify: bool) -> Self {
334 self.config.verify_blocks = verify;
335 self
336 }
337
338 pub fn validate_cids(mut self, validate: bool) -> Self {
340 self.config.validate_cids = validate;
341 self
342 }
343
344 pub fn enable_compression(mut self, enable: bool) -> Self {
346 self.config.enable_compression = enable;
347 self
348 }
349
350 pub fn compression_level(mut self, level: u8) -> Self {
352 self.config.compression_level = level;
353 self
354 }
355
356 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 config.chunk_size = 100;
382 assert!(config.validate().is_err());
383
384 config.chunk_size = 128 * 1024;
386 assert!(config.validate().is_ok());
387
388 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_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 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}