use crate::{
grid::config::{ColoredConfig, Entity},
grid::dimension::CompleteDimensionVecRecords,
grid::records::{ExactRecords, IntoRecords, PeekableRecords, Records, RecordsMut},
grid::util::string::{get_lines, get_text_width},
settings::{
measurement::Measurement,
peaker::{Peaker, PriorityNone},
CellOption, TableOption, Width,
},
};
use super::util::get_table_widths_with_total;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct MinWidth<W = usize, P = PriorityNone> {
width: W,
fill: char,
priority: P,
}
impl<W> MinWidth<W>
where
W: Measurement<Width>,
{
pub const fn new(width: W) -> Self {
Self {
width,
fill: ' ',
priority: PriorityNone::new(),
}
}
}
impl<W, P> MinWidth<W, P> {
pub fn fill_with(mut self, c: char) -> Self {
self.fill = c;
self
}
pub fn priority<PP: Peaker>(self, peacker: PP) -> MinWidth<W, PP> {
MinWidth {
fill: self.fill,
width: self.width,
priority: peacker,
}
}
}
impl<W, R> CellOption<R, ColoredConfig> for MinWidth<W>
where
W: Measurement<Width>,
R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
for<'a> &'a R: Records,
for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
{
fn change(self, records: &mut R, cfg: &mut ColoredConfig, entity: Entity) {
let width = self.width.measure(&*records, cfg);
let count_rows = records.count_rows();
let count_columns = records.count_columns();
for pos in entity.iter(count_rows, count_columns) {
let is_valid_pos = pos.0 < count_rows && pos.1 < count_columns;
if !is_valid_pos {
continue;
}
let cell = records.get_text(pos);
let cell_width = get_text_width(cell);
if cell_width >= width {
continue;
}
let content = increase_width(cell, width, self.fill);
records.set(pos, content);
}
}
fn hint_change(&self) -> Option<Entity> {
Some(Entity::Column(0))
}
}
impl<W, P, R> TableOption<R, ColoredConfig, CompleteDimensionVecRecords<'_>> for MinWidth<W, P>
where
W: Measurement<Width>,
P: Peaker,
R: Records + ExactRecords + PeekableRecords,
for<'a> &'a R: Records,
for<'a> <<&'a R as Records>::Iter as IntoRecords>::Cell: AsRef<str>,
{
fn change(
self,
records: &mut R,
cfg: &mut ColoredConfig,
dims: &mut CompleteDimensionVecRecords<'_>,
) {
if records.count_rows() == 0 || records.count_columns() == 0 {
return;
}
let necessary_width = self.width.measure(&*records, cfg);
let (widths, total_width) = get_table_widths_with_total(&*records, cfg);
if total_width >= necessary_width {
return;
}
let widths = get_increase_list(widths, necessary_width, total_width, self.priority);
dims.set_widths(widths);
}
fn hint_change(&self) -> Option<Entity> {
Some(Entity::Column(0))
}
}
fn get_increase_list<F>(
mut widths: Vec<usize>,
need: usize,
mut current: usize,
mut peaker: F,
) -> Vec<usize>
where
F: Peaker,
{
while need != current {
let col = match peaker.peak(&[], &widths) {
Some(col) => col,
None => break,
};
widths[col] += 1;
current += 1;
}
widths
}
fn increase_width(s: &str, width: usize, fill_with: char) -> String {
use crate::grid::util::string::get_line_width;
use std::{borrow::Cow, iter::repeat};
get_lines(s)
.map(|line| {
let length = get_line_width(&line);
if length < width {
let mut line = line.into_owned();
let remain = width - length;
line.extend(repeat(fill_with).take(remain));
Cow::Owned(line)
} else {
line
}
})
.collect::<Vec<_>>()
.join("\n")
}