comfy_table/utils/formatting/
borders.rs1use crate::style::TableComponent;
2use crate::table::Table;
3use crate::utils::ColumnDisplayInfo;
4
5pub(crate) fn draw_borders(
6 table: &Table,
7 rows: &[Vec<Vec<String>>],
8 display_info: &[ColumnDisplayInfo],
9) -> Vec<String> {
10 let mut lines = if let Some(capacity) = rows.first().map(|lines| lines.len()) {
13 Vec::with_capacity(capacity * 2 + 5)
16 } else {
17 Vec::new()
18 };
19
20 if should_draw_top_border(table) {
21 lines.push(draw_top_border(table, display_info));
22 }
23
24 draw_rows(&mut lines, rows, table, display_info);
25
26 if should_draw_bottom_border(table) {
27 lines.push(draw_bottom_border(table, display_info));
28 }
29
30 lines
31}
32
33fn draw_top_border(table: &Table, display_info: &[ColumnDisplayInfo]) -> String {
34 let left_corner = table.style_or_default(TableComponent::TopLeftCorner);
35 let top_border = table.style_or_default(TableComponent::TopBorder);
36 let intersection = table.style_or_default(TableComponent::TopBorderIntersections);
37 let right_corner = table.style_or_default(TableComponent::TopRightCorner);
38
39 let mut line = String::new();
40 if should_draw_left_border(table) {
42 line += &left_corner;
43 }
44
45 let mut first = true;
48 for info in display_info.iter() {
49 if !info.is_hidden {
51 if !first {
52 line += &intersection;
53 }
54 line += &top_border.repeat(info.width().into());
55 first = false;
56 }
57 }
58
59 if should_draw_right_border(table) {
61 line += &right_corner;
62 }
63
64 line
65}
66
67fn draw_rows(
68 lines: &mut Vec<String>,
69 rows: &[Vec<Vec<String>>],
70 table: &Table,
71 display_info: &[ColumnDisplayInfo],
72) {
73 let mut row_iter = rows.iter().enumerate().peekable();
75 while let Some((row_index, row)) = row_iter.next() {
76 for line_parts in row.iter() {
78 lines.push(embed_line(line_parts, table));
79 }
80
81 if row_index == 0 && table.header.is_some() {
83 if should_draw_header(table) {
84 lines.push(draw_horizontal_lines(table, display_info, true));
85 }
86 continue;
87 }
88
89 if row_iter.peek().is_some() && should_draw_horizontal_lines(table) {
91 lines.push(draw_horizontal_lines(table, display_info, false));
92 }
93 }
94}
95
96fn embed_line(line_parts: &[String], table: &Table) -> String {
98 let vertical_lines = table.style_or_default(TableComponent::VerticalLines);
99 let left_border = table.style_or_default(TableComponent::LeftBorder);
100 let right_border = table.style_or_default(TableComponent::RightBorder);
101
102 let mut line = String::new();
103 if should_draw_left_border(table) {
104 line += &left_border;
105 }
106
107 let mut part_iter = line_parts.iter().peekable();
108 while let Some(part) = part_iter.next() {
109 line += part;
110 if should_draw_vertical_lines(table) && part_iter.peek().is_some() {
111 line += &vertical_lines;
112 } else if should_draw_right_border(table) && part_iter.peek().is_none() {
113 line += &right_border;
114 }
115 }
116
117 line
118}
119
120fn draw_horizontal_lines(
122 table: &Table,
123 display_info: &[ColumnDisplayInfo],
124 header: bool,
125) -> String {
126 let (left_intersection, horizontal_lines, middle_intersection, right_intersection) = if header {
128 (
129 table.style_or_default(TableComponent::LeftHeaderIntersection),
130 table.style_or_default(TableComponent::HeaderLines),
131 table.style_or_default(TableComponent::MiddleHeaderIntersections),
132 table.style_or_default(TableComponent::RightHeaderIntersection),
133 )
134 } else {
135 (
136 table.style_or_default(TableComponent::LeftBorderIntersections),
137 table.style_or_default(TableComponent::HorizontalLines),
138 table.style_or_default(TableComponent::MiddleIntersections),
139 table.style_or_default(TableComponent::RightBorderIntersections),
140 )
141 };
142
143 let mut line = String::new();
144 if should_draw_left_border(table) {
146 line += &left_intersection;
147 }
148
149 let mut first = true;
152 for info in display_info.iter() {
153 if !info.is_hidden {
155 if !first {
156 line += &middle_intersection;
157 }
158 line += &horizontal_lines.repeat(info.width().into());
159 first = false;
160 }
161 }
162
163 if should_draw_right_border(table) {
165 line += &right_intersection;
166 }
167
168 line
169}
170
171fn draw_bottom_border(table: &Table, display_info: &[ColumnDisplayInfo]) -> String {
172 let left_corner = table.style_or_default(TableComponent::BottomLeftCorner);
173 let bottom_border = table.style_or_default(TableComponent::BottomBorder);
174 let middle_intersection = table.style_or_default(TableComponent::BottomBorderIntersections);
175 let right_corner = table.style_or_default(TableComponent::BottomRightCorner);
176
177 let mut line = String::new();
178 if should_draw_left_border(table) {
180 line += &left_corner;
181 }
182
183 let mut first = true;
186 for info in display_info.iter() {
187 if !info.is_hidden {
189 if !first {
190 line += &middle_intersection;
191 }
192 line += &bottom_border.repeat(info.width().into());
193 first = false;
194 }
195 }
196
197 if should_draw_right_border(table) {
199 line += &right_corner;
200 }
201
202 line
203}
204
205fn should_draw_top_border(table: &Table) -> bool {
206 if table.style_exists(TableComponent::TopLeftCorner)
207 || table.style_exists(TableComponent::TopBorder)
208 || table.style_exists(TableComponent::TopBorderIntersections)
209 || table.style_exists(TableComponent::TopRightCorner)
210 {
211 return true;
212 }
213
214 false
215}
216
217fn should_draw_bottom_border(table: &Table) -> bool {
218 if table.style_exists(TableComponent::BottomLeftCorner)
219 || table.style_exists(TableComponent::BottomBorder)
220 || table.style_exists(TableComponent::BottomBorderIntersections)
221 || table.style_exists(TableComponent::BottomRightCorner)
222 {
223 return true;
224 }
225
226 false
227}
228
229pub fn should_draw_left_border(table: &Table) -> bool {
230 if table.style_exists(TableComponent::TopLeftCorner)
231 || table.style_exists(TableComponent::LeftBorder)
232 || table.style_exists(TableComponent::LeftBorderIntersections)
233 || table.style_exists(TableComponent::LeftHeaderIntersection)
234 || table.style_exists(TableComponent::BottomLeftCorner)
235 {
236 return true;
237 }
238
239 false
240}
241
242pub fn should_draw_right_border(table: &Table) -> bool {
243 if table.style_exists(TableComponent::TopRightCorner)
244 || table.style_exists(TableComponent::RightBorder)
245 || table.style_exists(TableComponent::RightBorderIntersections)
246 || table.style_exists(TableComponent::RightHeaderIntersection)
247 || table.style_exists(TableComponent::BottomRightCorner)
248 {
249 return true;
250 }
251
252 false
253}
254
255fn should_draw_horizontal_lines(table: &Table) -> bool {
256 if table.style_exists(TableComponent::LeftBorderIntersections)
257 || table.style_exists(TableComponent::HorizontalLines)
258 || table.style_exists(TableComponent::MiddleIntersections)
259 || table.style_exists(TableComponent::RightBorderIntersections)
260 {
261 return true;
262 }
263
264 false
265}
266
267pub fn should_draw_vertical_lines(table: &Table) -> bool {
268 if table.style_exists(TableComponent::TopBorderIntersections)
269 || table.style_exists(TableComponent::MiddleHeaderIntersections)
270 || table.style_exists(TableComponent::VerticalLines)
271 || table.style_exists(TableComponent::MiddleIntersections)
272 || table.style_exists(TableComponent::BottomBorderIntersections)
273 {
274 return true;
275 }
276
277 false
278}
279
280fn should_draw_header(table: &Table) -> bool {
281 if table.style_exists(TableComponent::LeftHeaderIntersection)
282 || table.style_exists(TableComponent::HeaderLines)
283 || table.style_exists(TableComponent::MiddleHeaderIntersections)
284 || table.style_exists(TableComponent::RightHeaderIntersection)
285 {
286 return true;
287 }
288
289 false
290}