use ratatui::style::Color;
use crate::ui::palette as p;
pub fn fmt_size(b: u64) -> String {
const TB: u64 = 1_000_000_000_000;
const GB: u64 = 1_000_000_000;
const MB: u64 = 1_000_000;
const KB: u64 = 1_000;
if b >= TB {
format!("{:.1} TB", b as f64 / TB as f64)
} else if b >= GB {
format!("{:.0} GB", b as f64 / GB as f64)
} else if b >= MB {
format!("{:.0} MB", b as f64 / MB as f64)
} else if b >= KB {
format!("{:.0} KB", b as f64 / KB as f64)
} else {
format!("{} B", b)
}
}
pub fn fmt_rate(bps: f64) -> String {
if bps < 1.0 {
return " -- ".to_string();
}
if bps >= 1_000_000.0 {
format!("{:>5.1} MB/s", bps / 1_000_000.0)
} else if bps >= 1_000.0 {
format!("{:>5.1} KB/s", bps / 1_000.0)
} else {
format!("{:>5.0} B/s", bps)
}
}
pub fn fmt_rate_compact(bps: f64) -> String {
if bps >= 1_000_000_000.0 {
format!("{:.1} GB/s", bps / 1_000_000_000.0)
} else if bps >= 1_000_000.0 {
format!("{:.1} MB/s", bps / 1_000_000.0)
} else if bps >= 1000.0 {
format!("{:.0} kB/s", bps / 1000.0)
} else if bps > 0.0 {
format!("{:.0} B/s", bps)
} else {
"idle".to_string()
}
}
pub fn char_cells(ch: char) -> usize {
use unicode_width::UnicodeWidthChar;
if crate::sanitize::unsafe_for_display(ch) {
return 1;
}
ch.width().unwrap_or(1)
}
pub fn cells(s: &str) -> usize {
s.chars().map(char_cells).sum()
}
pub fn take_cells(s: &str, n: usize) -> (String, usize) {
let mut out = String::new();
let mut used = 0;
for ch in s.chars() {
let w = char_cells(ch);
if used + w > n {
break;
}
out.push(ch);
used += w;
}
(out, used)
}
pub fn take_cells_end(s: &str, n: usize) -> (String, usize) {
let mut kept = Vec::new();
let mut used = 0;
for ch in s.chars().rev() {
let w = char_cells(ch);
if used + w > n {
break;
}
kept.push(ch);
used += w;
}
(kept.into_iter().rev().collect(), used)
}
pub fn pad_right(s: &str, n: usize) -> String {
let (mut out, used) = take_cells(s, n);
out.push_str(&" ".repeat(n - used));
out
}
pub fn pad_left(s: &str, n: usize) -> String {
let (cut, used) = take_cells(s, n);
let mut out = " ".repeat(n - used);
out.push_str(&cut);
out
}
pub fn usage_color(used_pct: u32) -> Color {
if used_pct >= 90 {
p::red()
} else if used_pct >= 80 {
p::yellow()
} else {
p::fg()
}
}
pub fn usage_bar_color(used_pct: u32) -> Color {
if used_pct >= 90 {
p::red()
} else if used_pct >= 80 {
p::yellow()
} else {
p::green()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_compact_rate_reads_as_prose() {
assert_eq!(fmt_rate_compact(0.0), "idle");
assert_eq!(fmt_rate_compact(512.0), "512 B/s");
assert_eq!(fmt_rate_compact(2_000.0), "2 kB/s");
assert_eq!(fmt_rate_compact(5_400_000.0), "5.4 MB/s");
assert_eq!(fmt_rate_compact(2_100_000_000.0), "2.1 GB/s");
for bps in [0.0, 1.0, 999.0, 1e6, 1e12] {
assert!(!fmt_rate_compact(bps).starts_with(' '), "{bps}");
}
}
}
#[cfg(test)]
mod width_tests {
use super::*;
#[test]
fn padding_counts_columns_not_chars() {
assert_eq!(cells("日本語.log"), 10);
assert_eq!(cells(&pad_right("日本語.log", 12)), 12);
assert_eq!(cells(&pad_left("😀x", 6)), 6);
assert_eq!(pad_right("日本語", 5), "日本 ");
assert_eq!(take_cells_end("ab日本", 3), ("本".to_string(), 2));
assert_eq!(take_cells_end("ab日本", 5), ("b日本".to_string(), 5));
assert_eq!(cells("x\u{202E}y"), 3, "a bidi override shows as ·");
assert_eq!(pad_right("plain", 3), "pla");
}
}