1use crate::{Cell, TableStyle};
2use itertools::Itertools;
3use std::fmt::{self, Write};
4
5#[derive(Debug, Clone)]
7pub struct Row<'data> {
8 pub(crate) cells: Vec<Cell<'data>>,
9 pub(crate) has_separator: bool,
11}
12
13impl<'data> Default for Row<'data> {
14 fn default() -> Self {
15 Self {
16 cells: vec![],
17 has_separator: true,
18 }
19 }
20}
21
22impl<'data> Row<'data> {
23 pub fn new() -> Self {
24 Default::default()
25 }
26
27 pub fn with_separator(mut self, has_separator: bool) -> Self {
29 self.set_has_separator(has_separator);
30 self
31 }
32
33 pub fn set_has_separator(&mut self, has_separator: bool) -> &mut Self {
35 self.has_separator = has_separator;
36 self
37 }
38
39 pub fn add_cell(&mut self, cell: impl Into<Cell<'data>>) -> &mut Self {
40 self.cells.push(cell.into());
41 self
42 }
43
44 pub fn with_cell(mut self, cell: impl Into<Cell<'data>>) -> Self {
45 self.add_cell(cell);
46 self
47 }
48
49 pub(crate) fn columns(&self) -> usize {
51 self.cells.iter().map(|cell| cell.col_span).sum()
52 }
53
54 pub(crate) fn layout(&self, column_widths: &[usize], border_width: usize) -> usize {
58 let mut max_lines = 0;
59 let mut idx = 0;
60 for cell in self.cells.iter() {
61 let mut width = (cell.col_span - 1) * border_width;
63
64 for w in column_widths[idx..idx + cell.col_span].iter().copied() {
66 width += w;
67 }
68 let num_lines = cell.layout(Some(width));
69 idx += cell.col_span;
70 max_lines = max_lines.max(num_lines);
71 }
72 max_lines
73 }
74
75 pub fn render_top_separator(
76 &self,
77 cell_widths: &[usize],
78 style: &TableStyle,
79 f: &mut fmt::Formatter,
80 ) -> fmt::Result {
81 if !self.has_separator {
82 return Ok(());
84 }
85 f.write_char(style.top_left_corner)?;
87 let mut widths = cell_widths;
88 let mut width;
89 let mut cells = self.cells.iter();
90 if let Some(first_cell) = cells.next() {
91 (width, widths) = first_cell.width(style.border_width(), widths);
92 for _ in 0..width {
93 f.write_char(style.horizontal)?;
94 }
95 }
96 for cell in cells {
97 f.write_char(style.outer_top_horizontal)?;
98 (width, widths) = cell.width(style.border_width(), widths);
99 for _ in 0..width {
100 f.write_char(style.horizontal)?;
101 }
102 }
103 f.write_char(style.top_right_corner)?;
104 writeln!(f)
105 }
106
107 pub fn render_bottom_separator(
108 &self,
109 cell_widths: &[usize],
110 style: &TableStyle,
111 f: &mut fmt::Formatter,
112 ) -> fmt::Result {
113 if !self.has_separator {
114 return Ok(());
116 }
117 f.write_char(style.bottom_left_corner)?;
119 let mut widths = cell_widths;
120 let mut width;
121 let mut cells = self.cells.iter();
122 if let Some(first_cell) = cells.next() {
123 (width, widths) = first_cell.width(style.border_width(), widths);
124 for _ in 0..width {
125 f.write_char(style.horizontal)?;
126 }
127 }
128 for cell in cells {
129 f.write_char(style.outer_bottom_horizontal)?;
130 (width, widths) = cell.width(style.border_width(), widths);
131 for _ in 0..width {
132 f.write_char(style.horizontal)?;
133 }
134 }
135 f.write_char(style.bottom_right_corner)?;
136 writeln!(f)
137 }
138
139 pub fn render_separator(
140 &self,
141 prev: &Row,
142 cell_widths: &[usize],
143 style: &TableStyle,
144 f: &mut fmt::Formatter,
145 ) -> fmt::Result {
146 if !self.has_separator {
147 return Ok(());
149 }
150 f.write_char(style.outer_left_vertical)?;
151 let mut iter = cell_widths
152 .iter()
153 .copied()
154 .zip(self.iter_junctions(prev).skip(1))
155 .peekable();
156 while let Some((width, borders)) = iter.next() {
157 for _ in 0..width {
158 f.write_char(style.horizontal)?;
159 }
160 f.write_char(borders.joiner(style, iter.peek().is_none()))?;
161 }
162 writeln!(f)
163 }
164
165 pub(crate) fn render_content(
167 &self,
168 column_widths: &[usize],
169 num_lines: usize,
170 style: &TableStyle,
171 f: &mut fmt::Formatter,
172 ) -> fmt::Result {
173 for line_num in 0..num_lines {
174 let mut width;
175 let mut widths = column_widths;
176 for cell in &self.cells {
177 f.write_char(style.vertical)?;
178 (width, widths) = cell.width(style.border_width(), widths);
179 cell.render_line(line_num, width, f)?;
180 }
181 f.write_char(style.vertical)?;
182 writeln!(f)?;
183 }
184 Ok(())
185 }
186 pub fn num_columns(&self) -> usize {
190 self.cells.iter().map(|x| x.col_span).sum()
191 }
192
193 fn iter_joins(&'data self) -> impl Iterator<Item = BorderTy> + 'data {
195 struct IterJoins<'a> {
196 inner: std::slice::Iter<'a, Cell<'a>>,
197 cols_remaining: usize,
199 }
200
201 impl<'a> Iterator for IterJoins<'a> {
202 type Item = BorderTy;
203 fn next(&mut self) -> Option<Self::Item> {
204 let out = Some(match &mut self.cols_remaining {
205 0 => BorderTy::Empty,
207 n @ 1 => {
209 *n = self.inner.next().map(|cell| cell.col_span).unwrap_or(0);
210 BorderTy::End
211 }
212 n => {
214 *n -= 1;
215 BorderTy::Middle
216 }
217 });
218 out
219 }
220 }
221 IterJoins {
222 inner: self.cells.iter(),
223 cols_remaining: 1,
225 }
226 }
227
228 fn iter_junctions(&'data self, prev: &'data Self) -> impl Iterator<Item = Borders> + 'data {
230 prev.iter_joins()
231 .zip(self.iter_joins())
232 .map(|(above, below)| {
233 let borders = Borders { above, below };
234 if borders == Borders::EMPTY {
235 None
236 } else {
237 Some(borders)
238 }
239 })
240 .while_some()
241 }
242}
243
244#[derive(Debug, Copy, Clone, Eq, PartialEq)]
245struct Borders {
246 above: BorderTy,
247 below: BorderTy,
248}
249
250#[derive(Debug, Copy, Clone, Eq, PartialEq)]
251enum BorderTy {
252 Empty,
253 Middle,
254 End,
255}
256
257impl Borders {
258 const EMPTY: Borders = Borders {
259 above: BorderTy::Empty,
260 below: BorderTy::Empty,
261 };
262
263 fn joiner(&self, style: &TableStyle, final_end: bool) -> char {
264 use BorderTy::*;
265 match (self.above, self.below) {
266 (Empty, Empty) => unreachable!(),
267 (Empty, Middle) | (Middle, Empty) | (Middle, Middle) => style.horizontal,
268 (Empty, End) | (Middle, End) => {
269 if final_end {
270 style.top_right_corner
271 } else {
272 style.outer_top_horizontal
273 }
274 }
275 (End, Empty) | (End, Middle) => {
276 if final_end {
277 style.bottom_right_corner
278 } else {
279 style.outer_bottom_horizontal
280 }
281 }
282 (End, End) => {
283 if final_end {
284 style.outer_right_vertical
285 } else {
286 style.intersection
287 }
288 }
289 }
290 }
291}
292
293pub trait IntoRow {
299 fn headers(&self) -> Row;
301 fn into_row(&self) -> Row;
303}
304
305macro_rules! impl_row_for_tuple {
306 () => {};
307
308 (($first_label:expr, $first_ty:ident) $(,($rest_label:expr, $rest_ty:ident))*) => {
309 impl<$first_ty, $($rest_ty,)*> IntoRow for ($first_ty, $($rest_ty),*)
310 where $first_ty: ::std::fmt::Display,
311 $(
312 $rest_ty: ::std::fmt::Display,
313 )*
314 {
315 fn headers(&self) -> Row {
316 let mut row = Row::default();
317 row.add_cell(stringify!($first_ty));
318 $(
319 row.add_cell(stringify!($rest_ty));
320 )*
321 row
322 }
323
324 fn into_row(&self) -> Row {
325 #[allow(non_snake_case)]
326 let (
327 ref $first_ty,
328 $(
329 ref $rest_ty
330 ),*
331 ) = &self;
332 let mut row = Row::default();
333 row.add_cell($first_ty.to_string());
334 $(
335 row.add_cell($rest_ty.to_string());
336 )*
337 row
338 }
339 }
340
341 impl_row_for_tuple!($(($rest_label, $rest_ty)),*);
342 };
343}
344
345impl_row_for_tuple!(
346 ("_0", D0),
347 ("_1", D1),
348 ("_2", D2),
349 ("_3", D3),
350 ("_4", D4),
351 ("_5", D5),
352 ("_6", D6),
353 ("_7", D7),
354 ("_8", D8),
355 ("_9", D9)
356);