diff --git a/.github/assets/demo.png b/.github/assets/demo.png new file mode 100644 index 0000000..945e489 Binary files /dev/null and b/.github/assets/demo.png differ diff --git a/Cargo.lock b/Cargo.lock index 722e684..d6fe5ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -105,7 +105,7 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "microfetch" -version = "0.2.0" +version = "0.3.0" dependencies = [ "color-eyre", "nix", diff --git a/Cargo.toml b/Cargo.toml index 9b1e9e9..6bc2e47 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "microfetch" -version = "0.2.0" +version = "0.3.0" edition = "2021" [dependencies] -nix = {version = "0.29", features = ["fs"]} +nix = {version = "0.29", features = ["fs", "hostname"]} color-eyre = { version = "0.6", default-features = false } [profile.release] @@ -13,6 +13,3 @@ opt-level = "z" lto = true codegen-units = 1 panic = "abort" - - - diff --git a/README.md b/README.md index a04f899..724b7d9 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,35 @@ # Microfetch -A stupidly simple fetch program written in Rust. +A stupidly simple fetch tool, written in Rust. Runs in a fraction of a second, +displays most nonsense people on r/unixporn care about. Aims to replace +fastfetch on my system, but probably not on yours. Though you are more than +welcome to use it on your system: it's fast. + +![Demo](.github/assets/demo.png) ## Features -Microfetch can reliably detect and display the following: +- Fast +- Really fast +- Minimal dependencies +- Actually very fast +- Cool NixOS logo (other, inferior, distros are not supported) +- Reliable detection of following info: + - Hostname/Username + - Kernel + - Name + - Version + - Architecture + - Current shell (from $SHELL) + - WM/Compositor and display backend + - Memory Usage/Total Memory + - Storage Usage/Total Storage (for `/` only) + - Shell Colors -- Hostname/Username -- Kernel - - Name - - Version - - Architecture -- WM/Compositor and display backend -- Memory Usage/Total Memory -- Storage Usage/Total Storage (for `/` only) +## Customizing + +You can't. + +## License + +Microfetch is licensed under [GPL3](LICENSE). See the license file for details. diff --git a/src/colors.rs b/src/colors.rs index f9b8bbc..1aa8096 100644 --- a/src/colors.rs +++ b/src/colors.rs @@ -4,3 +4,10 @@ pub const CYAN: &str = "\x1b[36m"; pub const GREEN: &str = "\x1b[32m"; pub const YELLOW: &str = "\x1b[33m"; pub const RED: &str = "\x1b[31m"; +pub const MAGENTA: &str = "\x1b[35m"; + +pub fn print_dots() -> Result { + let colors = format!("{BLUE} {CYAN} {GREEN} {YELLOW} {RED} {MAGENTA} {RESET}"); + + Ok(colors) +} diff --git a/src/main.rs b/src/main.rs index 145283d..713bf70 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,13 @@ mod release; mod system; mod uptime; -use color_eyre::{Report, Result}; - -use crate::colors::{BLUE, CYAN, RESET}; +use crate::colors::{print_dots, BLUE, CYAN, RESET}; use crate::desktop::get_desktop_info; use crate::release::{get_os_pretty_name, get_system_info}; -use crate::system::{get_memory_usage, get_root_disk_usage, 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 color_eyre::Report; +use nix::sys::sysinfo::sysinfo; fn main() -> Result<(), Report> { color_eyre::install()?; @@ -18,19 +18,24 @@ fn main() -> Result<(), Report> { let user_info = get_username_and_hostname()?; let os_name = get_os_pretty_name()?; let kernel_version = get_system_info()?; + let shell = get_shell()?; let uptime = get_current()?; let window_manager = get_desktop_info()?; - let memory_usage = get_memory_usage()?; + let sys_info = sysinfo()?; + let memory_usage = get_memory_usage(sys_info); let storage = get_root_disk_usage()?; + let colors = print_dots()?; print_system_info( &user_info, &os_name, &kernel_version, + &shell, &uptime, &window_manager, &memory_usage, &storage, + &colors, ); Ok(()) @@ -40,20 +45,24 @@ fn print_system_info( user_info: &str, os_name: &str, kernel_version: &str, + shell: &str, uptime: &str, window_manager: &str, memory_usage: &str, storage: &str, + colors: &str, ) { println!( " -{CYAN} ▗▄ {BLUE}▗▄ ▄▖ {user_info} ~{RESET} -{CYAN} ▄▄🬸█▄▄▄{BLUE}🬸█▛ {CYAN}▃ {CYAN} {BLUE}System{RESET}  {os_name} -{BLUE} ▟▛ ▜{CYAN}▃▟🬕 {CYAN} {BLUE}Kernel{RESET}  {kernel_version} -{BLUE}🬋🬋🬫█ {CYAN}█🬛🬋🬋 {CYAN} {BLUE}Uptime{RESET}  {uptime} -{BLUE} 🬷▛🮃{CYAN}▙ ▟▛ {CYAN} {BLUE}WM{RESET}  {window_manager} -{BLUE} 🮃 {CYAN}▟█🬴{BLUE}▀▀▀█🬴▀▀ {CYAN}󰍛 {BLUE}Memory{RESET}  {memory_usage} -{CYAN} ▝▀ ▀▘ {BLUE}▀▘ {CYAN}󱥎 {BLUE}Storage (/){RESET}  {storage} + {CYAN} ▟█▖ {BLUE}▝█▙ ▗█▛ {user_info} ~{RESET} + {CYAN} ▗▄▄▟██▄▄▄▄▄{BLUE}▝█▙█▛ {CYAN}▖ {CYAN} {BLUE}System{RESET}  {os_name} + {CYAN} ▀▀▀▀▀▀▀▀▀▀▀▘{BLUE}▝██ {CYAN}▟█▖ {CYAN} {BLUE}Kernel{RESET}  {kernel_version} + {BLUE} ▟█▛ {BLUE}▝█▘{CYAN}▟█▛ {CYAN} {BLUE}Shell{RESET}  {shell} + {BLUE}▟█████▛ {CYAN}▟█████▛ {CYAN} {BLUE}Uptime{RESET}  {uptime} + {BLUE} ▟█▛{CYAN}▗█▖ {CYAN}▟█▛ {CYAN} {BLUE}WM{RESET}  {window_manager} + {BLUE} ▝█▛ {CYAN}██▖{BLUE}▗▄▄▄▄▄▄▄▄▄▄▄ {CYAN}󰍛 {BLUE}Memory{RESET}  {memory_usage} + {BLUE} ▝ {CYAN}▟█▜█▖{BLUE}▀▀▀▀▀██▛▀▀▘ {CYAN}󱥎 {BLUE}Storage (/){RESET}  {storage} + {CYAN} ▟█▘ ▜█▖ {BLUE}▝█▛ {CYAN} {BLUE}Colors{RESET}  {colors} " ); } diff --git a/src/release.rs b/src/release.rs index 8dee91a..25f5640 100644 --- a/src/release.rs +++ b/src/release.rs @@ -1,38 +1,27 @@ use color_eyre::Result; use std::fs::{self, read_to_string}; -use std::io::{self, Read}; +use std::io; // Try to detect OS type as accurately as possible and without depending on uname. // /etc/os-release should generally imply Linux, and /etc/bsd-release would imply BSD system. -fn detect_os() -> Result { +fn detect_os() -> Result<&'static str, io::Error> { if fs::metadata("/etc/os-release").is_ok() || fs::metadata("/usr/lib/os-release").is_ok() { - Ok("Linux".to_string()) + Ok("Linux") } else if fs::metadata("/etc/rc.conf").is_ok() || fs::metadata("/etc/bsd-release").is_ok() { - Ok("BSD".to_string()) + Ok("BSD") } else { - Ok("Unknown".to_string()) + Ok("Unknown") } } -fn get_architecture() -> Result { - // Read architecture from /proc/sys/kernel/arch - let mut arch = String::new(); - fs::File::open("/proc/sys/kernel/arch")?.read_to_string(&mut arch)?; - let arch = arch.trim().to_string(); - Ok(arch) -} - pub fn get_system_info() -> Result { let system = detect_os()?; - let mut kernel_release = String::new(); - fs::File::open("/proc/sys/kernel/osrelease")?.read_to_string(&mut kernel_release)?; - let kernel_release = kernel_release.trim().to_string(); + let kernel_release = read_to_string("/proc/sys/kernel/osrelease")?; + let kernel_release = kernel_release.trim(); - let mut cpuinfo = String::new(); - fs::File::open("/proc/cpuinfo")?.read_to_string(&mut cpuinfo)?; - - let architecture = get_architecture()?; + let architecture = read_to_string("/proc/sys/kernel/arch")?; + let architecture = architecture.trim(); let result = format!("{system} {kernel_release} ({architecture})"); Ok(result) diff --git a/src/system.rs b/src/system.rs index 43a02f9..cfd7745 100644 --- a/src/system.rs +++ b/src/system.rs @@ -1,21 +1,29 @@ use nix::sys::statvfs::statvfs; +use nix::sys::sysinfo::SysInfo; use std::env; -use std::fs::File; -use std::io::{self, BufRead}; -use std::path::Path; -use std::process::Command; +use std::io::{self}; use crate::colors::{CYAN, GREEN, RED, RESET, YELLOW}; pub fn get_username_and_hostname() -> Result { let username = env::var("USER").unwrap_or_else(|_| "unknown_user".to_string()); - let output = Command::new("hostname").output()?; - let hostname = String::from_utf8_lossy(&output.stdout).trim().to_string(); + let hostname = nix::unistd::gethostname()?; + let hostname = hostname.to_string_lossy(); Ok(format!("{YELLOW}{username}{RED}@{GREEN}{hostname}")) } +pub fn get_shell() -> Result { + // In some setups, $SHELL is set to the store path + // of the actual shell program. While we can consider + // trimming by name, I will leave it to the user to handle + // what their SHELL variable is really set to. + let shell = env::var("SHELL").unwrap_or_else(|_| "unknown_shell".to_string()); + + Ok(shell) +} + pub fn get_root_disk_usage() -> Result { let vfs = statvfs("/")?; let block_size = vfs.block_size() as u64; @@ -34,49 +42,21 @@ pub fn get_root_disk_usage() -> Result { )) } -pub fn get_memory_usage() -> Result { - fn parse_memory_info() -> Result<(f64, f64), io::Error> { - let path = Path::new("/proc/meminfo"); - let file = File::open(path)?; - let reader = io::BufReader::new(file); - - let mut total_memory_kb = 0.0; - let mut available_memory_kb = 0.0; - - for line in reader.lines() { - let line = line?; - if line.starts_with("MemTotal:") { - total_memory_kb = line - .split_whitespace() - .nth(1) - .ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "Failed to parse MemTotal") - })? - .parse::() - .unwrap_or(0.0); - } else if line.starts_with("MemAvailable:") { - available_memory_kb = line - .split_whitespace() - .nth(1) - .ok_or_else(|| { - io::Error::new(io::ErrorKind::InvalidData, "Failed to parse MemAvailable") - })? - .parse::() - .unwrap_or(0.0); - } - } +pub fn get_memory_usage(info: SysInfo) -> String { + #[inline(always)] + fn parse_memory_info(info: SysInfo) -> (f64, f64) { + let total_memory_kb = (info.ram_total() / 1024) as f64; + let available_memory_kb = (info.ram_unused() / 1024) as f64; let total_memory_gb = total_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; - Ok((used_memory_gb, total_memory_gb)) + (used_memory_gb, total_memory_gb) } - let (used_memory, total_memory) = parse_memory_info()?; + let (used_memory, total_memory) = parse_memory_info(info); let percentage_used = (used_memory / total_memory * 100.0).round() as u64; - Ok(format!( - "{used_memory:.2} GiB / {total_memory:.2} GiB ({CYAN}{percentage_used}%{RESET})" - )) + format!("{used_memory:.2} GiB / {total_memory:.2} GiB ({CYAN}{percentage_used}%{RESET})") } diff --git a/src/uptime.rs b/src/uptime.rs index ea991a1..2183143 100644 --- a/src/uptime.rs +++ b/src/uptime.rs @@ -25,7 +25,7 @@ pub fn get_current() -> Result { let hours = (total_minutes % (60 * 24)) / 60; let minutes = total_minutes % 60; - let mut parts = Vec::new(); + let mut parts = Vec::with_capacity(3); if days > 0 { parts.push(format!("{days} days")); }