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}