tabled/builder/
index_builder.rs

1use crate::{grid::records::vec_records::Text, Table};
2
3use super::Builder;
4
5/// [`IndexBuilder`] helps to add an index to the table.
6///
7/// Index is a column on the left of the table.
8///
9/// It also can be used to transpose the table.
10///
11/// Creates a new [`IndexBuilder`] instance.
12///
13/// It creates a default index a range from 0 to N. (N - count rows)
14/// It also sets a default columns to the range 0 .. N (N - count columns).
15///nfo<'a>
16/// # Example
17///
18/// ```
19/// use tabled::builder::Builder;
20///
21/// let mut builder = Builder::default();
22/// builder.push_record(["i", "col-1", "col-2"]);
23/// builder.push_record(["0", "value-1", "value-2"]);
24///
25/// let table = builder.index().build().to_string();
26///
27/// assert_eq!(
28///     table,
29///     "+---+---+---------+---------+\n\
30///      |   | i | col-1   | col-2   |\n\
31///      +---+---+---------+---------+\n\
32///      | 0 | 0 | value-1 | value-2 |\n\
33///      +---+---+---------+---------+"
34/// )
35/// ```
36///
37/// # Example
38///
39/// ```
40/// use tabled::builder::Builder;
41///
42/// let table = Builder::default()
43///     .index()
44///     .build();
45/// ```
46#[derive(Debug, Clone)]
47pub struct IndexBuilder {
48    /// Index is an index data.
49    /// It's always set.
50    index: Vec<Text<String>>,
51    /// Name of an index
52    name: Option<Text<String>>,
53    /// A flag which checks if we need to actually use index.
54    ///
55    /// It might happen when it's only necessary to [`Self::transpose`] table.
56    print_index: bool,
57    /// A flag which checks if table was transposed.
58    transposed: bool,
59    /// Data originated in [`Builder`].
60    data: Vec<Vec<Text<String>>>,
61    /// A size of columns
62    count_columns: usize,
63}
64
65impl IndexBuilder {
66    /// No flag makes builder to not use an index.
67    ///
68    /// It may be useful when only [`Self::transpose`] need to be used.
69    ///
70    /// ```
71    /// use tabled::builder::Builder;
72    ///
73    /// let mut builder = Builder::default();
74    /// builder.push_record(["i", "col-1", "col-2"]);
75    /// builder.push_record(["0", "value-1", "value-2"]);
76    /// builder.push_record(["2", "value-3", "value-4"]);
77    ///
78    /// let table = builder.index().hide().build().to_string();
79    ///
80    /// assert_eq!(
81    ///     table,
82    ///     "+---+---------+---------+\n\
83    ///      | i | col-1   | col-2   |\n\
84    ///      +---+---------+---------+\n\
85    ///      | 0 | value-1 | value-2 |\n\
86    ///      +---+---------+---------+\n\
87    ///      | 2 | value-3 | value-4 |\n\
88    ///      +---+---------+---------+"
89    /// )
90    /// ```
91    pub fn hide(mut self) -> Self {
92        self.print_index = false;
93        self
94    }
95
96    /// Set an index name.
97    ///
98    /// When [`None`] the name won't be used.
99    ///
100    /// # Example
101    ///
102    /// ```
103    /// use tabled::builder::Builder;
104    ///
105    /// let mut builder = Builder::default();
106    /// builder.push_record(["i", "column1", "column2"]);
107    /// builder.push_record(["0", "value1", "value2"]);
108    ///
109    /// let table = builder.index()
110    ///     .column(1)
111    ///     .name(Some(String::from("index")))
112    ///     .build();
113    ///
114    /// assert_eq!(
115    ///     table.to_string(),
116    ///     "+--------+---+---------+\n\
117    ///      |        | i | column2 |\n\
118    ///      +--------+---+---------+\n\
119    ///      | index  |   |         |\n\
120    ///      +--------+---+---------+\n\
121    ///      | value1 | 0 | value2  |\n\
122    ///      +--------+---+---------+"
123    /// )
124    /// ```
125    pub fn name(mut self, name: Option<String>) -> Self {
126        self.name = name.map(Text::new);
127        self
128    }
129
130    /// Sets a index to the chosen column.
131    ///
132    /// Also sets a name of the index to the column name.
133    ///
134    /// # Example
135    ///
136    /// ```
137    /// use tabled::builder::Builder;
138    ///
139    /// let mut builder = Builder::default();
140    /// builder.push_record(["i", "column1", "column2"]);
141    /// builder.push_record(["0", "value1", "value2"]);
142    ///
143    /// let table = builder.index().column(1).build();
144    ///
145    /// assert_eq!(
146    ///     table.to_string(),
147    ///     "+---------+---+---------+\n\
148    ///      |         | i | column2 |\n\
149    ///      +---------+---+---------+\n\
150    ///      | column1 |   |         |\n\
151    ///      +---------+---+---------+\n\
152    ///      | value1  | 0 | value2  |\n\
153    ///      +---------+---+---------+"
154    /// )
155    /// ```
156    pub fn column(mut self, column: usize) -> Self {
157        if column >= self.count_columns {
158            return self;
159        }
160
161        self.index = get_column(&mut self.data, column);
162
163        let name = self.index.remove(0);
164        self.name = Some(name);
165
166        self
167    }
168
169    /// Transpose index and columns.
170    ///
171    /// # Example
172    ///
173    /// ```
174    /// use tabled::builder::Builder;
175    ///
176    /// let mut builder = Builder::default();
177    /// builder.push_record(["i", "column-1", "column-2", "column-3"]);
178    /// builder.push_record(["0", "value-1", "value-2", "value-3"]);
179    /// builder.push_record(["1", "value-4", "value-5", "value-6"]);
180    /// builder.push_record(["2", "value-7", "value-8", "value-9"]);
181    ///
182    /// let table = builder.index().column(1).transpose().build();
183    ///
184    /// assert_eq!(
185    ///     table.to_string(),
186    ///     "+----------+---------+---------+---------+\n\
187    ///      | column-1 | value-1 | value-4 | value-7 |\n\
188    ///      +----------+---------+---------+---------+\n\
189    ///      | i        | 0       | 1       | 2       |\n\
190    ///      +----------+---------+---------+---------+\n\
191    ///      | column-2 | value-2 | value-5 | value-8 |\n\
192    ///      +----------+---------+---------+---------+\n\
193    ///      | column-3 | value-3 | value-6 | value-9 |\n\
194    ///      +----------+---------+---------+---------+"
195    /// )
196    /// ```
197    pub fn transpose(mut self) -> Self {
198        if self.data.is_empty() {
199            return self;
200        }
201
202        let mut columns = self.data.remove(0);
203        std::mem::swap(&mut self.index, &mut columns);
204
205        let count_columns = columns.len();
206        rotate_vector(&mut self.data, self.index.len());
207
208        self.data.insert(0, columns);
209
210        self.transposed = !self.transposed;
211        self.count_columns = count_columns;
212
213        self
214    }
215
216    /// Builds a table.
217    pub fn build(self) -> Table {
218        let builder: Builder = self.into();
219        builder.build()
220    }
221}
222
223impl From<Builder> for IndexBuilder {
224    fn from(builder: Builder) -> Self {
225        let count_columns = builder.count_columns();
226        let data: Vec<Vec<_>> = builder.into();
227
228        let mut index = Vec::new();
229        if !data.is_empty() {
230            // we exclude first row which contains a header
231            let count_rows = data.len() - 1;
232            index = build_range_index(count_rows);
233        }
234
235        Self {
236            index,
237            data,
238            count_columns,
239            name: None,
240            print_index: true,
241            transposed: false,
242        }
243    }
244}
245
246impl From<IndexBuilder> for Builder {
247    fn from(b: IndexBuilder) -> Self {
248        build_index(b)
249    }
250}
251
252fn build_index(mut b: IndexBuilder) -> Builder {
253    // we can skip the conversion if this builder has neither data rows nor header row
254    if b.index.is_empty() && b.count_columns == 0 {
255        return Builder::default();
256    }
257
258    // add index column
259    if b.print_index {
260        b.index.insert(0, Text::default());
261        insert_column(&mut b.data, b.index, 0);
262    }
263
264    if let Some(name) = b.name {
265        if b.transposed && b.print_index {
266            b.data[0][0] = name;
267        } else {
268            let count_columns = b.data[0].len();
269            let mut name_row = vec![Text::default(); count_columns];
270            name_row[0] = name;
271
272            b.data.insert(1, name_row);
273        }
274    }
275
276    Builder::from_vec(b.data)
277}
278
279fn build_range_index(n: usize) -> Vec<Text<String>> {
280    (0..n).map(|i| i.to_string()).map(Text::new).collect()
281}
282
283// note: Pretty heavy operation.
284fn rotate_vector<T>(rows: &mut Vec<Vec<T>>, count_columns: usize)
285where
286    T: Default + Clone,
287{
288    let count_rows = rows.len();
289    let mut columns = vec![vec![T::default(); count_rows]; count_columns];
290    for col in 0..count_columns {
291        for (row, data) in rows.iter_mut().enumerate() {
292            let value = data.pop().expect("expected to be controlled");
293            let col = count_columns - col - 1;
294            columns[col][row] = value;
295        }
296    }
297
298    *rows = columns;
299}
300
301fn insert_column<T: Default>(v: &mut [Vec<T>], mut column: Vec<T>, col: usize) {
302    for row in v.iter_mut() {
303        let value = column.remove(col);
304        row.insert(col, value);
305    }
306}
307
308fn get_column<T>(v: &mut [Vec<T>], col: usize) -> Vec<T>
309where
310    T: Default,
311{
312    let mut column = Vec::with_capacity(v.len());
313    for row in v.iter_mut() {
314        let value = row.remove(col);
315        column.push(value);
316    }
317
318    column
319}