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, C, D> for Rotate
67where
68 R: Records + ExactRecords + Resizable,
69{
70 fn change(self, records: &mut R, _: &mut C, _: &mut D) {
71 match self {
72 Self::Left => rotate_left(records),
73 Self::Right => rotate_right(records),
74 Self::Bottom | Self::Top => rotate_horizontal(records),
75 }
76 }
77}
78
79fn rotate_horizontal<R>(records: &mut R)
80where
81 R: Records + ExactRecords + Resizable,
82{
83 let count_rows: usize = records.count_rows();
84 let count_cols: usize = records.count_columns();
85
86 for row: usize in 0..count_rows / 2 {
87 for col: usize in 0..count_cols {
88 let last_row: usize = count_rows - row - 1;
89 records.swap((last_row, col), (row, col));
90 }
91 }
92}
93
94fn rotate_left<R>(records: &mut R)
95where
96 R: Records + ExactRecords + Resizable,
97{
98 let count_rows = records.count_rows();
99 let count_cols = records.count_columns();
100 let size = max(count_rows, count_cols);
101
102 {
103 for _ in count_rows..size {
104 records.push_row();
105 }
106
107 for _ in count_cols..size {
108 records.push_column();
109 }
110 }
111
112 for col in 0..size {
113 for row in col..size {
114 records.swap((col, row), (row, col));
115 }
116 }
117
118 for row in 0..count_cols / 2 {
119 records.swap_row(row, count_cols - row - 1);
120 }
121
122 {
123 for (shift, row) in (count_rows..size).enumerate() {
124 let row = row - shift;
125 records.remove_column(row);
126 }
127
128 for (shift, col) in (count_cols..size).enumerate() {
129 let col = col - shift;
130 records.remove_row(col);
131 }
132 }
133}
134
135fn rotate_right<R>(records: &mut R)
136where
137 R: Records + ExactRecords + Resizable,
138{
139 let count_rows = records.count_rows();
140 let count_cols = records.count_columns();
141 let size = max(count_rows, count_cols);
142
143 {
144 for _ in count_rows..size {
145 records.push_row();
146 }
147
148 for _ in count_cols..size {
149 records.push_column();
150 }
151 }
152
153 for col in 0..size {
154 for row in col..size {
155 records.swap((col, row), (row, col));
156 }
157 }
158
159 for col in 0..count_rows / 2 {
160 records.swap_column(col, count_rows - col - 1);
161 }
162
163 {
164 for (shift, row) in (count_rows..size).enumerate() {
165 let row = row - shift;
166 records.remove_column(row);
167 }
168
169 for (shift, col) in (count_cols..size).enumerate() {
170 let col = col - shift;
171 records.remove_row(col);
172 }
173 }
174}
175