1 | use std::iter::FromIterator; |
2 | |
3 | use crate::{grid::records::vec_records::CellInfo, Table}; |
4 | |
5 | use super::IndexBuilder; |
6 | |
7 | /// Builder creates a [`Table`] from dynamic data set. |
8 | /// |
9 | /// It useful when the amount of columns or rows is not known statically. |
10 | /// |
11 | /// ```rust |
12 | /// use tabled::builder::Builder; |
13 | /// |
14 | /// let mut builder = Builder::default(); |
15 | /// builder.set_header(["index" , "measure" , "value" ]); |
16 | /// builder.push_record(["0" , "weight" , "0.443" ]); |
17 | /// |
18 | /// let table = builder.build(); |
19 | /// |
20 | /// println!("{}" , table); |
21 | /// ``` |
22 | /// |
23 | /// It may be useful to use [`FromIterator`] for building. |
24 | /// |
25 | /// ```rust |
26 | /// use tabled::builder::Builder; |
27 | /// use std::iter::FromIterator; |
28 | /// |
29 | /// let data = vec![ |
30 | /// ["column1" , "column2" ], |
31 | /// ["data1" , "data2" ], |
32 | /// ["data3" , "data4" ], |
33 | /// ]; |
34 | /// |
35 | /// let table = Builder::from_iter(data).build(); |
36 | /// |
37 | /// println!("{}" , table); |
38 | /// ``` |
39 | #[derive (Debug, Default, Clone)] |
40 | pub struct Builder { |
41 | /// A list of rows. |
42 | data: Vec<Vec<CellInfo<String>>>, |
43 | /// A columns row. |
44 | columns: Option<Vec<CellInfo<String>>>, |
45 | /// A number of columns. |
46 | count_columns: usize, |
47 | /// A flag that the rows are not consistent. |
48 | is_consistent: bool, |
49 | /// A content of cells which are created in case rows has different length. |
50 | empty_cell_text: Option<String>, |
51 | } |
52 | |
53 | impl Builder { |
54 | /// Creates a [`Builder`] instance. |
55 | /// |
56 | /// ``` |
57 | /// use tabled::builder::Builder; |
58 | /// |
59 | /// let builder = Builder::new(); |
60 | /// ``` |
61 | pub fn new() -> Self { |
62 | Self::default() |
63 | } |
64 | |
65 | /// Creates a [`Builder`] instance with a given row capacity. |
66 | /// |
67 | /// ``` |
68 | /// use tabled::builder::Builder; |
69 | /// |
70 | /// let mut builder = Builder::with_capacity(2); |
71 | /// builder.push_record((0..3).map(|i| i.to_string())); |
72 | /// builder.push_record(["i" , "surname" , "lastname" ]); |
73 | /// ``` |
74 | pub fn with_capacity(capacity: usize) -> Self { |
75 | let mut b = Self::new(); |
76 | b.data = Vec::with_capacity(capacity); |
77 | |
78 | b |
79 | } |
80 | |
81 | /// Sets a [`Table`] header. |
82 | /// |
83 | /// ``` |
84 | /// # use tabled::builder::Builder; |
85 | /// let mut builder = Builder::default(); |
86 | /// builder.set_header((0..3).map(|i| i.to_string())); |
87 | /// ``` |
88 | pub fn set_header<H, T>(&mut self, columns: H) -> &mut Self |
89 | where |
90 | H: IntoIterator<Item = T>, |
91 | T: Into<String>, |
92 | { |
93 | let list = create_row(columns, self.count_columns); |
94 | |
95 | self.update_size(list.len()); |
96 | self.columns = Some(list); |
97 | |
98 | self |
99 | } |
100 | |
101 | /// Sets off a [`Table`] header. |
102 | /// |
103 | /// If not set its a nop. |
104 | /// |
105 | /// ```rust |
106 | /// use tabled::Table; |
107 | /// |
108 | /// let data = [("Hello" , 1u8, false), ("World" , 21u8, true)]; |
109 | /// |
110 | /// let table = Table::builder(data).build().to_string(); |
111 | /// |
112 | /// assert_eq!( |
113 | /// table, |
114 | /// "+-------+----+-------+ \n\ |
115 | /// | &str | u8 | bool | \n\ |
116 | /// +-------+----+-------+ \n\ |
117 | /// | Hello | 1 | false | \n\ |
118 | /// +-------+----+-------+ \n\ |
119 | /// | World | 21 | true | \n\ |
120 | /// +-------+----+-------+" |
121 | /// ); |
122 | /// |
123 | /// |
124 | /// let mut builder = Table::builder(data); |
125 | /// builder.remove_header(); |
126 | /// let table = builder.build().to_string(); |
127 | /// |
128 | /// assert_eq!( |
129 | /// table, |
130 | /// "+-------+----+-------+ \n\ |
131 | /// | Hello | 1 | false | \n\ |
132 | /// +-------+----+-------+ \n\ |
133 | /// | World | 21 | true | \n\ |
134 | /// +-------+----+-------+" |
135 | /// ); |
136 | /// |
137 | /// ``` |
138 | pub fn remove_header(&mut self) -> &mut Self { |
139 | self.columns = None; |
140 | self.count_columns = self.get_size(); |
141 | |
142 | self |
143 | } |
144 | |
145 | /// Sets a content of cells which are created in case rows has different length. |
146 | /// |
147 | /// |
148 | /// ```rust |
149 | /// use tabled::builder::Builder; |
150 | /// |
151 | /// let mut builder = Builder::default(); |
152 | /// builder |
153 | /// .set_default_text("undefined" ) |
154 | /// .set_header((0..3).map(|i| i.to_string())) |
155 | /// .push_record(["i" ]); |
156 | /// ``` |
157 | pub fn set_default_text<T>(&mut self, text: T) -> &mut Self |
158 | where |
159 | T: Into<String>, |
160 | { |
161 | self.empty_cell_text = Some(text.into()); |
162 | self |
163 | } |
164 | |
165 | /// Build creates a [`Table`] instance. |
166 | /// |
167 | /// ```rust |
168 | /// use tabled::builder::Builder; |
169 | /// |
170 | /// let mut builder = Builder::default(); |
171 | /// builder.set_header(["i" , "column1" , "column2" ]); |
172 | /// builder.push_record(["0" , "value1" , "value2" ]); |
173 | /// ``` |
174 | pub fn build(self) -> Table { |
175 | Table::from(self) |
176 | } |
177 | |
178 | /// Add an index to the [`Table`]. |
179 | /// |
180 | /// Default index is a range 0-N where N is amount of records. |
181 | /// |
182 | /// # Example |
183 | /// |
184 | /// ``` |
185 | /// use tabled::Table; |
186 | /// |
187 | /// let table = Table::builder(&["Hello" , "World" , "!" ]).index().build(); |
188 | /// |
189 | /// assert_eq!( |
190 | /// table.to_string(), |
191 | /// "+---+-------+ \n\ |
192 | /// | | &str | \n\ |
193 | /// +---+-------+ \n\ |
194 | /// | 0 | Hello | \n\ |
195 | /// +---+-------+ \n\ |
196 | /// | 1 | World | \n\ |
197 | /// +---+-------+ \n\ |
198 | /// | 2 | ! | \n\ |
199 | /// +---+-------+" |
200 | /// ) |
201 | /// ``` |
202 | pub fn index(self) -> IndexBuilder { |
203 | IndexBuilder::from(self) |
204 | } |
205 | |
206 | /// Adds a row to a [`Table`]. |
207 | /// |
208 | /// ``` |
209 | /// use tabled::builder::Builder; |
210 | /// |
211 | /// let mut builder = Builder::default(); |
212 | /// builder.push_record((0..3).map(|i| i.to_string())); |
213 | /// builder.push_record(["i" , "surname" , "lastname" ]); |
214 | /// ``` |
215 | pub fn push_record<R, T>(&mut self, row: R) -> &mut Self |
216 | where |
217 | R: IntoIterator<Item = T>, |
218 | T: Into<String>, |
219 | { |
220 | let list = create_row(row, self.count_columns); |
221 | |
222 | self.update_size(list.len()); |
223 | self.data.push(list); |
224 | |
225 | self |
226 | } |
227 | |
228 | /// Insert a row into a specific position. |
229 | /// |
230 | /// # Panics |
231 | /// |
232 | /// Panics if `index > count_rows`. |
233 | pub fn insert_record<R>(&mut self, index: usize, record: R) -> bool |
234 | where |
235 | R: IntoIterator, |
236 | R::Item: Into<String>, |
237 | { |
238 | let list = create_row(record, self.count_columns); |
239 | |
240 | self.update_size(list.len()); |
241 | self.data.insert(index, list); |
242 | |
243 | true |
244 | } |
245 | |
246 | /// Clean removes empty columns and rows. |
247 | /// |
248 | /// # Example |
249 | /// |
250 | /// ``` |
251 | /// use tabled::Table; |
252 | /// |
253 | /// let mut builder = Table::builder(&["Hello" , "World" , "" ]); |
254 | /// builder.clean(); |
255 | /// |
256 | /// let table = builder.build(); |
257 | /// |
258 | /// assert_eq!( |
259 | /// table.to_string(), |
260 | /// "+-------+ \n\ |
261 | /// | &str | \n\ |
262 | /// +-------+ \n\ |
263 | /// | Hello | \n\ |
264 | /// +-------+ \n\ |
265 | /// | World | \n\ |
266 | /// +-------+" |
267 | /// ) |
268 | /// ``` |
269 | pub fn clean(&mut self) -> &mut Self { |
270 | self.clean_columns(); |
271 | self.clean_rows(); |
272 | self |
273 | } |
274 | |
275 | /// Set a column size. |
276 | /// |
277 | /// If it make it lower then it was originally it is considered NOP. |
278 | pub fn hint_column_size(&mut self, size: usize) -> &mut Self { |
279 | self.count_columns = size; |
280 | self.is_consistent = true; |
281 | self |
282 | } |
283 | |
284 | /// Returns an amount of columns which would be present in a built table. |
285 | pub fn count_columns(&self) -> usize { |
286 | self.count_columns |
287 | } |
288 | |
289 | /// Returns an amount of rows which would be present in a built table. |
290 | pub fn count_rows(&self) -> usize { |
291 | self.data.len() |
292 | } |
293 | |
294 | /// Checks whether a builder contains a header set. |
295 | pub fn has_header(&self) -> bool { |
296 | self.columns.is_some() |
297 | } |
298 | |
299 | fn clean_columns(&mut self) { |
300 | let mut i = 0; |
301 | for col in 0..self.count_columns { |
302 | let col = col - i; |
303 | |
304 | let mut is_empty = true; |
305 | for row in 0..self.data.len() { |
306 | let cell = &self.data[row][col]; |
307 | if !cell.as_ref().is_empty() { |
308 | is_empty = false; |
309 | break; |
310 | } |
311 | } |
312 | |
313 | if is_empty { |
314 | for row in 0..self.data.len() { |
315 | let _ = self.data[row].remove(col); |
316 | } |
317 | |
318 | if let Some(columns) = self.columns.as_mut() { |
319 | if columns.len() > col { |
320 | let _ = columns.remove(col); |
321 | } |
322 | } |
323 | |
324 | i += 1; |
325 | } |
326 | } |
327 | |
328 | self.count_columns -= i; |
329 | } |
330 | |
331 | fn clean_rows(&mut self) { |
332 | for row in (0..self.data.len()).rev() { |
333 | let mut is_empty = true; |
334 | for col in 0..self.count_columns { |
335 | let cell = &self.data[row][col]; |
336 | if !cell.as_ref().is_empty() { |
337 | is_empty = false; |
338 | break; |
339 | } |
340 | } |
341 | |
342 | if is_empty { |
343 | let _ = self.data.remove(row); |
344 | } |
345 | |
346 | if row == 0 { |
347 | break; |
348 | } |
349 | } |
350 | } |
351 | |
352 | fn update_size(&mut self, size: usize) { |
353 | use std::cmp::Ordering; |
354 | |
355 | match size.cmp(&self.count_columns) { |
356 | Ordering::Less => { |
357 | if !self.data.is_empty() { |
358 | self.is_consistent = false; |
359 | } |
360 | } |
361 | Ordering::Greater => { |
362 | self.count_columns = size; |
363 | |
364 | if !self.data.is_empty() || self.columns.is_some() { |
365 | self.is_consistent = false; |
366 | } |
367 | } |
368 | Ordering::Equal => (), |
369 | } |
370 | } |
371 | |
372 | fn get_size(&mut self) -> usize { |
373 | let mut max = self.columns.as_ref().map_or(0, Vec::len); |
374 | let max_records = self.data.iter().map(Vec::len).max().unwrap_or(0); |
375 | max = std::cmp::max(max_records, max); |
376 | |
377 | max |
378 | } |
379 | |
380 | fn fix_rows(&mut self) { |
381 | let empty_cell = self.empty_cell_text.to_owned().unwrap_or_default(); |
382 | let empty = CellInfo::new(empty_cell); |
383 | |
384 | if let Some(header) = self.columns.as_mut() { |
385 | if self.count_columns > header.len() { |
386 | let count = self.count_columns - header.len(); |
387 | append_vec(header, empty.clone(), count); |
388 | } |
389 | } |
390 | |
391 | for row in &mut self.data { |
392 | if self.count_columns > row.len() { |
393 | let count = self.count_columns - row.len(); |
394 | append_vec(row, empty.clone(), count); |
395 | } |
396 | } |
397 | } |
398 | } |
399 | |
400 | impl From<Builder> for Vec<Vec<String>> { |
401 | fn from(mut builder: Builder) -> Self { |
402 | if !builder.is_consistent { |
403 | builder.fix_rows(); |
404 | } |
405 | |
406 | if let Some(columns: Vec>) = builder.columns { |
407 | builder.data.insert(index:0, element:columns); |
408 | } |
409 | |
410 | builderimpl Iterator- >
|
411 | .data |
412 | .into_iter() |
413 | .map(|row: Vec>| row.into_iter().map(|c: CellInfo| c.into_inner()).collect()) |
414 | .collect() |
415 | } |
416 | } |
417 | |
418 | impl From<Builder> for Vec<Vec<CellInfo<String>>> { |
419 | fn from(mut builder: Builder) -> Self { |
420 | if !builder.is_consistent { |
421 | builder.fix_rows(); |
422 | } |
423 | |
424 | if let Some(columns: Vec>) = builder.columns { |
425 | builder.data.insert(index:0, element:columns); |
426 | } |
427 | |
428 | builder.data |
429 | } |
430 | } |
431 | |
432 | impl<R, V> FromIterator<R> for Builder |
433 | where |
434 | R: IntoIterator<Item = V>, |
435 | V: Into<String>, |
436 | { |
437 | fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self { |
438 | let mut builder: Builder = Self::default(); |
439 | for row in iter { |
440 | let _ = builder.push_record(row); |
441 | } |
442 | |
443 | builder |
444 | } |
445 | } |
446 | |
447 | impl<D> Extend<D> for Builder |
448 | where |
449 | D: Into<String>, |
450 | { |
451 | fn extend<T: IntoIterator<Item = D>>(&mut self, iter: T) { |
452 | let _ = self.push_record(row:iter); |
453 | } |
454 | } |
455 | |
456 | impl From<Vec<Vec<String>>> for Builder { |
457 | fn from(data: Vec<Vec<String>>) -> Self { |
458 | let count_columns: usize = data.get(0).map_or(default:0, |row: &Vec| row.len()); |
459 | |
460 | let data: Vec>> = dataimpl Iterator- >>
|
461 | .into_iter() |
462 | .map(|row: Vec| row.into_iter().map(CellInfo::new).collect()) |
463 | .collect(); |
464 | |
465 | Self { |
466 | data, |
467 | count_columns, |
468 | columns: None, |
469 | is_consistent: false, |
470 | empty_cell_text: None, |
471 | } |
472 | } |
473 | } |
474 | |
475 | impl From<Vec<Vec<CellInfo<String>>>> for Builder { |
476 | fn from(data: Vec<Vec<CellInfo<String>>>) -> Self { |
477 | let count_columns: usize = data.get(0).map_or(default:0, |row: &Vec>| row.len()); |
478 | |
479 | Self { |
480 | data, |
481 | count_columns, |
482 | columns: None, |
483 | is_consistent: false, |
484 | empty_cell_text: None, |
485 | } |
486 | } |
487 | } |
488 | |
489 | fn create_row<R, T>(row: R, size: usize) -> Vec<CellInfo<String>> |
490 | where |
491 | R: IntoIterator<Item = T>, |
492 | T: Into<String>, |
493 | { |
494 | let mut list: Vec> = Vec::with_capacity(size); |
495 | for text: T in row { |
496 | let text: String = text.into(); |
497 | let info: CellInfo = CellInfo::new(text); |
498 | list.push(info); |
499 | } |
500 | |
501 | list |
502 | } |
503 | |
504 | fn append_vec<T: Clone>(v: &mut Vec<T>, value: T, n: usize) { |
505 | v.extend((0..n).map(|_| value.clone())); |
506 | } |
507 | |