added system info slimes

This commit is contained in:
2026-03-24 16:37:18 +01:00
parent fca667e380
commit 03b1b54299
6 changed files with 1616 additions and 54 deletions

1153
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,12 @@
[package] [package]
name = "slimefetch" name = "slimes"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
clap = { version = "4.6.0", features = ["derive"] } clap = { version = "4.6.0", features = ["derive"] }
colored = "3.1.1" colored = "3.1.1"
cpal = "0.17.3"
display-info = "0.5.9"
num_cpus = "1.17.0" num_cpus = "1.17.0"
sysinfo = "0.38.4" sysinfo = "0.38.4"

View File

@@ -92,15 +92,15 @@ pub fn run_benchmark_multithread(prime_limit: u64, jobs: usize) -> BenchmarkResu
let final_count = *shared_total_prime_count.lock().unwrap(); let final_count = *shared_total_prime_count.lock().unwrap();
// Calculate average time a thread spent working // Calculate average time a thread spent working
let durations_guard = shared_thread_durations.lock().unwrap(); // let durations_guard = shared_thread_durations.lock().unwrap();
let total_thread_microseconds: u128 = durations_guard.iter().map(|d| d.as_micros()).sum(); // let total_thread_microseconds: u128 = durations_guard.iter().map(|d| d.as_micros()).sum();
let average_thread_microseconds = if !durations_guard.is_empty() { // let average_thread_microseconds = if !durations_guard.is_empty() {
total_thread_microseconds / durations_guard.len() as u128 // total_thread_microseconds / durations_guard.len() as u128
} else { // } else {
0 // 0
}; // };
let average_thread_duration = Duration::from_micros(average_thread_microseconds as u64); // let average_thread_duration = Duration::from_micros(average_thread_microseconds as u64);
// Score normalized by load factor to represent "Speed per unit of work" // Score normalized by load factor to represent "Speed per unit of work"
let score = calculate_score(duration, MULTI_THREAD_LOAD_FACTOR as u64, prime_limit); let score = calculate_score(duration, MULTI_THREAD_LOAD_FACTOR as u64, prime_limit);

View File

@@ -1,7 +1,7 @@
// use colored::Colorize; // use colored::Colorize;
mod benchmark; pub mod benchmark;
mod slimes; pub mod slimes;
pub fn application_header() -> &'static str { pub fn application_header() -> &'static str {
let ascii_art = r#" let ascii_art = r#"

View File

@@ -1,9 +1,12 @@
use clap::Parser; use clap::Parser;
use colored::Colorize; use colored::Colorize;
use slimefetch::application_header; use sysinfo::System;
mod benchmark; use slimes::{
use crate::benchmark::{BenchmarkResults, run_benchmark_multithread, run_benchmark_singlethread}; application_header,
benchmark::{BenchmarkResults, run_benchmark_multithread, run_benchmark_singlethread},
slimes::get_all_slimes,
};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@@ -31,6 +34,17 @@ fn main() {
println!("{}", application_header().bright_blue()); println!("{}", application_header().bright_blue());
let slimes = get_all_slimes();
let mut sys = System::new_all();
sys.refresh_all();
for slime in slimes {
slime.print(&sys);
}
println!();
if !cli.skip_benchmark {
let logical_core_count = match cli.jobs { let logical_core_count = match cli.jobs {
Some(j) => j, Some(j) => j,
None => num_cpus::get(), None => num_cpus::get(),
@@ -61,6 +75,7 @@ fn main() {
scaling_color_formatter(format!("{:.2}x", multi_thread_speedup_ratio)).bold() scaling_color_formatter(format!("{:.2}x", multi_thread_speedup_ratio)).bold()
); );
} }
}
pub fn print_section_header(title_text: &str) { pub fn print_section_header(title_text: &str) {
println!( println!(

View File

@@ -1,18 +1,420 @@
use colored::Color; use colored::Color;
use colored::Colorize; use colored::Colorize;
use sysinfo::System;
trait Slime { pub trait Slime {
fn label(&self) -> String; fn label(&self) -> &str;
fn value(&self) -> String; fn values(&self, sys: &System) -> Vec<String>;
fn icon(&self) -> String; fn icon(&self) -> &str;
fn color(&self) -> Color; fn color(&self) -> Color;
fn print(&self) { fn print(&self, sys: &System) {
println!( for (i, val) in self.values(sys).iter().enumerate() {
"{} {:<12} {}", if i == 0 {
print!(
"{} {:<10} ",
self.icon().color(self.color()), self.icon().color(self.color()),
format!("{}:", self.label()).bold().color(self.color()), format!("{}:", self.label()).bold().color(self.color())
self.value().white() );
} else {
print!("{} {:<10} ", " ", " ");
}
println!("{}", val.white());
}
}
}
pub fn get_all_slimes() -> Vec<Box<dyn Slime>> {
vec![
Box::new(OsSlime),
Box::new(KernelSlime),
Box::new(HostnameSlime),
Box::new(BoardSlime),
Box::new(CpuSlime),
Box::new(GpuSlime),
Box::new(RamSlime),
Box::new(MonitorSlime),
Box::new(NetworkSlime),
Box::new(AudioSlime),
]
}
/// OS name
pub struct OsSlime;
impl Slime for OsSlime {
fn label(&self) -> &str {
"OS"
}
fn values(&self, _sys: &System) -> Vec<String> {
vec![format!(
"{}",
System::long_os_version().unwrap_or_else(|| "Unknown".into()),
)]
}
fn icon(&self) -> &str {
""
}
fn color(&self) -> Color {
Color::Blue
}
}
/// Kernel version
pub struct KernelSlime;
impl Slime for KernelSlime {
fn label(&self) -> &str {
"Kernel"
}
fn values(&self, _sys: &System) -> Vec<String> {
vec![System::kernel_version().unwrap_or_else(|| "Unknown".into())]
}
fn icon(&self) -> &str {
""
}
fn color(&self) -> Color {
Color::Blue
}
}
/// Hostname
pub struct HostnameSlime;
impl Slime for HostnameSlime {
fn label(&self) -> &str {
"Hostname"
}
fn values(&self, _sys: &System) -> Vec<String> {
vec![System::host_name().unwrap_or_else(|| "Unknown".into())]
}
fn icon(&self) -> &str {
"󰒋"
}
fn color(&self) -> Color {
Color::Blue
}
}
/// CPU name with highest frequency found in all cores
pub struct CpuSlime;
impl Slime for CpuSlime {
fn label(&self) -> &str {
"CPU"
}
fn values(&self, sys: &System) -> Vec<String> {
// sys.cpus()
// .iter()
// .map(|cpu| format!("{} @ {:.2}GHz", cpu.name(), cpu.frequency() as f32 / 1000.0))
// .collect()
let cpus = sys.cpus();
let max_freq_mhz = cpus.iter().map(|c| c.frequency()).max().unwrap_or(0) as f32 / 1000.0;
if let Some(cpu) = cpus.first() {
vec![format!(
"{} @ ~{:.2}GHz",
cpu.brand(),
// c.vendor_id(),
max_freq_mhz
)]
} else {
vec![String::from("Unknown")]
}
}
fn icon(&self) -> &str {
""
}
fn color(&self) -> Color {
Color::Yellow
}
}
/// RAM usage over available
pub struct RamSlime;
impl Slime for RamSlime {
fn label(&self) -> &str {
"RAM"
}
fn values(&self, sys: &System) -> Vec<String> {
let total_ram = sys.total_memory() / 1024 / 1024;
let used_ram = sys.used_memory() / 1024 / 1024;
vec![format!(
"{}MB / {}MB",
used_ram.to_string(),
total_ram.to_string()
)]
}
fn icon(&self) -> &str {
""
}
fn color(&self) -> Color {
Color::Magenta
}
}
/// Motherboard / chassis
pub struct BoardSlime;
impl Slime for BoardSlime {
fn label(&self) -> &str {
"Board"
}
fn icon(&self) -> &str {
""
}
fn color(&self) -> Color {
Color::Green
}
fn values(&self, _sys: &System) -> Vec<String> {
let Some(mobo) = sysinfo::Motherboard::new() else {
return vec!["Unknown Model".into()];
};
let parts = [
mobo.vendor_name(),
mobo.version().filter(|v| v != "Default string"),
mobo.name(),
];
let result = parts
.into_iter()
.flatten()
.filter(|s| !s.is_empty())
.collect::<Vec<_>>()
.join(" ");
vec![if result.is_empty() {
"Unknown Model".into()
} else {
result
}]
}
}
/// GPUs
pub struct GpuSlime;
impl Slime for GpuSlime {
fn label(&self) -> &str {
"GPU"
}
fn icon(&self) -> &str {
"󰢮"
}
fn color(&self) -> Color {
Color::Cyan
}
fn values(&self, _sys: &System) -> Vec<String> {
let mut gpus = Vec::new();
#[cfg(target_os = "windows")]
{
if let Ok(output) = Command::new("wmic")
.args(["path", "win32_VideoController", "get", "name"])
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines().skip(1) {
let name = line.trim();
if !name.is_empty() {
gpus.push(name.to_string());
}
}
}
}
#[cfg(target_os = "linux")]
{
use std::process::Command;
if let Ok(output) = Command::new("sh")
.arg("-c")
.arg("lspci | grep -E 'VGA|3D'")
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines() {
if let Some(pos) = line.find(": ") {
gpus.push(
line[pos + 2..]
.to_string()
.replace("Advanced Micro Devices, Inc. [AMD/ATI]", "AMD"),
); );
} }
} }
}
}
if gpus.is_empty() {
vec!["Unknown GPU".into()]
} else {
gpus
}
}
}
/// Monitors
pub struct MonitorSlime;
impl Slime for MonitorSlime {
fn label(&self) -> &str {
"Monitors"
}
fn icon(&self) -> &str {
"󰍹"
}
fn color(&self) -> Color {
Color::Blue
}
fn values(&self, _sys: &System) -> Vec<String> {
match display_info::DisplayInfo::all() {
Ok(displays) => displays
.iter()
.map(|d| {
format!(
"{} {}x{} @ {}Hz {}",
d.friendly_name,
d.width,
d.height,
d.frequency,
if d.is_primary {
"[Primary]"
} else {
"[External]"
}
)
})
.collect(),
Err(_) => vec!["No monitors found".into()],
}
}
}
/// Network (wifi/ethernet) hardware
pub struct NetworkSlime;
impl Slime for NetworkSlime {
fn label(&self) -> &str {
"Network"
}
fn icon(&self) -> &str {
"󰖩"
}
fn color(&self) -> Color {
Color::Cyan
}
fn values(&self, _sys: &System) -> Vec<String> {
let mut nets = Vec::new();
#[cfg(target_os = "windows")]
{
use std::process::Command;
if let Ok(output) = Command::new("wmic")
.args([
"path",
"win32_networkadapter",
"where",
"PhysicalAdapter=True",
"get",
"name",
])
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines().skip(1) {
let name = line.trim();
if !name.is_empty() {
nets.push(name.to_string());
}
}
}
}
#[cfg(target_os = "linux")]
{
use std::process::Command;
if let Ok(output) = Command::new("sh")
.arg("-c")
.arg("lspci | grep -E 'Network|Ethernet'")
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines() {
if let Some(pos) = line.find(": ") {
nets.push(line[pos + 2..].trim().to_string());
}
}
}
}
if nets.is_empty() {
vec!["Unknown Network Controller".into()]
} else {
nets
}
}
}
/// Audio hardware
pub struct AudioSlime;
impl Slime for AudioSlime {
fn label(&self) -> &str {
"Audio"
}
fn icon(&self) -> &str {
"󰓃"
}
fn color(&self) -> Color {
Color::Red
}
fn values(&self, _sys: &System) -> Vec<String> {
let mut audio_cards = Vec::new();
#[cfg(target_os = "windows")]
{
use std::process::Command;
if let Ok(output) = Command::new("wmic")
.args(["path", "win32_sounddevice", "get", "name"])
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines().skip(1) {
let name = line.trim();
if !name.is_empty() && name != "Microsoft Streaming Service Proxy" {
audio_cards.push(name.to_string());
}
}
}
}
#[cfg(target_os = "linux")]
{
use std::process::Command;
if let Ok(output) = Command::new("sh")
.arg("-c")
.arg("lspci | grep -i 'Audio'")
.output()
{
let s = String::from_utf8_lossy(&output.stdout);
for line in s.lines() {
if let Some(pos) = line.find(": ") {
audio_cards.push(
line[pos + 2..]
.trim()
.to_string()
.replace("Advanced Micro Devices, Inc. [AMD/ATI]", "AMD")
.replace("Advanced Micro Devices, Inc. [AMD]", "AMD"),
);
}
}
}
}
if audio_cards.is_empty() {
vec!["No hardware audio found".into()]
} else {
// Deduplicate in case multiple logical devices refer to one controller
audio_cards.dedup();
audio_cards
}
}
}