use std::io::Write;
fn atty_stdout() -> bool {
unsafe { libc_isatty(1) != 0 }
}
extern "C" {
#[link_name = "isatty"]
fn libc_isatty(fd: i32) -> i32;
}
pub fn color_enabled() -> bool {
if std::env::var("NO_COLOR").is_ok() {
return false;
}
if std::env::var("ACB_NO_COLOR").is_ok() {
return false;
}
atty_stdout()
}
#[derive(Clone, Copy)]
pub struct Styled {
use_color: bool,
}
impl Styled {
pub fn auto() -> Self {
Self {
use_color: color_enabled(),
}
}
pub fn plain() -> Self {
Self { use_color: false }
}
#[allow(dead_code)]
pub fn colored() -> Self {
Self { use_color: true }
}
pub fn ok(&self) -> &str {
if self.use_color {
"\x1b[32m\u{2713}\x1b[0m"
} else {
"OK"
}
}
pub fn fail(&self) -> &str {
if self.use_color {
"\x1b[31m\u{2717}\x1b[0m"
} else {
"FAIL"
}
}
pub fn warn(&self) -> &str {
if self.use_color {
"\x1b[33m\u{26A0}\x1b[0m"
} else {
"!!"
}
}
pub fn info(&self) -> &str {
if self.use_color {
"\x1b[34m\u{25CF}\x1b[0m"
} else {
"->"
}
}
pub fn arrow(&self) -> &str {
if self.use_color {
"\x1b[90m\u{2192}\x1b[0m"
} else {
"->"
}
}
pub fn bold(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[1m{}\x1b[0m", text)
} else {
text.to_string()
}
}
pub fn green(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[32m{}\x1b[0m", text)
} else {
text.to_string()
}
}
pub fn yellow(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[33m{}\x1b[0m", text)
} else {
text.to_string()
}
}
pub fn red(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[31m{}\x1b[0m", text)
} else {
text.to_string()
}
}
pub fn cyan(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[36m{}\x1b[0m", text)
} else {
text.to_string()
}
}
pub fn dim(&self, text: &str) -> String {
if self.use_color {
format!("\x1b[90m{}\x1b[0m", text)
} else {
text.to_string()
}
}
}
pub fn format_size(bytes: u64) -> String {
const KB: u64 = 1024;
const MB: u64 = 1024 * 1024;
const GB: u64 = 1024 * 1024 * 1024;
if bytes >= GB {
format!("{:.1} GB", bytes as f64 / GB as f64)
} else if bytes >= MB {
format!("{:.1} MB", bytes as f64 / MB as f64)
} else if bytes >= KB {
format!("{:.1} KB", bytes as f64 / KB as f64)
} else {
format!("{} B", bytes)
}
}
pub fn progress(label: &str, current: usize, total: usize) {
if total == 0 || !color_enabled() {
return;
}
let pct = (current as f64 / total as f64 * 100.0).min(100.0);
let bar_width = 20;
let filled = (pct / 100.0 * bar_width as f64) as usize;
let empty = bar_width - filled;
eprint!(
"\r {} [{}{}] {:>3.0}%",
label,
"\u{2588}".repeat(filled),
"\u{2591}".repeat(empty),
pct,
);
let _ = std::io::stderr().flush();
}
pub fn progress_done() {
if color_enabled() {
eprintln!();
}
}