1use std::io::{Result, Write};
2
3use termcolor::{ColorSpec, WriteColor};
4
5use crate::{
6 buffers::Buffers,
7 table::{Dimension as TableDimension, HorizontalLine, TableFormat, VerticalLine},
8};
9
10const ESC: char = '\x1b';
11
12pub(crate) fn display_width(text: &str) -> usize {
17 let mut width = 0;
18 let mut chars = text.chars();
19
20 #[allow(clippy::while_let_on_iterator)]
23 while let Some(c) = chars.next() {
24 if c == ESC {
27 let Some(c) = chars.next() else {
28 break;
29 };
30 match c {
31 '\\' => {
35 }
37 '[' => while !matches!(chars.next(), Some('\x40'..='\x7C') | None) {},
39 ']' => {
41 let mut last = c;
42 while let Some(new) = chars.next() {
43 if new == '\x07' || (new == '\\' && last == ESC) {
44 break;
45 }
46 last = new;
47 }
48 }
49 _ => {
52 width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
53 }
54 }
55 } else {
56 width += unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
59 }
60 }
61 width
62}
63
64pub fn transpose<T>(v: Vec<Vec<T>>) -> Vec<Vec<T>> {
65 if v.is_empty() || v[0].is_empty() {
66 return v;
67 }
68
69 let columns = v.len();
70 let height = v[0].len();
71
72 let mut optional_v: Vec<Vec<Option<T>>> = v
73 .into_iter()
74 .map(|cell_v| cell_v.into_iter().map(Some).collect())
75 .collect();
76
77 let mut transpose = Vec::with_capacity(height);
78
79 for i in 0..height {
80 let mut row_buffer = Vec::with_capacity(columns);
81
82 for inner_v in optional_v.iter_mut().take(columns) {
83 row_buffer.push(std::mem::take(&mut inner_v[i]).unwrap());
84 }
85
86 transpose.push(row_buffer);
87 }
88
89 transpose
90}
91
92pub(crate) fn print_horizontal_line(
93 buffers: &mut Buffers<'_>,
94 line: Option<&HorizontalLine>,
95 table_dimension: &TableDimension,
96 table_format: &TableFormat,
97 color_spec: &ColorSpec,
98) -> Result<()> {
99 if let Some(line) = line {
100 if table_format.border.left.is_some() {
101 print_char(buffers, line.left_end, color_spec)?;
102 }
103
104 let mut widths = table_dimension.widths.iter().peekable();
105
106 while let Some(width) = widths.next() {
107 let s = std::iter::repeat_n(line.filler, width + 2).collect::<String>();
108 print_str(buffers, &s, color_spec)?;
109
110 match widths.peek() {
111 Some(_) => {
112 if table_format.separator.column.is_some() {
113 print_char(buffers, line.junction, color_spec)?
114 }
115 }
116 None => {
117 if table_format.border.right.is_some() {
118 print_char(buffers, line.right_end, color_spec)?;
119 } else {
120 print_str(buffers, "", color_spec)?;
121 }
122 }
123 }
124 }
125
126 println(buffers)?;
127 }
128
129 Ok(())
130}
131
132pub(crate) fn print_vertical_line(
133 buffers: &mut Buffers<'_>,
134 line: Option<&VerticalLine>,
135 color_spec: &ColorSpec,
136) -> Result<()> {
137 if let Some(line) = line {
138 print_char(buffers, line.filler, color_spec)?;
139 }
140 Ok(())
141}
142
143pub(crate) fn print_str(buffers: &mut Buffers<'_>, s: &str, color_spec: &ColorSpec) -> Result<()> {
144 buffers.set_color(color_spec)?;
145 write!(buffers, "{}", s)?;
146 buffers.reset()
147}
148
149pub(crate) fn print_char(buffers: &mut Buffers<'_>, c: char, color_spec: &ColorSpec) -> Result<()> {
150 buffers.set_color(color_spec)?;
151 write!(buffers, "{}", c)?;
152 buffers.reset()
153}
154
155pub(crate) fn println(buffers: &mut Buffers<'_>) -> Result<()> {
156 writeln!(buffers)
157}