tabled/settings/rotate/
mod.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#![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 = records.count_rows();
84    let count_cols = records.count_columns();
85
86    for row in 0..count_rows / 2 {
87        for col in 0..count_cols {
88            let last_row = count_rows - row - 1;
89            records.swap((last_row, col).into(), (row, col).into());
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).into(), (row, col).into());
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).into(), (row, col).into());
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}