pub mod tbl;
use terminal_size::{terminal_size, Height, Width};
pub use yansi::{Color, Painted};
pub enum Types {
Paragraph(String)
}
#[derive(Copy, Clone, Debug)]
pub enum Align {
Left,
Center,
Right
}
pub struct Column {
pub title: String,
pub title_align: Align,
pub align: Align
}
impl Column {
#[allow(clippy::needless_pass_by_value)]
pub fn new(heading: impl ToString) -> Self {
Self {
title: heading.to_string(),
title_align: Align::Left,
align: Align::Left
}
}
#[must_use]
pub const fn title_align(mut self, align: Align) -> Self {
self.title_align = align;
self
}
#[must_use]
pub const fn align(mut self, align: Align) -> Self {
self.align = align;
self
}
}
fn wordify(s: &str) -> Vec<String> {
let mut words = Vec::new();
let splt = s.split_whitespace();
for w in splt {
words.push(w.to_string());
}
words
}
pub struct PPrint {
indent: u16,
hang: i16,
maxwidth: u16
}
impl Default for PPrint {
fn default() -> Self {
Self::new()
}
}
impl PPrint {
#[must_use]
pub fn new() -> Self {
let size = terminal_size();
let maxwidth = if let Some((Width(w), Height(_h))) = size {
w
} else {
80
};
Self {
indent: 0,
hang: 0,
maxwidth
}
}
pub const fn set_indent(&mut self, indent: u16) -> &mut Self {
self.indent = indent;
self
}
pub const fn set_hang(&mut self, hang: i16) -> &mut Self {
self.hang = hang;
self
}
#[allow(clippy::cast_possible_truncation)]
#[allow(clippy::cast_possible_wrap)]
#[allow(clippy::cast_sign_loss)]
pub fn print_words<I, S>(&self, out: &mut dyn std::io::Write, words: I)
where
I: IntoIterator<Item = S>,
S: AsRef<str>
{
let mut firstline = true;
let mut newline = true;
let mut space: u16 = 0;
let mut col: u16 = 0;
for w in words {
let w = w.as_ref();
if col + space + w.len() as u16 > self.maxwidth {
out.write_all(b"\n").unwrap();
newline = true;
}
if newline {
let mut indent: i16 = 0;
indent += self.indent as i16;
if firstline {
indent += self.hang;
}
out
.write_all(" ".repeat(indent as usize).as_bytes())
.unwrap();
col = indent as u16;
newline = false;
space = 0;
firstline = false;
}
out
.write_all(" ".repeat(space as usize).as_bytes())
.unwrap();
col += space;
out.write_all(w.as_bytes()).unwrap();
col += w.len() as u16;
let ch = w.chars().last().unwrap();
match ch {
'.' | '?' | '!' => {
space = 2;
}
_ => {
space = 1;
}
}
}
out.write_all(b"\n").unwrap();
}
pub fn print_p(&self, out: &mut dyn std::io::Write, para: &str) {
let words = wordify(para);
self.print_words(out, &words);
}
pub fn print_plist<I, S>(&self, out: &mut dyn std::io::Write, parit: I)
where
I: IntoIterator<Item = S>,
S: AsRef<str>
{
for p in parit {
self.print_p(out, p.as_ref());
}
}
}
#[allow(clippy::similar_names)]
pub fn print_table(cols: &[Column], body: &Vec<Vec<String>>) {
let mut colw: Vec<usize> = cols.iter().map(|col| col.title.len()).collect();
for row in body {
for (i, col) in row.iter().enumerate() {
if i == colw.len() {
break;
}
if col.len() > colw[i] {
colw[i] = col.len();
}
}
}
let mut fields = Vec::new();
for (i, col) in cols.iter().enumerate() {
match col.title_align {
Align::Left => {
fields.push(format!("{1:<0$}", colw[i], col.title));
}
Align::Center => {
fields.push(format!("{1:^0$}", colw[i], col.title));
}
Align::Right => {
fields.push(format!("{1:>0$}", colw[i], col.title));
}
}
}
println!("{}", fields.join(" "));
fields.clear();
for w in &colw {
fields.push("~".repeat(*w).clone());
}
println!("{}", fields.join(" "));
for row in body {
fields.clear();
for (i, cell) in row.iter().enumerate() {
match cols[i].align {
Align::Left => {
fields.push(format!("{1:<0$}", colw[i], cell));
}
Align::Center => {
fields.push(format!("{1:^0$}", colw[i], cell));
}
Align::Right => {
fields.push(format!("{1:>0$}", colw[i], cell));
}
}
}
println!("{}", fields.join(" "));
}
}