use std::io::Write;
use std::time::SystemTime;
use once_cell::sync::Lazy;
pub static PROG_NAME: Lazy<String> = Lazy::new(|| {
std::env::args_os()
.next()
.and_then(|p| {
std::path::Path::new(&p)
.file_stem()
.map(|s| s.to_string_lossy().to_string())
})
.filter(|s| !s.is_empty())
.unwrap_or_else(|| "colorls".to_string())
});
pub fn safe_println(s: &str) {
let stdout = std::io::stdout();
let mut lock = stdout.lock();
if let Err(e) = writeln!(lock, "{}", s) {
if e.kind() == std::io::ErrorKind::BrokenPipe {
std::process::exit(0);
}
eprintln!("{}: error writing output: {}", PROG_NAME.as_str(), e);
std::process::exit(1);
}
}
#[macro_export]
macro_rules! oprintln {
() => {
$crate::util::safe_println("")
};
($($arg:tt)*) => {
$crate::util::safe_println(&format!($($arg)*))
};
}
use chrono::{DateTime, Local};
use unicode_width::UnicodeWidthStr;
pub fn human_size(bytes: u64) -> String {
const UNITS: [&str; 6] = ["B", "K", "M", "G", "T", "P"];
if bytes == 0 {
return "0B".to_string();
}
let mut size = bytes as f64;
let mut unit_idx = 0;
while size >= 1024.0 && unit_idx < UNITS.len() - 1 {
size /= 1024.0;
unit_idx += 1;
}
if unit_idx == 0 {
format!("{}{}", bytes, UNITS[0])
} else {
format!("{:.1}{}", size, UNITS[unit_idx])
}
}
pub fn format_time(t: SystemTime) -> String {
let dt: DateTime<Local> = t.into();
dt.format("%d %b %H:%M").to_string()
}
pub fn terminal_width() -> usize {
terminal_size::terminal_size()
.map(|(w, _)| w.0 as usize)
.filter(|w| *w > 0)
.unwrap_or(80)
}
pub fn display_width(s: &str) -> usize {
s.chars()
.map(|c| {
let cp = c as u32;
if (0xE000..=0xF8FF).contains(&cp) || (0xF0000..=0xFFFFD).contains(&cp) {
2
} else {
UnicodeWidthStr::width(c.to_string().as_str())
}
})
.sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn human_size_bytes() {
assert_eq!(human_size(0), "0B");
assert_eq!(human_size(512), "512B");
assert_eq!(human_size(1023), "1023B");
}
#[test]
fn human_size_kilobytes_and_up() {
assert_eq!(human_size(1024), "1.0K");
assert_eq!(human_size(1536), "1.5K");
assert_eq!(human_size(1024 * 1024), "1.0M");
assert_eq!(human_size(1024 * 1024 * 1024), "1.0G");
}
#[test]
fn display_width_ascii() {
assert_eq!(display_width("hello"), 5);
assert_eq!(display_width(""), 0);
}
#[test]
fn display_width_icon_glyph_counts_as_two() {
let icon = "\u{f15b}";
assert_eq!(display_width(icon), 2);
}
}