use crate::config::BoxChars;
use unicode_width::UnicodeWidthStr;
pub struct TableRenderer {
label_width: usize,
data_width: usize,
chars: BoxChars,
total_width: usize,
}
impl TableRenderer {
pub fn new(label_width: usize, data_width: usize, chars: BoxChars) -> Self {
let total_width = label_width + data_width + 7;
Self {
label_width,
data_width,
chars,
total_width,
}
}
pub fn render_top_header(&self) -> String {
let mut line = String::new();
line.push(self.chars.top_left);
for _ in 0..(self.total_width - 2) {
line.push(self.chars.t_down);
}
line.push(self.chars.top_right);
line.push('\n');
line
}
pub fn render_header_bottom(&self) -> String {
let mut line = String::new();
line.push(self.chars.t_right);
for _ in 0..(self.total_width - 2) {
line.push(self.chars.t_up);
}
line.push(self.chars.t_left);
line.push('\n');
line
}
pub fn render_centered(&self, text: &str) -> String {
let text_width = text.width();
let inner_width = self.total_width - 2;
let padding = if text_width >= inner_width {
0
} else {
(inner_width - text_width) / 2
};
let extra = if text_width >= inner_width {
0
} else {
(inner_width - text_width) % 2
};
let mut line = String::new();
line.push(self.chars.vertical);
line.push_str(&" ".repeat(padding));
if text_width > inner_width {
let mut truncated = String::new();
let mut w = 0;
for c in text.chars() {
let cw = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
if w + cw > inner_width.saturating_sub(3) {
break;
}
truncated.push(c);
w += cw;
}
line.push_str(&truncated);
line.push_str("...");
} else {
line.push_str(text);
}
line.push_str(&" ".repeat(padding + extra));
line.push(self.chars.vertical);
line.push('\n');
line
}
pub fn render_top_divider(&self) -> String {
let mut line = String::new();
line.push(self.chars.t_right);
for _ in 0..(self.label_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_down);
for _ in 0..(self.data_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_left);
line.push('\n');
line
}
pub fn render_middle_divider(&self) -> String {
let mut line = String::new();
line.push(self.chars.t_right);
for _ in 0..(self.label_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.cross);
for _ in 0..(self.data_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_left);
line.push('\n');
line
}
pub fn render_bottom_divider(&self) -> String {
let mut line = String::new();
line.push(self.chars.t_right);
for _ in 0..(self.label_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_up);
for _ in 0..(self.data_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_left);
line.push('\n');
line
}
pub fn render_footer(&self) -> String {
let mut line = String::new();
line.push(self.chars.bottom_left);
for _ in 0..(self.label_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.t_up);
for _ in 0..(self.data_width + 2) {
line.push(self.chars.horizontal);
}
line.push(self.chars.bottom_right);
line.push('\n');
line
}
pub fn render_row(&self, label: &str, value: &str) -> String {
let label_display = self.fit_string(label, self.label_width);
let value_display = self.fit_string(value, self.data_width);
let mut line = String::new();
line.push(self.chars.vertical);
line.push(' ');
line.push_str(&label_display);
line.push(' ');
line.push(self.chars.vertical);
line.push(' ');
line.push_str(&value_display);
line.push(' ');
line.push(self.chars.vertical);
line.push('\n');
line
}
fn fit_string(&self, s: &str, width: usize) -> String {
let display_width = s.width();
if display_width > width {
if width <= 3 {
let mut result = String::new();
let mut w = 0;
for c in s.chars() {
let cw = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
if w + cw > width {
break;
}
result.push(c);
w += cw;
}
while w < width {
result.push(' ');
w += 1;
}
result
} else {
let mut result = String::new();
let mut w = 0;
for c in s.chars() {
let cw = unicode_width::UnicodeWidthChar::width(c).unwrap_or(0);
if w + cw > width - 3 {
break;
}
result.push(c);
w += cw;
}
result.push_str("...");
w += 3;
while w < width {
result.push(' ');
w += 1;
}
result
}
} else {
let padding = width - display_width;
format!("{}{}", s, " ".repeat(padding))
}
}
}