Skip to main content

sd_300/
cli.rs

1use clap::{Parser, Subcommand};
2
3#[derive(Subcommand, Debug, Clone, Copy, PartialEq, Eq)]
4pub enum Command {
5    /// Check for updates and install the latest release.
6    Update,
7}
8
9/// SD-300 System Diagnostic — Real-time interactive system diagnostics TUI
10#[derive(Parser, Debug)]
11#[command(name = "sd300")]
12#[command(
13    author,
14    version,
15    about = "SD-300 System Diagnostic — QubeTX Developer Tools"
16)]
17#[command(args_conflicts_with_subcommands = true)]
18#[command(
19    long_about = "SD-300 is a live, interactive terminal user interface for real-time \n\
20    system diagnostics and monitoring. Part of the QubeTX 300 Series \n\
21    alongside TR-300 (Machine Report) and ND-300 (Network Diagnostic).\n\n\
22    Run without flags to choose a diagnostic mode interactively, \n\
23    use --user / --tech to launch directly into a mode, or run \n\
24    sd300 update to check for and install the latest release."
25)]
26#[command(after_long_help = "\
27EXAMPLES:
28  sd300          Choose a diagnostic mode interactively
29  sd300 --user   Launch directly into User Mode
30  sd300 --tech   Launch directly into Technician Mode
31  sd300 update   Check for updates and install
32  sd300 --update Same as 'sd300 update' (legacy flag form)
33
34KEYBINDINGS:
35  1-9          Switch to section
36  q / Esc      Quit
37  Ctrl+C       Quit to shell
38  m            Return to mode selection
39  ?            Help overlay
40  f            Toggle temperature unit (C/F)
41  j / k        Scroll (Processes, Connections, Drivers, Disk)
42  c / M / p / n  Sort processes by CPU / Memory / PID / Name
43  r            Refresh drivers (Drivers section)
44
45SECTIONS:
46  1 Overview    System health dashboard / identity and gauges
47  2 CPU         Load, sparkline, per-core utilization
48  3 Memory      Usage, swap, top consumers
49  4 Disk        Drive health, partitions, SMART data
50  5 GPU         Utilization, VRAM, temperature
51  6 Network     Connectivity, interfaces, active connections
52  7 Processes   Running apps / sortable process table
53  8 Thermals    Temperature, fans, battery, power
54  9 Drivers     Device health, driver versions, services")]
55pub struct Cli {
56    /// Launch directly into User Mode (plain language diagnostics)
57    #[arg(long, conflicts_with_all = ["tech", "update"])]
58    pub user: bool,
59
60    /// Launch directly into Technician Mode (raw data and advanced metrics)
61    #[arg(long, conflicts_with_all = ["user", "update"])]
62    pub tech: bool,
63
64    /// Check for updates and install the latest version (legacy flag form of `sd300 update`)
65    #[arg(long, conflicts_with_all = ["user", "tech"])]
66    pub update: bool,
67
68    /// Action subcommand. If present, takes precedence over legacy action flags.
69    #[command(subcommand)]
70    pub command: Option<Command>,
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76    use clap::{CommandFactory, Parser};
77
78    #[test]
79    fn parses_positional_update_action() {
80        let cli = Cli::try_parse_from(["sd300", "update"]).expect("update action should parse");
81        assert_eq!(cli.command, Some(Command::Update));
82        assert!(!cli.update);
83    }
84
85    #[test]
86    fn parses_legacy_update_flag() {
87        let cli = Cli::try_parse_from(["sd300", "--update"]).expect("--update should parse");
88        assert!(cli.update);
89        assert_eq!(cli.command, None);
90    }
91
92    #[test]
93    fn rejects_update_with_mode_flags() {
94        let err = Cli::try_parse_from(["sd300", "update", "--tech"]).unwrap_err();
95        assert!(matches!(
96            err.kind(),
97            clap::error::ErrorKind::ArgumentConflict | clap::error::ErrorKind::UnknownArgument
98        ));
99    }
100
101    #[test]
102    fn help_includes_update_forms() {
103        let help = Cli::command().render_long_help().to_string();
104        assert!(help.contains("sd300 update"));
105        assert!(help.contains("--update"));
106    }
107}