ocpi_tariffs_cli/
print.rs1use std::fmt::{self, Write as _};
2
3use console::{measure_text_width, style};
4use ocpi_tariffs::UnknownFields;
5use tracing::error;
6
7const LINE: &str = "----------------------------------------------------------------";
9const TABLE_BUF_LEN: usize = 4096;
11
12pub fn unknown_cdr_fields(unknown_fields: UnknownFields) {
14 if unknown_fields.is_empty() {
15 return;
16 }
17
18 eprintln!(
19 "{}: {} Unknown fields found in the CDR",
20 style("WARN").yellow(),
21 unknown_fields.len(),
22 );
23
24 for field_name in unknown_fields {
25 eprintln!(" - {}", style(field_name).yellow());
26 }
27
28 let line = style(LINE).yellow();
29 eprintln!("{line}");
30}
31
32pub fn unknown_tariff_fields(unknown_fields: Vec<(String, UnknownFields)>) {
34 if unknown_fields
35 .iter()
36 .all(|(_, unknown_fields)| unknown_fields.is_empty())
37 {
38 return;
39 }
40
41 let line = style(LINE).yellow();
42
43 eprintln!(
44 "{}: Unknown fields found in the tariffs",
45 style("WARN").yellow(),
46 );
47
48 for (tariff_id, unknown_fields) in unknown_fields {
49 if unknown_fields.is_empty() {
50 continue;
51 }
52
53 eprintln!(
54 "{}: {} Unknown fields found in the tariff with id: {}",
55 style("WARN").yellow(),
56 unknown_fields.len(),
57 style(tariff_id).yellow(),
58 );
59
60 for field_name in unknown_fields {
61 eprintln!(" - {}", style(field_name).yellow());
62 }
63
64 eprintln!("{line}");
65 }
66
67 eprintln!("{line}");
68}
69
70pub struct Table {
72 widths: Vec<usize>,
74 buf: String,
76}
77
78pub struct Col<'a> {
80 pub label: &'a dyn fmt::Display,
81 pub width: usize,
82}
83
84impl Col<'_> {
85 pub fn empty(width: usize) -> Self {
86 Self { label: &"", width }
87 }
88}
89
90impl Table {
91 pub fn header(header: &[Col<'_>]) -> Self {
93 let widths = header
94 .iter()
95 .map(|Col { label: _, width }| *width)
96 .collect::<Vec<_>>();
97 let mut buf = String::with_capacity(TABLE_BUF_LEN);
98 let labels = header
99 .iter()
100 .map(|Col { label, width: _ }| *label)
101 .collect::<Vec<_>>();
102
103 print_table_line(&mut buf, &widths);
104 print_table_row(&mut buf, &widths, &labels);
105 print_table_line(&mut buf, &widths);
106
107 Self { widths, buf }
108 }
109
110 pub fn print_line(&mut self) {
112 print_table_line(&mut self.buf, &self.widths);
113 }
114
115 pub fn print_row(&mut self, values: &[&dyn fmt::Display]) {
117 print_table_row(&mut self.buf, &self.widths, values);
118 }
119
120 pub fn print_valid_row(
125 &mut self,
126 is_valid: bool,
127 label: &'static str,
128 values: &[&dyn fmt::Display],
129 ) {
130 let label = if is_valid {
131 style(label).green()
132 } else {
133 style(label).red()
134 };
135 print_table_row_with_label(&mut self.buf, &self.widths, &label, values);
136 }
137
138 pub fn finish(self) -> String {
140 let Self { widths, mut buf } = self;
141 print_table_line(&mut buf, &widths);
142 buf
143 }
144}
145
146#[macro_export]
153macro_rules! write_or {
154 ($dst:expr, $($arg:tt)*) => {{
155 let _ignore_result = $dst.write_fmt(std::format_args!($($arg)*));
156 }};
157}
158
159fn print_table_line(buf: &mut String, widths: &[usize]) {
161 write_or!(buf, "+");
162
163 for width in widths {
164 write_or!(buf, "{0:->1$}+", "", width + 2);
165 }
166
167 write_or!(buf, "\n");
168}
169
170fn print_table_row(buf: &mut String, widths: &[usize], values: &[&dyn fmt::Display]) {
172 assert_eq!(
173 widths.len(),
174 values.len(),
175 "The widths and values amounts should be the same"
176 );
177 print_table_row_(buf, widths, values, None);
178}
179
180fn print_table_row_with_label(
184 buf: &mut String,
185 widths: &[usize],
186 label: &dyn fmt::Display,
187 values: &[&dyn fmt::Display],
188) {
189 print_table_row_(buf, widths, values, Some(label));
190}
191
192fn print_table_row_(
194 buf: &mut String,
195 widths: &[usize],
196 values: &[&dyn fmt::Display],
197 label: Option<&dyn fmt::Display>,
198) {
199 write_or!(buf, "|");
200
201 if let Some(label) = label {
202 let mut widths = widths.iter();
203 let Some(width) = widths.next() else {
204 return;
205 };
206 print_col(buf, label, *width);
207
208 for (value, width) in values.iter().zip(widths) {
209 print_col(buf, *value, *width);
210 }
211 } else {
212 for (value, width) in values.iter().zip(widths) {
213 print_col(buf, *value, *width);
214 }
215 }
216
217 write_or!(buf, "\n");
218}
219
220fn print_col(buf: &mut String, value: &dyn fmt::Display, width: usize) {
222 write_or!(buf, " ");
223
224 let len_before = buf.len();
227 write_or!(buf, "{value}");
228 let len_after = buf.len();
229
230 let Some(s) = &buf.get(len_before..len_after) else {
233 error!("Non UTF8 values were written as a column value");
234 return;
235 };
236
237 let len = measure_text_width(s);
238 let padding = width.saturating_sub(len);
240
241 for _ in 0..padding {
243 write_or!(buf, " ");
244 }
245
246 write_or!(buf, " |");
247}