Skip to main content

nd_300/
config.rs

1pub const DEFAULT_TITLE: &str = "QUBETX DEVELOPER TOOLS";
2pub const DEFAULT_SUBTITLE: &str = "ND-300 NETWORK DIAGNOSTIC";
3
4pub const MIN_LABEL_WIDTH: usize = 5;
5pub const MAX_LABEL_WIDTH: usize = 16;
6pub const MIN_DATA_WIDTH: usize = 20;
7pub const MAX_DATA_WIDTH: usize = 40;
8pub const BORDERS_PADDING: usize = 7;
9
10pub mod chars {
11    pub const TOP_LEFT: char = '┌';
12    pub const TOP_RIGHT: char = '┐';
13    pub const BOTTOM_LEFT: char = '└';
14    pub const BOTTOM_RIGHT: char = '┘';
15    pub const HORIZONTAL: char = '─';
16    pub const VERTICAL: char = '│';
17    pub const T_DOWN: char = '┬';
18    pub const T_UP: char = '┴';
19    pub const T_RIGHT: char = '├';
20    pub const T_LEFT: char = '┤';
21    pub const CROSS: char = '┼';
22    pub const BAR_FILLED: char = '█';
23    pub const BAR_EMPTY: char = '░';
24}
25
26pub mod ascii_chars {
27    pub const TOP_LEFT: char = '+';
28    pub const TOP_RIGHT: char = '+';
29    pub const BOTTOM_LEFT: char = '+';
30    pub const BOTTOM_RIGHT: char = '+';
31    pub const HORIZONTAL: char = '-';
32    pub const VERTICAL: char = '|';
33    pub const T_DOWN: char = '+';
34    pub const T_UP: char = '+';
35    pub const T_RIGHT: char = '+';
36    pub const T_LEFT: char = '+';
37    pub const CROSS: char = '+';
38    pub const BAR_FILLED: char = '#';
39    pub const BAR_EMPTY: char = '.';
40}
41
42pub mod status_chars {
43    pub const OK: &str = "\u{2713}";
44    pub const WARN: &str = "\u{26A0}";
45    pub const FAIL: &str = "\u{2717}";
46    pub const SKIP: &str = "\u{2014}";
47
48    pub const OK_ASCII: &str = "[OK]";
49    pub const WARN_ASCII: &str = "[!!]";
50    pub const FAIL_ASCII: &str = "[XX]";
51    pub const SKIP_ASCII: &str = "[--]";
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub enum OutputFormat {
56    Table,
57    Json,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61pub enum OperatingMode {
62    User,
63    Technician,
64}
65
66#[derive(Debug, Clone)]
67pub struct Config {
68    pub use_unicode: bool,
69    pub use_colors: bool,
70    pub title: Option<String>,
71    pub mode: OperatingMode,
72    pub format: OutputFormat,
73    pub skip_speed: bool,
74    pub speed_duration: u64,
75    pub verbose: bool,
76    pub auto_confirm_medium_risk: bool,
77}
78
79impl Default for Config {
80    fn default() -> Self {
81        Self {
82            use_unicode: true,
83            use_colors: true,
84            title: None,
85            mode: OperatingMode::User,
86            format: OutputFormat::Table,
87            skip_speed: false,
88            speed_duration: 10,
89            verbose: false,
90            auto_confirm_medium_risk: false,
91        }
92    }
93}
94
95impl Config {
96    pub fn new() -> Self {
97        Self::default()
98    }
99
100    pub fn title(&self) -> &str {
101        self.title.as_deref().unwrap_or(DEFAULT_TITLE)
102    }
103
104    pub fn subtitle(&self) -> &str {
105        DEFAULT_SUBTITLE
106    }
107
108    pub fn box_chars(&self) -> BoxChars {
109        if self.use_unicode {
110            BoxChars::unicode()
111        } else {
112            BoxChars::ascii()
113        }
114    }
115
116    pub fn bar_chars(&self) -> (char, char) {
117        if self.use_unicode {
118            (chars::BAR_FILLED, chars::BAR_EMPTY)
119        } else {
120            (ascii_chars::BAR_FILLED, ascii_chars::BAR_EMPTY)
121        }
122    }
123
124    pub fn status_chars(&self, status: &crate::diagnostics::DiagnosticStatus) -> &'static str {
125        use crate::diagnostics::DiagnosticStatus;
126        if self.use_unicode {
127            match status {
128                DiagnosticStatus::Ok => status_chars::OK,
129                DiagnosticStatus::Warn => status_chars::WARN,
130                DiagnosticStatus::Fail => status_chars::FAIL,
131                DiagnosticStatus::Skip => status_chars::SKIP,
132            }
133        } else {
134            match status {
135                DiagnosticStatus::Ok => status_chars::OK_ASCII,
136                DiagnosticStatus::Warn => status_chars::WARN_ASCII,
137                DiagnosticStatus::Fail => status_chars::FAIL_ASCII,
138                DiagnosticStatus::Skip => status_chars::SKIP_ASCII,
139            }
140        }
141    }
142
143    pub fn is_tech_mode(&self) -> bool {
144        self.mode == OperatingMode::Technician
145    }
146
147    pub fn effective_width(&self) -> usize {
148        crossterm::terminal::size()
149            .map(|(w, _)| w as usize)
150            .unwrap_or(80)
151    }
152
153    pub fn calculate_widths(&self, max_label: usize, max_data: usize) -> (usize, usize) {
154        let label_width = max_label.clamp(MIN_LABEL_WIDTH, MAX_LABEL_WIDTH);
155        let data_width = max_data.clamp(MIN_DATA_WIDTH, MAX_DATA_WIDTH);
156        (label_width, data_width)
157    }
158
159    pub fn table_width(&self, label_width: usize, data_width: usize) -> usize {
160        label_width + data_width + BORDERS_PADDING
161    }
162
163    pub fn with_ascii(mut self) -> Self {
164        self.use_unicode = false;
165        self
166    }
167
168    pub fn with_colors(mut self, colors: bool) -> Self {
169        self.use_colors = colors;
170        self
171    }
172
173    pub fn with_title(mut self, title: impl Into<String>) -> Self {
174        self.title = Some(title.into());
175        self
176    }
177
178    pub fn with_tech_mode(mut self) -> Self {
179        self.mode = OperatingMode::Technician;
180        self
181    }
182
183    pub fn with_json(mut self) -> Self {
184        self.format = OutputFormat::Json;
185        self
186    }
187
188    pub fn with_skip_speed(mut self) -> Self {
189        self.skip_speed = true;
190        self
191    }
192
193    pub fn with_speed_duration(mut self, seconds: u64) -> Self {
194        self.speed_duration = seconds.max(4);
195        self
196    }
197
198    pub fn with_verbose(mut self) -> Self {
199        self.verbose = true;
200        self
201    }
202
203    pub fn with_auto_confirm_medium_risk(mut self) -> Self {
204        self.auto_confirm_medium_risk = true;
205        self
206    }
207}
208
209#[derive(Debug, Clone, Copy)]
210pub struct BoxChars {
211    pub top_left: char,
212    pub top_right: char,
213    pub bottom_left: char,
214    pub bottom_right: char,
215    pub horizontal: char,
216    pub vertical: char,
217    pub t_down: char,
218    pub t_up: char,
219    pub t_right: char,
220    pub t_left: char,
221    pub cross: char,
222}
223
224impl BoxChars {
225    pub fn unicode() -> Self {
226        Self {
227            top_left: chars::TOP_LEFT,
228            top_right: chars::TOP_RIGHT,
229            bottom_left: chars::BOTTOM_LEFT,
230            bottom_right: chars::BOTTOM_RIGHT,
231            horizontal: chars::HORIZONTAL,
232            vertical: chars::VERTICAL,
233            t_down: chars::T_DOWN,
234            t_up: chars::T_UP,
235            t_right: chars::T_RIGHT,
236            t_left: chars::T_LEFT,
237            cross: chars::CROSS,
238        }
239    }
240
241    pub fn ascii() -> Self {
242        Self {
243            top_left: ascii_chars::TOP_LEFT,
244            top_right: ascii_chars::TOP_RIGHT,
245            bottom_left: ascii_chars::BOTTOM_LEFT,
246            bottom_right: ascii_chars::BOTTOM_RIGHT,
247            horizontal: ascii_chars::HORIZONTAL,
248            vertical: ascii_chars::VERTICAL,
249            t_down: ascii_chars::T_DOWN,
250            t_up: ascii_chars::T_UP,
251            t_right: ascii_chars::T_RIGHT,
252            t_left: ascii_chars::T_LEFT,
253            cross: ascii_chars::CROSS,
254        }
255    }
256}