1use papergrid::records::{ExactRecords, PeekableRecords, Records};
4
5use crate::{
6 grid::{
7 config::{AlignmentHorizontal, AlignmentVertical},
8 records::{RecordsMut, Resizable},
9 },
10 settings::{Alignment, Rotate, TableOption},
11};
12
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
15pub struct Layout {
16 orientation: HeadPosition,
17 footer: bool,
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
21enum HeadPosition {
22 Top,
23 Bottom,
24 Left,
25 Right,
26}
27
28impl Layout {
29 pub fn new(stick: Alignment, footer: bool) -> Self {
31 let orientation = convert_orientation(stick);
32
33 Self {
34 footer,
35 orientation,
36 }
37 }
38}
39
40impl<R, C, D> TableOption<R, C, D> for Layout
41where
42 R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
43{
44 fn change(self, records: &mut R, _: &mut C, _: &mut D) {
45 move_head_if(records, self.orientation);
46
47 if self.footer {
48 copy_head(records, self.orientation);
49 }
50 }
51}
52
53const fn convert_orientation(position: Alignment) -> HeadPosition {
54 match (position.as_horizontal(), position.as_vertical()) {
55 (None, Some(AlignmentVertical::Top)) => HeadPosition::Top,
56 (None, Some(AlignmentVertical::Bottom)) => HeadPosition::Bottom,
57 (Some(AlignmentHorizontal::Left), None) => HeadPosition::Left,
58 (Some(AlignmentHorizontal::Right), None) => HeadPosition::Right,
59 (None, Some(AlignmentVertical::Center)) => HeadPosition::Top,
60 (Some(AlignmentHorizontal::Center), None) => HeadPosition::Top,
61 (None, None) | (Some(_), Some(_)) => HeadPosition::Top,
62 }
63}
64
65fn copy_head<R>(records: &mut R, orientation: HeadPosition)
66where
67 R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
68{
69 let head = collect_head_by(records, orientation);
70 match orientation {
71 HeadPosition::Top => cp_row(records, head, records.count_rows()),
72 HeadPosition::Bottom => cp_row(records, head, 0),
73 HeadPosition::Left => cp_column(records, head, records.count_columns()),
74 HeadPosition::Right => cp_column(records, head, 0),
75 }
76}
77
78fn collect_head_by<R>(records: &mut R, orientation: HeadPosition) -> Vec<String>
79where
80 R: Records + PeekableRecords + ExactRecords,
81{
82 match orientation {
83 HeadPosition::Top => collect_head(records, 0),
84 HeadPosition::Bottom => collect_head(records, records.count_rows() - 1),
85 HeadPosition::Left => collect_head_vertical(records, 0),
86 HeadPosition::Right => collect_head_vertical(records, records.count_columns() - 1),
87 }
88}
89
90fn cp_row<R>(records: &mut R, row: Vec<String>, pos: usize)
91where
92 R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
93{
94 records.insert_row(pos);
95
96 for (col, text) in row.into_iter().enumerate() {
97 records.set((pos, col).into(), text);
98 }
99}
100
101fn cp_column<R>(records: &mut R, column: Vec<String>, pos: usize)
102where
103 R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
104{
105 records.insert_column(pos);
106
107 for (row, text) in column.into_iter().enumerate() {
108 records.set((row, pos).into(), text);
109 }
110}
111
112fn move_head_if<R>(records: &mut R, orientation: HeadPosition)
113where
114 R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
115{
116 match orientation {
117 HeadPosition::Top => {}
118 HeadPosition::Bottom => {
119 let head = collect_head(records, 0);
120 push_row(records, head);
121 records.remove_row(0);
122 }
123 HeadPosition::Left => {
124 Rotate::Left.change(records, &mut (), &mut ());
125 Rotate::Bottom.change(records, &mut (), &mut ());
126 }
127 HeadPosition::Right => {
128 Rotate::Right.change(records, &mut (), &mut ());
129 }
130 }
131}
132
133fn collect_head<R>(records: &mut R, row: usize) -> Vec<String>
134where
135 R: Records + PeekableRecords,
136{
137 (0..records.count_columns())
138 .map(|column| records.get_text((row, column).into()))
139 .map(ToString::to_string)
140 .collect()
141}
142
143fn collect_head_vertical<R>(records: &mut R, column: usize) -> Vec<String>
144where
145 R: Records + PeekableRecords + ExactRecords,
146{
147 (0..records.count_rows())
148 .map(|row| records.get_text((row, column).into()))
149 .map(ToString::to_string)
150 .collect()
151}
152
153fn push_row<R>(records: &mut R, row: Vec<String>)
154where
155 R: Records + ExactRecords + Resizable + RecordsMut<String>,
156{
157 records.push_row();
158
159 let last_row = records.count_rows() - 1;
160
161 for (col, text) in row.into_iter().enumerate() {
162 records.set((last_row, col).into(), text);
163 }
164}