tabled/tables/
table.rs

1//! This module contains a main table representation [`Table`].
2
3use core::ops::DerefMut;
4use std::{borrow::Cow, fmt, iter::FromIterator};
5
6use crate::{
7    builder::Builder,
8    grid::{
9        colors::NoColors,
10        config::{
11            AlignmentHorizontal, ColorMap, ColoredConfig, CompactConfig, Entity, Indent, Sides,
12            SpannedConfig,
13        },
14        dimension::{CompleteDimension, Dimension, Estimate, PeekableGridDimension},
15        records::{
16            vec_records::{Text, VecRecords},
17            ExactRecords, Records,
18        },
19        PeekableGrid,
20    },
21    settings::{object::Object, CellOption, Style, TableOption},
22    Tabled,
23};
24
25/// The structure provides an interface for building a table for types that implements [`Tabled`].
26///
27/// To build a string representation of a table you must use a [`std::fmt::Display`].
28/// Or simply call `.to_string()` method.
29///
30/// The default table [`Style`] is [`Style::ascii`],
31/// with a single left and right [`Padding`].
32///
33/// ## Example
34///
35/// ### Basic usage
36///
37/// ```rust,no_run
38/// use tabled::Table;
39///
40/// let table = Table::new(&["Year", "2021"]);
41/// ```
42///
43/// ### With settings
44///
45/// ```rust,no_run
46/// use tabled::{Table, settings::{Style, Alignment}};
47///
48/// let data = vec!["Hello", "2021"];
49/// let mut table = Table::new(&data);
50/// table
51///     .with(Style::psql())
52///     .with(Alignment::left());
53/// ```
54///
55/// ### With a [`Tabled`] trait.
56///
57/// ```rust,no_run
58/// use tabled::{Table, Tabled};
59///
60/// #[derive(Tabled)]
61/// struct Character {
62///     good: f32,
63///     bad: f32,
64///     encouraging: f32,
65///     destructive: f32,
66/// }
67///
68/// #[derive(Tabled)]
69/// struct Person<'a>(
70///     #[tabled(rename = "name")] &'a str,
71///     #[tabled(inline)] Character,
72/// );
73///
74/// let data = vec![
75///     Person("007", Character { good: 0.8, bad: 0.2, encouraging: 0.8, destructive: 0.1}),
76///     Person("001", Character { good: 0.2, bad: 0.5, encouraging: 0.2, destructive: 0.1}),
77///     Person("006", Character { good: 0.4, bad: 0.1, encouraging: 0.5, destructive: 0.8}),
78/// ];
79///
80/// let table = Table::new(&data);
81/// ```
82///
83/// [`Padding`]: crate::settings::Padding
84/// [`Style`]: crate::settings::Style
85/// [`Style::ascii`]: crate::settings::Style::ascii
86#[derive(Debug, Clone, PartialEq, Eq)]
87pub struct Table {
88    records: VecRecords<Text<String>>,
89    config: ColoredConfig,
90    dimension: CompleteDimension,
91}
92
93impl Table {
94    /// Creates a Table instance, from a list of [`Tabled`] values.
95    ///
96    /// If you use a reference iterator you'd better use [`FromIterator`] instead.
97    /// As it has a different lifetime constraints and make less copies therefore.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use tabled::{Table, Tabled, assert::assert_table};
103    ///
104    /// #[derive(Tabled)]
105    /// struct Relationship {
106    ///     love: bool
107    /// }
108    ///
109    /// let list = vec![
110    ///     Relationship { love: true },
111    ///     Relationship { love: false },
112    /// ];
113    ///
114    /// let table = Table::new(list);
115    ///
116    /// assert_table!(
117    ///     table,
118    ///     "+-------+"
119    ///     "| love  |"
120    ///     "+-------+"
121    ///     "| true  |"
122    ///     "+-------+"
123    ///     "| false |"
124    ///     "+-------+"
125    /// );
126    /// ```
127    ///
128    /// ## Don't hesitate to use iterators.
129    ///
130    /// ```
131    /// use tabled::{Table, Tabled, assert::assert_table};
132    ///
133    /// #[derive(Tabled)]
134    /// struct Relationship {
135    ///     person: String,
136    ///     love: bool
137    /// }
138    ///
139    /// let list = vec![
140    ///     Relationship { person: String::from("Clara"), love: true },
141    ///     Relationship { person: String::from("Greg"), love: false },
142    /// ];
143    ///
144    /// // Maybe don't love but don't hate :)
145    /// let iter = list.into_iter()
146    ///     .map(|mut rel| {
147    ///         if !rel.love {
148    ///             rel.love = true;
149    ///         }
150    ///
151    ///         rel
152    ///     });
153    ///
154    /// let table = Table::new(iter);
155    ///
156    /// assert_table!(
157    ///     table,
158    ///     "+--------+------+"
159    ///     "| person | love |"
160    ///     "+--------+------+"
161    ///     "| Clara  | true |"
162    ///     "+--------+------+"
163    ///     "| Greg   | true |"
164    ///     "+--------+------+"
165    /// );
166    /// ```
167    ///
168    /// ## Notice that you can pass tuples.
169    ///
170    /// ```
171    /// use tabled::{Table, Tabled, assert::assert_table};
172    ///
173    /// #[derive(Tabled)]
174    /// struct Relationship {
175    ///     love: bool
176    /// }
177    ///
178    /// let list = vec![
179    ///     ("Kate", Relationship { love: true }),
180    ///     ("", Relationship { love: false }),
181    /// ];
182    ///
183    /// let table = Table::new(list);
184    ///
185    /// assert_table!(
186    ///     table,
187    ///     "+------+-------+"
188    ///     "| &str | love  |"
189    ///     "+------+-------+"
190    ///     "| Kate | true  |"
191    ///     "+------+-------+"
192    ///     "|      | false |"
193    ///     "+------+-------+"
194    /// );
195    /// ```
196    ///
197    /// ## As a different way to create a [`Table`], you can use [`Table::from_iter`].  
198    ///
199    /// ```
200    /// use std::iter::FromIterator;
201    /// use tabled::{Table, assert::assert_table};
202    ///
203    /// let list = vec![
204    ///     vec!["Kate", "+", "+", "+", "-"],
205    ///     vec!["", "-", "-", "-", "-"],
206    /// ];
207    ///
208    /// let table = Table::from_iter(list);
209    ///
210    /// assert_table!(
211    ///     table,
212    ///     "+------+---+---+---+---+"
213    ///     "| Kate | + | + | + | - |"
214    ///     "+------+---+---+---+---+"
215    ///     "|      | - | - | - | - |"
216    ///     "+------+---+---+---+---+"
217    /// );
218    /// ```
219    pub fn new<I, T>(iter: I) -> Self
220    where
221        I: IntoIterator<Item = T>,
222        T: Tabled,
223    {
224        let mut header = Vec::with_capacity(T::LENGTH);
225        for text in T::headers() {
226            let text = text.into_owned();
227            let cell = Text::new(text);
228            header.push(cell);
229        }
230
231        let mut records = vec![header];
232        for row in iter.into_iter() {
233            let mut list = Vec::with_capacity(T::LENGTH);
234            for text in row.fields().into_iter() {
235                let text = text.into_owned();
236                let cell = Text::new(text);
237
238                list.push(cell);
239            }
240
241            records.push(list);
242        }
243
244        let records = VecRecords::new(records);
245        let config = ColoredConfig::new(configure_grid());
246        let dimension = CompleteDimension::default();
247
248        Self {
249            records,
250            config,
251            dimension,
252        }
253    }
254
255    /// Creates a Table instance, from a list of [`Tabled`] values.
256    ///
257    /// It's an optimized version of [`Table::new`].
258    ///
259    /// ```
260    /// use tabled::{Table, Tabled, assert::assert_table};
261    ///
262    /// #[derive(Tabled)]
263    /// struct Relationship {
264    ///     person: String,
265    ///     love: bool
266    /// }
267    ///
268    /// let list = vec![
269    ///     Relationship { person: String::from("Clara"), love: true },
270    ///     Relationship { person: String::from("Greg"), love: false },
271    /// ];
272    ///
273    /// let table = Table::with_capacity(&list, list.len());
274    ///
275    /// assert_table!(
276    ///     table,
277    ///     "+--------+-------+"
278    ///     "| person | love  |"
279    ///     "+--------+-------+"
280    ///     "| Clara  | true  |"
281    ///     "+--------+-------+"
282    ///     "| Greg   | false |"
283    ///     "+--------+-------+"
284    /// );
285    /// ```
286    pub fn with_capacity<I, T>(iter: I, count_rows: usize) -> Self
287    where
288        I: IntoIterator<Item = T>,
289        T: Tabled,
290    {
291        let mut header = Vec::with_capacity(T::LENGTH);
292        for text in T::headers() {
293            let text = text.into_owned();
294            let cell = Text::new(text);
295            header.push(cell);
296        }
297
298        let mut records = Vec::with_capacity(count_rows + 1);
299        records.push(header);
300
301        for row in iter.into_iter() {
302            let mut list = Vec::with_capacity(T::LENGTH);
303            for text in row.fields().into_iter() {
304                let text = text.into_owned();
305                let cell = Text::new(text);
306
307                list.push(cell);
308            }
309
310            records.push(list);
311        }
312
313        let records = VecRecords::new(records);
314        let config = ColoredConfig::new(configure_grid());
315        let dimension = CompleteDimension::default();
316
317        Self {
318            records,
319            config,
320            dimension,
321        }
322    }
323
324    /// Creates a Table instance, from a list of [`Tabled`] values.
325    ///
326    /// Compared to [`Table::new`] it does not use a "header" (first line).
327    ///
328    /// If you use a reference iterator you'd better use [`FromIterator`] instead.
329    /// As it has a different lifetime constraints and make less copies therefore.
330    ///
331    /// # Examples
332    ///
333    /// ```
334    /// use tabled::{Table, Tabled, assert::assert_table};
335    ///
336    /// #[derive(Tabled)]
337    /// struct Relationship {
338    ///     love: bool
339    /// }
340    ///
341    /// let list = vec![
342    ///     ("Kate", Relationship { love: true }),
343    ///     ("", Relationship { love: false }),
344    /// ];
345    ///
346    /// let table = Table::nohead(list);
347    ///
348    /// assert_table!(
349    ///     table,
350    ///     "+------+-------+"
351    ///     "| Kate | true  |"
352    ///     "+------+-------+"
353    ///     "|      | false |"
354    ///     "+------+-------+"
355    /// );
356    /// ```
357    pub fn nohead<I, T>(iter: I) -> Self
358    where
359        I: IntoIterator<Item = T>,
360        T: Tabled,
361    {
362        let mut records = vec![];
363        for row in iter.into_iter() {
364            let mut list = Vec::with_capacity(T::LENGTH);
365            for text in row.fields().into_iter() {
366                let text = text.into_owned();
367                let cell = Text::new(text);
368
369                list.push(cell);
370            }
371
372            records.push(list);
373        }
374
375        let records = VecRecords::new(records);
376        let config = ColoredConfig::new(configure_grid());
377        let dimension = CompleteDimension::default();
378
379        Self {
380            records,
381            config,
382            dimension,
383        }
384    }
385
386    /// Creates a Key-Value [`Table`] instance, from a list of [`Tabled`] values.
387    ///
388    /// # Examples
389    ///
390    /// ```
391    /// use tabled::{Table, Tabled, assert::assert_table};
392    ///
393    /// #[derive(Tabled)]
394    /// #[tabled(rename_all = "PascalCase")]
395    /// struct Swim {
396    ///     event: String,
397    ///     time: String,
398    ///     #[tabled(rename = "Pool Length")]
399    ///     pool: u8,
400    /// }
401    ///
402    /// const POOL_25: u8 = 25;
403    /// const POOL_50: u8 = 50;
404    ///
405    /// let list = vec![
406    ///     Swim { event: String::from("Men 100 Freestyle"), time: String::from("47.77"), pool: POOL_25 },
407    ///     Swim { event: String::from("Men 400 Freestyle"), time: String::from("03:59.16"), pool: POOL_25 },
408    ///     Swim { event: String::from("Men 800 Freestyle"), time: String::from("08:06.70"), pool: POOL_25 },
409    ///     Swim { event: String::from("Men 4x100 Medley Relay"), time: String::from("03:27.28"), pool: POOL_50 },
410    /// ];
411    ///
412    /// let table = Table::kv(list);
413    ///
414    /// assert_table!(
415    ///     table,
416    ///     "+-------------+------------------------+"
417    ///     "| Event       | Men 100 Freestyle      |"
418    ///     "+-------------+------------------------+"
419    ///     "| Time        | 47.77                  |"
420    ///     "+-------------+------------------------+"
421    ///     "| Pool Length | 25                     |"
422    ///     "+-------------+------------------------+"
423    ///     "| Event       | Men 400 Freestyle      |"
424    ///     "+-------------+------------------------+"
425    ///     "| Time        | 03:59.16               |"
426    ///     "+-------------+------------------------+"
427    ///     "| Pool Length | 25                     |"
428    ///     "+-------------+------------------------+"
429    ///     "| Event       | Men 800 Freestyle      |"
430    ///     "+-------------+------------------------+"
431    ///     "| Time        | 08:06.70               |"
432    ///     "+-------------+------------------------+"
433    ///     "| Pool Length | 25                     |"
434    ///     "+-------------+------------------------+"
435    ///     "| Event       | Men 4x100 Medley Relay |"
436    ///     "+-------------+------------------------+"
437    ///     "| Time        | 03:27.28               |"
438    ///     "+-------------+------------------------+"
439    ///     "| Pool Length | 50                     |"
440    ///     "+-------------+------------------------+"
441    /// );
442    /// ```
443    ///
444    /// Next you'll find a more complex example with a subtle style.
445    ///
446    /// ```
447    /// use tabled::{Table, Tabled, settings::Style};
448    /// use tabled::settings::{style::HorizontalLine, Theme};
449    /// use tabled::assert::assert_table;
450    ///
451    /// #[derive(Tabled)]
452    /// #[tabled(rename_all = "PascalCase")]
453    /// struct Swim {
454    ///     event: String,
455    ///     time: String,
456    ///     #[tabled(rename = "Pool Length")]
457    ///     pool: u8,
458    /// }
459    ///
460    /// const POOL_25: u8 = 25;
461    /// const POOL_50: u8 = 50;
462    ///
463    /// let list = vec![
464    ///     Swim { event: String::from("Men 100 Freestyle"), time: String::from("47.77"), pool: POOL_25 },
465    ///     Swim { event: String::from("Men 400 Freestyle"), time: String::from("03:59.16"), pool: POOL_25 },
466    ///     Swim { event: String::from("Men 800 Freestyle"), time: String::from("08:06.70"), pool: POOL_25 },
467    ///     Swim { event: String::from("Men 4x100 Medley Relay"), time: String::from("03:27.28"), pool: POOL_50 },
468    /// ];
469    ///
470    /// let mut table = Table::kv(list);
471    ///
472    /// let mut style = Theme::from_style(Style::rounded().remove_horizontals());
473    /// for entry in 1 .. table.count_rows() / Swim::LENGTH {
474    ///     style.insert_horizontal_line(entry * Swim::LENGTH, HorizontalLine::inherit(Style::modern()));
475    /// }
476    ///
477    /// table.with(style);
478    ///
479    /// assert_table!(
480    ///     table,
481    ///     "╭─────────────┬────────────────────────╮"
482    ///     "│ Event       │ Men 100 Freestyle      │"
483    ///     "│ Time        │ 47.77                  │"
484    ///     "│ Pool Length │ 25                     │"
485    ///     "├─────────────┼────────────────────────┤"
486    ///     "│ Event       │ Men 400 Freestyle      │"
487    ///     "│ Time        │ 03:59.16               │"
488    ///     "│ Pool Length │ 25                     │"
489    ///     "├─────────────┼────────────────────────┤"
490    ///     "│ Event       │ Men 800 Freestyle      │"
491    ///     "│ Time        │ 08:06.70               │"
492    ///     "│ Pool Length │ 25                     │"
493    ///     "├─────────────┼────────────────────────┤"
494    ///     "│ Event       │ Men 4x100 Medley Relay │"
495    ///     "│ Time        │ 03:27.28               │"
496    ///     "│ Pool Length │ 50                     │"
497    ///     "╰─────────────┴────────────────────────╯"
498    /// );
499    /// ```
500    pub fn kv<I, T>(iter: I) -> Self
501    where
502        I: IntoIterator<Item = T>,
503        T: Tabled,
504    {
505        let headers = T::headers();
506
507        let mut records = Vec::new();
508        for row in iter.into_iter() {
509            for (text, name) in row.fields().into_iter().zip(headers.iter()) {
510                let key = Text::new(name.clone().into_owned());
511                let value = Text::new(text.into_owned());
512
513                records.push(vec![key, value]);
514            }
515        }
516
517        let records = VecRecords::new(records);
518        let config = ColoredConfig::new(configure_grid());
519        let dimension = CompleteDimension::default();
520
521        Self {
522            records,
523            config,
524            dimension,
525        }
526    }
527
528    /// Creates a builder from a data set given.
529    ///
530    /// # Example
531    ///
532    #[cfg_attr(feature = "derive", doc = "```")]
533    #[cfg_attr(not(feature = "derive"), doc = "```ignore")]
534    /// use tabled::{
535    ///     Table, Tabled,
536    ///     settings::{object::Segment, Modify, Alignment}
537    /// };
538    ///
539    /// #[derive(Tabled)]
540    /// struct User {
541    ///     name: &'static str,
542    ///     #[tabled(inline("device::"))]
543    ///     device: Device,
544    /// }
545    ///
546    /// #[derive(Tabled)]
547    /// enum Device {
548    ///     PC,
549    ///     Mobile
550    /// }
551    ///
552    /// let data = vec![
553    ///     User { name: "Vlad", device: Device::Mobile },
554    ///     User { name: "Dimitry", device: Device::PC },
555    ///     User { name: "John", device: Device::PC },
556    /// ];
557    ///
558    /// let mut table = Table::builder(data)
559    ///     .index()
560    ///     .column(0)
561    ///     .transpose()
562    ///     .build()
563    ///     .modify(Segment::new(1.., 1..), Alignment::center())
564    ///     .to_string();
565    ///
566    /// assert_eq!(
567    ///     table,
568    ///     "+----------------+------+---------+------+\n\
569    ///      | name           | Vlad | Dimitry | John |\n\
570    ///      +----------------+------+---------+------+\n\
571    ///      | device::PC     |      |    +    |  +   |\n\
572    ///      +----------------+------+---------+------+\n\
573    ///      | device::Mobile |  +   |         |      |\n\
574    ///      +----------------+------+---------+------+"
575    /// )
576    /// ```
577    pub fn builder<I, T>(iter: I) -> Builder
578    where
579        T: Tabled,
580        I: IntoIterator<Item = T>,
581    {
582        let mut builder = Builder::with_capacity(1, T::LENGTH);
583        builder.push_record(T::headers());
584
585        for row in iter {
586            builder.push_record(row.fields().into_iter());
587        }
588
589        builder
590    }
591
592    /// It's a generic function which applies options to the [`Table`].
593    ///
594    /// It applies settings immediately.
595    ///
596    /// ```
597    /// use tabled::{Table, settings::Style};
598    /// use tabled::assert::assert_table;
599    ///
600    /// let data = vec![
601    ///     ("number", "name"),
602    ///     ("285-324-7322", "Rosalia"),
603    ///     ("564.549.6468", "Mary"),
604    /// ];
605    ///
606    /// let mut table = Table::new(data);
607    /// table.with(Style::markdown());
608    ///
609    /// assert_table!(
610    ///     table,
611    ///     "| &str         | &str    |"
612    ///     "|--------------|---------|"
613    ///     "| number       | name    |"
614    ///     "| 285-324-7322 | Rosalia |"
615    ///     "| 564.549.6468 | Mary    |"
616    /// );
617    /// ```
618    pub fn with<O>(&mut self, option: O) -> &mut Self
619    where
620        O: TableOption<VecRecords<Text<String>>, ColoredConfig, CompleteDimension>,
621    {
622        let reastimation_hint = option.hint_change();
623
624        option.change(&mut self.records, &mut self.config, &mut self.dimension);
625        self.dimension.reastimate(reastimation_hint);
626
627        self
628    }
629
630    /// It's a generic function which applies options to particular cells on the [`Table`].
631    /// Target cells using [`Object`]s such as [`Cell`], [`Rows`], [`Location`] and more.
632    ///
633    /// It applies settings immediately.
634    ///
635    /// ```
636    /// use tabled::{Table, settings::{object::Columns, Alignment}};
637    /// use tabled::assert::assert_table;
638    ///
639    /// let data = vec![
640    ///     ("number", "name"),
641    ///     ("285-324-7322", "Rosalia"),
642    ///     ("564.549.6468", "Mary"),
643    /// ];
644    ///
645    /// let mut table = Table::new(data);
646    /// table.modify(Columns::first(), Alignment::right());
647    /// table.modify(Columns::one(1), Alignment::center());
648    ///
649    /// assert_table!(
650    ///     table,
651    ///     "+--------------+---------+"
652    ///     "|         &str |  &str   |"
653    ///     "+--------------+---------+"
654    ///     "|       number |  name   |"
655    ///     "+--------------+---------+"
656    ///     "| 285-324-7322 | Rosalia |"
657    ///     "+--------------+---------+"
658    ///     "| 564.549.6468 |  Mary   |"
659    ///     "+--------------+---------+"
660    /// );
661    /// ```
662    ///
663    /// [`Cell`]: crate::settings::object::Cell
664    /// [`Rows`]: crate::settings::object::Rows
665    /// [`Location`]: crate::settings::location::Locator
666    pub fn modify<T, O>(&mut self, target: T, option: O) -> &mut Self
667    where
668        T: Object<VecRecords<Text<String>>>,
669        O: CellOption<VecRecords<Text<String>>, ColoredConfig> + Clone,
670    {
671        for entity in target.cells(&self.records) {
672            let opt = option.clone();
673            opt.change(&mut self.records, &mut self.config, entity);
674        }
675
676        let hint = option.hint_change();
677        self.dimension.reastimate(hint);
678
679        self
680    }
681
682    /// Returns a table shape (count rows, count columns).
683    pub fn shape(&self) -> (usize, usize) {
684        (self.count_rows(), self.count_columns())
685    }
686
687    /// Returns an amount of rows in the table.
688    pub fn count_rows(&self) -> usize {
689        self.records.count_rows()
690    }
691
692    /// Returns an amount of columns in the table.
693    pub fn count_columns(&self) -> usize {
694        self.records.count_columns()
695    }
696
697    /// Returns a table shape (count rows, count columns).
698    pub fn is_empty(&self) -> bool {
699        let (count_rows, count_cols) = self.shape();
700        count_rows == 0 || count_cols == 0
701    }
702
703    /// Returns total widths of a table, including margin and horizontal lines.
704    pub fn total_height(&self) -> usize {
705        let mut dims = self.dimension.clone();
706        dims.estimate(&self.records, self.config.as_ref());
707
708        let total = (0..self.count_rows())
709            .map(|row| dims.get_height(row))
710            .sum::<usize>();
711        let counth = self.config.count_horizontal(self.count_rows());
712
713        let margin = self.config.get_margin();
714
715        total + counth + margin.top.size + margin.bottom.size
716    }
717
718    /// Returns total widths of a table, including margin and vertical lines.
719    pub fn total_width(&self) -> usize {
720        let mut dims = self.dimension.clone();
721        dims.estimate(&self.records, self.config.as_ref());
722
723        let total = (0..self.count_columns())
724            .map(|col| dims.get_width(col))
725            .sum::<usize>();
726        let countv = self.config.count_vertical(self.count_columns());
727
728        let margin = self.config.get_margin();
729
730        total + countv + margin.left.size + margin.right.size
731    }
732
733    /// Returns a table config.
734    pub fn get_config(&self) -> &ColoredConfig {
735        &self.config
736    }
737
738    /// Returns a table config.
739    pub fn get_config_mut(&mut self) -> &mut ColoredConfig {
740        &mut self.config
741    }
742
743    /// Returns a used records.
744    pub fn get_records(&self) -> &VecRecords<Text<String>> {
745        &self.records
746    }
747
748    /// Returns a used records.
749    pub fn get_records_mut(&mut self) -> &mut VecRecords<Text<String>> {
750        &mut self.records
751    }
752
753    /// Returns a dimension.
754    pub fn get_dimension(&self) -> &CompleteDimension {
755        &self.dimension
756    }
757
758    /// Returns a dimension.
759    pub fn get_dimension_mut(&mut self) -> &mut CompleteDimension {
760        &mut self.dimension
761    }
762}
763
764impl Default for Table {
765    fn default() -> Self {
766        Self {
767            records: VecRecords::default(),
768            config: ColoredConfig::new(configure_grid()),
769            dimension: CompleteDimension::default(),
770        }
771    }
772}
773
774impl fmt::Display for Table {
775    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
776        if self.is_empty() {
777            return Ok(());
778        }
779
780        let config = use_format_configuration(f, self);
781        let colors = self.config.get_colors();
782
783        if !self.dimension.is_empty() {
784            let mut dims = self.dimension.clone();
785            dims.estimate(&self.records, config.as_ref());
786
787            print_grid(f, &self.records, &config, &dims, colors)
788        } else {
789            let mut dims = PeekableGridDimension::default();
790            dims.estimate(&self.records, &config);
791
792            print_grid(f, &self.records, &config, &dims, colors)
793        }
794    }
795}
796
797impl<T> FromIterator<T> for Table
798where
799    T: IntoIterator,
800    T::Item: Into<String>,
801{
802    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
803        Builder::from_iter(iter.into_iter().map(|i| i.into_iter().map(|s| s.into()))).build()
804    }
805}
806
807impl From<Builder> for Table {
808    fn from(builder: Builder) -> Self {
809        let data = builder.into();
810        let records = VecRecords::new(data);
811        let config = ColoredConfig::new(configure_grid());
812        let dimension = CompleteDimension::default();
813
814        Self {
815            records,
816            config,
817            dimension,
818        }
819    }
820}
821
822impl From<Table> for Builder {
823    fn from(val: Table) -> Self {
824        let data = val.records.into();
825        Builder::from_vec(data)
826    }
827}
828
829impl<R, D> TableOption<R, ColoredConfig, D> for CompactConfig {
830    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
831        *cfg.deref_mut() = self.into();
832    }
833}
834
835impl<R, D> TableOption<R, ColoredConfig, D> for ColoredConfig {
836    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
837        *cfg = self;
838    }
839}
840
841impl<R, D> TableOption<R, ColoredConfig, D> for SpannedConfig {
842    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
843        *cfg.deref_mut() = self;
844    }
845}
846
847impl<R, D> TableOption<R, ColoredConfig, D> for &SpannedConfig {
848    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
849        *cfg.deref_mut() = self.clone();
850    }
851}
852
853fn convert_fmt_alignment(alignment: fmt::Alignment) -> AlignmentHorizontal {
854    match alignment {
855        fmt::Alignment::Left => AlignmentHorizontal::Left,
856        fmt::Alignment::Right => AlignmentHorizontal::Right,
857        fmt::Alignment::Center => AlignmentHorizontal::Center,
858    }
859}
860
861fn table_padding(alignment: fmt::Alignment, available: usize) -> (usize, usize) {
862    match alignment {
863        fmt::Alignment::Left => (available, 0),
864        fmt::Alignment::Right => (0, available),
865        fmt::Alignment::Center => {
866            let left = available / 2;
867            let right = available - left;
868            (left, right)
869        }
870    }
871}
872
873fn configure_grid() -> SpannedConfig {
874    let mut cfg = SpannedConfig::default();
875    cfg.set_padding(
876        Entity::Global,
877        Sides::new(
878            Indent::spaced(1),
879            Indent::spaced(1),
880            Indent::default(),
881            Indent::default(),
882        ),
883    );
884    cfg.set_alignment_horizontal(Entity::Global, AlignmentHorizontal::Left);
885    cfg.set_line_alignment(Entity::Global, false);
886    cfg.set_trim_horizontal(Entity::Global, false);
887    cfg.set_trim_vertical(Entity::Global, false);
888    cfg.set_borders(Style::ascii().get_borders());
889
890    cfg
891}
892
893fn use_format_configuration<'a>(
894    f: &mut fmt::Formatter<'_>,
895    table: &'a Table,
896) -> Cow<'a, SpannedConfig> {
897    if f.align().is_some() || f.width().is_some() {
898        let mut cfg = table.config.as_ref().clone();
899
900        set_align_table(f, &mut cfg);
901        set_width_table(f, &mut cfg, table);
902
903        Cow::Owned(cfg)
904    } else {
905        Cow::Borrowed(table.config.as_ref())
906    }
907}
908
909fn set_align_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig) {
910    if let Some(alignment) = f.align() {
911        let alignment = convert_fmt_alignment(alignment);
912        cfg.set_alignment_horizontal(Entity::Global, alignment);
913    }
914}
915
916fn set_width_table(f: &fmt::Formatter<'_>, cfg: &mut SpannedConfig, table: &Table) {
917    if let Some(width) = f.width() {
918        let total_width = table.total_width();
919        if total_width >= width {
920            return;
921        }
922
923        let mut fill = f.fill();
924        if fill == char::default() {
925            fill = ' ';
926        }
927
928        let available = width - total_width;
929        let alignment = f.align().unwrap_or(fmt::Alignment::Left);
930        let (left, right) = table_padding(alignment, available);
931
932        let mut margin = cfg.get_margin();
933        margin.left.size += left;
934        margin.right.size += right;
935
936        if (margin.left.size > 0 && margin.left.fill == char::default()) || fill != char::default()
937        {
938            margin.left.fill = fill;
939        }
940
941        if (margin.right.size > 0 && margin.right.fill == char::default())
942            || fill != char::default()
943        {
944            margin.right.fill = fill;
945        }
946
947        cfg.set_margin(margin);
948    }
949}
950
951fn print_grid<F: fmt::Write, D: Dimension>(
952    f: &mut F,
953    records: &VecRecords<Text<String>>,
954    cfg: &SpannedConfig,
955    dims: D,
956    colors: &ColorMap,
957) -> fmt::Result {
958    if !colors.is_empty() {
959        PeekableGrid::new(records, cfg, &dims, colors).build(f)
960    } else {
961        PeekableGrid::new(records, cfg, &dims, NoColors).build(f)
962    }
963}