fast_yaml_cli/batch/
config.rs

1//! Configuration types for batch file processing.
2
3/// Configuration for parallel file processing.
4#[derive(Debug, Clone)]
5pub struct ProcessingConfig {
6    /// Indentation width (2-8 spaces)
7    pub indent: u8,
8    /// Maximum line width
9    pub width: usize,
10    /// Edit files in-place
11    pub in_place: bool,
12    /// Show changes without modifying files (dry-run mode)
13    pub dry_run: bool,
14    /// Number of parallel workers (0 = auto-detect)
15    pub workers: usize,
16    /// File size threshold for memory-mapped reading (bytes)
17    pub mmap_threshold: usize,
18    /// Enable verbose progress output
19    pub verbose: bool,
20}
21
22impl ProcessingConfig {
23    /// Default memory-map threshold: 512KB
24    pub const DEFAULT_MMAP_THRESHOLD: usize = 512 * 1024;
25
26    /// Minimum indentation width
27    pub const MIN_INDENT: u8 = 2;
28
29    /// Maximum indentation width
30    pub const MAX_INDENT: u8 = 8;
31
32    /// Creates a new `ProcessingConfig` with default values
33    pub fn new() -> Self {
34        Self::default()
35    }
36
37    /// Returns the effective number of workers to use.
38    /// If workers is 0, returns the number of logical CPU cores.
39    pub fn effective_workers(&self) -> usize {
40        if self.workers == 0 {
41            num_cpus::get()
42        } else {
43            self.workers
44        }
45    }
46
47    /// Sets the indentation width (clamped to `MIN_INDENT..=MAX_INDENT`)
48    #[must_use]
49    pub fn with_indent(mut self, indent: u8) -> Self {
50        self.indent = indent.clamp(Self::MIN_INDENT, Self::MAX_INDENT);
51        self
52    }
53
54    /// Sets the maximum line width
55    #[must_use]
56    pub const fn with_width(mut self, width: usize) -> Self {
57        self.width = width;
58        self
59    }
60
61    /// Enables in-place file editing
62    #[must_use]
63    pub const fn with_in_place(mut self, in_place: bool) -> Self {
64        self.in_place = in_place;
65        self
66    }
67
68    /// Enables dry-run mode (show changes without modifying files)
69    #[must_use]
70    pub const fn with_dry_run(mut self, dry_run: bool) -> Self {
71        self.dry_run = dry_run;
72        self
73    }
74
75    /// Sets the number of parallel workers (0 = auto-detect)
76    #[must_use]
77    pub const fn with_workers(mut self, workers: usize) -> Self {
78        self.workers = workers;
79        self
80    }
81
82    /// Sets the memory-map threshold
83    #[must_use]
84    pub const fn with_mmap_threshold(mut self, threshold: usize) -> Self {
85        self.mmap_threshold = threshold;
86        self
87    }
88
89    /// Enables verbose progress output
90    #[must_use]
91    pub const fn with_verbose(mut self, verbose: bool) -> Self {
92        self.verbose = verbose;
93        self
94    }
95}
96
97impl Default for ProcessingConfig {
98    fn default() -> Self {
99        Self {
100            indent: 2,
101            width: 80,
102            in_place: false,
103            dry_run: false,
104            workers: 0, // Auto-detect
105            mmap_threshold: Self::DEFAULT_MMAP_THRESHOLD,
106            verbose: false,
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_default_config() {
117        let config = ProcessingConfig::default();
118        assert_eq!(config.indent, 2);
119        assert_eq!(config.width, 80);
120        assert!(!config.in_place);
121        assert!(!config.dry_run);
122        assert_eq!(config.workers, 0);
123        assert_eq!(
124            config.mmap_threshold,
125            ProcessingConfig::DEFAULT_MMAP_THRESHOLD
126        );
127        assert!(!config.verbose);
128    }
129
130    #[test]
131    fn test_effective_workers_default() {
132        let config = ProcessingConfig::default();
133        let workers = config.effective_workers();
134        assert!(workers > 0);
135        assert_eq!(workers, num_cpus::get());
136    }
137
138    #[test]
139    fn test_effective_workers_custom() {
140        let config = ProcessingConfig::default().with_workers(4);
141        assert_eq!(config.effective_workers(), 4);
142    }
143
144    #[test]
145    fn test_builder_pattern() {
146        let config = ProcessingConfig::new()
147            .with_indent(4)
148            .with_width(120)
149            .with_in_place(true)
150            .with_dry_run(false)
151            .with_workers(8)
152            .with_mmap_threshold(2 * 1024 * 1024)
153            .with_verbose(true);
154
155        assert_eq!(config.indent, 4);
156        assert_eq!(config.width, 120);
157        assert!(config.in_place);
158        assert!(!config.dry_run);
159        assert_eq!(config.workers, 8);
160        assert_eq!(config.mmap_threshold, 2 * 1024 * 1024);
161        assert!(config.verbose);
162    }
163
164    #[test]
165    fn test_indent_clamping() {
166        let config = ProcessingConfig::new().with_indent(1);
167        assert_eq!(config.indent, ProcessingConfig::MIN_INDENT);
168
169        let config = ProcessingConfig::new().with_indent(10);
170        assert_eq!(config.indent, ProcessingConfig::MAX_INDENT);
171
172        let config = ProcessingConfig::new().with_indent(4);
173        assert_eq!(config.indent, 4);
174    }
175
176    #[test]
177    fn test_new_equals_default() {
178        let new_config = ProcessingConfig::new();
179        let default_config = ProcessingConfig::default();
180
181        assert_eq!(new_config.indent, default_config.indent);
182        assert_eq!(new_config.width, default_config.width);
183        assert_eq!(new_config.in_place, default_config.in_place);
184        assert_eq!(new_config.dry_run, default_config.dry_run);
185        assert_eq!(new_config.workers, default_config.workers);
186        assert_eq!(new_config.mmap_threshold, default_config.mmap_threshold);
187        assert_eq!(new_config.verbose, default_config.verbose);
188    }
189
190    #[test]
191    fn test_conflicting_flags() {
192        let config = ProcessingConfig::new()
193            .with_in_place(true)
194            .with_dry_run(true);
195
196        assert!(config.in_place);
197        assert!(config.dry_run);
198    }
199}