mirror of
https://github.com/NotAShelf/microfetch.git
synced 2025-02-12 22:43:15 +00:00
Compare commits
8 commits
1b0d15a24f
...
4fff13a51f
Author | SHA1 | Date | |
---|---|---|---|
![]() |
4fff13a51f | ||
ea8280ef77 | |||
4b7836d572 | |||
c97fa33aec | |||
fd18e9d244 | |||
e19abcedae | |||
a96effb875 | |||
065216af7c |
7 changed files with 152 additions and 70 deletions
9
Cargo.lock
generated
9
Cargo.lock
generated
|
@ -306,6 +306,12 @@ dependencies = [
|
|||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
|
@ -326,10 +332,11 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
|||
|
||||
[[package]]
|
||||
name = "microfetch"
|
||||
version = "0.4.0"
|
||||
version = "0.4.4"
|
||||
dependencies = [
|
||||
"color-eyre",
|
||||
"criterion",
|
||||
"lazy_static",
|
||||
"nix",
|
||||
]
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "microfetch"
|
||||
version = "0.4.0"
|
||||
version = "0.4.4"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
@ -14,6 +14,7 @@ path = "src/main.rs"
|
|||
[dependencies]
|
||||
nix = { version = "0.29", features = ["fs", "hostname", "feature"] }
|
||||
color-eyre = { version = "0.6", default-features = false }
|
||||
lazy_static = "1.5.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = "0.5"
|
||||
|
|
69
README.md
69
README.md
|
@ -11,7 +11,7 @@ of maintainability. Runs in a _fraction of a millisecond_ and displays _most_ of
|
|||
the nonsense you'd see posted on r/unixporn or other internet communities. Aims
|
||||
to replace [fastfetch](https://github.com/fastfetch-cli/fastfetch) on my
|
||||
personal system, but [probably not yours](#customizing). Though, you are more
|
||||
than welcome to use it on your system: it's pretty [fast...](#benchmarks)
|
||||
than welcome to use it on your system: it's pretty [fast](#benchmarks)...
|
||||
|
||||
<p align="center">
|
||||
<img
|
||||
|
@ -34,42 +34,51 @@ than welcome to use it on your system: it's pretty [fast...](#benchmarks)
|
|||
- Name
|
||||
- Version
|
||||
- Architecture
|
||||
- Current shell (from $SHELL, trimmed if store path)
|
||||
- Current shell (from `$SHELL`, trimmed if store path)
|
||||
- Current Desktop (DE/WM/Compositor and display backend)
|
||||
- Memory Usage/Total Memory
|
||||
- Storage Usage/Total Storage (for `/` only)
|
||||
- Shell Colors
|
||||
- Did I mention fast?
|
||||
- Respects [`NO_COLOR` spec](https://no-color.org/)
|
||||
|
||||
## Motivation
|
||||
|
||||
Fastfetch, as its name indicates, a very fast fetch tool written in C, however,
|
||||
I am not interested in any of its additional features and I very much dislike
|
||||
the defaults. Microfetch is a fetch tool that you would normally write in Bash
|
||||
and put in your `~/.bashrc` but actually _really_ fast because it opts-out of
|
||||
all customization options provided by Fastfetch. Why? Because I can, and because
|
||||
I prefer Rust for "structured" Bash scripts.
|
||||
I am not interested in any of its additional features, such as customization,
|
||||
and I very much dislike the defaults. Microfetch is my response to this problem,
|
||||
a _very fast_ fetch tool that you would normally write in Bash and put in your
|
||||
`~/.bashrc` but actually _really_ fast because it opts-out of all customization
|
||||
options provided by Fastfetch, and is written in Rust. Why? Because I can, and
|
||||
because I prefer Rust for "structured" Bash scripts.
|
||||
|
||||
I cannot re-iterate it enough, Microfetch is annoyingly fast.
|
||||
I cannot re-iterate it enough, Microfetch is _annoyingly fast_.
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Microfetch's performance is capped by hardware-specific race conditions, meaning
|
||||
it may (at times) depend on your hardware. However, the overall trend seems to
|
||||
be < 2ms on any modern (2015 and after) CPU. Below are the benchmarks with
|
||||
Hyperfine on my desktop system.
|
||||
The performance may be sometimes influenced by hardware-specific race
|
||||
conditions, or even your kernel configuration meaning it may (at times) depend
|
||||
on your hardware. However, the overall trend appears to be less than 1.3ms on
|
||||
any modern (2015 and after) CPU that I own. Below are the benchmarks with
|
||||
Hyperfine on my desktop system. Please note that those benchmarks will not be
|
||||
always kept up to date, but I will try to update the numbers as I make
|
||||
Microfetch faster.
|
||||
|
||||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative | Written by raf? |
|
||||
| :----------- | ----------: | -------: | -------: | -------------: | --------------: |
|
||||
| `microfetch` | 1.3 ± 0.0 | 1.3 | 1.4 | 1.00 | yes |
|
||||
| `fastfetch` | 31.9 ± 0.8 | 30.8 | 33.8 | 24.08 ± 0.98 | no |
|
||||
| `pfetch` | 254.2 ± 4.8 | 246.7 | 264.9 | 191.97 ± 7.10 | no |
|
||||
| `neofetch` | 735.4 ± 9.5 | 721.1 | 752.8 | 555.48 ± 19.08 | no |
|
||||
| Command | Mean [ms] | Min [ms] | Max [ms] | Relative | Written by raf? |
|
||||
| :----------- | -----------: | -------: | -------: | -------------: | --------------: |
|
||||
| `microfetch` | 1.0 ± 0.1 | 0.9 | 1.7 | 1.00 | yes |
|
||||
| `fastfetch` | 48.6 ± 1.6 | 45.8 | 61.3 | 46.65 ± 4.75 | no |
|
||||
| `pfetch` | 206.0 ± 4.5 | 198.0 | 241.4 | 197.50 ± 19.53 | no |
|
||||
| `neofetch` | 689.1 ± 29.1 | 637.7 | 811.2 | 660.79 ± 69.56 | no |
|
||||
|
||||
_As far as I'm concerned, Microfetch is faster than almost every fetch tool
|
||||
there is. The only downsides of using Rust are introducing more "bloated"
|
||||
dependency trees and increasing build times. The latter is easily mitigated with
|
||||
Nix's binary cache, though._
|
||||
As far as I'm concerned, Microfetch is significantly faster than every other
|
||||
fetch tool that I have tried. The only downsides of using Rust for the project
|
||||
(in exchange for speed and maintainability) is the slightly "bloated" dependency
|
||||
trees, and the increased build times. The latter is very easily mitigated with
|
||||
Nix's binary cache. Since Microfetch is already in Nixpkgs, you are recommended
|
||||
to use it to utilize the binary cache properly
|
||||
|
||||
### Benchmarking Individual Functions
|
||||
|
||||
[Criterion.rs]: https://github.com/bheisler/criterion.rs
|
||||
[Getting Started Guide]: https://bheisler.github.io/criterion.rs/book/getting_started.html
|
||||
|
@ -84,9 +93,19 @@ features of Microfetch.
|
|||
> You will need a Nerdfonts patched font installed, and for your terminal
|
||||
> emulator to support said font. Microfetch uses nerdfonts glyphs by default.
|
||||
|
||||
Microfetch is packaged in [nixpkgs](https://github.com/nixos/nixpkgs). You can
|
||||
get it through the unstable channel for the time being. The Nix flake can also
|
||||
be used for bleeding-edge builds.
|
||||
Microfetch is packaged in [nixpkgs](https://github.com/nixos/nixpkgs). It can be
|
||||
installed by adding `pkgs.microfetch` to your `environment.systemPackages`.
|
||||
Additionally, you can try out Microfetch in a Nix shell.
|
||||
|
||||
```bash
|
||||
nix shell nixpkgs#microfetch
|
||||
```
|
||||
|
||||
Or run it directly with `nix run`
|
||||
|
||||
```bash
|
||||
nix run nixpkgs#microfetch
|
||||
```
|
||||
|
||||
Non-Nix users will have to build Microfetch with `cargo`. It is not published
|
||||
anywhere but I imagine you can use `cargo install --git` to install it from
|
||||
|
|
|
@ -1,11 +1,57 @@
|
|||
pub const RESET: &str = "\x1b[0m";
|
||||
pub const BLUE: &str = "\x1b[34m";
|
||||
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";
|
||||
use std::env;
|
||||
|
||||
pub struct Colors {
|
||||
pub reset: &'static str,
|
||||
pub blue: &'static str,
|
||||
pub cyan: &'static str,
|
||||
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 {
|
||||
format!("{BLUE} {CYAN} {GREEN} {YELLOW} {RED} {MAGENTA} {RESET}")
|
||||
format!(
|
||||
"{} {} {} {} {} {} {}",
|
||||
COLORS.blue,
|
||||
COLORS.cyan,
|
||||
COLORS.green,
|
||||
COLORS.yellow,
|
||||
COLORS.red,
|
||||
COLORS.magenta,
|
||||
COLORS.reset,
|
||||
)
|
||||
}
|
||||
|
|
38
src/main.rs
38
src/main.rs
|
@ -4,15 +4,13 @@ mod release;
|
|||
mod system;
|
||||
mod uptime;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
use crate::colors::{print_dots, BLUE, CYAN, RESET};
|
||||
use crate::colors::print_dots;
|
||||
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_shell, get_username_and_hostname};
|
||||
use crate::uptime::get_current;
|
||||
|
||||
use color_eyre::Report;
|
||||
use std::io::Write;
|
||||
|
||||
fn main() -> Result<(), Report> {
|
||||
color_eyre::install()?;
|
||||
|
@ -56,6 +54,8 @@ struct Fields {
|
|||
}
|
||||
|
||||
fn print_system_info(fields: &Fields) {
|
||||
use crate::colors::COLORS;
|
||||
|
||||
let Fields {
|
||||
user_info,
|
||||
os_name,
|
||||
|
@ -68,16 +68,22 @@ fn print_system_info(fields: &Fields) {
|
|||
colors,
|
||||
} = fields;
|
||||
|
||||
let _ = std::io::stdout().write_all(format!(
|
||||
"
|
||||
{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}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}
|
||||
").as_bytes());
|
||||
let cyan = COLORS.cyan;
|
||||
let blue = COLORS.blue;
|
||||
let reset = COLORS.reset;
|
||||
let system_info = format!("
|
||||
{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}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}");
|
||||
|
||||
std::io::stdout()
|
||||
.lock()
|
||||
.write_all(format!("{}\n", system_info).as_bytes())
|
||||
.expect("Failed to write to stdout");
|
||||
}
|
||||
|
|
|
@ -21,7 +21,13 @@ pub fn get_os_pretty_name() -> Result<String, io::Error> {
|
|||
for line in reader.lines() {
|
||||
let line = line?;
|
||||
if let Some(pretty_name) = line.strip_prefix("PRETTY_NAME=") {
|
||||
return Ok(pretty_name.trim_matches('"').to_string());
|
||||
if let Some(trimmed) = pretty_name
|
||||
.strip_prefix('"')
|
||||
.and_then(|s| s.strip_suffix('"'))
|
||||
{
|
||||
return Ok(trimmed.to_string());
|
||||
}
|
||||
return Ok(pretty_name.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
use crate::colors::COLORS;
|
||||
use color_eyre::Result;
|
||||
use nix::sys::{statvfs::statvfs, utsname::UtsName};
|
||||
|
||||
use std::{
|
||||
env,
|
||||
fs::File,
|
||||
io::{self, Read},
|
||||
};
|
||||
|
||||
use crate::colors::{CYAN, GREEN, RED, RESET, YELLOW};
|
||||
|
||||
pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
||||
let username = env::var("USER").unwrap_or("unknown_user".to_string());
|
||||
let hostname = utsname
|
||||
|
@ -16,14 +14,18 @@ pub fn get_username_and_hostname(utsname: &UtsName) -> String {
|
|||
.to_str()
|
||||
.unwrap_or("unknown_host")
|
||||
.to_string();
|
||||
|
||||
format!("{YELLOW}{username}{RED}@{GREEN}{hostname}")
|
||||
format!(
|
||||
"{yellow}{username}{red}@{green}{hostname}{reset}",
|
||||
yellow = COLORS.yellow,
|
||||
red = COLORS.red,
|
||||
green = COLORS.green,
|
||||
reset = COLORS.reset,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_shell() -> String {
|
||||
let shell_path = env::var("SHELL").unwrap_or("unknown_shell".to_string());
|
||||
let shell_name = shell_path.rsplit('/').next().unwrap_or("unknown_shell");
|
||||
|
||||
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 total_blocks = vfs.blocks();
|
||||
let available_blocks = vfs.blocks_available();
|
||||
|
||||
let total_size = block_size * total_blocks;
|
||||
let used_size = total_size - (block_size * available_blocks);
|
||||
|
||||
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 usage = (used_size as f64 / total_size as f64) * 100.0;
|
||||
|
||||
let usage = (used_size / total_size) * 100.0;
|
||||
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> {
|
||||
let mut total_memory_kb = 0.0;
|
||||
let mut available_memory_kb = 0.0;
|
||||
|
||||
let mut meminfo = String::with_capacity(2048);
|
||||
File::open("/proc/meminfo")?.read_to_string(&mut meminfo)?;
|
||||
|
||||
for line in meminfo.lines() {
|
||||
let mut split = line.split_whitespace();
|
||||
match split.next().unwrap_or_default() {
|
||||
"MemTotal:" => total_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0),
|
||||
"MemAvailable:" => {
|
||||
available_memory_kb = split.next().unwrap_or("0").parse().unwrap_or(0.0);
|
||||
|
||||
// MemTotal comes before MemAvailable, stop parsing
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
|
||||
let (used_memory, total_memory) = parse_memory_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})"
|
||||
"{used_memory:.2} GiB / {total_memory:.2} GiB ({cyan}{percentage_used}%{reset})",
|
||||
cyan = COLORS.cyan,
|
||||
reset = COLORS.reset,
|
||||
))
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue