use std::borrow::Cow;
use std::cmp;
use wcwidth::{char_width, str_width};
use regex::Regex;
use std::borrow::Borrow;
#[derive(Clone, Copy)]
pub enum Alignment {
Left,
Right,
Center,
}
pub struct TableCell<'data> {
pub data: Cow<'data, str>,
pub col_span: usize,
pub alignment: Alignment,
pub pad_content: bool,
}
impl<'data> TableCell<'data> {
pub fn new<T>(data: T) -> TableCell<'data>
where
T: ToString
{
return TableCell {
data: data.to_string().into(),
col_span: 1,
alignment: Alignment::Left,
pad_content: true,
};
}
pub fn new_with_col_span<T>(data: T, col_span: usize) -> TableCell<'data>
where
T: ToString
{
return TableCell {
data: data.to_string().into(),
alignment: Alignment::Left,
pad_content: true,
col_span,
};
}
pub fn new_with_alignment<T>(data: T, col_span: usize, alignment: Alignment) -> TableCell<'data>
where
T: ToString,
{
return TableCell {
data: data.to_string().into(),
pad_content: true,
col_span,
alignment,
};
}
pub fn new_with_alignment_and_padding<T>(
data: T,
col_span: usize,
alignment: Alignment,
pad_content: bool,
) -> TableCell<'data>
where
T: ToString,
{
return TableCell {
data: data.to_string().into(),
col_span,
alignment,
pad_content,
};
}
pub fn width(&self) -> usize {
let wrapped = self.wrapped_content(std::usize::MAX);
let mut max = 0;
for s in wrapped {
let str_width = string_width(&s);
max = cmp::max(max, str_width);
}
return max +
if self.pad_content{
2 * char_width(' ').unwrap_or(1) as usize
}else{
0
};
}
pub fn split_width(&self) -> f32 {
let res = self.width() as f32 / self.col_span as f32;
return res;
}
pub fn min_width(&self) -> usize {
let mut max_char_width:usize = 0;
for c in self.data.chars(){
max_char_width = cmp::max(max_char_width, char_width(c).unwrap_or(1) as usize);
}
return if self.pad_content{
max_char_width + char_width(' ').unwrap_or(1) as usize * 2
}else{
max_char_width
};
}
pub fn wrapped_content(&self, width: usize) -> Vec<String> {
let pad_char = if self.pad_content {' '} else {'\0'};
let mut res: Vec<String> = Vec::new();
let mut buf = String::new();
buf.push(pad_char);
for c in self.data.chars().enumerate() {
if string_width(&buf) as usize
>= width - char_width(pad_char).unwrap_or(1) as usize
|| c.1 == '\n'
{
buf.push(pad_char);
res.push(buf);
buf = String::new();
buf.push(pad_char);
if c.1 == '\n' {
continue;
}
}
buf.push(c.1);
}
buf.push(pad_char);
res.push(buf);
return res;
}
}
impl<'data, T> From<T> for TableCell<'data> where T: ToString{
fn from(other:T)-> Self{
return TableCell::new(other);
}
}
lazy_static! {
static ref STRIP_ANSI_RE: Regex = Regex::new(
r"[\x1b\x9b][\[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-PRZcf-nqry=><]").unwrap();
}
pub fn string_width(string:&str) -> usize{
let stripped = STRIP_ANSI_RE.replace_all(string, "");
return str_width(stripped.borrow()).unwrap_or_default();
}