colors: respect NO_COLOR spec

Microfetch will now respect the NO_COLOR environment variable if it has
been passed to the program. The performance overhead of this operation
is almost none. In addition, the main function has been updated to lock
stdout.
This commit is contained in:
raf 2024-12-19 17:20:46 +03:00
parent 1b0d15a24f
commit 065216af7c
No known key found for this signature in database
GPG key ID: EED98D11B85A2819
5 changed files with 99 additions and 42 deletions

7
Cargo.lock generated
View file

@ -306,6 +306,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "lazy_static"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.155" version = "0.2.155"
@ -330,6 +336,7 @@ version = "0.4.0"
dependencies = [ dependencies = [
"color-eyre", "color-eyre",
"criterion", "criterion",
"lazy_static",
"nix", "nix",
] ]

View file

@ -14,6 +14,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
nix = { version = "0.29", features = ["fs", "hostname", "feature"] } nix = { version = "0.29", features = ["fs", "hostname", "feature"] }
color-eyre = { version = "0.6", default-features = false } color-eyre = { version = "0.6", default-features = false }
lazy_static = "1.5.0"
[dev-dependencies] [dev-dependencies]
criterion = "0.5" criterion = "0.5"

View file

@ -1,11 +1,57 @@
pub const RESET: &str = "\x1b[0m"; use std::env;
pub const BLUE: &str = "\x1b[34m";
pub const CYAN: &str = "\x1b[36m"; pub struct Colors {
pub const GREEN: &str = "\x1b[32m"; pub reset: &'static str,
pub const YELLOW: &str = "\x1b[33m"; pub blue: &'static str,
pub const RED: &str = "\x1b[31m"; pub cyan: &'static str,
pub const MAGENTA: &str = "\x1b[35m"; pub green: &'static str,
pub yellow: &'static str,
pub red: &'static str,
pub magenta: &'static str,
}
impl Colors {
const fn new(is_no_color: bool) -> Self {
match is_no_color {
true => Self {
reset: "",
blue: "",
cyan: "",
green: "",
yellow: "",
red: "",
magenta: "",
},
false => Self {
reset: "\x1b[0m",
blue: "\x1b[34m",
cyan: "\x1b[36m",
green: "\x1b[32m",
yellow: "\x1b[33m",
red: "\x1b[31m",
magenta: "\x1b[35m",
},
}
}
}
lazy_static::lazy_static! {
pub static ref COLORS: Colors = {
// check for NO_COLOR once at startup
let is_no_color = env::var("NO_COLOR").is_ok();
Colors::new(is_no_color)
};
}
pub fn print_dots() -> String { pub fn print_dots() -> String {
format!("{BLUE}{CYAN}{GREEN}{YELLOW}{RED}{MAGENTA}{RESET}") format!(
"{} {} {} {} {} {} {}",
COLORS.blue,
COLORS.cyan,
COLORS.green,
COLORS.yellow,
COLORS.red,
COLORS.magenta,
COLORS.reset,
)
} }

View file

@ -4,15 +4,13 @@ mod release;
mod system; mod system;
mod uptime; mod uptime;
use std::io::Write; use crate::colors::print_dots;
use crate::colors::{print_dots, BLUE, CYAN, RESET};
use crate::desktop::get_desktop_info; use crate::desktop::get_desktop_info;
use crate::release::{get_os_pretty_name, get_system_info}; use crate::release::{get_os_pretty_name, get_system_info};
use crate::system::{get_memory_usage, get_root_disk_usage, get_shell, get_username_and_hostname}; use crate::system::{get_memory_usage, get_root_disk_usage, get_shell, get_username_and_hostname};
use crate::uptime::get_current; use crate::uptime::get_current;
use color_eyre::Report; use color_eyre::Report;
use std::io::Write;
fn main() -> Result<(), Report> { fn main() -> Result<(), Report> {
color_eyre::install()?; color_eyre::install()?;
@ -56,6 +54,8 @@ struct Fields {
} }
fn print_system_info(fields: &Fields) { fn print_system_info(fields: &Fields) {
use crate::colors::COLORS;
let Fields { let Fields {
user_info, user_info,
os_name, os_name,
@ -68,16 +68,22 @@ fn print_system_info(fields: &Fields) {
colors, colors,
} = fields; } = fields;
let _ = std::io::stdout().write_all(format!( let cyan = COLORS.cyan;
" let blue = COLORS.blue;
{CYAN} {BLUE} {user_info} ~{RESET} let reset = COLORS.reset;
{CYAN} {BLUE} {CYAN} {CYAN} {BLUE}System{RESET} {os_name} let system_info = format!("
{CYAN} {BLUE} {CYAN} {CYAN} {BLUE}Kernel{RESET} {kernel_version} {cyan} {blue} {user_info} ~{reset}
{BLUE} {BLUE}{CYAN} {CYAN} {BLUE}Shell{RESET} {shell} {cyan} {blue} {cyan} {cyan} {blue}System{reset} {os_name}
{BLUE} {CYAN} {CYAN} {BLUE}Uptime{RESET} {uptime} {cyan} {blue} {cyan} {cyan} {blue}Kernel{reset} {kernel_version}
{BLUE} {CYAN} {CYAN} {CYAN} {BLUE}Desktop{RESET} {desktop} {blue} {blue}{cyan} {cyan} {blue}Shell{reset} {shell}
{BLUE} {CYAN}{BLUE} {CYAN}󰍛 {BLUE}Memory{RESET} {memory_usage} {blue} {cyan} {cyan} {blue}Uptime{reset} {uptime}
{BLUE} {CYAN}{BLUE} {CYAN}󱥎 {BLUE}Storage (/){RESET} {storage} {blue} {cyan} {cyan} {cyan} {blue}Desktop{reset} {desktop}
{CYAN} {BLUE} {CYAN} {BLUE}Colors{RESET} {colors} {blue} {cyan}{blue} {cyan}󰍛 {blue}Memory{reset} {memory_usage}
").as_bytes()); {blue} {cyan}{blue} {cyan}󱥎 {blue}Storage (/){reset} {storage}
{cyan} {blue} {cyan} {blue}Colors{reset} {colors}");
std::io::stdout()
.lock()
.write_all(system_info.as_bytes())
.expect("Failed to write to stdout");
} }

View file

@ -1,14 +1,12 @@
use crate::colors::COLORS;
use color_eyre::Result; use color_eyre::Result;
use nix::sys::{statvfs::statvfs, utsname::UtsName}; use nix::sys::{statvfs::statvfs, utsname::UtsName};
use std::{ use std::{
env, env,
fs::File, fs::File,
io::{self, Read}, io::{self, Read},
}; };
use crate::colors::{CYAN, GREEN, RED, RESET, YELLOW};
pub fn get_username_and_hostname(utsname: &UtsName) -> String { pub fn get_username_and_hostname(utsname: &UtsName) -> String {
let username = env::var("USER").unwrap_or("unknown_user".to_string()); let username = env::var("USER").unwrap_or("unknown_user".to_string());
let hostname = utsname let hostname = utsname
@ -16,14 +14,18 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String {
.to_str() .to_str()
.unwrap_or("unknown_host") .unwrap_or("unknown_host")
.to_string(); .to_string();
format!(
format!("{YELLOW}{username}{RED}@{GREEN}{hostname}") "{yellow}{username}{red}@{green}{hostname}{reset}",
yellow = COLORS.yellow,
red = COLORS.red,
green = COLORS.green,
reset = COLORS.reset,
)
} }
pub fn get_shell() -> String { pub fn get_shell() -> String {
let shell_path = env::var("SHELL").unwrap_or("unknown_shell".to_string()); let shell_path = env::var("SHELL").unwrap_or("unknown_shell".to_string());
let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell"); let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell");
shell_name.to_string() shell_name.to_string()
} }
@ -32,16 +34,15 @@ pub fn get_root_disk_usage() -> Result<String, io::Error> {
let block_size = vfs.block_size() as u64; let block_size = vfs.block_size() as u64;
let total_blocks = vfs.blocks(); let total_blocks = vfs.blocks();
let available_blocks = vfs.blocks_available(); let available_blocks = vfs.blocks_available();
let total_size = block_size * total_blocks; let total_size = block_size * total_blocks;
let used_size = total_size - (block_size * available_blocks); let used_size = total_size - (block_size * available_blocks);
let total_size = total_size as f64 / (1024.0 * 1024.0 * 1024.0); let total_size = total_size as f64 / (1024.0 * 1024.0 * 1024.0);
let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0); let used_size = used_size as f64 / (1024.0 * 1024.0 * 1024.0);
let usage = (used_size as f64 / total_size as f64) * 100.0; let usage = (used_size / total_size) * 100.0;
Ok(format!( Ok(format!(
"{used_size:.2} GiB / {total_size:.2} GiB ({CYAN}{usage:.0}%{RESET})" "{used_size:.2} GiB / {total_size:.2} GiB ({cyan}{usage:.0}%{reset})",
cyan = COLORS.cyan,
reset = COLORS.reset,
)) ))
} }
@ -50,35 +51,31 @@ pub fn get_memory_usage() -> Result<String, io::Error> {
fn parse_memory_info() -> Result<(f64, f64), io::Error> { fn parse_memory_info() -> Result<(f64, f64), io::Error> {
let mut total_memory_kb = 0.0; let mut total_memory_kb = 0.0;
let mut available_memory_kb = 0.0; let mut available_memory_kb = 0.0;
let mut meminfo = String::with_capacity(2048); let mut meminfo = String::with_capacity(2048);
File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?; File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?;
for line in meminfo.lines() { for line in meminfo.lines() {
let mut split = line.split_whitespace(); let mut split = line.split_whitespace();
match split.next().unwrap_or_default() { match split.next().unwrap_or_default() {
"MemTotal:" => total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0), "MemTotal:" => total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0),
"MemAvailable:" => { "MemAvailable:" => {
available_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0); available_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0);
// MemTotal comes before MemAvailable, stop parsing // MemTotal comes before MemAvailable, stop parsing
break; break;
} }
_ => (), _ => (),
} }
} }
let total_memory_gb = total_memory_kb / 1024.0 / 1024.0; let total_memory_gb = total_memory_kb / 1024.0 / 1024.0;
let available_memory_gb = available_memory_kb / 1024.0 / 1024.0; let available_memory_gb = available_memory_kb / 1024.0 / 1024.0;
let used_memory_gb = total_memory_gb - available_memory_gb; let used_memory_gb = total_memory_gb - available_memory_gb;
Ok((used_memory_gb, total_memory_gb)) Ok((used_memory_gb, total_memory_gb))
} }
let (used_memory, total_memory) = parse_memory_info()?; let (used_memory, total_memory) = parse_memory_info()?;
let percentage_used = (used_memory / total_memory * 100.0).round() as u64; let percentage_used = (used_memory / total_memory * 100.0).round() as u64;
Ok(format!( Ok(format!(
"{used_memory:.2} GiB / {total_memory:.2} GiB ({CYAN}{percentage_used}%{RESET})" "{used_memory:.2} GiB / {total_memory:.2} GiB ({cyan}{percentage_used}%{reset})",
cyan = COLORS.cyan,
reset = COLORS.reset,
)) ))
} }