pub struct BoxDrawer {
tl: char,
tr: char,
bl: char,
br: char,
h: char,
v: char,
t_left: char,
t_right: char,
t_top: char,
t_bottom: char,
cross: char,
}
impl Default for BoxDrawer {
fn default() -> Self {
BoxDrawer {
tl: '┌',
tr: '┐',
bl: '└',
br: '┘',
h: '─',
v: '│',
t_left: '├',
t_right: '┤',
t_top: '┬',
t_bottom: '┴',
cross: '┼',
}
}
}
impl BoxDrawer {
pub fn double() -> Self {
BoxDrawer {
tl: '╔',
tr: '╗',
bl: '╚',
br: '╝',
h: '═',
v: '║',
t_left: '╠',
t_right: '╣',
t_top: '╦',
t_bottom: '╩',
cross: '╬',
}
}
pub fn horizontal(&self, width: usize) -> String {
self.h.to_string().repeat(width)
}
pub fn draw_box(&self, content: &[&str], width: usize) -> String {
let mut lines = Vec::new();
lines.push(format!("{}{}{}", self.tl, self.horizontal(width), self.tr));
for line in content {
let padding = width.saturating_sub(line.chars().count());
lines.push(format!(
"{} {}{} {}",
self.v,
line,
" ".repeat(padding.saturating_sub(2)),
self.v
));
}
lines.push(format!("{}{}{}", self.bl, self.horizontal(width), self.br));
lines.join("\n")
}
pub fn section_header(&self, title: &str, width: usize) -> String {
let dash_count = width.saturating_sub(title.len() + 2);
format!(
"{} {} {}",
self.horizontal(2),
title,
self.horizontal(dash_count)
)
}
}
pub struct TableRenderer {
widths: Vec<usize>,
alignments: Vec<bool>,
#[allow(dead_code)]
use_box_chars: bool,
}
impl TableRenderer {
pub fn new(widths: Vec<usize>) -> Self {
let alignments = vec![false; widths.len()];
TableRenderer {
widths,
alignments,
use_box_chars: true,
}
}
pub fn with_alignments(mut self, alignments: Vec<bool>) -> Self {
self.alignments = alignments;
self
}
pub fn render_header(&self, headers: &[&str]) -> String {
let box_drawer = BoxDrawer::default();
let mut lines = Vec::new();
let top: String = self
.widths
.iter()
.map(|&w| box_drawer.horizontal(w + 2))
.collect::<Vec<_>>()
.join(&box_drawer.t_top.to_string());
lines.push(format!("{}{}{}", box_drawer.tl, top, box_drawer.tr));
let header_cells: String = headers
.iter()
.zip(&self.widths)
.map(|(h, &w)| {
let truncated: String = h.chars().take(w).collect();
let padding = w.saturating_sub(truncated.chars().count());
format!(" {}{} ", truncated, " ".repeat(padding))
})
.collect::<Vec<_>>()
.join(&box_drawer.v.to_string());
lines.push(format!("{}{}{}", box_drawer.v, header_cells, box_drawer.v));
let sep: String = self
.widths
.iter()
.map(|&w| box_drawer.horizontal(w + 2))
.collect::<Vec<_>>()
.join(&box_drawer.cross.to_string());
lines.push(format!(
"{}{}{}",
box_drawer.t_left, sep, box_drawer.t_right
));
lines.join("\n")
}
pub fn render_row(&self, cells: &[&str]) -> String {
let box_drawer = BoxDrawer::default();
let cell_strings: String = cells
.iter()
.zip(&self.widths)
.zip(&self.alignments)
.map(|((c, &w), &right_align)| {
let truncated: String = c.chars().take(w).collect();
let padding = w.saturating_sub(truncated.chars().count());
if right_align {
format!(" {}{} ", " ".repeat(padding), truncated)
} else {
format!(" {}{} ", truncated, " ".repeat(padding))
}
})
.collect::<Vec<_>>()
.join(&box_drawer.v.to_string());
format!("{}{}{}", box_drawer.v, cell_strings, box_drawer.v)
}
pub fn render_footer(&self) -> String {
let box_drawer = BoxDrawer::default();
let bottom: String = self
.widths
.iter()
.map(|&w| box_drawer.horizontal(w + 2))
.collect::<Vec<_>>()
.join(&box_drawer.t_bottom.to_string());
format!("{}{}{}", box_drawer.bl, bottom, box_drawer.br)
}
}
pub struct TreeRenderer;
impl TreeRenderer {
pub fn branch(text: &str) -> String {
format!("├── {}", text)
}
pub fn last_branch(text: &str) -> String {
format!("└── {}", text)
}
pub fn continuation(text: &str) -> String {
format!("│ {}", text)
}
pub fn empty_continuation(text: &str) -> String {
format!(" {}", text)
}
}