lexa_logger/layout/
row.rs1use super::cell::{str_len, Cell};
12use super::style::{Alignment, Position, Style};
13
14#[derive(Debug)]
19#[derive(Clone)]
20pub struct Row<'d>
21{
22 pub cells: Vec<Cell<'d>>,
23 pub separator: bool,
24}
25
26impl<'d> Row<'d>
31{
32 pub fn new(cells: impl IntoIterator<Item = impl Into<Cell<'d>>>) -> Row<'d>
33 {
34 let mut row = Row {
35 cells: vec![],
36 separator: true,
37 };
38
39 cells.into_iter().for_each(|entry| row.cells.push(entry.into()));
40
41 row
42 }
43}
44
45impl<'d> Row<'d>
46{
47 pub(crate) fn format(&self, widths: &[usize], style: &Style) -> String
48 {
49 let mut temporary_buffer = String::new();
50 let mut wrapped_cells = Vec::default();
51
52 let mut spanned_columns = 0;
53 let mut row_height = 0;
54
55 for cell in &self.cells {
56 let width = (0..cell.colspan).fold(0, |width, j| width + widths[j + spanned_columns]);
57 let wrapped_cell = cell.wrapped_content(width + cell.colspan - 1);
58 row_height = core::cmp::max(row_height, wrapped_cell.len());
59 wrapped_cells.push(wrapped_cell);
60 spanned_columns += cell.colspan;
61 }
62
63 spanned_columns = 0;
64
65 let mut lines = vec![String::new(); row_height];
66
67 for (column_index, _) in wrapped_cells.iter().enumerate().take(widths.len()) {
68 if self.cells.len() > column_index {
69 let cell = &self.cells[column_index];
70
71 let cell_span = (0..cell.colspan).fold(0, |n, c| n + widths[spanned_columns + c]);
72
73 let callback_fn = |(row_index, row): (usize, &mut String)| {
74 let padding = if wrapped_cells[column_index].len() > row_index {
75 let str_width = str_len(&wrapped_cells[column_index][row_index]);
76
77 let mut padding = 0;
78 if cell_span >= str_width {
79 padding += cell_span - str_width;
80
81 if cell.colspan > 1 {
82 padding += style.vertical.len_utf8() * (cell.colspan - 1);
83 }
84 }
85
86 self.padding_string(padding, cell.alignment, &wrapped_cells[column_index][row_index])
87 } else {
88 str::repeat(" ", widths[spanned_columns] * cell.colspan + cell.colspan - 1)
89 };
90
91 row.push_str(&format!("{}{padding}", style.vertical));
92 };
93
94 lines.iter_mut().enumerate().take(row_height).for_each(callback_fn);
95
96 spanned_columns += cell.colspan;
97 } else {
98 lines.iter_mut().take(row_height).for_each(|line| {
99 line.push_str(&format!(
100 "{}{}",
101 style.vertical,
102 str::repeat(" ", widths[spanned_columns])
103 ));
104 });
105
106 spanned_columns += 1;
107 }
108
109 if spanned_columns == widths.len() {
110 break;
111 }
112 }
113
114 lines.iter().for_each(|line| {
115 temporary_buffer.push_str(line);
116 temporary_buffer.push(style.vertical);
117 temporary_buffer.push('\n');
118 });
119
120 temporary_buffer.pop();
121
122 temporary_buffer
123 }
124
125 pub(crate) fn generate_separator(
126 &self,
127 widths: &[usize],
128 style: &Style,
129 row: Position,
130 previous_separator: Option<&String>,
131 ) -> String
132 {
133 let mut temporary_buffer = String::new();
134
135 let mut next_intersection = match self.cells.first() {
136 | Some(cell) => cell.colspan,
137 | None => 1,
138 };
139
140 temporary_buffer.push(style.start_position(row));
141
142 let mut current_column = 0;
143
144 for (index, width) in widths.iter().enumerate() {
145 if index == next_intersection {
146 temporary_buffer.push(style.intersect_position(row));
147
148 current_column += 1;
149
150 if self.cells.len() > current_column {
151 next_intersection += self.cells[current_column].colspan;
152 } else {
153 next_intersection += 1;
154 }
155 } else if index > 0 {
156 temporary_buffer.push(style.horizontal);
157 }
158
159 temporary_buffer.push_str(&str::repeat(&style.horizontal.to_string(), *width));
160 }
161
162 temporary_buffer.push(style.end_position(row));
163
164 if let Some(prev) = previous_separator {
165 let mut output = String::new();
166
167 for pair in temporary_buffer.chars().zip(prev.chars()) {
168 if pair.0 == style.left || pair.0 == style.right {
169 output.push(pair.0);
170 } else if pair.0 != style.horizontal || pair.1 != style.horizontal {
171 output.push(style.merge_intersection_position(pair.1, pair.0, row));
172 } else {
173 output.push(style.horizontal);
174 }
175 }
176
177 return output;
178 }
179
180 temporary_buffer
181 }
182
183 pub(crate) fn split_column(&self) -> Vec<(f32, usize)>
184 {
185 let callback_fn = |mut output: Vec<(f32, usize)>, cell: &Cell| {
186 let value = cell.split_width();
187
188 let min_w = (cell.min_width() as f32 / cell.colspan as f32) as usize;
189 let add_1 = cell.min_width() as f32 % cell.colspan as f32 > 0.001;
190
191 for i in 0..cell.colspan {
192 if add_1 && i == cell.colspan - 1 {
193 output.push((value + 1.0, min_w + 1));
194 } else {
195 output.push((value, min_w));
196 }
197 }
198
199 output
200 };
201
202 let output = self.cells.iter().fold(Vec::default(), callback_fn);
203
204 output
205 }
206
207 pub(crate) fn total_columns(&self) -> usize
208 {
209 self.cells.iter().map(|cell| cell.colspan).sum()
210 }
211
212 fn padding_string(&self, padding: usize, alignment: Alignment, text: &str) -> String
213 {
214 match alignment {
215 | Alignment::Left => {
216 let r = str::repeat(" ", padding);
217 format!("{text}{r}")
218 }
219 | Alignment::Right => {
220 let l = str::repeat(" ", padding);
221 format!("{l}{text}")
222 }
223 | Alignment::Center => {
224 let half_padding = padding as f32 / 2.0;
225 let l = str::repeat(" ", half_padding.ceil() as usize);
226 let r = str::repeat(" ", half_padding.floor() as usize);
227 format!("{l}{text}{r}",)
228 }
229 }
230 }
231}