Skip to main content

hexz_common/
config.rs

1//! Global Configuration Structure Definitions.
2//!
3//! This module defines the configuration parameters that control the behavior
4//! of the filesystem, including cache sizing, prefetching policies, and
5//! network timeouts. It allows for fine-tuning the performance characteristics
6//! based on the deployment environment and available system resources.
7
8use crate::constants::{DEFAULT_CACHE_SIZE, DEFAULT_NETWORK_TIMEOUT, DEFAULT_PREFETCH_COUNT};
9
10/// Pre-defined optimization profiles for the `hexz build` command.
11///
12/// **Architectural intent:** Simplifies the configuration surface for common
13/// use cases by grouping block size, compression, and alignment settings into
14/// named presets.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum BuildProfile {
17    /// Balanced defaults for general-purpose use (64 KiB blocks, LZ4/Zstd).
18    Generic,
19    /// EDA/Text focus: smaller blocks (16 KiB) and dictionary compression.
20    Eda,
21    /// Embedded systems: high compression (Zstd), small blocks (4 KiB).
22    Embedded,
23    /// Machine Learning: columnar alignment, large blocks (e.g., 1 MiB) or matched to tensor sizes.
24    Ml,
25}
26
27impl BuildProfile {
28    /// Returns the recommended block size for this profile.
29    pub fn block_size(&self) -> u32 {
30        match self {
31            Self::Generic => 65536, // 64 KiB
32            Self::Eda => 16384,     // 16 KiB
33            Self::Embedded => 4096, // 4 KiB
34            Self::Ml => 1048576,    // 1 MiB
35        }
36    }
37
38    /// Returns the recommended compression algorithm for this profile.
39    /// Note: This returns a string compatible with the CLI argument parser.
40    pub fn compression_algo(&self) -> &'static str {
41        match self {
42            Self::Generic => "lz4",
43            Self::Eda => "zstd",
44            Self::Embedded => "zstd",
45            Self::Ml => "lz4",
46        }
47    }
48
49    /// Whether this profile recommends dictionary training.
50    pub fn recommended_dict_training(&self) -> bool {
51        match self {
52            Self::Generic => false,
53            Self::Eda => true,
54            Self::Embedded => true,
55            Self::Ml => false,
56        }
57    }
58}
59
60/// Aggregated configuration for the filesystem runtime.
61///
62/// This struct holds all tunable parameters for the system. It is typically
63/// constructed from command-line arguments or a configuration file and passed
64/// down to the core components during initialization. The configuration
65/// affects memory usage, I/O behavior, and network operation timeouts.
66#[derive(Debug, Clone)]
67pub struct Config {
68    /// The maximum size of the in-memory block cache in bytes.
69    ///
70    /// This parameter controls the memory footprint of the application.
71    /// A larger cache improves read performance for repeated access but
72    /// consumes more system RAM. The cache uses an LRU eviction policy
73    /// when this limit is reached.
74    pub cache_size_bytes: usize,
75
76    /// The number of blocks to prefetch sequentially during read operations.
77    ///
78    /// This setting optimizes read throughput for sequential access patterns
79    /// by fetching ahead of the request cursor. A value of 0 disables prefetching,
80    /// which may be desirable for random access workloads where prefetching
81    /// would waste bandwidth.
82    pub prefetch_count: u32,
83
84    /// The timeout duration in seconds for network operations.
85    ///
86    /// This applies to remote storage backends like S3 or HTTP. It ensures
87    /// that operations do not hang indefinitely in case of network partitions
88    /// or unresponsive servers. Operations that exceed this timeout will
89    /// return an I/O error.
90    pub network_timeout_secs: u64,
91}
92
93impl Default for Config {
94    /// Provides sensible default values for the configuration.
95    ///
96    /// These defaults are chosen to provide a balance between performance
97    /// and resource usage for a typical desktop environment: 512MB cache,
98    /// 4-block prefetch, and 30-second network timeout. These values can be
99    /// overridden based on available system resources and workload characteristics.
100    ///
101    /// # Returns
102    ///
103    /// Returns a new `Config` instance with default values.
104    fn default() -> Self {
105        Self {
106            cache_size_bytes: DEFAULT_CACHE_SIZE,
107            prefetch_count: DEFAULT_PREFETCH_COUNT,
108            network_timeout_secs: DEFAULT_NETWORK_TIMEOUT,
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_build_profile_generic() {
119        let profile = BuildProfile::Generic;
120        assert_eq!(profile.block_size(), 65536);
121        assert_eq!(profile.compression_algo(), "lz4");
122        assert!(!profile.recommended_dict_training());
123    }
124
125    #[test]
126    fn test_build_profile_eda() {
127        let profile = BuildProfile::Eda;
128        assert_eq!(profile.block_size(), 16384);
129        assert_eq!(profile.compression_algo(), "zstd");
130        assert!(profile.recommended_dict_training());
131    }
132
133    #[test]
134    fn test_build_profile_embedded() {
135        let profile = BuildProfile::Embedded;
136        assert_eq!(profile.block_size(), 4096);
137        assert_eq!(profile.compression_algo(), "zstd");
138        assert!(profile.recommended_dict_training());
139    }
140
141    #[test]
142    fn test_build_profile_ml() {
143        let profile = BuildProfile::Ml;
144        assert_eq!(profile.block_size(), 1048576);
145        assert_eq!(profile.compression_algo(), "lz4");
146        assert!(!profile.recommended_dict_training());
147    }
148
149    #[test]
150    fn test_build_profile_equality() {
151        assert_eq!(BuildProfile::Generic, BuildProfile::Generic);
152        assert_eq!(BuildProfile::Eda, BuildProfile::Eda);
153        assert_ne!(BuildProfile::Generic, BuildProfile::Eda);
154        assert_ne!(BuildProfile::Embedded, BuildProfile::Ml);
155    }
156
157    #[test]
158    fn test_build_profile_copy() {
159        let profile1 = BuildProfile::Generic;
160        let profile2 = profile1; // Copy
161
162        assert_eq!(profile1, profile2);
163        assert_eq!(profile1.block_size(), profile2.block_size());
164    }
165
166    #[test]
167    fn test_build_profile_clone() {
168        let profile1 = BuildProfile::Eda;
169        let profile2 = profile1;
170
171        assert_eq!(profile1, profile2);
172    }
173
174    #[test]
175    fn test_build_profile_debug() {
176        let profile = BuildProfile::Ml;
177        let debug_str = format!("{:?}", profile);
178
179        assert!(debug_str.contains("Ml"));
180    }
181
182    #[test]
183    fn test_config_default() {
184        let config = Config::default();
185
186        assert_eq!(config.cache_size_bytes, DEFAULT_CACHE_SIZE);
187        assert_eq!(config.prefetch_count, DEFAULT_PREFETCH_COUNT);
188        assert_eq!(config.network_timeout_secs, DEFAULT_NETWORK_TIMEOUT);
189    }
190
191    #[test]
192    fn test_config_clone() {
193        let config1 = Config {
194            cache_size_bytes: 1024 * 1024 * 1024,
195            prefetch_count: 8,
196            network_timeout_secs: 60,
197        };
198
199        let config2 = config1.clone();
200
201        assert_eq!(config2.cache_size_bytes, 1024 * 1024 * 1024);
202        assert_eq!(config2.prefetch_count, 8);
203        assert_eq!(config2.network_timeout_secs, 60);
204    }
205
206    #[test]
207    fn test_config_debug() {
208        let config = Config::default();
209        let debug_str = format!("{:?}", config);
210
211        assert!(debug_str.contains("Config"));
212        assert!(debug_str.contains("cache_size_bytes"));
213        assert!(debug_str.contains("prefetch_count"));
214        assert!(debug_str.contains("network_timeout_secs"));
215    }
216
217    #[test]
218    fn test_config_custom_values() {
219        let config = Config {
220            cache_size_bytes: 2048,
221            prefetch_count: 16,
222            network_timeout_secs: 120,
223        };
224
225        assert_eq!(config.cache_size_bytes, 2048);
226        assert_eq!(config.prefetch_count, 16);
227        assert_eq!(config.network_timeout_secs, 120);
228    }
229
230    #[test]
231    fn test_build_profile_all_variants() {
232        // Ensure all variants can be constructed
233        let _ = BuildProfile::Generic;
234        let _ = BuildProfile::Eda;
235        let _ = BuildProfile::Embedded;
236        let _ = BuildProfile::Ml;
237    }
238
239    #[test]
240    fn test_build_profile_block_sizes_ordered() {
241        // Verify block sizes make sense
242        assert!(BuildProfile::Embedded.block_size() < BuildProfile::Eda.block_size());
243        assert!(BuildProfile::Eda.block_size() < BuildProfile::Generic.block_size());
244        assert!(BuildProfile::Generic.block_size() < BuildProfile::Ml.block_size());
245    }
246
247    #[test]
248    fn test_compression_algo_returns_valid_strings() {
249        assert_eq!(BuildProfile::Generic.compression_algo(), "lz4");
250        assert_eq!(BuildProfile::Eda.compression_algo(), "zstd");
251        assert_eq!(BuildProfile::Embedded.compression_algo(), "zstd");
252        assert_eq!(BuildProfile::Ml.compression_algo(), "lz4");
253    }
254
255    #[test]
256    fn test_dict_training_recommendations() {
257        // Generic and ML don't need dictionary training
258        assert!(!BuildProfile::Generic.recommended_dict_training());
259        assert!(!BuildProfile::Ml.recommended_dict_training());
260
261        // EDA and Embedded benefit from dictionary training
262        assert!(BuildProfile::Eda.recommended_dict_training());
263        assert!(BuildProfile::Embedded.recommended_dict_training());
264    }
265}