tabled/features/
rotate.rs

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