use papergrid::records::{ExactRecords, PeekableRecords, Records};
use crate::{
grid::{
config::{AlignmentHorizontal, AlignmentVertical},
records::{RecordsMut, Resizable},
},
settings::{Alignment, Rotate, TableOption},
};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Layout {
orientation: HeadPosition,
footer: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
enum HeadPosition {
Top,
Bottom,
Left,
Right,
}
impl Layout {
pub fn new(stick: Alignment, footer: bool) -> Self {
let orientation = convert_orientation(stick);
Self {
footer,
orientation,
}
}
}
impl<R, C, D> TableOption<R, C, D> for Layout
where
R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
{
fn change(self, records: &mut R, _: &mut C, _: &mut D) {
move_head_if(records, self.orientation);
if self.footer {
copy_head(records, self.orientation);
}
}
}
const fn convert_orientation(position: Alignment) -> HeadPosition {
match (position.as_horizontal(), position.as_vertical()) {
(None, Some(AlignmentVertical::Top)) => HeadPosition::Top,
(None, Some(AlignmentVertical::Bottom)) => HeadPosition::Bottom,
(Some(AlignmentHorizontal::Left), None) => HeadPosition::Left,
(Some(AlignmentHorizontal::Right), None) => HeadPosition::Right,
(None, Some(AlignmentVertical::Center)) => HeadPosition::Top,
(Some(AlignmentHorizontal::Center), None) => HeadPosition::Top,
(None, None) | (Some(_), Some(_)) => HeadPosition::Top,
}
}
fn copy_head<R>(records: &mut R, orientation: HeadPosition)
where
R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
{
let head = collect_head_by(records, orientation);
match orientation {
HeadPosition::Top => cp_row(records, head, records.count_rows()),
HeadPosition::Bottom => cp_row(records, head, 0),
HeadPosition::Left => cp_column(records, head, records.count_columns()),
HeadPosition::Right => cp_column(records, head, 0),
}
}
fn collect_head_by<R>(records: &mut R, orientation: HeadPosition) -> Vec<String>
where
R: Records + PeekableRecords + ExactRecords,
{
match orientation {
HeadPosition::Top => collect_head(records, 0),
HeadPosition::Bottom => collect_head(records, records.count_rows() - 1),
HeadPosition::Left => collect_head_vertical(records, 0),
HeadPosition::Right => collect_head_vertical(records, records.count_columns() - 1),
}
}
fn cp_row<R>(records: &mut R, row: Vec<String>, pos: usize)
where
R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
{
records.insert_row(pos);
for (col, text) in row.into_iter().enumerate() {
records.set((pos, col).into(), text);
}
}
fn cp_column<R>(records: &mut R, column: Vec<String>, pos: usize)
where
R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
{
records.insert_column(pos);
for (row, text) in column.into_iter().enumerate() {
records.set((row, pos).into(), text);
}
}
fn move_head_if<R>(records: &mut R, orientation: HeadPosition)
where
R: Records + Resizable + ExactRecords + PeekableRecords + RecordsMut<String>,
{
match orientation {
HeadPosition::Top => {}
HeadPosition::Bottom => {
let head = collect_head(records, 0);
push_row(records, head);
records.remove_row(0);
}
HeadPosition::Left => {
Rotate::Left.change(records, &mut (), &mut ());
Rotate::Bottom.change(records, &mut (), &mut ());
}
HeadPosition::Right => {
Rotate::Right.change(records, &mut (), &mut ());
}
}
}
fn collect_head<R>(records: &mut R, row: usize) -> Vec<String>
where
R: Records + PeekableRecords,
{
(0..records.count_columns())
.map(|column| records.get_text((row, column).into()))
.map(ToString::to_string)
.collect()
}
fn collect_head_vertical<R>(records: &mut R, column: usize) -> Vec<String>
where
R: Records + PeekableRecords + ExactRecords,
{
(0..records.count_rows())
.map(|row| records.get_text((row, column).into()))
.map(ToString::to_string)
.collect()
}
fn push_row<R>(records: &mut R, row: Vec<String>)
where
R: Records + ExactRecords + Resizable + RecordsMut<String>,
{
records.push_row();
let last_row = records.count_rows() - 1;
for (col, text) in row.into_iter().enumerate() {
records.set((last_row, col).into(), text);
}
}