tabled/builder/
table_builder.rs

1use std::iter::FromIterator;
2
3use crate::{grid::records::vec_records::Text, 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.push_record(["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<Text<String>>>,
43    /// A number of columns.
44    count_columns: usize,
45    /// A content of cells which are created in case rows has different length.
46    empty_text: Text<String>,
47}
48
49impl Builder {
50    /// Creates a [`Builder`] instance.
51    ///
52    /// ```
53    /// use tabled::builder::Builder;
54    ///
55    /// let builder = Builder::new();
56    /// ```
57    pub fn new() -> Self {
58        Self::default()
59    }
60
61    /// Creates a [`Builder`] instance with a given row capacity.
62    ///
63    /// ```
64    /// use tabled::builder::Builder;
65    ///
66    /// let mut builder = Builder::with_capacity(2, 3);
67    /// builder.push_record((0..3).map(|i| i.to_string()));
68    /// builder.push_record(["i", "surname", "lastname"]);
69    /// ```
70    pub fn with_capacity(count_records: usize, count_columns: usize) -> Self {
71        let mut builder = Self::new();
72        builder.data = Vec::with_capacity(count_records);
73        builder.count_columns = count_columns;
74
75        builder
76    }
77
78    /// Creates a [`Builder`] instance.
79    ///
80    /// # Safety
81    ///
82    /// It's marked unsafe to emphasize that you shall make sure that all rows bound to have the same length.
83    ///
84    /// ```
85    /// use tabled::builder::Builder;
86    ///
87    /// let data = vec![];
88    /// let builder = Builder::from_vec(data);
89    /// ```
90    pub fn from_vec(data: Vec<Vec<Text<String>>>) -> Self {
91        let count_columns = if data.is_empty() { 0 } else { data[0].len() };
92
93        Self {
94            data,
95            count_columns,
96            empty_text: Text::default(),
97        }
98    }
99
100    /// Sets a content of cells which are created in case rows has different length.
101    ///
102    ///
103    /// ```rust
104    /// use tabled::builder::Builder;
105    ///
106    /// let mut builder = Builder::default();
107    /// builder.set_empty("undefined");
108    /// builder.push_record((0..3).map(|i| i.to_string()));
109    /// builder.push_record(["i"]);
110    /// ```
111    pub fn set_empty<T>(&mut self, text: T)
112    where
113        T: Into<String>,
114    {
115        self.empty_text = Text::new(text.into());
116    }
117
118    /// Build creates a [`Table`] instance.
119    ///
120    /// ```rust
121    /// use tabled::builder::Builder;
122    ///
123    /// let mut builder = Builder::default();
124    /// builder.push_record(["i", "column1", "column2"]);
125    /// builder.push_record(["0", "value1", "value2"]);
126    /// ```
127    pub fn build(self) -> Table {
128        Table::from(self)
129    }
130
131    /// Add an index to the [`Table`].
132    ///
133    /// Default index is a range 0-N where N is amount of records.
134    ///
135    /// # Example
136    ///
137    /// ```
138    /// use tabled::Table;
139    ///
140    /// let table = Table::builder(&["Hello", "World", "!"]).index().build();
141    ///
142    /// assert_eq!(
143    ///     table.to_string(),
144    ///     "+---+-------+\n\
145    ///      |   | &str  |\n\
146    ///      +---+-------+\n\
147    ///      | 0 | Hello |\n\
148    ///      +---+-------+\n\
149    ///      | 1 | World |\n\
150    ///      +---+-------+\n\
151    ///      | 2 | !     |\n\
152    ///      +---+-------+"
153    /// )
154    /// ```
155    pub fn index(self) -> IndexBuilder {
156        IndexBuilder::from(self)
157    }
158
159    /// Adds a row to a [`Table`].
160    ///
161    /// ```
162    /// use tabled::builder::Builder;
163    ///
164    /// let mut builder = Builder::default();
165    /// builder.push_record((0..3).map(|i| i.to_string()));
166    /// builder.push_record(["i", "surname", "lastname"]);
167    /// ```
168    pub fn push_record<R>(&mut self, record: R)
169    where
170        R: IntoIterator,
171        R::Item: Into<String>,
172    {
173        let list = create_row(record, self.count_columns, &self.empty_text);
174        let list_length = list.len();
175
176        if !is_size_eq(self.count_columns, list_length) {
177            let size = list_length - self.count_columns;
178            resize_rows(&mut self.data, size, &self.empty_text)
179        }
180
181        self.count_columns = list_length;
182        self.data.push(list);
183    }
184
185    /// Insert a row into a specific position.
186    ///
187    /// # Panics
188    ///
189    /// Panics if `index > count_rows`.
190    pub fn insert_record<R>(&mut self, index: usize, record: R)
191    where
192        R: IntoIterator,
193        R::Item: Into<String>,
194    {
195        let list = create_row(record, self.count_columns, &self.empty_text);
196        let list_length = list.len();
197
198        if !is_size_eq(self.count_columns, list_length) {
199            let size = list_length - self.count_columns;
200            resize_rows(&mut self.data, size, &self.empty_text)
201        }
202
203        self.count_columns = list_length;
204        self.data.insert(index, list);
205    }
206
207    /// Clean removes empty columns and rows.
208    ///
209    /// # Example
210    ///
211    /// ```
212    /// use tabled::Table;
213    ///
214    /// let mut builder = Table::builder(&["Hello", "World", ""]);
215    /// builder.clean();
216    ///
217    /// let table = builder.build();
218    ///
219    /// assert_eq!(
220    ///     table.to_string(),
221    ///     "+-------+\n\
222    ///      | &str  |\n\
223    ///      +-------+\n\
224    ///      | Hello |\n\
225    ///      +-------+\n\
226    ///      | World |\n\
227    ///      +-------+"
228    /// )
229    /// ```
230    pub fn clean(&mut self) {
231        self.count_columns -= remove_empty_columns(&mut self.data, self.count_columns);
232        remove_empty_rows(&mut self.data, self.count_columns);
233    }
234
235    /// Removes a row with a specific position.
236    ///
237    /// Index expected to be in range.
238    /// `Builder::count_records() < x >= 0`
239    ///
240    /// # Panics
241    ///
242    /// Panics if `row_index > count_rows`.
243    pub fn remove_record(&mut self, index: usize) {
244        let _ = self.data.remove(index);
245    }
246
247    /// Removes a column with a specific position.
248    ///
249    /// Index expected to be in range.
250    /// `Builder::count_columns() < x >= 0`
251    ///
252    /// # Panics
253    ///
254    /// Panics if `index > count_columns`.
255    pub fn remove_column(&mut self, index: usize) {
256        for row in &mut self.data {
257            let _ = row.remove(index);
258        }
259
260        self.count_columns -= 1;
261    }
262
263    /// Push a column.
264    pub fn push_column<I>(&mut self, column: I)
265    where
266        I: IntoIterator,
267        I::Item: Into<String>,
268    {
269        let mut iter = column.into_iter();
270
271        for row in self.data.iter_mut() {
272            let text = iter
273                .next()
274                .map(Into::into)
275                .map(Text::new)
276                .unwrap_or(self.empty_text.clone());
277
278            row.push(text);
279        }
280
281        for text in iter {
282            let text = Text::new(text.into());
283
284            let mut row = Vec::with_capacity(self.count_columns + 1);
285            for _ in 0..self.count_columns {
286                row.push(self.empty_text.clone());
287            }
288
289            row.push(text);
290            self.data.push(row);
291        }
292
293        self.count_columns += 1;
294    }
295
296    /// Insert a column with a specific position.
297    ///
298    /// In case a column is bigger then the total amount of rows it will be truncated.
299    ///
300    /// # Panics
301    ///
302    /// Panics if `index > count_columns`.
303    pub fn insert_column<I>(&mut self, index: usize, column: I)
304    where
305        I: IntoIterator,
306        I::Item: Into<String>,
307    {
308        let mut iter = column.into_iter();
309
310        for row in self.data.iter_mut() {
311            let text = iter
312                .next()
313                .map(Into::into)
314                .map(Text::new)
315                .unwrap_or(self.empty_text.clone());
316
317            row.insert(index, text);
318        }
319
320        for text in iter {
321            let text = Text::new(text.into());
322
323            let mut row = Vec::with_capacity(self.count_columns + 1);
324            for _ in 0..index {
325                row.push(self.empty_text.clone());
326            }
327
328            row.push(text);
329
330            for _ in index..self.count_columns {
331                row.push(self.empty_text.clone());
332            }
333        }
334
335        self.count_columns += 1;
336    }
337
338    /// Remove all records.
339    pub fn clear(&mut self) {
340        self.data.clear();
341        self.count_columns = 0;
342    }
343
344    /// Returns an amount of columns which would be present in a built table.
345    pub fn count_columns(&self) -> usize {
346        self.count_columns
347    }
348
349    /// Returns an amount of rows which would be present in a built table.
350    ///
351    /// Notice that it does not include header if present;
352    /// It returns only amount of records.
353    pub fn count_records(&self) -> usize {
354        self.data.len()
355    }
356}
357
358impl From<Builder> for Vec<Vec<String>> {
359    fn from(builder: Builder) -> Self {
360        builder
361            .data
362            .into_iter()
363            .map(|row| row.into_iter().map(Text::into_inner).collect())
364            .collect()
365    }
366}
367
368impl From<Builder> for Vec<Vec<Text<String>>> {
369    fn from(builder: Builder) -> Self {
370        builder.data
371    }
372}
373
374impl<K, V, S> From<std::collections::HashMap<K, V, S>> for Builder
375where
376    K: ToString,
377    V: ToString,
378{
379    fn from(m: std::collections::HashMap<K, V, S>) -> Self {
380        let mut b = Self::with_capacity(m.len(), 2);
381        for (k, v) in m {
382            b.push_record([k.to_string(), v.to_string()]);
383        }
384
385        b
386    }
387}
388
389impl<K, V> From<std::collections::BTreeMap<K, V>> for Builder
390where
391    K: ToString,
392    V: ToString,
393{
394    fn from(m: std::collections::BTreeMap<K, V>) -> Self {
395        let mut b = Self::with_capacity(m.len(), 2);
396        for (k, v) in m {
397            b.push_record([k.to_string(), v.to_string()]);
398        }
399
400        b
401    }
402}
403
404impl<V> From<std::collections::HashSet<V>> for Builder
405where
406    V: ToString,
407{
408    fn from(m: std::collections::HashSet<V>) -> Self {
409        let mut b = Self::with_capacity(m.len(), 1);
410        for v in m {
411            b.push_record([v.to_string()]);
412        }
413
414        b
415    }
416}
417
418impl<V> From<std::collections::BTreeSet<V>> for Builder
419where
420    V: ToString,
421{
422    fn from(m: std::collections::BTreeSet<V>) -> Self {
423        let mut b = Self::with_capacity(m.len(), 1);
424        for v in m {
425            b.push_record([v.to_string()]);
426        }
427
428        b
429    }
430}
431
432impl<R> FromIterator<R> for Builder
433where
434    R: IntoIterator,
435    R::Item: Into<String>,
436{
437    fn from_iter<T: IntoIterator<Item = R>>(iter: T) -> Self {
438        let mut builder = Self::new();
439        for row in iter {
440            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        self.push_record(iter);
453    }
454}
455
456impl From<Vec<Vec<String>>> for Builder {
457    fn from(data: Vec<Vec<String>>) -> Self {
458        let mut data = data
459            .into_iter()
460            .map(|row| row.into_iter().map(Text::new).collect())
461            .collect();
462
463        let count_columns = equalize_row_length(&mut data);
464
465        Self {
466            data,
467            count_columns,
468            empty_text: Text::default(),
469        }
470    }
471}
472
473impl From<Vec<Vec<Text<String>>>> for Builder {
474    fn from(mut data: Vec<Vec<Text<String>>>) -> Self {
475        let count_columns = equalize_row_length(&mut data);
476
477        Self {
478            data,
479            count_columns,
480            empty_text: Text::default(),
481        }
482    }
483}
484
485fn create_row<R>(row: R, size: usize, default: &Text<String>) -> Vec<Text<String>>
486where
487    R: IntoIterator,
488    R::Item: Into<String>,
489{
490    let mut list = Vec::with_capacity(size);
491    for text in row {
492        let text = text.into();
493        let text = Text::new(text);
494        list.push(text);
495    }
496
497    if list.len() < size {
498        for _ in 0..size - list.len() {
499            let text = default.clone();
500            list.push(text);
501        }
502    }
503
504    list
505}
506
507fn remove_empty_columns(data: &mut [Vec<Text<String>>], count_columns: usize) -> usize {
508    let mut deleted = 0;
509    for col in 0..count_columns {
510        let col = col - deleted;
511
512        let mut is_empty_column = true;
513        for row in data.iter() {
514            let text = &row[col];
515            if !text.as_ref().is_empty() {
516                is_empty_column = false;
517                break;
518            }
519        }
520
521        if is_empty_column {
522            for row in data.iter_mut() {
523                let _ = row.remove(col);
524            }
525
526            deleted += 1;
527        }
528    }
529
530    deleted
531}
532
533fn remove_empty_rows(data: &mut Vec<Vec<Text<String>>>, count_columns: usize) {
534    let mut deleted = 0;
535
536    for row in 0..data.len() {
537        let row = row - deleted;
538
539        let mut is_empty_row = true;
540        for col in 0..count_columns {
541            let cell = &data[row][col];
542            if !cell.as_ref().is_empty() {
543                is_empty_row = false;
544                break;
545            }
546        }
547
548        if is_empty_row {
549            let _ = data.remove(row);
550            deleted += 1;
551        }
552    }
553}
554
555fn resize_rows(data: &mut Vec<Vec<Text<String>>>, size: usize, empty_text: &Text<String>) {
556    for row in data {
557        append_vec(row, empty_text.clone(), size);
558    }
559}
560
561fn append_vec<T>(v: &mut Vec<T>, value: T, n: usize)
562where
563    T: Clone,
564{
565    for _ in 0..n {
566        v.push(value.clone());
567    }
568}
569
570fn is_size_eq(expected: usize, new: usize) -> bool {
571    use std::cmp::Ordering;
572
573    match new.cmp(&expected) {
574        Ordering::Less => {
575            unreachable!("must be impossible due to the assumptions/checks we do");
576        }
577        Ordering::Greater => false,
578        Ordering::Equal => true,
579    }
580}
581
582fn equalize_row_length(data: &mut Vec<Vec<Text<String>>>) -> usize {
583    if data.is_empty() {
584        return 0;
585    }
586
587    let first_row_length = data[0].len();
588    let init = (first_row_length, true);
589    let (count_columns, is_consistent) = data.iter().fold(init, |mut acc, cur| {
590        let length = cur.len();
591        acc.1 = acc.1 && acc.0 == length;
592        acc.0 = std::cmp::max(acc.0, length);
593        acc
594    });
595
596    if !is_consistent {
597        let empty_text = Text::default();
598        for row in data {
599            let size = count_columns - row.len();
600            append_vec(row, empty_text.clone(), size);
601        }
602    }
603
604    count_columns
605}