Skip to main content

netwatch_rs/
cli.rs

1use crate::validation;
2use clap::Parser;
3
4#[derive(Parser, Default)]
5#[command(name = "netwatch", about = "A modern network traffic monitor")]
6#[command(version, long_about = None)]
7pub struct Args {
8    /// Network devices to monitor (default: auto-detect all)
9    pub devices: Vec<String>,
10
11    /// List available network interfaces and exit
12    #[arg(short, long)]
13    pub list: bool,
14
15    /// Average window in seconds
16    #[arg(short = 'a', long = "average", default_value = "300")]
17    pub average_window: u32,
18
19    /// Max incoming bandwidth scaling (kBit/s, 0 = auto)
20    #[arg(short = 'i', long = "incoming", default_value = "0")]
21    pub max_incoming: u64,
22
23    /// Max outgoing bandwidth scaling (kBit/s, 0 = auto)  
24    #[arg(short = 'o', long = "outgoing", default_value = "0")]
25    pub max_outgoing: u64,
26
27    /// Refresh interval in milliseconds
28    #[arg(short = 't', long = "interval", default_value = "1000")]
29    pub refresh_interval: u64,
30
31    /// High performance mode - reduces CPU usage for heavy traffic scenarios
32    #[arg(
33        long = "high-perf",
34        help = "Enable high performance mode (slower updates, less CPU)"
35    )]
36    pub high_performance: bool,
37
38    /// Traffic unit format (h=human-bit, H=human-byte, b=bit, B=byte, k=kbit, K=kbyte, m=mbit, M=mbyte, g=gbit, G=gbyte)
39    #[arg(short = 'u', long = "unit", default_value = "k")]
40    pub traffic_unit: TrafficUnit,
41
42    /// Data unit format (same as -u but for totals)
43    #[arg(short = 'U', long = "data-unit", default_value = "M")]
44    pub data_unit: DataUnit,
45
46    /// Show multiple devices without graphs
47    #[arg(short = 'm', long = "multiple")]
48    pub multiple_devices: bool,
49
50    /// Log traffic data to file
51    #[arg(short = 'f', long = "file")]
52    pub log_file: Option<String>,
53
54    /// Test mode - print statistics once and exit (bypass TUI)
55    #[arg(long)]
56    pub test: bool,
57
58    /// Show dashboard data without TUI (debug mode)
59    #[arg(long)]
60    pub debug_dashboard: bool,
61
62    /// Show before/after comparison of dashboard enhancements
63    #[arg(long)]
64    pub show_comparison: bool,
65
66    /// Show overview panel data in text mode (no TUI)
67    #[arg(long)]
68    pub show_overview: bool,
69
70    /// Force terminal mode (bypass TUI for testing)
71    #[arg(long)]
72    pub force_terminal: bool,
73
74    /// Force SRE forensics terminal mode
75    #[arg(long)]
76    pub sre_terminal: bool,
77}
78
79#[derive(clap::ValueEnum, Clone, Debug, PartialEq, Default)]
80pub enum TrafficUnit {
81    #[value(name = "h")]
82    #[default]
83    HumanBit, // Auto-scale bits
84    #[value(name = "H")]
85    HumanByte, // Auto-scale bytes
86    #[value(name = "b")]
87    Bit, // Bit/s
88    #[value(name = "B")]
89    Byte, // Byte/s
90    #[value(name = "k")]
91    KiloBit, // kBit/s
92    #[value(name = "K")]
93    KiloByte, // kByte/s
94    #[value(name = "m")]
95    MegaBit, // MBit/s
96    #[value(name = "M")]
97    MegaByte, // MByte/s
98    #[value(name = "g")]
99    GigaBit, // GBit/s
100    #[value(name = "G")]
101    GigaByte, // GByte/s
102}
103
104pub use TrafficUnit as DataUnit;
105
106impl Args {
107    /// Validate all command-line arguments for security
108    pub fn validate(&self) -> crate::error::Result<()> {
109        // Validate device names
110        for device in &self.devices {
111            validation::validate_interface_name(device)?;
112        }
113
114        // Validate refresh interval
115        validation::validate_refresh_interval(self.refresh_interval)?;
116
117        // Validate bandwidth values
118        validation::validate_bandwidth(self.max_incoming)?;
119        validation::validate_bandwidth(self.max_outgoing)?;
120
121        // Validate log file path if provided
122        if let Some(ref log_file) = self.log_file {
123            if log_file != "-" {
124                // stdout is allowed
125                validation::validate_file_path(log_file, Some("log"))?;
126            }
127        }
128
129        Ok(())
130    }
131}
132
133impl TrafficUnit {
134    #[must_use]
135    pub fn next(&self) -> Self {
136        match self {
137            Self::HumanBit => Self::HumanByte,
138            Self::HumanByte => Self::Bit,
139            Self::Bit => Self::Byte,
140            Self::Byte => Self::KiloBit,
141            Self::KiloBit => Self::KiloByte,
142            Self::KiloByte => Self::MegaBit,
143            Self::MegaBit => Self::MegaByte,
144            Self::MegaByte => Self::GigaBit,
145            Self::GigaBit => Self::GigaByte,
146            Self::GigaByte => Self::HumanBit,
147        }
148    }
149
150    #[must_use]
151    pub fn to_string(&self) -> &'static str {
152        match self {
153            Self::HumanBit => "h",
154            Self::HumanByte => "H",
155            Self::Bit => "b",
156            Self::Byte => "B",
157            Self::KiloBit => "k",
158            Self::KiloByte => "K",
159            Self::MegaBit => "m",
160            Self::MegaByte => "M",
161            Self::GigaBit => "g",
162            Self::GigaByte => "G",
163        }
164    }
165
166    #[must_use]
167    pub fn from_string(s: &str) -> Option<Self> {
168        match s {
169            "h" => Some(Self::HumanBit),
170            "H" => Some(Self::HumanByte),
171            "b" => Some(Self::Bit),
172            "B" => Some(Self::Byte),
173            "k" => Some(Self::KiloBit),
174            "K" => Some(Self::KiloByte),
175            "m" => Some(Self::MegaBit),
176            "M" => Some(Self::MegaByte),
177            "g" => Some(Self::GigaBit),
178            "G" => Some(Self::GigaByte),
179            _ => None,
180        }
181    }
182}