Skip to main content

saorsa_fec/
config.rs

1//! Configuration for the encryption and FEC pipeline
2//!
3//! This module provides configuration options for encryption modes,
4//! storage settings, and FEC parameters. The v0.3 specification requires
5//! a builder pattern for configuration.
6
7use serde::{Deserialize, Serialize};
8use std::time::Duration;
9
10/// Encryption mode selection for the v0.3 API
11#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
12pub enum EncryptionMode {
13    /// Pure convergent encryption (deduplication across all users)
14    Convergent,
15    /// Convergent encryption with secret (controlled deduplication)
16    ConvergentWithSecret,
17    /// Random key encryption (no deduplication)
18    RandomKey,
19}
20
21/// Main configuration for the Saorsa FEC system
22/// Supports builder pattern as specified in v0.3
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct Config {
25    /// Encryption mode
26    pub encryption_mode: EncryptionMode,
27    /// FEC data shards (k)
28    pub data_shards: u8,
29    /// FEC parity shards (n-k)
30    pub parity_shards: u8,
31    /// Chunk size in bytes (default ~64 KiB)
32    pub chunk_size: usize,
33    /// Whether compression is enabled
34    pub compression_enabled: bool,
35    /// Compression level (1-9)
36    pub compression_level: u8,
37    /// Legacy fields for backward compatibility
38    pub encryption: EncryptionConfig,
39    pub fec: FecConfig,
40    pub storage: StorageConfig,
41    pub gc: GcConfig,
42    pub version: VersionConfig,
43}
44
45impl Config {
46    /// Create a new configuration with default settings
47    /// Required by v0.3 specification
48    pub fn new() -> Self {
49        Self {
50            encryption_mode: EncryptionMode::Convergent,
51            data_shards: 16,
52            parity_shards: 4,
53            chunk_size: 64 * 1024, // 64 KiB as specified
54            compression_enabled: true,
55            compression_level: 6,
56            // Legacy fields
57            encryption: EncryptionConfig::default(),
58            fec: FecConfig::default(),
59            storage: StorageConfig::default(),
60            gc: GcConfig::default(),
61            version: VersionConfig::default(),
62        }
63    }
64
65    /// Set encryption mode (v0.3 builder pattern)
66    pub fn with_encryption_mode(mut self, mode: EncryptionMode) -> Self {
67        self.encryption_mode = mode;
68        self
69    }
70
71    /// Set FEC parameters (v0.3 builder pattern)
72    /// overhead = parity_shards / data_shards
73    pub fn with_fec_params(mut self, data_shards: u8, parity_shards: u8) -> Self {
74        self.data_shards = data_shards;
75        self.parity_shards = parity_shards;
76        // Update legacy fields for compatibility
77        self.fec.data_shares = data_shards as u16;
78        self.fec.parity_shares = parity_shards as u16;
79        self
80    }
81
82    /// Set chunk size (v0.3 builder pattern)
83    pub fn with_chunk_size(mut self, bytes: usize) -> Self {
84        self.chunk_size = bytes;
85        // Update legacy field
86        self.fec.stripe_size = bytes;
87        self
88    }
89
90    /// Set compression settings (v0.3 builder pattern)
91    pub fn with_compression(mut self, on: bool, level: u8) -> Self {
92        self.compression_enabled = on;
93        self.compression_level = level.clamp(1, 9);
94        // Update legacy fields
95        self.encryption.compress_before_encrypt = on;
96        self.encryption.compression_level = level as u32;
97        self
98    }
99
100    /// Create a high-performance configuration
101    pub fn high_performance() -> Self {
102        Self {
103            encryption_mode: EncryptionMode::Convergent,
104            data_shards: 16,
105            parity_shards: 4,
106            chunk_size: 128 * 1024,
107            compression_enabled: true,
108            compression_level: 3,
109            encryption: EncryptionConfig {
110                mode: EncryptionMode::Convergent,
111                compress_before_encrypt: true,
112                compression_level: 3,
113            },
114            fec: FecConfig {
115                data_shares: 16,
116                parity_shares: 4,
117                stripe_size: 128 * 1024,
118                auto_params: true,
119            },
120            storage: StorageConfig {
121                backend: StorageBackend::Local {
122                    path: "/var/lib/saorsa".into(),
123                },
124                cache_size: 1024 * 1024 * 1024,
125                parallel_operations: 8,
126            },
127            gc: GcConfig {
128                enabled: true,
129                retention_days: 30,
130                min_free_space_gb: 10,
131                run_interval: Duration::from_secs(3600),
132            },
133            version: VersionConfig {
134                max_versions: 100,
135                auto_tag_interval: 10,
136                diff_compression: true,
137            },
138        }
139    }
140
141    /// Create a high-reliability configuration
142    pub fn high_reliability() -> Self {
143        Self {
144            encryption_mode: EncryptionMode::RandomKey,
145            data_shards: 10,
146            parity_shards: 10,
147            chunk_size: 64 * 1024,
148            compression_enabled: true,
149            compression_level: 6,
150            encryption: EncryptionConfig {
151                mode: EncryptionMode::RandomKey,
152                compress_before_encrypt: true,
153                compression_level: 6,
154            },
155            fec: FecConfig {
156                data_shares: 10,
157                parity_shares: 10,
158                stripe_size: 64 * 1024,
159                auto_params: false,
160            },
161            storage: StorageConfig {
162                backend: StorageBackend::Multi {
163                    backends: vec![
164                        StorageBackend::Local {
165                            path: "/var/lib/saorsa/primary".into(),
166                        },
167                        StorageBackend::Local {
168                            path: "/var/lib/saorsa/backup".into(),
169                        },
170                    ],
171                },
172                cache_size: 512 * 1024 * 1024,
173                parallel_operations: 4,
174            },
175            gc: GcConfig {
176                enabled: true,
177                retention_days: 90,
178                min_free_space_gb: 50,
179                run_interval: Duration::from_secs(7200),
180            },
181            version: VersionConfig {
182                max_versions: 1000,
183                auto_tag_interval: 1,
184                diff_compression: true,
185            },
186        }
187    }
188
189    /// Create a minimal storage configuration
190    pub fn minimal_storage() -> Self {
191        Self {
192            encryption_mode: EncryptionMode::Convergent,
193            data_shards: 20,
194            parity_shards: 2,
195            chunk_size: 32 * 1024,
196            compression_enabled: true,
197            compression_level: 9,
198            encryption: EncryptionConfig {
199                mode: EncryptionMode::Convergent,
200                compress_before_encrypt: true,
201                compression_level: 9,
202            },
203            fec: FecConfig {
204                data_shares: 20,
205                parity_shares: 2,
206                stripe_size: 32 * 1024,
207                auto_params: true,
208            },
209            storage: StorageConfig {
210                backend: StorageBackend::Local {
211                    path: "/var/lib/saorsa".into(),
212                },
213                cache_size: 64 * 1024 * 1024,
214                parallel_operations: 2,
215            },
216            gc: GcConfig {
217                enabled: true,
218                retention_days: 7,
219                min_free_space_gb: 1,
220                run_interval: Duration::from_secs(1800),
221            },
222            version: VersionConfig {
223                max_versions: 10,
224                auto_tag_interval: 0,
225                diff_compression: true,
226            },
227        }
228    }
229
230    /// Validate configuration
231    pub fn validate(&self) -> anyhow::Result<()> {
232        if self.fec.data_shares == 0 {
233            anyhow::bail!("Data shares must be greater than 0");
234        }
235        if self.fec.parity_shares == 0 {
236            anyhow::bail!("Parity shares must be greater than 0");
237        }
238        if self.fec.data_shares + self.fec.parity_shares > 255 {
239            anyhow::bail!("Total shares cannot exceed 255");
240        }
241        if self.fec.stripe_size == 0 {
242            anyhow::bail!("Stripe size must be greater than 0");
243        }
244        if self.storage.cache_size == 0 {
245            anyhow::bail!("Cache size must be greater than 0");
246        }
247        Ok(())
248    }
249}
250
251impl Default for Config {
252    fn default() -> Self {
253        Self::new()
254    }
255}
256/// Encryption configuration
257#[derive(Debug, Clone, Serialize, Deserialize)]
258pub struct EncryptionConfig {
259    /// Encryption mode to use
260    pub mode: EncryptionMode,
261    /// Whether to compress before encryption
262    pub compress_before_encrypt: bool,
263    /// Compression level (1-9)
264    pub compression_level: u32,
265}
266impl Default for EncryptionConfig {
267    fn default() -> Self {
268        Self {
269            mode: EncryptionMode::Convergent,
270            compress_before_encrypt: true,
271            compression_level: 6,
272        }
273    }
274}
275
276/// FEC configuration
277#[derive(Debug, Clone, Serialize, Deserialize)]
278pub struct FecConfig {
279    /// Number of data shares
280    pub data_shares: u16,
281    /// Number of parity shares
282    pub parity_shares: u16,
283    /// Size of each stripe in bytes
284    pub stripe_size: usize,
285    /// Automatically adjust parameters based on content
286    pub auto_params: bool,
287}
288
289impl Default for FecConfig {
290    fn default() -> Self {
291        Self {
292            data_shares: 16,
293            parity_shares: 4,
294            stripe_size: 64 * 1024,
295            auto_params: true,
296        }
297    }
298}
299
300/// Storage configuration
301#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct StorageConfig {
303    /// Storage backend to use
304    pub backend: StorageBackend,
305    /// Cache size in bytes
306    pub cache_size: usize,
307    /// Number of parallel storage operations
308    pub parallel_operations: usize,
309}
310
311impl Default for StorageConfig {
312    fn default() -> Self {
313        Self {
314            backend: StorageBackend::Local {
315                path: "/var/lib/saorsa".into(),
316            },
317            cache_size: 256 * 1024 * 1024,
318            parallel_operations: 4,
319        }
320    }
321}
322
323/// Storage backend type
324#[derive(Debug, Clone, Serialize, Deserialize)]
325pub enum StorageBackend {
326    /// Local filesystem storage
327    Local {
328        /// Base path for storage
329        path: String,
330    },
331    /// Network storage
332    Network {
333        /// List of node addresses
334        nodes: Vec<String>,
335        /// Replication factor
336        replication: usize,
337    },
338    /// Multiple backends
339    Multi {
340        /// List of backends
341        backends: Vec<StorageBackend>,
342    },
343}
344
345/// Garbage collection configuration
346#[derive(Debug, Clone, Serialize, Deserialize)]
347pub struct GcConfig {
348    /// Whether GC is enabled
349    pub enabled: bool,
350    /// Number of days to retain unreferenced chunks
351    pub retention_days: u32,
352    /// Minimum free space in GB before triggering GC
353    pub min_free_space_gb: u32,
354    /// How often to run GC
355    pub run_interval: Duration,
356}
357
358impl Default for GcConfig {
359    fn default() -> Self {
360        Self {
361            enabled: true,
362            retention_days: 30,
363            min_free_space_gb: 10,
364            run_interval: Duration::from_secs(3600),
365        }
366    }
367}
368
369/// Version management configuration
370#[derive(Debug, Clone, Serialize, Deserialize)]
371pub struct VersionConfig {
372    /// Maximum number of versions to keep
373    pub max_versions: usize,
374    /// Auto-tag every N versions (0 = disabled)
375    pub auto_tag_interval: usize,
376    /// Use compression for version diffs
377    pub diff_compression: bool,
378}
379
380impl Default for VersionConfig {
381    fn default() -> Self {
382        Self {
383            max_versions: 100,
384            auto_tag_interval: 10,
385            diff_compression: true,
386        }
387    }
388}
389
390#[cfg(test)]
391mod tests {
392    use super::*;
393
394    #[test]
395    fn test_config_default() {
396        let config = Config::default();
397        assert!(config.validate().is_ok());
398    }
399
400    #[test]
401    fn test_config_high_performance() {
402        let config = Config::high_performance();
403        assert!(config.validate().is_ok());
404        assert_eq!(config.fec.data_shares, 16);
405        assert_eq!(config.fec.parity_shares, 4);
406    }
407
408    #[test]
409    fn test_config_high_reliability() {
410        let config = Config::high_reliability();
411        assert!(config.validate().is_ok());
412        assert_eq!(config.fec.data_shares, 10);
413        assert_eq!(config.fec.parity_shares, 10);
414    }
415
416    #[test]
417    fn test_config_minimal_storage() {
418        let config = Config::minimal_storage();
419        assert!(config.validate().is_ok());
420        assert_eq!(config.fec.data_shares, 20);
421        assert_eq!(config.fec.parity_shares, 2);
422    }
423
424    #[test]
425    fn test_config_validation() {
426        let mut config = Config::default();
427
428        config.fec.data_shares = 0;
429        assert!(config.validate().is_err());
430
431        config.fec.data_shares = 200;
432        config.fec.parity_shares = 100;
433        assert!(config.validate().is_err());
434
435        config.fec.data_shares = 16;
436        config.fec.parity_shares = 4;
437        config.fec.stripe_size = 0;
438        assert!(config.validate().is_err());
439    }
440}