1use std::iter::FromIterator;
2
3use crate::{grid::records::vec_records::CellInfo, Table};
4
5use 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)]
40pub 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
53impl 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
400impl 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
418impl 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
432impl<R, V> FromIterator<R> for Builder
433where
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
447impl<D> Extend<D> for Builder
448where
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
456impl 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
475impl 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
489fn create_row<R, T>(row: R, size: usize) -> Vec<CellInfo<String>>
490where
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
504fn append_vec<T: Clone>(v: &mut Vec<T>, value: T, n: usize) {
505 v.extend((0..n).map(|_| value.clone()));
506}
507