From 91970655296806fb5c47103aab3c7b6cc9023c1d Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:00:47 +0100 Subject: [PATCH 1/7] push almost all of colors back to compile time using macros --- benches/benchmark.rs | 2 +- src/colors.rs | 78 ++++++++++++++++---------------------------- src/dots.rs | 33 +++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 11 ++++--- 5 files changed, 70 insertions(+), 55 deletions(-) create mode 100644 src/dots.rs diff --git a/benches/benchmark.rs b/benches/benchmark.rs index 8d62082..da3250a 100644 --- a/benches/benchmark.rs +++ b/benches/benchmark.rs @@ -1,8 +1,8 @@ use criterion::{Criterion, criterion_group, criterion_main}; use microfetch_lib::{ UtsName, - colors::print_dots, desktop::get_desktop_info, + dots::print_dots, release::{get_os_pretty_name, get_system_info}, system::{ get_memory_usage, diff --git a/src/colors.rs b/src/colors.rs index 7c65944..0a7af23 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -1,5 +1,22 @@ use std::sync::LazyLock; +// All this because concat!() doesn't accept const parameters +// See https://github.com/rust-lang/rust/issues/31383 +#[macro_export] +macro_rules! RESET {() => {"\x1b[0m"}} +#[macro_export] +macro_rules! BLUE {() => {"\x1b[34m"}} +#[macro_export] +macro_rules! CYAN {() => {"\x1b[34m"}} +#[macro_export] +macro_rules! GREEN {() => {"\x1b[32m"}} +#[macro_export] +macro_rules! YELLOW {() => {"\x1b[33m"}} +#[macro_export] +macro_rules! RED {() => {"\x1b[31m"}} +#[macro_export] +macro_rules! MAGENTA {() => {"\x1b[35m"}} + pub struct Colors { pub reset: &'static str, pub blue: &'static str, @@ -7,7 +24,6 @@ pub struct Colors { pub green: &'static str, pub yellow: &'static str, pub red: &'static str, - pub magenta: &'static str, } impl Colors { @@ -20,62 +36,26 @@ impl Colors { green: "", yellow: "", red: "", - magenta: "", } } else { Self { - reset: "\x1b[0m", - blue: "\x1b[34m", - cyan: "\x1b[36m", - green: "\x1b[32m", - yellow: "\x1b[33m", - red: "\x1b[31m", - magenta: "\x1b[35m", + reset: RESET!(), + blue: BLUE!(), + cyan: CYAN!(), + green: GREEN!(), + yellow: YELLOW!(), + red: RED!(), } } } } -pub static COLORS: LazyLock = LazyLock::new(|| { +pub static IS_NO_COLOR: LazyLock = LazyLock::new(|| { + // Check for NO_COLOR once at startup const NO_COLOR: *const libc::c_char = c"NO_COLOR".as_ptr(); - let is_no_color = unsafe { !libc::getenv(NO_COLOR).is_null() }; - Colors::new(is_no_color) + unsafe { !libc::getenv(NO_COLOR).is_null() } }); -#[must_use] -#[cfg_attr(feature = "hotpath", hotpath::measure)] -pub fn print_dots() -> String { - // Pre-calculate capacity: 6 color codes + " " (glyph + 2 spaces) per color - const GLYPH: &str = ""; - let capacity = COLORS.blue.len() - + COLORS.cyan.len() - + COLORS.green.len() - + COLORS.yellow.len() - + COLORS.red.len() - + COLORS.magenta.len() - + COLORS.reset.len() - + (GLYPH.len() + 2) * 6; - - let mut result = String::with_capacity(capacity); - result.push_str(COLORS.blue); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.cyan); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.green); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.yellow); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.red); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.magenta); - result.push_str(GLYPH); - result.push_str(" "); - result.push_str(COLORS.reset); - - result -} +pub static COLORS: LazyLock = LazyLock::new(|| { + Colors::new(*IS_NO_COLOR) +}); diff --git a/src/dots.rs b/src/dots.rs new file mode 100644 index 0000000..068bee2 --- /dev/null +++ b/src/dots.rs @@ -0,0 +1,33 @@ +use crate::{colors::IS_NO_COLOR, BLUE, CYAN, GREEN, MAGENTA, RED, RESET, YELLOW}; + +macro_rules! GLYPH {() => {""}} + +macro_rules! GAP {() => {" "}} + +const NO_COLORS_STR: &str = concat!( + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), +); + +const COLORS_STR: &str = concat!( + BLUE!(), GLYPH!(), GAP!(), + CYAN!(), GLYPH!(), GAP!(), + GREEN!(), GLYPH!(), GAP!(), + YELLOW!(), GLYPH!(), GAP!(), + RED!(), GLYPH!(), GAP!(), + MAGENTA!(), GLYPH!(), RESET!(), +); + +#[must_use] +#[cfg_attr(feature = "hotpath", hotpath::measure)] +pub fn print_dots() -> &'static str { + if *IS_NO_COLOR { + NO_COLORS_STR + } else { + COLORS_STR + } +} diff --git a/src/lib.rs b/src/lib.rs index 1e0f9f3..18a992e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,6 @@ pub mod colors; pub mod desktop; +pub mod dots; pub mod release; pub mod syscall; pub mod system; diff --git a/src/main.rs b/src/main.rs index e29d6d1..176e5c0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod colors; mod desktop; +mod dots; mod release; mod syscall; mod system; @@ -10,8 +11,8 @@ use std::io::{self, Cursor, Write}; pub use microfetch_lib::UtsName; use crate::{ - colors::print_dots, desktop::get_desktop_info, + dots::print_dots, release::{get_os_pretty_name, get_system_info}, system::{ get_memory_usage, @@ -37,7 +38,7 @@ fn main() -> Result<(), Box> { uptime: get_current()?, memory_usage: get_memory_usage()?, storage: get_root_disk_usage()?, - colors: print_dots(), + dots: print_dots(), }; print_system_info(&fields)?; } @@ -57,7 +58,7 @@ struct Fields { desktop: String, memory_usage: String, storage: String, - colors: String, + dots: &'static str, } #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -75,7 +76,7 @@ fn print_system_info( desktop, memory_usage, storage, - colors, + dots, } = fields; let cyan = COLORS.cyan; @@ -96,7 +97,7 @@ fn print_system_info( {blue} ▟█▛{cyan}▗█▖ {cyan}▟█▛ {cyan} {blue}Desktop{reset}  {desktop} {blue} ▝█▛ {cyan}██▖{blue}▗▄▄▄▄▄▄▄▄▄▄▄ {cyan}󰍛 {blue}Memory{reset}  {memory_usage} {blue} ▝ {cyan}▟█▜█▖{blue}▀▀▀▀▀██▛▀▀▘ {cyan}󱥎 {blue}Storage (/){reset}  {storage} - {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {colors}\n\n" + {cyan} ▟█▘ ▜█▖ {blue}▝█▛ {cyan} {blue}Colors{reset}  {dots}\n\n" )?; let len = cursor.position() as usize; From e812605d45aa1eb830afe97253513eedbf3503cb Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:06:37 +0100 Subject: [PATCH 2/7] get rid of useless format in desktop module --- src/desktop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/desktop.rs b/src/desktop.rs index 501e967..ab285c5 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,4 +1,4 @@ -use std::{ffi::CStr, fmt::Write}; +use std::ffi::CStr; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -33,7 +33,7 @@ pub fn get_desktop_info() -> String { // Capitalize first character of backend if let Some(first_char) = backend_str.chars().next() { - let _ = write!(result, "{}", first_char.to_ascii_uppercase()); + result.push(first_char.to_ascii_uppercase()); result.push_str(&backend_str[first_char.len_utf8()..]); } From 73b6b7a0d001da75f2fe3ec8eac65211a6778e5c Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:26:09 +0100 Subject: [PATCH 3/7] get rid of formatting in release module --- src/release.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/release.rs b/src/release.rs index 8d3232e..dac028e 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,4 +1,4 @@ -use std::{fmt::Write as _, io}; +use std::io; use crate::{UtsName, syscall::read_file_fast}; @@ -13,7 +13,13 @@ pub fn get_system_info(utsname: &UtsName) -> String { let capacity = sysname.len() + 1 + release.len() + 2 + machine.len() + 1; let mut result = String::with_capacity(capacity); - write!(result, "{sysname} {release} ({machine})").unwrap(); + result.push_str(sysname); + result.push(' '); + result.push_str(release); + result.push(' '); + result.push('('); + result.push_str(machine); + result.push(')'); result } From f18aaa494551c8e8c61b8922ae329f69d7d64e47 Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:45:55 +0100 Subject: [PATCH 4/7] mark error paths as #[cold] --- src/desktop.rs | 14 +++++++++----- src/lib.rs | 10 ++++++++-- src/main.rs | 2 +- src/syscall.rs | 14 +++++++++++--- src/system.rs | 24 ++++++++++++++++++------ src/uptime.rs | 4 +++- 6 files changed, 50 insertions(+), 18 deletions(-) diff --git a/src/desktop.rs b/src/desktop.rs index ab285c5..19a154a 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,5 +1,9 @@ use std::ffi::CStr; +#[inline] +#[cold] +const fn unknown() -> &'static str { "Unknown" } + #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_desktop_info() -> String { @@ -7,9 +11,9 @@ pub fn get_desktop_info() -> String { let desktop_str = unsafe { let ptr = libc::getenv(c"XDG_CURRENT_DESKTOP".as_ptr()); if ptr.is_null() { - "Unknown" + unknown() } else { - let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown"); + let s = CStr::from_ptr(ptr).to_str().unwrap_or_else(|_| unknown()); s.strip_prefix("none+").unwrap_or(s) } }; @@ -17,10 +21,10 @@ pub fn get_desktop_info() -> String { let backend_str = unsafe { let ptr = libc::getenv(c"XDG_SESSION_TYPE".as_ptr()); if ptr.is_null() { - "Unknown" + unknown() } else { - let s = CStr::from_ptr(ptr).to_str().unwrap_or("Unknown"); - if s.is_empty() { "Unknown" } else { s } + let s = CStr::from_ptr(ptr).to_str().unwrap_or_else(|_| unknown()); + if s.is_empty() { unknown() } else { s } } }; diff --git a/src/lib.rs b/src/lib.rs index 18a992e..9a177e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,13 @@ pub mod syscall; pub mod system; pub mod uptime; -use std::mem::MaybeUninit; +use std::{io, mem::MaybeUninit}; + +#[inline] +#[cold] +pub fn last_os_error() -> io::Result { + Err(io::Error::last_os_error()) +} /// Wrapper for `libc::utsname` with safe accessor methods pub struct UtsName(libc::utsname); @@ -20,7 +26,7 @@ impl UtsName { pub fn uname() -> Result { let mut uts = MaybeUninit::uninit(); if unsafe { libc::uname(uts.as_mut_ptr()) } != 0 { - return Err(std::io::Error::last_os_error()); + return last_os_error(); } Ok(Self(unsafe { uts.assume_init() })) } diff --git a/src/main.rs b/src/main.rs index 176e5c0..0263c15 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ mod uptime; use std::io::{self, Cursor, Write}; -pub use microfetch_lib::UtsName; +pub use microfetch_lib::{UtsName, last_os_error}; use crate::{ desktop::get_desktop_info, diff --git a/src/syscall.rs b/src/syscall.rs index 0c8634b..1afda3a 100644 --- a/src/syscall.rs +++ b/src/syscall.rs @@ -10,6 +10,8 @@ use std::io; +use crate::last_os_error; + /// Direct syscall to open a file /// /// # Returns @@ -169,6 +171,12 @@ pub unsafe fn sys_close(fd: i32) -> i32 { } } +#[inline] +#[cold] +fn path_too_long() -> io::Result { + Err(io::Error::new(io::ErrorKind::InvalidInput, "Path too long")) +} + /// Read entire file using direct syscalls. This avoids libc overhead and can be /// significantly faster for small files. /// @@ -182,7 +190,7 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> io::Result { // Use stack-allocated buffer for null-terminated path (max 256 bytes) let path_bytes = path.as_bytes(); if path_bytes.len() >= 256 { - return Err(io::Error::new(io::ErrorKind::InvalidInput, "Path too long")); + return path_too_long(); } let mut path_buf = [0u8; 256]; @@ -192,14 +200,14 @@ pub fn read_file_fast(path: &str, buffer: &mut [u8]) -> io::Result { unsafe { let fd = sys_open(path_buf.as_ptr(), O_RDONLY); if fd < 0 { - return Err(io::Error::last_os_error()); + return last_os_error(); } let bytes_read = sys_read(fd, buffer.as_mut_ptr(), buffer.len()); let _ = sys_close(fd); if bytes_read < 0 { - return Err(io::Error::last_os_error()); + return last_os_error(); } #[allow(clippy::cast_sign_loss)] diff --git a/src/system.rs b/src/system.rs index ba8fe79..a152210 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,6 +1,14 @@ use std::{ffi::CStr, fmt::Write as _, io, mem::MaybeUninit}; -use crate::{UtsName, colors::COLORS, syscall::read_file_fast}; +use crate::{colors::COLORS, last_os_error, syscall::read_file_fast, UtsName}; + +#[inline] +#[cold] +const fn unknown_user() -> &'static str { "unknown_user" } + +#[inline] +#[cold] +const fn unknown_host() -> &'static str { "unknown_host" } #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] @@ -8,12 +16,12 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { let username = unsafe { let ptr = libc::getenv(c"USER".as_ptr()); if ptr.is_null() { - "unknown_user" + unknown_user() } else { - CStr::from_ptr(ptr).to_str().unwrap_or("unknown_user") + CStr::from_ptr(ptr).to_str().unwrap_or_else(|_| unknown_user()) } }; - let hostname = utsname.nodename().to_str().unwrap_or("unknown_host"); + let hostname = utsname.nodename().to_str().unwrap_or_else(|_| unknown_host()); let capacity = COLORS.yellow.len() + username.len() @@ -35,13 +43,17 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String { result } +#[inline] +#[cold] +const fn unknown_shell() -> &'static str { "unknown_shell" } + #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_shell() -> String { unsafe { let ptr = libc::getenv(c"SHELL".as_ptr()); if ptr.is_null() { - return "unknown_shell".into(); + return unknown_shell().into(); } let bytes = CStr::from_ptr(ptr).to_bytes(); @@ -63,7 +75,7 @@ pub fn get_root_disk_usage() -> Result { let path = b"/\0"; if unsafe { libc::statvfs(path.as_ptr().cast(), vfs.as_mut_ptr()) } != 0 { - return Err(io::Error::last_os_error()); + return last_os_error(); } let vfs = unsafe { vfs.assume_init() }; diff --git a/src/uptime.rs b/src/uptime.rs index 095af7d..6a2cdfb 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -1,5 +1,7 @@ use std::{io, mem::MaybeUninit}; +use crate::last_os_error; + /// Faster integer to string conversion without the formatting overhead. #[inline] fn itoa(mut n: u64, buf: &mut [u8]) -> &str { @@ -73,7 +75,7 @@ pub fn get_current() -> Result { let uptime_seconds = { let mut info = MaybeUninit::uninit(); if unsafe { sys_sysinfo(info.as_mut_ptr()) } != 0 { - return Err(io::Error::last_os_error()); + return last_os_error(); } #[allow(clippy::cast_sign_loss)] unsafe { From 9dd03a900b6cf1e9c21234d2773616a69b82a36a Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:49:20 +0100 Subject: [PATCH 5/7] avoid pushing str instead of chars --- src/desktop.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/desktop.rs b/src/desktop.rs index 19a154a..c3b179b 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -33,7 +33,8 @@ pub fn get_desktop_info() -> String { let mut result = String::with_capacity(desktop_str.len() + backend_str.len() + 3); result.push_str(desktop_str); - result.push_str(" ("); + result.push(' '); + result.push('('); // Capitalize first character of backend if let Some(first_char) = backend_str.chars().next() { From 5a484b0769e765ff0b1e3bbb4cdd7326dbd2d31a Mon Sep 17 00:00:00 2001 From: poz Date: Fri, 12 Dec 2025 22:58:10 +0100 Subject: [PATCH 6/7] more cold functions --- src/desktop.rs | 4 +--- src/lib.rs | 4 ++++ src/main.rs | 2 +- src/release.rs | 10 +++++----- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/desktop.rs b/src/desktop.rs index c3b179b..09caf3b 100644 --- a/src/desktop.rs +++ b/src/desktop.rs @@ -1,8 +1,6 @@ use std::ffi::CStr; -#[inline] -#[cold] -const fn unknown() -> &'static str { "Unknown" } +use crate::unknown; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] diff --git a/src/lib.rs b/src/lib.rs index 9a177e5..71e4e3d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,10 @@ pub mod uptime; use std::{io, mem::MaybeUninit}; +#[inline] +#[cold] +pub const fn unknown() -> &'static str { "Unknown" } + #[inline] #[cold] pub fn last_os_error() -> io::Result { diff --git a/src/main.rs b/src/main.rs index 0263c15..dd2e570 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,7 @@ mod uptime; use std::io::{self, Cursor, Write}; -pub use microfetch_lib::{UtsName, last_os_error}; +pub use microfetch_lib::{UtsName, last_os_error, unknown}; use crate::{ desktop::get_desktop_info, diff --git a/src/release.rs b/src/release.rs index dac028e..5cf79ee 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,13 +1,13 @@ use std::io; -use crate::{UtsName, syscall::read_file_fast}; +use crate::{syscall::read_file_fast, unknown, UtsName}; #[must_use] #[cfg_attr(feature = "hotpath", hotpath::measure)] pub fn get_system_info(utsname: &UtsName) -> String { - let sysname = utsname.sysname().to_str().unwrap_or("Unknown"); - let release = utsname.release().to_str().unwrap_or("Unknown"); - let machine = utsname.machine().to_str().unwrap_or("Unknown"); + let sysname = utsname.sysname().to_str().unwrap_or_else(|_| unknown()); + let release = utsname.release().to_str().unwrap_or_else(|_| unknown()); + let machine = utsname.machine().to_str().unwrap_or_else(|_| unknown()); // Pre-allocate capacity: sysname + " " + release + " (" + machine + ")" let capacity = sysname.len() + 1 + release.len() + 2 + machine.len() + 1; @@ -71,5 +71,5 @@ pub fn get_os_pretty_name() -> Result { offset += line_end + 1; } - Ok("Unknown".to_owned()) + Ok(unknown().to_owned()) } From d27e8b36c41772103926916b911f43f626cc55c4 Mon Sep 17 00:00:00 2001 From: poz Date: Sat, 13 Dec 2025 17:04:11 +0100 Subject: [PATCH 7/7] fix formatting --- src/dots.rs | 24 ++++++++++++------------ src/main.rs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/dots.rs b/src/dots.rs index 068bee2..3ed3e0a 100644 --- a/src/dots.rs +++ b/src/dots.rs @@ -5,21 +5,21 @@ macro_rules! GLYPH {() => {""}} macro_rules! GAP {() => {" "}} const NO_COLORS_STR: &str = concat!( - GLYPH!(), GAP!(), - GLYPH!(), GAP!(), - GLYPH!(), GAP!(), - GLYPH!(), GAP!(), - GLYPH!(), GAP!(), - GLYPH!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), GAP!(), + GLYPH!(), ); const COLORS_STR: &str = concat!( - BLUE!(), GLYPH!(), GAP!(), - CYAN!(), GLYPH!(), GAP!(), - GREEN!(), GLYPH!(), GAP!(), - YELLOW!(), GLYPH!(), GAP!(), - RED!(), GLYPH!(), GAP!(), - MAGENTA!(), GLYPH!(), RESET!(), + BLUE!(), GLYPH!(), GAP!(), + CYAN!(), GLYPH!(), GAP!(), + GREEN!(), GLYPH!(), GAP!(), + YELLOW!(), GLYPH!(), GAP!(), + RED!(), GLYPH!(), GAP!(), + MAGENTA!(), GLYPH!(), RESET!(), ); #[must_use] diff --git a/src/main.rs b/src/main.rs index dd2e570..edbe14a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,7 +38,7 @@ fn main() -> Result<(), Box> { uptime: get_current()?, memory_usage: get_memory_usage()?, storage: get_root_disk_usage()?, - dots: print_dots(), + dots: print_dots(), }; print_system_info(&fields)?; }