adaptive_pipeline_bootstrap/cli/parser.rs
1// /////////////////////////////////////////////////////////////////////////////
2// Adaptive Pipeline
3// Copyright (c) 2025 Michael Gardner, A Bit of Help, Inc.
4// SPDX-License-Identifier: BSD-3-Clause
5// See LICENSE file in the project root.
6// /////////////////////////////////////////////////////////////////////////////
7
8//! # CLI Parser
9//!
10//! Command-line interface parsing using clap.
11//!
12//! This module defines the CLI structure and handles argument parsing.
13//! Security validation happens in the validator module after parsing.
14
15use clap::{Parser, Subcommand};
16use std::path::PathBuf;
17
18/// Main CLI structure
19#[derive(Parser, Debug, Clone)]
20#[command(name = "pipeline")]
21#[command(about = concat!("Adaptive Pipeline v", env!("CARGO_PKG_VERSION")))]
22#[command(version)]
23pub struct Cli {
24 #[command(subcommand)]
25 pub command: Commands,
26
27 /// Enable verbose logging
28 #[arg(short, long)]
29 pub verbose: bool,
30
31 /// Configuration file path
32 #[arg(short, long)]
33 pub config: Option<PathBuf>,
34
35 // === Resource Configuration Flags ===
36 // Educational: These flags control the GlobalResourceManager's token allocation
37 // for CPU-bound and I/O-bound operations.
38 /// Override CPU worker thread count
39 ///
40 /// Controls the number of concurrent CPU-bound operations (compression,
41 /// encryption). Default: num_cpus - 1 (reserves 1 core for I/O and
42 /// coordination)
43 ///
44 /// Educational: Setting this too high causes thrashing, too low wastes
45 /// cores. Monitor CPU saturation metrics to tune appropriately.
46 #[arg(long)]
47 pub cpu_threads: Option<usize>,
48
49 /// Override I/O worker thread count
50 ///
51 /// Controls the number of concurrent I/O operations (file reads/writes).
52 /// Default: Device-specific (NVMe: 24, SSD: 12, HDD: 4)
53 ///
54 /// Educational: This should match your storage device's queue depth for
55 /// optimal throughput. Check --storage-type if auto-detection is
56 /// incorrect.
57 #[arg(long)]
58 pub io_threads: Option<usize>,
59
60 /// Specify storage device type for I/O optimization
61 ///
62 /// Affects default I/O thread count if --io-threads not specified.
63 /// Values: nvme (queue depth 24), ssd (12), hdd (4)
64 /// Default: auto-detect based on filesystem characteristics
65 ///
66 /// Educational: Different storage devices have different optimal queue
67 /// depths. NVMe handles more concurrent I/O than SSD, which handles
68 /// more than HDD.
69 #[arg(long, value_parser = parse_storage_type)]
70 pub storage_type: Option<String>,
71
72 /// Channel depth for pipeline stages (Reader → Workers → Writer)
73 ///
74 /// Controls backpressure in the three-stage pipeline architecture.
75 /// Default: 4
76 ///
77 /// Educational: Lower values reduce memory usage but may cause stalls.
78 /// Higher values increase buffering but consume more memory.
79 /// Optimal value depends on chunk processing time and I/O latency.
80 ///
81 /// Example: If chunk processing = 2ms and I/O = 1ms, depth=4 keeps pipeline
82 /// full.
83 #[arg(long, default_value = "4")]
84 pub channel_depth: usize,
85}
86
87/// CLI subcommands
88#[derive(Subcommand, Debug, Clone)]
89pub enum Commands {
90 /// Process a file through a pipeline
91 Process {
92 /// Input file path
93 #[arg(short, long)]
94 input: PathBuf,
95
96 /// Output file path
97 #[arg(short, long)]
98 output: PathBuf,
99
100 /// Pipeline name or ID
101 #[arg(short, long)]
102 pipeline: String,
103
104 /// Chunk size in MB
105 #[arg(long)]
106 chunk_size_mb: Option<usize>,
107
108 /// Number of parallel workers
109 #[arg(long)]
110 workers: Option<usize>,
111 },
112
113 /// Create a new pipeline
114 Create {
115 /// Pipeline name
116 #[arg(short, long)]
117 name: String,
118
119 /// Pipeline stages (comma-separated: compression,encryption,integrity)
120 #[arg(short, long)]
121 stages: String,
122
123 /// Save pipeline to file
124 #[arg(short, long)]
125 output: Option<PathBuf>,
126 },
127
128 /// List available pipelines
129 List,
130
131 /// Show pipeline details
132 Show {
133 /// Pipeline name
134 pipeline: String,
135 },
136
137 /// Delete a pipeline
138 Delete {
139 /// Pipeline name to delete
140 pipeline: String,
141
142 /// Skip confirmation prompt
143 #[arg(long)]
144 force: bool,
145 },
146
147 /// Benchmark system performance
148 Benchmark {
149 /// Test file path
150 #[arg(short, long)]
151 file: Option<PathBuf>,
152
153 /// Test data size in MB
154 #[arg(long, default_value = "100")]
155 size_mb: usize,
156
157 /// Number of iterations
158 #[arg(long, default_value = "3")]
159 iterations: usize,
160 },
161
162 /// Validate pipeline configuration
163 Validate {
164 /// Pipeline configuration file
165 config: PathBuf,
166 },
167
168 /// Validate .adapipe processed file
169 ValidateFile {
170 /// .adapipe file to validate
171 #[arg(short, long)]
172 file: PathBuf,
173
174 /// Perform full streaming validation (decrypt/decompress and verify
175 /// checksum)
176 #[arg(long)]
177 full: bool,
178 },
179
180 /// Restore original file from .adapipe file
181 Restore {
182 /// .adapipe file to restore from
183 #[arg(short, long)]
184 input: PathBuf,
185
186 /// Output directory for restored file (optional - uses original
187 /// directory if not specified)
188 #[arg(short, long)]
189 output_dir: Option<PathBuf>,
190
191 /// Create directories without prompting
192 #[arg(long)]
193 mkdir: bool,
194
195 /// Overwrite existing files without prompting
196 #[arg(long)]
197 overwrite: bool,
198 },
199
200 /// Compare original file against .adapipe file
201 Compare {
202 /// Original file to compare
203 #[arg(short, long)]
204 original: PathBuf,
205
206 /// .adapipe file to compare against
207 #[arg(short, long)]
208 adapipe: PathBuf,
209
210 /// Show detailed differences
211 #[arg(long)]
212 detailed: bool,
213 },
214}
215
216/// Parse and validate storage type from CLI argument
217///
218/// Educational: Custom value parser for clap that validates
219/// storage type strings and provides helpful error messages.
220fn parse_storage_type(s: &str) -> Result<String, String> {
221 match s.to_lowercase().as_str() {
222 "nvme" | "ssd" | "hdd" => Ok(s.to_lowercase()),
223 _ => Err(format!("Invalid storage type '{}'. Valid options: nvme, ssd, hdd", s)),
224 }
225}
226
227/// Parse CLI arguments
228///
229/// This is the entry point for CLI parsing. It uses clap to parse
230/// arguments and returns the parsed CLI structure.
231///
232/// # Returns
233///
234/// Parsed `Cli` structure with all arguments
235///
236/// # Panics
237///
238/// Clap will exit the process with appropriate error message if parsing fails
239pub fn parse_cli() -> Cli {
240 Cli::parse()
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn test_parse_storage_type_valid() {
249 assert_eq!(parse_storage_type("nvme").unwrap(), "nvme");
250 assert_eq!(parse_storage_type("SSD").unwrap(), "ssd");
251 assert_eq!(parse_storage_type("HDD").unwrap(), "hdd");
252 }
253
254 #[test]
255 fn test_parse_storage_type_invalid() {
256 assert!(parse_storage_type("invalid").is_err());
257 assert!(parse_storage_type("usb").is_err());
258 }
259}