hedl_cli/cli/batch.rs
1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Batch processing commands for HEDL.
19//!
20//! This module provides commands for processing multiple HEDL files in parallel,
21//! enabling efficient bulk operations on large collections of files.
22
23use crate::commands;
24use clap::Subcommand;
25
26/// Batch processing commands.
27///
28/// These commands operate on multiple HEDL files simultaneously, with automatic
29/// parallelization for improved performance. All batch commands support glob
30/// patterns for file selection.
31///
32/// # Performance
33///
34/// Batch commands automatically use parallel processing when beneficial:
35/// - CPU-bound operations scale with available cores
36/// - I/O-bound operations use async parallelization
37/// - Progress reporting shows real-time status
38///
39/// # Design
40///
41/// All batch commands follow consistent patterns:
42/// - Multiple file inputs (with glob support)
43/// - Optional parallel processing flag
44/// - Verbose mode for detailed progress
45#[derive(Subcommand)]
46pub enum BatchCommands {
47 /// Batch validate multiple HEDL files
48 ///
49 /// Validates multiple HEDL files in parallel. Supports glob patterns for
50 /// file selection and provides aggregated results.
51 BatchValidate {
52 /// Input file paths (supports glob patterns)
53 #[arg(value_name = "FILES", num_args = 1..)]
54 files: Vec<String>,
55
56 /// Strict mode (fail on any error)
57 #[arg(short, long)]
58 strict: bool,
59
60 /// Force parallel processing
61 #[arg(short, long)]
62 parallel: bool,
63
64 /// Show verbose progress
65 #[arg(short, long)]
66 verbose: bool,
67
68 /// Use streaming mode for memory-efficient processing
69 ///
70 /// Streaming mode uses constant memory regardless of file size,
71 /// making it ideal for files > 100MB or memory-constrained environments.
72 #[arg(long)]
73 streaming: bool,
74
75 /// Automatically use streaming for files > 100MB
76 ///
77 /// Enables hybrid mode where large files use streaming and small files
78 /// use standard processing for optimal performance and memory usage.
79 #[arg(long)]
80 auto_streaming: bool,
81
82 /// Maximum number of files to process (unlimited if not specified)
83 #[arg(long)]
84 max_files: Option<usize>,
85 },
86
87 /// Batch format multiple HEDL files
88 ///
89 /// Formats multiple HEDL files to canonical form in parallel. Can either
90 /// modify files in-place or write to an output directory.
91 BatchFormat {
92 /// Input file paths (supports glob patterns)
93 #[arg(value_name = "FILES", num_args = 1..)]
94 files: Vec<String>,
95
96 /// Output directory for formatted files
97 #[arg(short, long)]
98 output_dir: Option<String>,
99
100 /// Check only (exit 1 if not canonical)
101 #[arg(short, long)]
102 check: bool,
103
104 /// Use ditto optimization
105 #[arg(long, default_value = "true")]
106 ditto: bool,
107
108 /// Automatically add count hints to all matrix lists
109 #[arg(long)]
110 with_counts: bool,
111
112 /// Force parallel processing
113 #[arg(short, long)]
114 parallel: bool,
115
116 /// Show verbose progress
117 #[arg(short, long)]
118 verbose: bool,
119
120 /// Maximum number of files to process (unlimited if not specified)
121 #[arg(long)]
122 max_files: Option<usize>,
123 },
124
125 /// Batch lint multiple HEDL files
126 ///
127 /// Lints multiple HEDL files in parallel, checking for best practices
128 /// and style issues. Provides aggregated results across all files.
129 BatchLint {
130 /// Input file paths (supports glob patterns)
131 #[arg(value_name = "FILES", num_args = 1..)]
132 files: Vec<String>,
133
134 /// Treat warnings as errors
135 #[arg(short = 'W', long)]
136 warn_error: bool,
137
138 /// Force parallel processing
139 #[arg(short, long)]
140 parallel: bool,
141
142 /// Show verbose progress
143 #[arg(short, long)]
144 verbose: bool,
145
146 /// Maximum number of files to process (unlimited if not specified)
147 #[arg(long)]
148 max_files: Option<usize>,
149 },
150}
151
152impl BatchCommands {
153 /// Execute the batch command.
154 ///
155 /// # Returns
156 ///
157 /// Returns `Ok(())` on success, or an error message on failure.
158 ///
159 /// # Errors
160 ///
161 /// Returns `Err` if:
162 /// - No files match the provided patterns
163 /// - Any file operation fails
164 /// - Processing fails for any file
165 ///
166 /// # Performance
167 ///
168 /// Batch commands automatically parallelize when beneficial. The `parallel`
169 /// flag forces parallelization even for small file sets.
170 pub fn execute(self) -> Result<(), crate::error::CliError> {
171 // Convert CLI max_files option: 0 means unlimited (None), >0 means limit
172 fn convert_max_files(max_files: Option<usize>) -> Option<Option<usize>> {
173 max_files.map(|n| if n == 0 { None } else { Some(n) })
174 }
175
176 match self {
177 BatchCommands::BatchValidate {
178 files,
179 strict,
180 parallel,
181 verbose,
182 streaming: _,
183 auto_streaming: _,
184 max_files,
185 } => commands::batch_validate_with_config(
186 files,
187 strict,
188 false,
189 100,
190 parallel,
191 verbose,
192 convert_max_files(max_files),
193 ),
194 BatchCommands::BatchFormat {
195 files,
196 output_dir,
197 check,
198 ditto,
199 with_counts,
200 parallel,
201 verbose,
202 max_files,
203 } => commands::batch_format_with_config(commands::BatchFormatParams {
204 patterns: files,
205 output_dir,
206 check,
207 ditto,
208 with_counts,
209 recursive: false,
210 max_depth: 100,
211 parallel,
212 verbose,
213 max_files_override: convert_max_files(max_files),
214 }),
215 BatchCommands::BatchLint {
216 files,
217 warn_error,
218 parallel,
219 verbose,
220 max_files,
221 } => commands::batch_lint_with_config(
222 files,
223 warn_error,
224 false,
225 100,
226 parallel,
227 verbose,
228 convert_max_files(max_files),
229 ),
230 }
231 }
232}