use crate::chunking::{ChunkingStrategy, DEFAULT_CHUNK_SIZE, MAX_CHUNK_SIZE, MIN_CHUNK_SIZE};
use crate::cid::HashAlgorithm;
use crate::error::{Error, Result};
use once_cell::sync::Lazy;
use std::sync::{Arc, RwLock};
pub static GLOBAL_CONFIG: Lazy<Arc<RwLock<Config>>> =
Lazy::new(|| Arc::new(RwLock::new(Config::default())));
pub fn global_config() -> Arc<RwLock<Config>> {
Arc::clone(&GLOBAL_CONFIG)
}
pub fn set_global_config(config: Config) {
*GLOBAL_CONFIG.write().unwrap_or_else(|e| e.into_inner()) = config;
}
#[derive(Debug, Clone)]
pub struct Config {
pub chunk_size: usize,
pub chunking_strategy: ChunkingStrategy,
pub max_links_per_node: usize,
pub hash_algorithm: HashAlgorithm,
pub num_threads: Option<usize>,
pub enable_parallel_chunking: bool,
pub parallel_threshold: usize,
pub enable_pooling: bool,
pub pool_max_size: usize,
pub enable_metrics: bool,
pub metrics_max_samples: usize,
pub verify_blocks: bool,
pub validate_cids: bool,
pub enable_compression: bool,
pub compression_level: u8,
}
impl Default for Config {
fn default() -> Self {
Self {
chunk_size: DEFAULT_CHUNK_SIZE,
chunking_strategy: ChunkingStrategy::FixedSize,
max_links_per_node: 174,
hash_algorithm: HashAlgorithm::Sha256,
num_threads: None,
enable_parallel_chunking: true,
parallel_threshold: 1_000_000,
enable_pooling: true,
pool_max_size: 100 * 1024 * 1024,
enable_metrics: true,
metrics_max_samples: 10_000,
verify_blocks: true,
validate_cids: true,
enable_compression: false,
compression_level: 3,
}
}
}
impl Config {
pub fn new() -> Self {
Self::default()
}
pub fn validate(&self) -> Result<()> {
if self.chunk_size < MIN_CHUNK_SIZE {
return Err(Error::InvalidInput(format!(
"Chunk size {} is below minimum {}",
self.chunk_size, MIN_CHUNK_SIZE
)));
}
if self.chunk_size > MAX_CHUNK_SIZE {
return Err(Error::InvalidInput(format!(
"Chunk size {} exceeds maximum {}",
self.chunk_size, MAX_CHUNK_SIZE
)));
}
if self.compression_level > 9 {
return Err(Error::InvalidInput(format!(
"Compression level {} exceeds maximum 9",
self.compression_level
)));
}
if self.pool_max_size == 0 && self.enable_pooling {
return Err(Error::InvalidInput(
"Pool max size cannot be zero when pooling is enabled".to_string(),
));
}
Ok(())
}
pub fn high_performance() -> Self {
Self {
hash_algorithm: HashAlgorithm::Sha3_256,
chunk_size: 512 * 1024, enable_parallel_chunking: true,
parallel_threshold: 100_000, num_threads: None, chunking_strategy: ChunkingStrategy::FixedSize,
enable_pooling: true,
pool_max_size: 200 * 1024 * 1024, enable_metrics: true,
..Default::default()
}
}
pub fn storage_optimized() -> Self {
Self {
chunking_strategy: ChunkingStrategy::ContentDefined,
chunk_size: 128 * 1024, enable_compression: true,
compression_level: 6,
hash_algorithm: HashAlgorithm::Sha256,
..Default::default()
}
}
pub fn embedded() -> Self {
Self {
chunk_size: 64 * 1024, enable_parallel_chunking: false,
num_threads: Some(1),
enable_pooling: false,
pool_max_size: 10 * 1024 * 1024, enable_metrics: false,
hash_algorithm: HashAlgorithm::Sha256,
..Default::default()
}
}
pub fn testing() -> Self {
Self {
chunk_size: 16 * 1024, verify_blocks: true,
validate_cids: true,
enable_metrics: true,
enable_parallel_chunking: false, hash_algorithm: HashAlgorithm::Sha256,
..Default::default()
}
}
}
#[derive(Debug, Default)]
pub struct ConfigBuilder {
config: Config,
}
impl ConfigBuilder {
pub fn new() -> Self {
Self {
config: Config::default(),
}
}
pub fn chunk_size(mut self, size: usize) -> Self {
self.config.chunk_size = size;
self
}
pub fn chunking_strategy(mut self, strategy: ChunkingStrategy) -> Self {
self.config.chunking_strategy = strategy;
self
}
pub fn hash_algorithm(mut self, algorithm: HashAlgorithm) -> Self {
self.config.hash_algorithm = algorithm;
self
}
pub fn num_threads(mut self, threads: usize) -> Self {
self.config.num_threads = Some(threads);
self
}
pub fn enable_parallel_chunking(mut self, enable: bool) -> Self {
self.config.enable_parallel_chunking = enable;
self
}
pub fn parallel_threshold(mut self, threshold: usize) -> Self {
self.config.parallel_threshold = threshold;
self
}
pub fn enable_pooling(mut self, enable: bool) -> Self {
self.config.enable_pooling = enable;
self
}
pub fn pool_max_size(mut self, size: usize) -> Self {
self.config.pool_max_size = size;
self
}
pub fn enable_metrics(mut self, enable: bool) -> Self {
self.config.enable_metrics = enable;
self
}
pub fn metrics_max_samples(mut self, samples: usize) -> Self {
self.config.metrics_max_samples = samples;
self
}
pub fn verify_blocks(mut self, verify: bool) -> Self {
self.config.verify_blocks = verify;
self
}
pub fn validate_cids(mut self, validate: bool) -> Self {
self.config.validate_cids = validate;
self
}
pub fn enable_compression(mut self, enable: bool) -> Self {
self.config.enable_compression = enable;
self
}
pub fn compression_level(mut self, level: u8) -> Self {
self.config.compression_level = level;
self
}
pub fn build(self) -> Result<Config> {
self.config.validate()?;
Ok(self.config)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_config() {
let config = Config::default();
assert_eq!(config.chunk_size, DEFAULT_CHUNK_SIZE);
assert!(config.enable_metrics);
assert!(config.verify_blocks);
}
#[test]
fn test_config_validation() {
let mut config = Config::default();
assert!(config.validate().is_ok());
config.chunk_size = 100;
assert!(config.validate().is_err());
config.chunk_size = 128 * 1024;
assert!(config.validate().is_ok());
config.compression_level = 10;
assert!(config.validate().is_err());
}
#[test]
fn test_high_performance_preset() {
let config = Config::high_performance();
assert_eq!(config.hash_algorithm, HashAlgorithm::Sha3_256);
assert_eq!(config.chunk_size, 512 * 1024);
assert!(config.enable_parallel_chunking);
}
#[test]
fn test_storage_optimized_preset() {
let config = Config::storage_optimized();
assert_eq!(config.chunking_strategy, ChunkingStrategy::ContentDefined);
assert!(config.enable_compression);
assert_eq!(config.compression_level, 6);
}
#[test]
fn test_embedded_preset() {
let config = Config::embedded();
assert_eq!(config.chunk_size, 64 * 1024);
assert!(!config.enable_parallel_chunking);
assert_eq!(config.num_threads, Some(1));
assert!(!config.enable_pooling);
}
#[test]
fn test_testing_preset() {
let config = Config::testing();
assert_eq!(config.chunk_size, 16 * 1024);
assert!(config.verify_blocks);
assert!(config.validate_cids);
assert!(!config.enable_parallel_chunking);
}
#[test]
fn test_config_builder() {
let config = ConfigBuilder::new()
.chunk_size(256 * 1024)
.hash_algorithm(HashAlgorithm::Sha3_256)
.enable_metrics(true)
.num_threads(4)
.build()
.unwrap();
assert_eq!(config.chunk_size, 256 * 1024);
assert_eq!(config.hash_algorithm, HashAlgorithm::Sha3_256);
assert!(config.enable_metrics);
assert_eq!(config.num_threads, Some(4));
}
#[test]
fn test_config_builder_validation() {
let result = ConfigBuilder::new().chunk_size(100).build();
assert!(result.is_err());
let result = ConfigBuilder::new().chunk_size(128 * 1024).build();
assert!(result.is_ok());
}
#[test]
fn test_global_config() {
let config = global_config();
{
let cfg = config.read().unwrap_or_else(|e| e.into_inner());
assert_eq!(cfg.chunk_size, DEFAULT_CHUNK_SIZE);
}
set_global_config(Config::high_performance());
{
let cfg = config.read().unwrap_or_else(|e| e.into_inner());
assert_eq!(cfg.hash_algorithm, HashAlgorithm::Sha3_256);
}
set_global_config(Config::default());
}
#[test]
fn test_builder_fluent_interface() {
let config = ConfigBuilder::new()
.chunk_size(128 * 1024)
.enable_parallel_chunking(true)
.parallel_threshold(500_000)
.enable_compression(true)
.compression_level(5)
.build()
.unwrap();
assert_eq!(config.chunk_size, 128 * 1024);
assert!(config.enable_parallel_chunking);
assert_eq!(config.parallel_threshold, 500_000);
assert!(config.enable_compression);
assert_eq!(config.compression_level, 5);
}
}