use std::borrow::Cow;
use crate::{
grid::config::{Entity, Position},
grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
settings::{CellOption, TableOption},
};
#[cfg_attr(feature = "assert", doc = "```")]
#[cfg_attr(not(feature = "assert"), doc = "```ignore")]
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Charset {
clean: bool,
tab_size: Option<usize>,
}
impl Charset {
pub fn new() -> Self {
Self {
clean: false,
tab_size: None,
}
}
#[cfg_attr(feature = "assert", doc = "```")]
#[cfg_attr(not(feature = "assert"), doc = "```ignore")]
pub fn clean(mut self) -> Self {
self.clean = true;
self
}
#[cfg_attr(feature = "assert", doc = "```")]
#[cfg_attr(not(feature = "assert"), doc = "```ignore")]
pub fn tab_size(mut self, size: usize) -> Self {
self.tab_size = Some(size);
self
}
pub fn charset_clean(s: &str) -> Cow<'_, str> {
Cow::Owned(charset_clean(s, 4))
}
}
impl<R, D, C> TableOption<R, C, D> for Charset
where
R: Records + ExactRecords + RecordsMut<String> + PeekableRecords,
{
fn change(self, records: &mut R, _: &mut C, _: &mut D) {
if !self.clean && self.tab_size.is_none() {
return;
}
for row in 0..records.count_rows() {
for col in 0..records.count_columns() {
let pos = Position::new(row, col);
let text = records.get_text(pos);
let text = match self {
Charset {
clean: true,
tab_size: None,
} => charset_clean(text, 0),
Charset {
clean: true,
tab_size: Some(tab),
} => charset_clean(text, tab),
Charset {
clean: false,
tab_size: Some(tab),
} => charset_tab(text, tab),
Charset {
clean: false,
tab_size: None,
} => unreachable!(),
};
records.set(pos, text);
}
}
}
}
impl<R, C> CellOption<R, C> for Charset
where
R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
{
fn change(self, records: &mut R, _: &mut C, entity: Entity) {
if !self.clean && self.tab_size.is_none() {
return;
}
let count_rows = records.count_rows();
let count_cols = records.count_columns();
for pos in entity.iter(count_rows, count_cols) {
let text = records.get_text(pos);
let text = match self {
Charset {
clean: true,
tab_size: None,
} => charset_clean(text, 0),
Charset {
clean: true,
tab_size: Some(tab),
} => charset_clean(text, tab),
Charset {
clean: false,
tab_size: Some(tab),
} => charset_tab(text, tab),
Charset {
clean: false,
tab_size: None,
} => unreachable!(),
};
records.set(pos, text);
}
}
}
fn charset_clean(text: &str, tab_size: usize) -> String {
let mut out = String::with_capacity(text.len());
for c in text.chars() {
match c {
'\t' => {
for _ in 0..tab_size {
out.push(' ');
}
}
'\n' => out.push(c),
c if c < ' ' => {}
_ => out.push(c),
}
}
out
}
fn charset_tab(text: &str, tab_size: usize) -> String {
let mut out = String::with_capacity(text.len());
for c in text.chars() {
match c {
'\t' => {
for _ in 0..tab_size {
out.push(' ');
}
}
_ => out.push(c),
}
}
out
}