1use clap::{Args, Parser, Subcommand};
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 -d Change DNS configuration\n\
20 \x20 nd300 fix Diagnostic-driven triage and recovery loop\n\
21 \x20 nd300 -f Same as 'nd300 fix' (legacy flag form)\n\
22 \x20 nd300 update Check for updates and install\n\
23 \x20 nd300 clear-dns Reset DNS cache\n\
24 \x20 nd300 uninstall Remove nd300 from this system\n\
25 \x20 nd300 --fast Skip speed test for faster execution\n\
26 \x20 nd300 --json Output results as JSON\n\n\
27 Run 'nd300 --help' for full details, or 'nd300 -h' for a summary."
28)]
29pub struct Nd300Cli {
30 #[arg(
32 short = 't',
33 long = "tech",
34 alias = "technician",
35 help_heading = "Modes",
36 global = true
37 )]
38 pub tech: bool,
39
40 #[arg(short = 'T', long, help_heading = "Modes", global = true)]
42 pub title: Option<String>,
43
44 #[arg(long, help_heading = "Output", global = true)]
46 pub json: bool,
47
48 #[arg(long, help_heading = "Output", global = true)]
50 pub ascii: bool,
51
52 #[arg(long, help_heading = "Output", global = true)]
54 pub no_color: bool,
55
56 #[arg(long, help_heading = "Output", global = true)]
58 pub verbose: bool,
59
60 #[arg(long, help_heading = "Speed Test", global = true)]
62 pub fast: bool,
63
64 #[arg(long, default_value = "10", help_heading = "Speed Test", global = true)]
66 pub speed_duration: u64,
67
68 #[arg(short = 'd', long = "dns", help_heading = "Actions")]
70 pub dns: bool,
71
72 #[arg(short = 'f', long = "fix", help_heading = "Actions")]
74 pub fix: bool,
75
76 #[arg(short = 'c', long = "clear-dns", help_heading = "Actions")]
78 pub clear_dns: bool,
79
80 #[arg(long = "uninstall", help_heading = "Actions")]
82 pub uninstall: bool,
83
84 #[arg(long = "update", help_heading = "Actions")]
86 pub update: bool,
87
88 #[arg(
91 short = 'y',
92 long = "yes",
93 alias = "non-interactive",
94 help_heading = "Actions",
95 global = true
96 )]
97 pub yes: bool,
98
99 #[arg(short = 'v', long = "version", action = clap::ArgAction::Version)]
101 pub version: (),
102
103 #[command(subcommand)]
105 pub command: Option<Nd300Command>,
106}
107
108#[derive(Subcommand, Debug, Clone)]
113pub enum Nd300Command {
114 Fix(FixArgs),
119
120 Update,
122
123 #[command(name = "clear-dns")]
125 ClearDns,
126
127 Uninstall,
129}
130
131#[derive(Args, Debug, Clone, Default)]
137pub struct FixArgs {}
138
139#[derive(Parser)]
143#[command(
144 name = "speedqx",
145 author,
146 version,
147 disable_version_flag = true,
148 about = "SpeedQX Internet Speed Test - QubeTX Developer Tools",
149 long_about = "SpeedQX Internet Speed Test - QubeTX Developer Tools\n\n\
150 Quad-provider speed test using Cloudflare, M-Lab NDT7, LibreSpeed, and fast.com (Netflix).\n\
151 All four providers run and results are aggregated for maximum accuracy.",
152 after_long_help = "EXAMPLES:\n\
153 \x20 speedqx Run full speed test (all 4 providers)\n\
154 \x20 speedqx --duration 60 60s per direction for CF/NDT7/LS\n\
155 \x20 speedqx --fastcom-duration 30 Override fast.com to 30s/dir\n\
156 \x20 speedqx update Check for updates and install\n\
157 \x20 speedqx --update Same as 'speedqx update' (legacy flag form)\n\
158 \x20 speedqx --json Output results as JSON\n\n\
159 Run 'speedqx --help' for full details, or 'speedqx -h' for a summary."
160)]
161pub struct SpeedQXCli {
162 #[arg(long, help_heading = "Output", global = true)]
164 pub json: bool,
165
166 #[arg(long, help_heading = "Output", global = true)]
168 pub ascii: bool,
169
170 #[arg(long, help_heading = "Output", global = true)]
172 pub no_color: bool,
173
174 #[arg(
176 long,
177 default_value = "30",
178 value_parser = parse_duration,
179 help_heading = "Speed Test"
180 )]
181 pub duration: TestDuration,
182
183 #[arg(
185 long,
186 default_value = "auto",
187 value_parser = parse_duration,
188 help_heading = "Speed Test"
189 )]
190 pub fastcom_duration: TestDuration,
191
192 #[arg(long, default_value = "20", help_heading = "Speed Test")]
194 pub latency_probes: u32,
195
196 #[arg(long = "update", help_heading = "Actions")]
198 pub update: bool,
199
200 #[arg(short = 'v', long = "version", action = clap::ArgAction::Version)]
202 pub version: (),
203
204 #[command(subcommand)]
206 pub command: Option<SpeedQXCommand>,
207}
208
209#[derive(Subcommand, Debug, Clone)]
212pub enum SpeedQXCommand {
213 Update,
215}
216
217fn parse_duration(s: &str) -> Result<TestDuration, String> {
218 if s.eq_ignore_ascii_case("auto") {
219 Ok(TestDuration::Auto)
220 } else {
221 s.parse::<u64>()
222 .map(TestDuration::Seconds)
223 .map_err(|_| format!("invalid duration '{}': expected a number or \"auto\"", s))
224 }
225}