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}