1use clap::{Args, Parser, Subcommand, ValueEnum};
2
3use crate::speedtest::TestDuration;
4
5#[derive(Parser)]
7#[command(
8 name = "nd300",
9 author,
10 version,
11 disable_version_flag = true,
12 about = "ND-300 Network Diagnostic - QubeTX Developer Tools",
13 long_about = "ND-300 Network Diagnostic - QubeTX Developer Tools\n\n\
14 Cross-platform network diagnostics with 25+ concurrent checks,\n\
15 multi-stage network repair, and DNS configuration management.",
16 after_long_help = "EXAMPLES:\n\
17 \x20 nd300 Run standard diagnostics\n\
18 \x20 nd300 -t Technician mode (deep diagnostics)\n\
19 \x20 nd300 dns Change DNS servers and verify connectivity\n\
20 \x20 nd300 -d Same as 'nd300 dns' (legacy flag form)\n\
21 \x20 nd300 fix Diagnostic-driven triage and recovery loop\n\
22 \x20 nd300 -f Same as 'nd300 fix' (legacy flag form)\n\
23 \x20 nd300 update Check for updates and install\n\
24 \x20 nd300 clear-dns Reset DNS cache\n\
25 \x20 nd300 uninstall Remove nd300 from this system\n\
26 \x20 nd300 --fast Skip speed test for faster execution\n\
27 \x20 nd300 --json Output results as JSON\n\n\
28 Run 'nd300 --help' for full details, or 'nd300 -h' for a summary."
29)]
30pub struct Nd300Cli {
31 #[arg(
33 short = 't',
34 long = "tech",
35 alias = "technician",
36 help_heading = "Modes",
37 global = true
38 )]
39 pub tech: bool,
40
41 #[arg(short = 'T', long, help_heading = "Modes", global = true)]
43 pub title: Option<String>,
44
45 #[arg(long, help_heading = "Output", global = true)]
47 pub json: bool,
48
49 #[arg(long, help_heading = "Output", global = true)]
51 pub ascii: bool,
52
53 #[arg(long, help_heading = "Output", global = true)]
55 pub no_color: bool,
56
57 #[arg(long, help_heading = "Output", global = true)]
59 pub verbose: bool,
60
61 #[arg(long, help_heading = "Speed Test", global = true)]
63 pub fast: bool,
64
65 #[arg(long, default_value = "15", help_heading = "Speed Test", global = true)]
67 pub speed_duration: u64,
68
69 #[arg(short = 'd', long = "dns", help_heading = "Actions")]
71 pub dns: bool,
72
73 #[arg(short = 'f', long = "fix", help_heading = "Actions")]
75 pub fix: bool,
76
77 #[arg(short = 'c', long = "clear-dns", help_heading = "Actions")]
79 pub clear_dns: bool,
80
81 #[arg(long = "uninstall", help_heading = "Actions")]
83 pub uninstall: bool,
84
85 #[arg(long = "update", help_heading = "Actions")]
87 pub update: bool,
88
89 #[arg(
92 short = 'y',
93 long = "yes",
94 alias = "non-interactive",
95 help_heading = "Actions",
96 global = true
97 )]
98 pub yes: bool,
99
100 #[arg(short = 'v', long = "version", action = clap::ArgAction::Version)]
102 pub version: (),
103
104 #[command(subcommand)]
106 pub command: Option<Nd300Command>,
107}
108
109#[derive(Subcommand, Debug, Clone)]
114pub enum Nd300Command {
115 Fix(FixArgs),
120
121 Dns,
124
125 Update,
127
128 #[command(name = "clear-dns")]
130 ClearDns,
131
132 Uninstall,
134
135 #[command(name = "migrate-cleanup", hide = true)]
142 MigrateCleanup(MigrateArgs),
143}
144
145#[derive(Args, Debug, Clone, Default)]
151pub struct FixArgs {}
152
153#[derive(Args, Debug, Clone, Default)]
158pub struct MigrateArgs {
159 #[arg(long = "cargo-copy")]
161 pub cargo_copy: bool,
162
163 #[arg(long = "other-edition")]
165 pub other_edition: bool,
166
167 #[arg(long = "quiet")]
170 pub quiet: bool,
171
172 #[arg(long = "dry-run")]
174 pub dry_run: bool,
175
176 #[arg(long = "json")]
178 pub json: bool,
179
180 #[arg(long = "user-profile", value_name = "PATH")]
184 pub user_profile: Option<String>,
185
186 #[arg(long = "cargo-home", value_name = "PATH")]
189 pub cargo_home: Option<String>,
190
191 #[arg(long = "install-origin", value_enum, hide = true)]
194 pub install_origin: Option<MigrateInstallOrigin>,
195}
196
197#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
202pub enum MigrateInstallOrigin {
203 MsiGlobal,
204 MsiCorporate,
205 ExeGlobal,
206 ExeCorporate,
207}
208
209#[derive(Parser)]
215#[command(
216 name = "speedqx",
217 author,
218 version,
219 disable_version_flag = true,
220 about = "SpeedQX Internet Speed Test - QubeTX Developer Tools",
221 long_about = "SpeedQX Internet Speed Test - QubeTX Developer Tools\n\n\
222 Eight-provider speed test (SpeedQX Methodology v4) using Cloudflare, M-Lab NDT7,\n\
223 M-Lab MSAK (multi-stream), LibreSpeed, fast.com (Netflix), CacheFly, Vultr, and\n\
224 Apple networkQuality. Providers run sequentially and are merged (capacity +\n\
225 consensus with confidence intervals) for maximum accuracy. --fast runs a quick\n\
226 three-provider subset with early stopping.",
227 after_long_help = "EXAMPLES:\n\
228 \x20 speedqx Run the full 8-provider test\n\
229 \x20 speedqx --fast Quick run (Cloudflare + NDT7 + MSAK, early-stop)\n\
230 \x20 speedqx --duration 60 60s per direction for the fixed-duration providers\n\
231 \x20 speedqx --fastcom-duration 30 Override fast.com to 30s/dir\n\
232 \x20 speedqx --skip-msak --skip-apple Drop MSAK + Apple from the full run\n\
233 \x20 speedqx update Check for updates and install\n\
234 \x20 speedqx --update Same as 'speedqx update' (legacy flag form)\n\
235 \x20 speedqx --json Output results as JSON\n\n\
236 Run 'speedqx --help' for full details, or 'speedqx -h' for a summary."
237)]
238pub struct SpeedQXCli {
239 #[arg(long, help_heading = "Output", global = true)]
241 pub json: bool,
242
243 #[arg(long, help_heading = "Output", global = true)]
245 pub ascii: bool,
246
247 #[arg(long, help_heading = "Output", global = true)]
249 pub no_color: bool,
250
251 #[arg(
253 long,
254 default_value = "30",
255 value_parser = parse_duration,
256 help_heading = "Speed Test"
257 )]
258 pub duration: TestDuration,
259
260 #[arg(
262 long,
263 default_value = "auto",
264 value_parser = parse_duration,
265 help_heading = "Speed Test"
266 )]
267 pub fastcom_duration: TestDuration,
268
269 #[arg(long, default_value = "20", help_heading = "Speed Test")]
271 pub latency_probes: u32,
272
273 #[arg(long = "fast", help_heading = "Speed Test")]
276 pub fast: bool,
277
278 #[arg(long = "skip-msak", help_heading = "Speed Test")]
280 pub skip_msak: bool,
281
282 #[arg(long = "skip-apple", help_heading = "Speed Test")]
284 pub skip_apple: bool,
285
286 #[arg(long = "update", help_heading = "Actions")]
288 pub update: bool,
289
290 #[arg(short = 'v', long = "version", action = clap::ArgAction::Version)]
292 pub version: (),
293
294 #[command(subcommand)]
296 pub command: Option<SpeedQXCommand>,
297}
298
299#[derive(Subcommand, Debug, Clone)]
302pub enum SpeedQXCommand {
303 Update,
305}
306
307fn parse_duration(s: &str) -> Result<TestDuration, String> {
308 if s.eq_ignore_ascii_case("auto") {
309 Ok(TestDuration::Auto)
310 } else {
311 s.parse::<u64>()
312 .map(TestDuration::Seconds)
313 .map_err(|_| format!("invalid duration '{}': expected a number or \"auto\"", s))
314 }
315}