Skip to main content

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}