1#[macro_use]
59extern crate lazy_static;
60
61pub mod row;
62pub mod table_cell;
63
64use crate::row::Row;
65use crate::table_cell::Alignment;
66
67use std::cmp::{max, min};
68use std::collections::HashMap;
69
70#[macro_export]
71macro_rules! row {
72 [ $($x:expr),* ] => {
73 Row::new(vec![$(Into::<TableCell>::into($x)),*])
74 };
75 [ $($x:expr,)* ] => (row![$($x),*])
76}
77
78#[macro_export]
79macro_rules! row_no_separator {
80 [ $($x:expr),* ] => {
81 Row::without_separator(vec![$(Into::<TableCell>::into($x)),*])
82 };
83 [ $($x:expr,)* ] => (row![$($x),*])
84}
85
86#[macro_export]
87macro_rules! rows {
88 [ $($x:expr),* ] => {
89 vec![$($x),*]
90 };
91 [ $($x:expr,)* ] => (rows![$($x),*])
92}
93
94#[derive(Eq, PartialEq, Copy, Clone)]
96pub enum RowPosition {
97 First,
98 Mid,
99 Last,
100}
101
102#[derive(Debug, Clone, Copy)]
122pub struct TableStyle {
123 pub top_left_corner: char,
124 pub top_right_corner: char,
125 pub bottom_left_corner: char,
126 pub bottom_right_corner: char,
127 pub outer_left_vertical: char,
128 pub outer_right_vertical: char,
129 pub outer_bottom_horizontal: char,
130 pub outer_top_horizontal: char,
131 pub intersection: char,
132 pub vertical: char,
133 pub horizontal: char,
134}
135
136impl TableStyle {
137 pub fn simple() -> TableStyle {
154 TableStyle {
155 top_left_corner: '+',
156 top_right_corner: '+',
157 bottom_left_corner: '+',
158 bottom_right_corner: '+',
159 outer_left_vertical: '+',
160 outer_right_vertical: '+',
161 outer_bottom_horizontal: '+',
162 outer_top_horizontal: '+',
163 intersection: '+',
164 vertical: '|',
165 horizontal: '-',
166 }
167 }
168
169 pub fn extended() -> TableStyle {
186 TableStyle {
187 top_left_corner: '╔',
188 top_right_corner: '╗',
189 bottom_left_corner: '╚',
190 bottom_right_corner: '╝',
191 outer_left_vertical: '╠',
192 outer_right_vertical: '╣',
193 outer_bottom_horizontal: '╩',
194 outer_top_horizontal: '╦',
195 intersection: '╬',
196 vertical: '║',
197 horizontal: '═',
198 }
199 }
200
201 pub fn thin() -> TableStyle {
214 TableStyle {
215 top_left_corner: '┌',
216 top_right_corner: '┐',
217 bottom_left_corner: '└',
218 bottom_right_corner: '┘',
219 outer_left_vertical: '├',
220 outer_right_vertical: '┤',
221 outer_bottom_horizontal: '┴',
222 outer_top_horizontal: '┬',
223 intersection: '┼',
224 vertical: '│',
225 horizontal: '─',
226 }
227 }
228
229 pub fn rounded() -> TableStyle {
242 TableStyle {
243 top_left_corner: '╭',
244 top_right_corner: '╮',
245 bottom_left_corner: '╰',
246 bottom_right_corner: '╯',
247 outer_left_vertical: '├',
248 outer_right_vertical: '┤',
249 outer_bottom_horizontal: '┴',
250 outer_top_horizontal: '┬',
251 intersection: '┼',
252 vertical: '│',
253 horizontal: '─',
254 }
255 }
256
257 pub fn elegant() -> TableStyle {
271 TableStyle {
272 top_left_corner: '╔',
273 top_right_corner: '╗',
274 bottom_left_corner: '╚',
275 bottom_right_corner: '╝',
276 outer_left_vertical: '╠',
277 outer_right_vertical: '╣',
278 outer_bottom_horizontal: '╩',
279 outer_top_horizontal: '╦',
280 intersection: '┼',
281 vertical: '│',
282 horizontal: '─',
283 }
284 }
285
286 pub fn blank() -> TableStyle {
301 TableStyle {
302 top_left_corner: '\0',
303 top_right_corner: '\0',
304 bottom_left_corner: '\0',
305 bottom_right_corner: '\0',
306 outer_left_vertical: '\0',
307 outer_right_vertical: '\0',
308 outer_bottom_horizontal: '\0',
309 outer_top_horizontal: '\0',
310 intersection: '\0',
311 vertical: '\0',
312 horizontal: '\0',
313 }
314 }
315
316 pub fn empty() -> TableStyle {
332 TableStyle {
333 top_left_corner: ' ',
334 top_right_corner: ' ',
335 bottom_left_corner: ' ',
336 bottom_right_corner: ' ',
337 outer_left_vertical: ' ',
338 outer_right_vertical: ' ',
339 outer_bottom_horizontal: ' ',
340 outer_top_horizontal: ' ',
341 intersection: ' ',
342 vertical: ' ',
343 horizontal: ' ',
344 }
345 }
346
347 fn start_for_position(&self, pos: RowPosition) -> char {
350 match pos {
351 RowPosition::First => self.top_left_corner,
352 RowPosition::Mid => self.outer_left_vertical,
353 RowPosition::Last => self.bottom_left_corner,
354 }
355 }
356
357 fn end_for_position(&self, pos: RowPosition) -> char {
360 match pos {
361 RowPosition::First => self.top_right_corner,
362 RowPosition::Mid => self.outer_right_vertical,
363 RowPosition::Last => self.bottom_right_corner,
364 }
365 }
366
367 fn intersect_for_position(&self, pos: RowPosition) -> char {
370 match pos {
371 RowPosition::First => self.outer_top_horizontal,
372 RowPosition::Mid => self.intersection,
373 RowPosition::Last => self.outer_bottom_horizontal,
374 }
375 }
376
377 fn merge_intersection_for_position(&self, top: char, bottom: char, pos: RowPosition) -> char {
380 if (top == self.horizontal || top == self.outer_bottom_horizontal)
381 && bottom == self.intersection
382 {
383 return self.outer_top_horizontal;
384 } else if (top == self.intersection || top == self.outer_top_horizontal)
385 && bottom == self.horizontal
386 {
387 return self.outer_bottom_horizontal;
388 } else if top == self.outer_bottom_horizontal && bottom == self.horizontal {
389 return self.horizontal;
390 } else {
391 return self.intersect_for_position(pos);
392 }
393 }
394}
395
396#[derive(Clone, Debug)]
398pub struct Table {
399 pub rows: Vec<Row>,
400 pub style: TableStyle,
401 pub max_column_width: usize,
403 pub max_column_widths: HashMap<usize, usize>,
405 pub separate_rows: bool,
407 pub has_top_boarder: bool,
410 pub has_bottom_boarder: bool,
412}
413
414impl Table {
415 pub fn new() -> Table {
416 Self {
417 rows: Vec::new(),
418 style: TableStyle::extended(),
419 max_column_width: std::usize::MAX,
420 max_column_widths: HashMap::new(),
421 separate_rows: true,
422 has_top_boarder: true,
423 has_bottom_boarder: true,
424 }
425 }
426
427 pub fn builder() -> TableBuilder {
428 TableBuilder::new()
429 }
430
431 #[deprecated(since = "1.4.0", note = "Use builder instead")]
432 pub fn with_rows(rows: Vec<Row>) -> Table {
433 Self {
434 rows,
435 style: TableStyle::extended(),
436 max_column_width: std::usize::MAX,
437 max_column_widths: HashMap::new(),
438 separate_rows: true,
439 has_top_boarder: true,
440 has_bottom_boarder: true,
441 }
442 }
443
444 pub fn max_column_width(&mut self, max_column_width: usize) -> &mut Self {
445 self.max_column_width = max_column_width;
446 self
447 }
448
449 pub fn set_max_width_for_column(&mut self, column_index: usize, width: usize) {
451 self.max_column_widths.insert(column_index, width);
452 }
453
454 pub fn set_max_column_widths(&mut self, index_width_pairs: Vec<(usize, usize)>) {
456 for pair in index_width_pairs {
457 self.max_column_widths.insert(pair.0, pair.1);
458 }
459 }
460
461 pub fn add_row(&mut self, row: Row) {
463 self.rows.push(row);
464 }
465
466 pub fn render(&self) -> String {
469 let mut print_buffer = String::new();
470 let max_widths = self.calculate_max_column_widths();
471 let mut previous_separator = None;
472 if !self.rows.is_empty() {
473 for i in 0..self.rows.len() {
474 let row_pos = if i == 0 {
475 RowPosition::First
476 } else {
477 RowPosition::Mid
478 };
479
480 let separator = self.rows[i].gen_separator(
481 &max_widths,
482 &self.style,
483 row_pos,
484 previous_separator.clone(),
485 );
486
487 previous_separator = Some(separator.clone());
488
489 if self.rows[i].has_separator
490 && ((i == 0 && self.has_top_boarder) || i != 0 && self.separate_rows)
491 {
492 Table::buffer_line(&mut print_buffer, &separator);
493 }
494
495 Table::buffer_line(
496 &mut print_buffer,
497 &self.rows[i].format(&max_widths, &self.style),
498 );
499 }
500 if self.has_bottom_boarder {
501 let separator = self.rows.last().unwrap().gen_separator(
502 &max_widths,
503 &self.style,
504 RowPosition::Last,
505 None,
506 );
507 Table::buffer_line(&mut print_buffer, &separator);
508 }
509 }
510 return print_buffer;
511 }
512
513 fn calculate_max_column_widths(&self) -> Vec<usize> {
518 let mut num_columns = 0;
519
520 for row in &self.rows {
521 num_columns = max(row.num_columns(), num_columns);
522 }
523 let mut max_widths: Vec<usize> = vec![0; num_columns];
524 let mut min_widths: Vec<usize> = vec![0; num_columns];
525 for row in &self.rows {
526 let column_widths = row.split_column_widths();
527 for i in 0..column_widths.len() {
528 min_widths[i] = max(min_widths[i], column_widths[i].1);
529 let mut max_width = *self
530 .max_column_widths
531 .get(&i)
532 .unwrap_or(&self.max_column_width);
533 max_width = max(min_widths[i] as usize, max_width);
534 max_widths[i] = min(max_width, max(max_widths[i], column_widths[i].0 as usize));
535 }
536 }
537
538 for row in &self.rows {
541 let mut col_index = 0;
542 for cell in row.cells.iter() {
543 let mut total_col_width = 0;
544 for i in col_index..col_index + cell.col_span {
545 total_col_width += max_widths[i];
546 }
547 if cell.width() != total_col_width
548 && cell.alignment == Alignment::Center
549 && total_col_width as f32 % 2.0 <= 0.001
550 {
551 let mut max_col_width = self.max_column_width;
552 if let Some(specific_width) = self.max_column_widths.get(&col_index) {
553 max_col_width = *specific_width;
554 }
555
556 if max_widths[col_index] < max_col_width {
557 max_widths[col_index] += 1;
558 }
559 }
560 if cell.col_span > 1 {
561 col_index += cell.col_span - 1;
562 } else {
563 col_index += 1;
564 }
565 }
566 }
567
568 return max_widths;
569 }
570
571 fn buffer_line(buffer: &mut String, line: &str) {
573 buffer.push_str(format!("{}\n", line).as_str());
574 }
575}
576
577impl Default for Table {
578 fn default() -> Self {
579 return Table::new();
580 }
581}
582
583impl ToString for Table {
584 fn to_string(&self) -> String {
585 return self.render();
586 }
587}
588
589#[derive(Clone, Debug)]
591pub struct TableBuilder {
592 rows: Vec<Row>,
593 style: TableStyle,
594 max_column_width: usize,
595 max_column_widths: HashMap<usize, usize>,
596 separate_rows: bool,
597 has_top_boarder: bool,
598 has_bottom_boarder: bool,
599}
600
601impl TableBuilder {
602 pub fn new() -> TableBuilder {
603 TableBuilder {
604 rows: Vec::new(),
605 style: TableStyle::extended(),
606 max_column_width: std::usize::MAX,
607 max_column_widths: HashMap::new(),
608 separate_rows: true,
609 has_top_boarder: true,
610 has_bottom_boarder: true,
611 }
612 }
613
614 pub fn rows(&mut self, rows: Vec<Row>) -> &mut Self {
615 self.rows = rows;
616 self
617 }
618
619 pub fn style(&mut self, style: TableStyle) -> &mut Self {
620 self.style = style;
621 self
622 }
623
624 pub fn max_column_width(&mut self, max_column_width: usize) -> &mut Self {
626 self.max_column_width = max_column_width;
627 self
628 }
629
630 pub fn max_column_widths(&mut self, max_column_widths: HashMap<usize, usize>) -> &mut Self {
632 self.max_column_widths = max_column_widths;
633 self
634 }
635
636 pub fn separate_rows(&mut self, separate_rows: bool) -> &mut Self {
638 self.separate_rows = separate_rows;
639 self
640 }
641
642 pub fn has_top_boarder(&mut self, has_top_boarder: bool) -> &mut Self {
645 self.has_top_boarder = has_top_boarder;
646 self
647 }
648
649 pub fn has_bottom_boarder(&mut self, has_bottom_boarder: bool) -> &mut Self {
651 self.has_bottom_boarder = has_bottom_boarder;
652 self
653 }
654
655 pub fn build(&self) -> Table {
657 Table {
658 rows: self.rows.clone(),
659 style: self.style,
660 max_column_width: self.max_column_width,
661 max_column_widths: self.max_column_widths.clone(),
662 separate_rows: self.separate_rows,
663 has_top_boarder: self.has_top_boarder,
664 has_bottom_boarder: self.has_bottom_boarder,
665 }
666 }
667}
668
669impl Default for TableBuilder {
670 fn default() -> Self {
671 Self::new()
672 }
673}
674
675#[cfg(test)]
676mod test {
677 use crate::row::Row;
678 use crate::table_cell::{Alignment, TableCell};
679 use crate::Table;
680 use crate::TableBuilder;
681 use crate::TableStyle;
682 use pretty_assertions::assert_eq;
683
684 #[test]
685 fn correct_default_padding() {
686 let table = Table::builder()
687 .separate_rows(false)
688 .style(TableStyle::simple())
689 .rows(rows![
690 row![
691 TableCell::builder("A").alignment(Alignment::Center),
692 TableCell::builder("B").alignment(Alignment::Center),
693 ],
694 row![TableCell::builder("1"), TableCell::builder(1),],
695 row![TableCell::builder("2"), TableCell::builder(10),],
696 row![TableCell::builder("3"), TableCell::builder(100),],
697 ])
698 .build();
699
700 let expected = r"+---+-----+
701| A | B |
702| 1 | 1 |
703| 2 | 10 |
704| 3 | 100 |
705+---+-----+
706";
707 println!("{}", table.render());
708 assert_eq!(expected, table.render());
709 }
710
711 #[test]
712 fn uneven_center_alignment() {
713 let table = Table::builder()
714 .separate_rows(false)
715 .style(TableStyle::simple())
716 .rows(rows![
717 row![TableCell::builder("A").alignment(Alignment::Center),],
718 row![TableCell::builder(11),],
719 row![TableCell::builder(2),],
720 row![TableCell::builder(3),],
721 ])
722 .build();
723
724 let expected = r"+-----+
725| A |
726| 11 |
727| 2 |
728| 3 |
729+-----+
730";
731 println!("{}", table.render());
732 assert_eq!(expected, table.render());
733 }
734
735 #[test]
736 fn uneven_center_alignment_2() {
737 let table = Table::builder()
738 .separate_rows(false)
739 .style(TableStyle::simple())
740 .rows(rows![row![
741 TableCell::builder("A1").alignment(Alignment::Center),
742 TableCell::builder("B").alignment(Alignment::Center),
743 ],])
744 .build();
745 println!("{}", table.render());
746 let expected = r"+----+---+
747| A1 | B |
748+----+---+
749";
750 println!("{}", table.render());
751 assert_eq!(expected, table.render());
752 }
753
754 #[test]
755 fn simple_table_style() {
756 let mut builder = TableBuilder::new().style(TableStyle::simple()).to_owned();
757 add_data_to_test_table(&mut builder);
758 let table = builder.build();
759
760 let expected = r"+---------------------------------------------------------------------------------+
761| This is some centered text |
762+----------------------------------------+----------------------------------------+
763| This is left aligned text | This is right aligned text |
764+----------------------------------------+----------------------------------------+
765| This is left aligned text | This is right aligned text |
766+----------------------------------------+----------------------------------------+
767| This is some really really really really really really really really really tha |
768| t is going to wrap to the next line |
769+---------------------------------------------------------------------------------+
770";
771 println!("{}", table.render());
772 assert_eq!(expected, table.render());
773 }
774
775 #[test]
776 fn uneven_with_varying_col_span() {
777 let table = Table::builder()
778 .separate_rows(true)
779 .style(TableStyle::simple())
780 .rows(rows![
781 row![
782 TableCell::builder("A1111111").alignment(Alignment::Center),
783 TableCell::builder("B").alignment(Alignment::Center),
784 ],
785 row![1, "1"],
786 row![2, "10"],
787 row![
788 TableCell::builder(3)
789 .alignment(Alignment::Left)
790 .pad_content(false),
791 "100",
792 ],
793 row![TableCell::builder("S")
794 .alignment(Alignment::Center)
795 .col_span(2),],
796 ])
797 .build();
798
799 let expected = "+----------+-----+
800| A1111111 | B |
801+----------+-----+
802| 1 | 1 |
803+----------+-----+
804| 2 | 10 |
805+----------+-----+
806|\03\0 | 100 |
807+----------+-----+
808| S |
809+----------------+
810";
811 println!("{}", table.render());
812 assert_eq!(expected.trim(), table.render().trim());
813 }
814
815 #[test]
818 fn uneven_with_varying_col_span_2() {
819 let table = Table::builder()
820 .separate_rows(false)
821 .style(TableStyle::simple())
822 .rows(rows![
823 row![
824 TableCell::builder("A").alignment(Alignment::Center),
825 TableCell::builder("B").alignment(Alignment::Center),
826 ],
827 row![TableCell::builder(1), TableCell::builder(1),],
828 row![TableCell::builder(2), TableCell::builder(10),],
829 row![TableCell::builder(3), TableCell::builder(100),],
830 row![TableCell::builder("Spanner")
831 .col_span(2)
832 .alignment(Alignment::Center),],
833 ])
834 .build();
835
836 let expected = "+------+-----+
837| A | B |
838| 1 | 1 |
839| 2 | 10 |
840| 3 | 100 |
841| Spanner |
842+------------+
843";
844 println!("{}", table.render());
845 assert_eq!(expected.trim(), table.render().trim());
846 }
847
848 #[test]
849 fn extended_table_style_wrapped() {
850 let table = Table::builder()
851 .max_column_width(40)
852 .style(TableStyle::extended())
853 .max_column_widths(vec![(0, 1), (1, 1)].into_iter().collect())
854 .rows(rows![
855 row![
856 TableCell::builder("This is some centered text").alignment(Alignment::Center).col_span(2),
857 ],
858 row![
859 TableCell::builder("This is left aligned text"),
860 TableCell::builder("This is right aligned text").alignment(Alignment::Right),
861 ],
862 row![
863 TableCell::builder("This is left aligned text"),
864 TableCell::builder("This is right aligned text").alignment(Alignment::Right),
865 ],
866 row![
867 TableCell::builder("This is some really really really really really really really really really that is going to wrap to the next line\n1\n2").col_span(2),
868 ],
869 ])
870 .build();
871
872 let expected = r"╔═══════╗
873║ This ║
874║ is so ║
875║ me ce ║
876║ ntere ║
877║ d tex ║
878║ t ║
879╠═══╦═══╣
880║ T ║ T ║
881║ h ║ h ║
882║ i ║ i ║
883║ s ║ s ║
884║ ║ ║
885║ i ║ i ║
886║ s ║ s ║
887║ ║ ║
888║ l ║ r ║
889║ e ║ i ║
890║ f ║ g ║
891║ t ║ h ║
892║ ║ t ║
893║ a ║ ║
894║ l ║ a ║
895║ i ║ l ║
896║ g ║ i ║
897║ n ║ g ║
898║ e ║ n ║
899║ d ║ e ║
900║ ║ d ║
901║ t ║ ║
902║ e ║ t ║
903║ x ║ e ║
904║ t ║ x ║
905║ ║ t ║
906╠═══╬═══╣
907║ T ║ T ║
908║ h ║ h ║
909║ i ║ i ║
910║ s ║ s ║
911║ ║ ║
912║ i ║ i ║
913║ s ║ s ║
914║ ║ ║
915║ l ║ r ║
916║ e ║ i ║
917║ f ║ g ║
918║ t ║ h ║
919║ ║ t ║
920║ a ║ ║
921║ l ║ a ║
922║ i ║ l ║
923║ g ║ i ║
924║ n ║ g ║
925║ e ║ n ║
926║ d ║ e ║
927║ ║ d ║
928║ t ║ ║
929║ e ║ t ║
930║ x ║ e ║
931║ t ║ x ║
932║ ║ t ║
933╠═══╩═══╣
934║ This ║
935║ is so ║
936║ me re ║
937║ ally ║
938║ reall ║
939║ y rea ║
940║ lly r ║
941║ eally ║
942║ real ║
943║ ly re ║
944║ ally ║
945║ reall ║
946║ y rea ║
947║ lly r ║
948║ eally ║
949║ that ║
950║ is g ║
951║ oing ║
952║ to wr ║
953║ ap to ║
954║ the ║
955║ next ║
956║ line ║
957║ 1 ║
958║ 2 ║
959╚═══════╝
960";
961 println!("{}", table.render());
962 assert_eq!(expected, table.render());
963 }
964
965 #[test]
966 fn elegant_table_style() {
967 let mut builder = Table::builder().style(TableStyle::elegant()).to_owned();
968 add_data_to_test_table(&mut builder);
969 let table = builder.build();
970
971 let expected = r"╔─────────────────────────────────────────────────────────────────────────────────╗
972│ This is some centered text │
973╠────────────────────────────────────────╦────────────────────────────────────────╣
974│ This is left aligned text │ This is right aligned text │
975╠────────────────────────────────────────┼────────────────────────────────────────╣
976│ This is left aligned text │ This is right aligned text │
977╠────────────────────────────────────────╩────────────────────────────────────────╣
978│ This is some really really really really really really really really really tha │
979│ t is going to wrap to the next line │
980╚─────────────────────────────────────────────────────────────────────────────────╝
981";
982 println!("{}", table.render());
983 assert_eq!(expected, table.render());
984 }
985
986 #[test]
987 fn thin_table_style() {
988 let mut builder = Table::builder().style(TableStyle::thin()).to_owned();
989 add_data_to_test_table(&mut builder);
990 let table = builder.build();
991
992 let expected = r"┌─────────────────────────────────────────────────────────────────────────────────┐
993│ This is some centered text │
994├────────────────────────────────────────┬────────────────────────────────────────┤
995│ This is left aligned text │ This is right aligned text │
996├────────────────────────────────────────┼────────────────────────────────────────┤
997│ This is left aligned text │ This is right aligned text │
998├────────────────────────────────────────┴────────────────────────────────────────┤
999│ This is some really really really really really really really really really tha │
1000│ t is going to wrap to the next line │
1001└─────────────────────────────────────────────────────────────────────────────────┘
1002";
1003 println!("{}", table.render());
1004 assert_eq!(expected, table.render());
1005 }
1006
1007 #[test]
1008 fn rounded_table_style() {
1009 let mut builder = Table::builder().style(TableStyle::rounded()).to_owned();
1010 add_data_to_test_table(&mut builder);
1011 let table = builder.build();
1012
1013 let expected = r"╭─────────────────────────────────────────────────────────────────────────────────╮
1014│ This is some centered text │
1015├────────────────────────────────────────┬────────────────────────────────────────┤
1016│ This is left aligned text │ This is right aligned text │
1017├────────────────────────────────────────┼────────────────────────────────────────┤
1018│ This is left aligned text │ This is right aligned text │
1019├────────────────────────────────────────┴────────────────────────────────────────┤
1020│ This is some really really really really really really really really really tha │
1021│ t is going to wrap to the next line │
1022╰─────────────────────────────────────────────────────────────────────────────────╯
1023";
1024 println!("{}", table.render());
1025 assert_eq!(expected, table.render());
1026 }
1027
1028 #[test]
1029 fn complex_table() {
1030 let mut table = Table::builder()
1031 .rows(rows![
1032 row![
1033 TableCell::builder("Col*1*Span*2").col_span(2),
1034 "Col 2 Span 1",
1035 TableCell::builder("Col 3 Span 2").col_span(2),
1036 "Col 4 Span 1"
1037 ],
1038 row![
1039 "Col 1 Span 1",
1040 "Col 2 Span 1",
1041 "Col 3 Span 1",
1042 TableCell::builder("Col 4 Span 2").col_span(2)
1043 ],
1044 row!["fasdaff", "fff", "fff",],
1045 row![
1046 TableCell::builder("fasdff")
1047 .col_span(3)
1048 .alignment(Alignment::Right),
1049 TableCell::builder("fffdff").col_span(4),
1050 ],
1051 row!["fasdsaff", "fff", "f\nf\nf\nfff\nrrr\n\n\n",],
1052 row!["fasdsaff",],
1053 ])
1054 .build();
1055
1056 let s = table.render();
1057
1058 table.add_row(row![TableCell::builder(s)
1059 .col_span(3)
1060 .alignment(Alignment::Left)]);
1061
1062 let expected = r"╔═════════════════════════════════════════════════════════╦════════════════════════════╦════════════════╦══════════════╦═══╗
1063║ Col*1*Span*2 ║ Col 2 Span 1 ║ Col 3 Span 2 ║ Col 4 Span 1 ║ ║
1064╠════════════════════════════╦════════════════════════════╬════════════════════════════╬════════════════╬══════════════╬═══╣
1065║ Col 1 Span 1 ║ Col 2 Span 1 ║ Col 3 Span 1 ║ Col 4 Span 2 ║ ║ ║
1066╠════════════════════════════╬════════════════════════════╬════════════════════════════╬═══════╦════════╬══════════════╬═══╣
1067║ fasdaff ║ fff ║ fff ║ ║ ║ ║ ║
1068╠════════════════════════════╩════════════════════════════╩════════════════════════════╬═══════╩════════╩══════════════╩═══╣
1069║ fasdff ║ fffdff ║
1070╠════════════════════════════╦════════════════════════════╦════════════════════════════╬═══════╦════════╦══════════════╦═══╣
1071║ fasdsaff ║ fff ║ f ║ ║ ║ ║ ║
1072║ ║ ║ f ║ ║ ║ ║ ║
1073║ ║ ║ f ║ ║ ║ ║ ║
1074║ ║ ║ fff ║ ║ ║ ║ ║
1075║ ║ ║ rrr ║ ║ ║ ║ ║
1076║ ║ ║ ║ ║ ║ ║ ║
1077║ ║ ║ ║ ║ ║ ║ ║
1078║ ║ ║ ║ ║ ║ ║ ║
1079╠════════════════════════════╬════════════════════════════╬════════════════════════════╬═══════╬════════╬══════════════╬═══╣
1080║ fasdsaff ║ ║ ║ ║ ║ ║ ║
1081╠════════════════════════════╩════════════════════════════╩════════════════════════════╬═══════╬════════╬══════════════╬═══╣
1082║ ╔═════════════════════════════╦══════════════╦════════════════╦══════════════╦═══╗ ║ ║ ║ ║ ║
1083║ ║ Col*1*Span*2 ║ Col 2 Span 1 ║ Col 3 Span 2 ║ Col 4 Span 1 ║ ║ ║ ║ ║ ║ ║
1084║ ╠══════════════╦══════════════╬══════════════╬════════════════╬══════════════╬═══╣ ║ ║ ║ ║ ║
1085║ ║ Col 1 Span 1 ║ Col 2 Span 1 ║ Col 3 Span 1 ║ Col 4 Span 2 ║ ║ ║ ║ ║ ║ ║ ║
1086║ ╠══════════════╬══════════════╬══════════════╬═══════╦════════╬══════════════╬═══╣ ║ ║ ║ ║ ║
1087║ ║ fasdaff ║ fff ║ fff ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1088║ ╠══════════════╩══════════════╩══════════════╬═══════╩════════╩══════════════╩═══╣ ║ ║ ║ ║ ║
1089║ ║ fasdff ║ fffdff ║ ║ ║ ║ ║ ║
1090║ ╠══════════════╦══════════════╦══════════════╬═══════╦════════╦══════════════╦═══╣ ║ ║ ║ ║ ║
1091║ ║ fasdsaff ║ fff ║ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1092║ ║ ║ ║ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1093║ ║ ║ ║ f ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1094║ ║ ║ ║ fff ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1095║ ║ ║ ║ rrr ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1096║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1097║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1098║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1099║ ╠══════════════╬══════════════╬══════════════╬═══════╬════════╬══════════════╬═══╣ ║ ║ ║ ║ ║
1100║ ║ fasdsaff ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║ ║
1101║ ╚══════════════╩══════════════╩══════════════╩═══════╩════════╩══════════════╩═══╝ ║ ║ ║ ║ ║
1102║ ║ ║ ║ ║ ║
1103╚══════════════════════════════════════════════════════════════════════════════════════╩═══════╩════════╩══════════════╩═══╝
1104";
1105 println!("{}", table.render());
1106 assert_eq!(expected, table.render());
1107 }
1108
1109 #[test]
1110 fn no_top_boarder() {
1111 let mut builder = Table::builder()
1112 .style(TableStyle::simple())
1113 .has_top_boarder(false)
1114 .to_owned();
1115 add_data_to_test_table(&mut builder);
1116 let table = builder.build();
1117
1118 let expected = r"| This is some centered text |
1119+----------------------------------------+----------------------------------------+
1120| This is left aligned text | This is right aligned text |
1121+----------------------------------------+----------------------------------------+
1122| This is left aligned text | This is right aligned text |
1123+----------------------------------------+----------------------------------------+
1124| This is some really really really really really really really really really tha |
1125| t is going to wrap to the next line |
1126+---------------------------------------------------------------------------------+
1127";
1128 println!("{}", table.render());
1129 assert_eq!(expected, table.render());
1130 }
1131
1132 #[test]
1133 fn no_bottom_boarder() {
1134 let mut builder = Table::builder()
1135 .style(TableStyle::simple())
1136 .has_bottom_boarder(false)
1137 .to_owned();
1138 add_data_to_test_table(&mut builder);
1139 let table = builder.build();
1140
1141 let expected = r"+---------------------------------------------------------------------------------+
1142| This is some centered text |
1143+----------------------------------------+----------------------------------------+
1144| This is left aligned text | This is right aligned text |
1145+----------------------------------------+----------------------------------------+
1146| This is left aligned text | This is right aligned text |
1147+----------------------------------------+----------------------------------------+
1148| This is some really really really really really really really really really tha |
1149| t is going to wrap to the next line |
1150";
1151 println!("{}", table.render());
1152 assert_eq!(expected, table.render());
1153 }
1154
1155 #[test]
1156 fn no_separators() {
1157 let mut builder = Table::builder()
1158 .style(TableStyle::simple())
1159 .separate_rows(false)
1160 .to_owned();
1161
1162 add_data_to_test_table(&mut builder);
1163 let table = builder.build();
1164
1165 let expected = r"+---------------------------------------------------------------------------------+
1166| This is some centered text |
1167| This is left aligned text | This is right aligned text |
1168| This is left aligned text | This is right aligned text |
1169| This is some really really really really really really really really really tha |
1170| t is going to wrap to the next line |
1171+---------------------------------------------------------------------------------+
1172";
1173 println!("{}", table.render());
1174 assert_eq!(expected, table.render());
1175 }
1176
1177 #[test]
1178 fn some_rows_no_separators() {
1179 let mut builder = Table::builder().style(TableStyle::simple()).to_owned();
1180 add_data_to_test_table(&mut builder);
1181 let mut table = builder.build();
1182
1183 table.rows[2].has_separator = false;
1184
1185 let expected = r"+---------------------------------------------------------------------------------+
1186| This is some centered text |
1187+----------------------------------------+----------------------------------------+
1188| This is left aligned text | This is right aligned text |
1189| This is left aligned text | This is right aligned text |
1190+----------------------------------------+----------------------------------------+
1191| This is some really really really really really really really really really tha |
1192| t is going to wrap to the next line |
1193+---------------------------------------------------------------------------------+
1194";
1195 println!("{}", table.render());
1196 assert_eq!(expected, table.render());
1197 }
1198
1199 #[test]
1200 fn colored_data_works() {
1201 let table = Table::builder()
1202 .rows(rows![row![TableCell::builder("\u{1b}[31ma\u{1b}[0m")]])
1203 .build();
1204 let expected = "╔═══╗
1205║ \u{1b}[31ma\u{1b}[0m ║
1206╚═══╝
1207";
1208 println!("{}", table.render());
1209 assert_eq!(expected, table.render());
1210 }
1211
1212 fn add_data_to_test_table(builder: &mut TableBuilder) {
1213 builder
1214 .max_column_width(40)
1215 .rows(
1216 rows![
1217 row![
1218 TableCell::builder("This is some centered text")
1219 .col_span(2)
1220 .alignment(Alignment::Center)
1221 ],
1222 row![
1223 "This is left aligned text",
1224 TableCell::builder("This is right aligned text")
1225 .alignment(Alignment::Right)
1226 ],
1227 row![
1228 "This is left aligned text",
1229 TableCell::builder("This is right aligned text")
1230 .alignment(Alignment::Right)
1231 ],
1232 row![
1233 TableCell::builder("This is some really really really really really really really really really that is going to wrap to the next line")
1234 .col_span(2)
1235 ]
1236 ]
1237 );
1238 }
1239}