format_tools/format/output_format/
table.rs1use crate::*;
14use print::
15{
16 InputExtract,
17 Context,
18};
19use core::fmt;
20use std::sync::OnceLock;
21
22#[ derive( Debug ) ]
37pub struct Table
38{
39 pub delimitting_header : bool,
41 pub cell_prefix : String,
43 pub cell_postfix : String,
45 pub cell_separator : String,
47 pub row_prefix : String,
49 pub row_postfix : String,
51 pub row_separator : String,
53 pub h : char,
55 pub v : char,
57 pub t_l : char,
59 pub t_r : char,
61 pub t_t : char,
63 pub t_b : char,
65 pub cross : char,
67 pub corner_lt : char,
69 pub corner_rt : char,
71 pub corner_lb : char,
73 pub corner_rb : char,
75 pub max_width: usize,
77}
78
79impl Default for Table
80{
81 fn default() -> Self
82 {
83
84 let delimitting_header = true;
85
86 let cell_prefix = "".to_string();
87 let cell_postfix = "".to_string();
88 let cell_separator = " │ ".to_string();
89 let row_prefix = "│ ".to_string();
90 let row_postfix = " │".to_string();
91 let row_separator = "\n".to_string();
92
93 let h = '─';
94 let v = '|';
95 let t_l = '├';
96 let t_r = '┤';
97 let t_t = '┬';
98 let t_b = '┴';
99 let cross = '┼';
100 let corner_lt = '┌';
101 let corner_rt = '┐';
102 let corner_lb = '└';
103 let corner_rb = '┘';
104 let max_width = 0;
105
106 Self
107 {
108 delimitting_header,
109 cell_prefix,
110 cell_postfix,
111 cell_separator,
112 row_prefix,
113 row_postfix,
114 row_separator,
115 h,
116 v,
117 t_l,
118 t_r,
119 t_t,
120 t_b,
121 cross,
122 corner_lt,
123 corner_rt,
124 corner_lb,
125 corner_rb,
126 max_width
127 }
128 }
129}
130
131impl Default for &'static Table
132{
133 fn default() -> Self
134 {
135 static STYLES : OnceLock< Table > = OnceLock::new();
137 STYLES.get_or_init( ||
138 {
139 Table::default()
140 })
141 }
142}
143
144impl Table
145{
146
147 pub fn instance() -> & 'static dyn TableOutputFormat
152 {
153
154 static INSTANCE : OnceLock< Table > = OnceLock::new();
155 INSTANCE.get_or_init( ||
156 {
157 Self::default()
158 })
159
160 }
161
162 pub fn min_width
169 (
170 &self,
171 column_count : usize,
172 ) -> usize
173 {
174 self.row_prefix.chars().count()
175 + self.row_postfix.chars().count()
176 + column_count * ( self.cell_postfix.chars().count() + self.cell_prefix.chars().count() )
177 + if column_count == 0 { 0 } else { ( column_count - 1 ) * self.cell_separator.chars().count() }
178 + column_count
179 }
180}
181
182impl TableOutputFormat for Table
183{
184 fn extract_write< 'buf, 'data >( &self, x : &InputExtract< 'data >, c : &mut Context< 'buf > ) -> fmt::Result
185 {
186 use format::text_wrap::text_wrap;
187
188 let cell_prefix = &self.cell_prefix;
189 let cell_postfix = &self.cell_postfix;
190 let cell_separator = &self.cell_separator;
191 let row_prefix = &self.row_prefix;
192 let row_postfix = &self.row_postfix;
193 let row_separator = &self.row_separator;
194 let h = self.h.to_string();
195
196 let column_count = x.col_descriptors.len();
197
198 if self.max_width != 0 && ( self.min_width( column_count ) > self.max_width )
199 {
200 return Err( fmt::Error );
201 }
202
203 let columns_nowrap_width = x.col_descriptors.iter().map( |c| c.width ).sum::<usize>();
204 let visual_elements_width = self.min_width( column_count ) - column_count;
205
206 let filtered_data = x.row_descriptors.iter().filter_map( | r |
207 {
208 if r.vis
209 {
210 Some( &x.data[ r.irow ] )
211 }
212 else
213 {
214 None
215 }
216 });
217
218 let wrapped_text = text_wrap
219 (
220 filtered_data,
221 x.col_descriptors.iter().map( | c | c.width ).collect::< Vec< usize > >(),
222 if self.max_width == 0 { 0 } else { self.max_width - visual_elements_width },
223 columns_nowrap_width
224 );
225
226 let new_columns_widthes = wrapped_text.column_widthes.iter().sum::<usize>();
227 let new_row_width = new_columns_widthes + visual_elements_width;
228
229 let mut printed_row_count = 0;
230
231 for row in wrapped_text.data.iter()
232 {
233 if printed_row_count == wrapped_text.first_row_height && x.has_header && self.delimitting_header
234 {
235 write!( c.buf, "{}", row_separator )?;
236 write!( c.buf, "{}", h.repeat( new_row_width ) )?;
237 }
238
239 if printed_row_count > 0
240 {
241 write!( c.buf, "{}", row_separator )?;
242 }
243
244 printed_row_count += 1;
245
246 write!( c.buf, "{}", row_prefix )?;
247
248 for ( icol, col ) in row.iter().enumerate()
249 {
250 let cell_wrapped_width = col.wrap_width;
251 let column_width = wrapped_text.column_widthes[ icol ];
252 let slice_width = col.content.chars().count();
253
254 if icol > 0
255 {
256 write!( c.buf, "{}", cell_separator )?;
257 }
258
259 write!( c.buf, "{}", cell_prefix )?;
260
261 let lspaces = column_width.saturating_sub( cell_wrapped_width ) / 2;
262 let rspaces = ( ( column_width.saturating_sub( cell_wrapped_width ) as f32 / 2 as f32 ) ).round() as usize + cell_wrapped_width.saturating_sub(slice_width);
263
264 if lspaces > 0
265 {
266 write!( c.buf, "{:<width$}", " ", width = lspaces )?;
267 }
268
269 write!( c.buf, "{}", col.content )?;
270
271 if rspaces > 0
272 {
273 write!( c.buf, "{:>width$}", " ", width = rspaces )?;
274 }
275
276 write!( c.buf, "{}", cell_postfix )?;
277 }
278
279 write!( c.buf, "{}", row_postfix )?;
280 }
281
282 Ok(())
283 }
284}