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 pub devices: Vec<String>,
10
11 #[arg(short, long)]
13 pub list: bool,
14
15 #[arg(short = 'a', long = "average", default_value = "300")]
17 pub average_window: u32,
18
19 #[arg(short = 'i', long = "incoming", default_value = "0")]
21 pub max_incoming: u64,
22
23 #[arg(short = 'o', long = "outgoing", default_value = "0")]
25 pub max_outgoing: u64,
26
27 #[arg(short = 't', long = "interval", default_value = "1000")]
29 pub refresh_interval: u64,
30
31 #[arg(
33 long = "high-perf",
34 help = "Enable high performance mode (slower updates, less CPU)"
35 )]
36 pub high_performance: bool,
37
38 #[arg(short = 'u', long = "unit", default_value = "k")]
40 pub traffic_unit: TrafficUnit,
41
42 #[arg(short = 'U', long = "data-unit", default_value = "M")]
44 pub data_unit: DataUnit,
45
46 #[arg(short = 'm', long = "multiple")]
48 pub multiple_devices: bool,
49
50 #[arg(short = 'f', long = "file")]
52 pub log_file: Option<String>,
53
54 #[arg(long)]
56 pub test: bool,
57
58 #[arg(long)]
60 pub debug_dashboard: bool,
61
62 #[arg(long)]
64 pub show_comparison: bool,
65
66 #[arg(long)]
68 pub show_overview: bool,
69
70 #[arg(long)]
72 pub force_terminal: bool,
73
74 #[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, #[value(name = "H")]
85 HumanByte, #[value(name = "b")]
87 Bit, #[value(name = "B")]
89 Byte, #[value(name = "k")]
91 KiloBit, #[value(name = "K")]
93 KiloByte, #[value(name = "m")]
95 MegaBit, #[value(name = "M")]
97 MegaByte, #[value(name = "g")]
99 GigaBit, #[value(name = "G")]
101 GigaByte, }
103
104pub use TrafficUnit as DataUnit;
105
106impl Args {
107 pub fn validate(&self) -> crate::error::Result<()> {
109 for device in &self.devices {
111 validation::validate_interface_name(device)?;
112 }
113
114 validation::validate_refresh_interval(self.refresh_interval)?;
116
117 validation::validate_bandwidth(self.max_incoming)?;
119 validation::validate_bandwidth(self.max_outgoing)?;
120
121 if let Some(ref log_file) = self.log_file {
123 if log_file != "-" {
124 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}