use std::cmp::min;
use std::io::IsTerminal;
pub fn term_width() -> usize {
match terminal_size::terminal_size() {
Some((terminal_size::Width(w), _)) => {
let w = w as usize;
w.clamp(60, 200)
}
None => 80,
}
}
pub fn is_tty() -> bool {
std::io::stdout().is_terminal()
}
#[allow(dead_code)]
pub fn is_tty_stderr() -> bool {
std::io::stderr().is_terminal()
}
pub fn divider(width: usize) -> String {
"─".repeat(width)
}
pub fn pad_right(s: &str, width: usize) -> String {
if s.len() >= width {
s.to_string()
} else {
format!("{}{}", s, " ".repeat(width - s.len()))
}
}
#[allow(dead_code)]
pub fn pad_left(s: &str, width: usize) -> String {
if s.len() >= width {
s.to_string()
} else {
format!("{}{}", " ".repeat(width - s.len()), s)
}
}
pub fn wrap_text(text: &str, width: usize) -> Vec<String> {
if text.is_empty() || width == 0 {
return vec![];
}
let mut lines = Vec::new();
let mut current_line = String::new();
for word in text.split_whitespace() {
if current_line.is_empty() {
if word.len() <= width {
current_line = word.to_string();
} else {
lines.push(word.to_string());
}
} else if current_line.len() + 1 + word.len() <= width {
current_line.push(' ');
current_line.push_str(word);
} else {
lines.push(current_line);
current_line = word.to_string();
}
}
if !current_line.is_empty() {
lines.push(current_line);
}
lines
}
pub fn box_line(content: &str, width: usize) -> String {
let interior_width = width.saturating_sub(4); let padded = pad_right(content, interior_width);
format!("│ {} │", padded)
}
pub fn empty_box_line(width: usize) -> String {
let interior_width = width.saturating_sub(4);
format!("│{}│", " ".repeat(interior_width + 2))
}
#[allow(dead_code)]
pub fn border_line(
left_char: &str,
right_char: &str,
fill_char: &str,
width: usize,
middle_char: Option<&str>,
middle_pos: Option<usize>,
) -> String {
let mut line = String::new();
line.push_str(left_char);
let fill_width = width.saturating_sub(2);
if let (Some(mid_char), Some(pos)) = (middle_char, middle_pos) {
let left_fill = pos.saturating_sub(1);
let right_fill = fill_width.saturating_sub(left_fill + mid_char.len());
line.push_str(&fill_char.repeat(left_fill));
line.push_str(mid_char);
line.push_str(&fill_char.repeat(right_fill));
} else {
line.push_str(&fill_char.repeat(fill_width));
}
line.push_str(right_char);
line
}
pub fn severity_bar(count: usize, max: usize, filled_char: &str, empty_char: &str) -> String {
let bar_width = 10;
if max == 0 {
return empty_char.repeat(bar_width);
}
let filled = (count * bar_width) / max;
let filled = min(filled, bar_width);
let empty = bar_width - filled;
format!("{}{}", filled_char.repeat(filled), empty_char.repeat(empty))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wrap_text_empty() {
let result = wrap_text("", 10);
assert!(result.is_empty());
}
#[test]
fn test_wrap_text_single_word() {
let result = wrap_text("hello", 10);
assert_eq!(result.len(), 1);
assert_eq!(result[0], "hello");
}
#[test]
fn test_wrap_text_multiple_words() {
let result = wrap_text("hello world test", 10);
assert_eq!(result.len(), 2);
assert_eq!(result[0], "hello");
assert_eq!(result[1], "world test");
}
#[test]
fn test_wrap_text_exact_boundary() {
let result = wrap_text("hello world", 11);
assert_eq!(result.len(), 1);
assert_eq!(result[0], "hello world");
}
#[test]
fn test_wrap_text_long_word() {
let result = wrap_text("supercalifragilisticexpialidocious short", 10);
assert_eq!(result[0], "supercalifragilisticexpialidocious");
assert_eq!(result[1], "short");
}
#[test]
fn test_pad_right() {
assert_eq!(pad_right("hi", 5), "hi ");
assert_eq!(pad_right("hello", 5), "hello");
assert_eq!(pad_right("hello world", 5), "hello world");
}
#[test]
fn test_pad_left() {
assert_eq!(pad_left("hi", 5), " hi");
assert_eq!(pad_left("hello", 5), "hello");
}
#[test]
fn test_divider() {
let div = divider(5);
assert_eq!(div.chars().count(), 5);
assert_eq!(div, "─────");
}
#[test]
fn test_severity_bar_zero_max() {
let bar = severity_bar(0, 0, "█", "░");
assert_eq!(bar.chars().count(), 10);
assert_eq!(bar, "░░░░░░░░░░");
}
#[test]
fn test_severity_bar_proportional() {
let bar = severity_bar(5, 10, "█", "░");
assert_eq!(bar, "█████░░░░░");
}
#[test]
fn test_severity_bar_max() {
let bar = severity_bar(10, 10, "█", "░");
assert_eq!(bar, "██████████");
}
#[test]
fn test_box_line() {
let line = box_line("test", 20);
assert!(line.starts_with("│"));
assert!(line.ends_with("│"));
assert!(line.contains("test"));
}
#[test]
fn test_empty_box_line() {
let line = empty_box_line(20);
assert!(line.starts_with("│"));
assert!(line.ends_with("│"));
assert_eq!(line.chars().count(), 20);
}
}