1//! This module contains a [`Rotate`] primitive which can be used in order to rotate [`Table`].
2//!
3//! It's also possible to transpose the table at the point of construction.
4//! See [`Builder::index`].
5//!
6//! # Example
7//!
8#![cfg_attr(feature = "std", doc = "```")]
9#![cfg_attr(not(feature = "std"), doc = "```ignore")]
10//! use tabled::{Table, settings::Rotate};
11//!
12//! let data = [[1, 2, 3], [4, 5, 6]];
13//!
14//! let table = Table::new(data).with(Rotate::Left).to_string();
15//!
16//! assert_eq!(
17//! table,
18//! concat!(
19//! "+---+---+---+\n",
20//! "| 2 | 3 | 6 |\n",
21//! "+---+---+---+\n",
22//! "| 1 | 2 | 5 |\n",
23//! "+---+---+---+\n",
24//! "| 0 | 1 | 4 |\n",
25//! "+---+---+---+",
26//! )
27//! );
28//! ```
29//!
30//! [`Table`]: crate::Table
31//! [`Builder::index`]: crate::builder::Builder::index
32
33// use core::cmp::max;
34use core::cmp::max;
35
36use crate::{
37 grid::records::{ExactRecords, Records, Resizable},
38 settings::TableOption,
39};
40
41/// Rotate can be used to rotate a table by 90 degrees.
42#[derive(Debug)]
43pub enum Rotate {
44 /// Rotate [`Table`] to the left.
45 ///
46 /// [`Table`]: crate::Table
47 Left,
48 /// Rotate [`Table`] to the right.
49 ///
50 /// [`Table`]: crate::Table
51 Right,
52 /// Rotate [`Table`] to the top.
53 ///
54 /// So the top becomes the bottom.
55 ///
56 /// [`Table`]: crate::Table
57 Top,
58 /// Rotate [`Table`] to the bottom.
59 ///
60 /// So the top becomes the bottom.
61 ///
62 /// [`Table`]: crate::Table
63 Bottom,
64}
65
66impl<R, D, C> TableOption<R, D, C> for Rotate
67where
68 R: Records + ExactRecords + Resizable,
69{
70 fn change(self, records: &mut R, _: &mut C, _: &mut D) {
71 let count_rows = records.count_rows();
72 let count_cols = records.count_columns();
73
74 match self {
75 Self::Left => {
76 let size = max(count_rows, count_cols);
77
78 {
79 for _ in count_rows..size {
80 records.push_row();
81 }
82
83 for _ in count_cols..size {
84 records.push_column();
85 }
86 }
87
88 for col in 0..size {
89 for row in col..size {
90 records.swap((col, row), (row, col));
91 }
92 }
93
94 for row in 0..count_cols / 2 {
95 records.swap_row(row, count_cols - row - 1);
96 }
97
98 {
99 for (shift, row) in (count_rows..size).enumerate() {
100 let row = row - shift;
101 records.remove_column(row);
102 }
103
104 for (shift, col) in (count_cols..size).enumerate() {
105 let col = col - shift;
106 records.remove_row(col);
107 }
108 }
109 }
110 Self::Right => {
111 let size = max(count_rows, count_cols);
112
113 {
114 for _ in count_rows..size {
115 records.push_row();
116 }
117
118 for _ in count_cols..size {
119 records.push_column();
120 }
121 }
122
123 for col in 0..size {
124 for row in col..size {
125 records.swap((col, row), (row, col));
126 }
127 }
128
129 for col in 0..count_rows / 2 {
130 records.swap_column(col, count_rows - col - 1);
131 }
132
133 {
134 for (shift, row) in (count_rows..size).enumerate() {
135 let row = row - shift;
136 records.remove_column(row);
137 }
138
139 for (shift, col) in (count_cols..size).enumerate() {
140 let col = col - shift;
141 records.remove_row(col);
142 }
143 }
144 }
145 Self::Bottom | Self::Top => {
146 for row in 0..count_rows / 2 {
147 for col in 0..count_cols {
148 let last_row = count_rows - row - 1;
149 records.swap((last_row, col), (row, col));
150 }
151 }
152 }
153 }
154 }
155}
156