evobench_tools/output_table/
mod.rs1use itertools::{EitherOrBoth, Itertools};
4use kstring::KString;
5use std::borrow::Cow;
6
7pub mod html;
8pub mod terminal;
9
10#[derive(Debug)]
11pub struct OutputTableTitle<'s> {
12 pub text: Cow<'s, str>,
13 pub span: usize,
16 pub anchor_name: Option<KString>,
18}
19
20pub trait CellValue<'url>: AsRef<str> {
21 fn perhaps_url(&self) -> Option<Cow<'url, str>>;
23 fn perhaps_anchor_name(&self) -> Option<&KString>;
25}
26
27impl<'url> CellValue<'url> for str {
28 fn perhaps_url(&self) -> Option<Cow<'static, str>> {
29 None
30 }
31 fn perhaps_anchor_name(&self) -> Option<&KString> {
32 None
33 }
34}
35impl<'url> CellValue<'url> for &str {
36 fn perhaps_url(&self) -> Option<Cow<'static, str>> {
37 None
38 }
39 fn perhaps_anchor_name(&self) -> Option<&KString> {
40 None
41 }
42}
43impl<'url> CellValue<'url> for String {
44 fn perhaps_url(&self) -> Option<Cow<'static, str>> {
45 None
46 }
47 fn perhaps_anchor_name(&self) -> Option<&KString> {
48 None
49 }
50}
51impl<'t, 'url> CellValue<'url> for Cow<'t, str> {
52 fn perhaps_url(&self) -> Option<Cow<'static, str>> {
53 None
54 }
55 fn perhaps_anchor_name(&self) -> Option<&KString> {
56 None
57 }
58}
59impl<'t, 'url> CellValue<'url> for &dyn CellValue<'url> {
61 fn perhaps_url(&self) -> Option<Cow<'url, str>> {
62 (*self).perhaps_url()
63 }
64 fn perhaps_anchor_name(&self) -> Option<&KString> {
65 None
66 }
67}
68
69pub enum Row<'r, 's, V> {
72 WithSpans(&'r [OutputTableTitle<'s>]),
73 PlainStrings(&'r [V]),
74}
75
76impl<'r, 's, 'url, V: CellValue<'url>> Row<'r, 's, V> {
77 fn logical_len(&self) -> usize {
80 match self {
81 Row::WithSpans(terminal_table_titles) => {
82 let mut cols = 0;
83 for OutputTableTitle {
84 text: _,
85 span,
86 anchor_name: _,
87 } in *terminal_table_titles
88 {
89 cols += span;
90 }
91 cols
92 }
93 Row::PlainStrings(items) => items.len(),
94 }
95 }
96
97 fn string_and_widths(&self, widths: &[usize]) -> Vec<(Cow<'_, str>, Option<usize>)> {
101 match self {
102 Row::WithSpans(terminal_table_titles) => {
103 let mut v: Vec<(Cow<str>, Option<usize>)> = Vec::new();
104 let mut widths = widths.into_iter();
105 for OutputTableTitle {
106 text,
107 span,
108 anchor_name: _,
109 } in *terminal_table_titles
110 {
111 match *span {
112 0 => (),
113 n => {
114 let width = (|| {
115 let mut tot_width = 0;
116 for _ in 0..n {
117 if let Some(width) = widths.next() {
118 tot_width += width;
119 } else {
120 return None;
121 }
122 }
123 Some(tot_width)
124 })();
125 v.push((text.as_ref().into(), width));
126 }
127 }
128 }
129 v
130 }
131 Row::PlainStrings(items) => {
132 let mut v: Vec<(Cow<str>, Option<usize>)> = Vec::new();
133 for either_or_both in items.iter().zip_longest(widths) {
134 match either_or_both {
135 EitherOrBoth::Both(val, width) => {
136 v.push((val.as_ref().into(), Some(*width)))
137 }
138 EitherOrBoth::Left(val) => v.push((val.as_ref().into(), None)),
139 EitherOrBoth::Right(_) => {
140 unreachable!("given row len has been checked against widths len")
141 }
142 }
143 }
144 v
145 }
146 }
147 }
148}
149
150#[derive(Debug, Clone, Copy)]
151pub enum FontSize {
152 XxSmall,
153 XSmall,
154 Small,
155 Medium,
156 Large,
157 XLarge,
158 XxLarge,
159}
160
161impl AsRef<str> for FontSize {
162 fn as_ref(&self) -> &str {
163 match self {
164 FontSize::XxSmall => "xx-small",
165 FontSize::XSmall => "x-small",
166 FontSize::Small => "small",
167 FontSize::Medium => "medium",
168 FontSize::Large => "large",
169 FontSize::XLarge => "x-large",
170 FontSize::XxLarge => "xx-large",
171 }
172 }
173}
174
175#[derive(Debug, Clone, Copy, Default)]
178pub struct OutputStyle {
179 pub faded: bool,
180 pub bold: bool,
181 pub italic: bool,
182 pub font_size: Option<FontSize>,
184 pub color: Option<u8>,
185}
186
187#[derive(Debug, Clone, Copy)]
188pub enum BarKind {
189 Thin,
190 Thick,
191}
192
193pub trait OutputTable {
194 type Output;
195
196 fn num_columns(&self) -> usize;
199
200 fn write_row<'url, V: CellValue<'url>>(
202 &mut self,
203 row: Row<V>,
204 line_style: Option<OutputStyle>,
205 ) -> anyhow::Result<()>;
206
207 fn write_title_row(
208 &mut self,
209 titles: &[OutputTableTitle],
210 line_style: Option<OutputStyle>,
211 ) -> anyhow::Result<()>;
212
213 fn write_data_row<'url, V: CellValue<'url>>(
214 &mut self,
215 data: &[V],
216 line_style: Option<OutputStyle>,
217 ) -> anyhow::Result<()> {
218 self.write_row(Row::PlainStrings(data), line_style)
219 }
220
221 fn write_bar(&mut self, bar_kind: BarKind, anchor_name: Option<&str>) -> anyhow::Result<()>;
222
223 fn print<'url, V: CellValue<'url>>(&mut self, value: V) -> anyhow::Result<()>;
224
225 fn finish(self) -> anyhow::Result<Self::Output>;
226}
227
228#[derive(Clone)]
231pub struct WithUrlOnDemand<'s, 'url> {
232 pub text: &'s str,
233 pub gen_url: Option<&'s dyn Fn() -> Option<Cow<'url, str>>>,
235 pub anchor_name: Option<KString>,
236}
237
238impl<'s, 'url> From<&'s str> for WithUrlOnDemand<'s, 'url> {
239 fn from(text: &'s str) -> Self {
240 WithUrlOnDemand {
241 text,
242 gen_url: None,
243 anchor_name: None,
244 }
245 }
246}
247
248impl<'s, 'url> AsRef<str> for WithUrlOnDemand<'s, 'url> {
249 fn as_ref(&self) -> &str {
250 self.text
251 }
252}
253
254impl<'s, 'url> CellValue<'url> for WithUrlOnDemand<'s, 'url> {
255 fn perhaps_url(&self) -> Option<Cow<'url, str>> {
256 if let Some(gen_url) = self.gen_url {
257 gen_url()
258 } else {
259 None
260 }
261 }
262 fn perhaps_anchor_name(&self) -> Option<&KString> {
263 self.anchor_name.as_ref()
264 }
265}