From b1741a50f800ce0142dd17447bc3100b8f32c853 Mon Sep 17 00:00:00 2001 From: Asthowen Date: Fri, 11 Jul 2025 11:55:19 +0200 Subject: [PATCH] feat: add motherboard and product support --- Cargo.lock | 90 +++++++-------- Cargo.toml | 6 +- src/config/mod.rs | 1 + src/main.rs | 8 +- src/system/battery.rs | 106 ++++++------------ src/system/cpu.rs | 5 +- src/system/disk.rs | 5 +- src/system/disks.rs | 1 + src/system/host.rs | 3 +- src/system/kernel.rs | 6 +- src/system/loadavg.rs | 1 + src/system/memory.rs | 1 + src/system/mod.rs | 80 ++++++++++---- src/system/motherboard.rs | 29 +++++ src/system/product.rs | 211 ++++++++++++++++++++++++++++++++++++ src/system/uptime.rs | 2 +- src/translations/english.rs | 1 + src/translations/french.rs | 1 + src/util/filtered_values.rs | 45 ++++++++ src/util/mod.rs | 3 + 20 files changed, 447 insertions(+), 158 deletions(-) create mode 100644 src/system/motherboard.rs create mode 100644 src/system/product.rs create mode 100644 src/util/filtered_values.rs diff --git a/Cargo.lock b/Cargo.lock index e983293..a844a15 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -101,9 +101,9 @@ dependencies = [ [[package]] name = "avif-serialize" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +checksum = "2ea8ef51aced2b9191c08197f55450d830876d9933f8f48a429b354f1d496b42" dependencies = [ "arrayvec", ] @@ -170,9 +170,9 @@ checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" @@ -188,9 +188,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "cc" -version = "1.2.27" +version = "1.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d487aa071b5f64da6f19a3e848e3578944b726ee5a4854b82172f02aa876bfdc" +checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" dependencies = [ "jobserver", "libc", @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "csscolorparser" @@ -408,12 +408,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -481,9 +481,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -558,9 +558,9 @@ checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", "hashbrown", @@ -604,9 +604,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" [[package]] name = "lazy_static" @@ -634,9 +634,9 @@ checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libfuzzer-sys" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" +checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404" dependencies = [ "arbitrary", "cc", @@ -644,9 +644,9 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" dependencies = [ "bitflags 2.9.1", "libc", @@ -691,9 +691,9 @@ dependencies = [ [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -732,9 +732,9 @@ dependencies = [ [[package]] name = "minreq" -version = "2.13.4" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0d2aaba477837b46ec1289588180fabfccf0c3b1d1a0c6b1866240cd6cd5ce9" +checksum = "84885312a86831bff4a3cb04a1e54a3f698407e3274c83249313f194d3e0b678" dependencies = [ "log", ] @@ -948,9 +948,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "plist" -version = "1.7.2" +version = "1.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d77244ce2d584cd84f6a15f86195b8c9b2a0dfbfd817c09e0464244091a58ed" +checksum = "3af6b589e163c5a788fab00ce0c0366f6efbb9959c2f9874b224936af7fce7e1" dependencies = [ "base64", "indexmap", @@ -998,18 +998,18 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" dependencies = [ "profiling-procmacros", ] [[package]] name = "profiling-procmacros" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b" dependencies = [ "quote", "syn", @@ -1032,9 +1032,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.37.5" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" dependencies = [ "memchr", ] @@ -1121,9 +1121,9 @@ dependencies = [ [[package]] name = "ravif" -version = "0.11.12" +version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +checksum = "5825c26fddd16ab9f515930d49028a630efec172e903483c94796cfe31893e6b" dependencies = [ "avif-serialize", "imgref", @@ -1176,9 +1176,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.51" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "a457e416a0f90d246a4c3288bd7a25b2304ca727f253f95be383dd17af56be8f" dependencies = [ "bytemuck", ] @@ -1336,9 +1336,9 @@ checksum = "b7401a30af6cb5818bb64852270bb722533397edcfc7344954a38f420819ece2" [[package]] name = "syn" -version = "2.0.103" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -1356,9 +1356,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.35.2" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c3ffa3e4ff2b324a57f7aeb3c349656c7b127c3c189520251a648102a92496e" +checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4" dependencies = [ "libc", "memchr", @@ -1572,9 +1572,9 @@ checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" [[package]] name = "viuer" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3f25eeadaacb5253b24f4b576fecad471b07107e552e2a631a6d7ab5e1e49e0" +checksum = "0ae7c6870b98c838123f22cac9a594cbe2d74ea48d79271c08f8c9e680b40fac" dependencies = [ "ansi_colours", "base64", @@ -2050,9 +2050,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.4.17" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f6fe2e33d02a98ee64423802e16df3de99c43e5cf5ff983767e1128b394c8ac" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index 07da84c..9fd0439 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ colored = { version = "3.0.0", git = "https://github.com/colored-rs/colored", br whoami = { version = "1.5.2", default-features = false } serde = { version = "1.0.219", features = ["derive"] } image = { version = "0.25.6", optional = true } -viuer = { version = "0.9.1", optional = true } +viuer = { version = "0.9.2", optional = true } unicode-segmentation = "1.12.0" strip-ansi-escapes = "0.2.1" starship-battery = "0.10.2" @@ -35,9 +35,9 @@ supports-unicode = "3.0.0" csscolorparser = "0.7.2" serde_json = "1.0.140" sys-locale = "0.3.2" -sysinfo = "0.35.2" +sysinfo = "0.36.0" bitcode = "0.6.6" -minreq = "2.13.4" +minreq = "2.14.0" rayon = "1.10.0" which = "8.0.0" dirs = "6.0.0" diff --git a/src/config/mod.rs b/src/config/mod.rs index 4d47b3c..6409761 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -253,6 +253,7 @@ fn default_entries(locale: Locale) -> Vec> { content: "─".to_owned(), sizing: SeparatorSizing::Dynamic, }, + Entry::from_info(InfoKind::Product, language_func, None, None), Entry::from_info(InfoKind::Cpu, language_func, None, None), Entry::from_info(InfoKind::Kernel, language_func, None, None), Entry::from_info(InfoKind::Uptime, language_func, None, None), diff --git a/src/main.rs b/src/main.rs index 898fb17..287c02f 100755 --- a/src/main.rs +++ b/src/main.rs @@ -9,6 +9,8 @@ use afetch::system::host::get_hostname; use afetch::system::kernel::get_kernel; use afetch::system::loadavg::get_loadavg; use afetch::system::memory::get_memory; +use afetch::system::motherboard::get_motherboard; +use afetch::system::product::get_product; use afetch::system::uptime::get_uptime; use afetch::system::{InfoGroup, InfoKind, InfoResult}; use afetch::translations::get_language; @@ -39,9 +41,11 @@ fn main() -> Result<(), FetchInfoError> { InfoKind::Disks => get_disks(language_func, fields, &config), InfoKind::Host => get_hostname(language_func, fields, &config), InfoKind::Kernel => get_kernel(language_func, fields, &config), - InfoKind::Uptime => get_uptime(language_func, fields, &config), - InfoKind::Memory => get_memory(language_func, fields, &config), InfoKind::Loadavg => get_loadavg(language_func, fields, &config), + InfoKind::Memory => get_memory(language_func, fields, &config), + InfoKind::Motherboard => get_motherboard(language_func, fields, &config), + InfoKind::Product => get_product(language_func, fields, &config), + InfoKind::Uptime => get_uptime(language_func, fields, &config), }, ) }) diff --git a/src/system/battery.rs b/src/system/battery.rs index 2d7348d..ed7c58c 100644 --- a/src/system/battery.rs +++ b/src/system/battery.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use crate::util::format_time; use starship_battery::units::time::second; @@ -18,7 +19,7 @@ pub fn get_battery( let mut batteries_info: Vec = Vec::new(); while let Some(Ok(battery)) = batteries.next() { - let mut info_group = InfoGroup { + batteries_info.push(InfoGroup { values: filtered_values!( fields, [ @@ -52,80 +53,39 @@ pub fn get_battery( InfoField::BatteryVoltage, battery.voltage().value.to_string() ), + (InfoField::BatteryModel, battery.model()), + ( + InfoField::BatteryCycleCount, + battery.cycle_count().map(|value| value.to_string()) + ), + ( + InfoField::BatterySerialNumber, + battery.serial_number().map(|value| value.trim().to_owned()) + ), + (InfoField::BatteryVendor, battery.vendor()), + ( + InfoField::BatteryTemperature, + battery + .temperature() + .map(|temperature| temperature.value.to_string()) + ), + ( + InfoField::BatteryTimeToFull, + battery.time_to_full().and_then(|time| format_time( + time.get::().round() as u64, + languages_func + )) + ), + ( + InfoField::BatteryTimeToEmpty, + battery.time_to_empty().and_then(|time| format_time( + time.get::().round() as u64, + languages_func + )) + ), ] ), - }; - - if fields.contains(&InfoField::BatteryModel) { - if let Some(model) = battery.model() { - info_group.values.push(InfoValue { - field: InfoField::BatteryModel, - value: model.to_owned(), - }); - } - } - - if fields.contains(&InfoField::BatteryCycleCount) { - if let Some(cycle_count) = battery.cycle_count() { - info_group.values.push(InfoValue { - field: InfoField::BatteryCycleCount, - value: cycle_count.to_string(), - }); - } - } - - if fields.contains(&InfoField::BatterySerialNumber) { - if let Some(serial_number) = battery.serial_number() { - info_group.values.push(InfoValue { - field: InfoField::BatterySerialNumber, - value: serial_number.trim().to_owned(), - }); - } - } - - if fields.contains(&InfoField::BatteryVendor) { - if let Some(vendor) = battery.vendor() { - info_group.values.push(InfoValue { - field: InfoField::BatteryVendor, - value: vendor.to_owned(), - }); - } - } - - if fields.contains(&InfoField::BatteryTemperature) { - if let Some(temperature) = battery.temperature() { - info_group.values.push(InfoValue { - field: InfoField::BatteryTemperature, - value: temperature.value.to_string(), - }); - } - } - - if fields.contains(&InfoField::BatteryTimeToFull) { - if let Some(time_to_full) = battery - .time_to_full() - .and_then(|time| format_time(time.get::().round() as u64, languages_func)) - { - info_group.values.push(InfoValue { - field: InfoField::BatteryTimeToFull, - value: time_to_full, - }); - } - } - - if fields.contains(&InfoField::BatteryTimeToEmpty) { - if let Some(time_to_empty) = battery - .time_to_empty() - .and_then(|time| format_time(time.get::().round() as u64, languages_func)) - { - info_group.values.push(InfoValue { - field: InfoField::BatteryTimeToEmpty, - value: time_to_empty, - }); - } - } - - batteries_info.push(info_group); + }); } Ok(InfoResult::Several(batteries_info)) diff --git a/src/system/cpu.rs b/src/system/cpu.rs index 344d530..40b7acb 100644 --- a/src/system/cpu.rs +++ b/src/system/cpu.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use std::collections::HashSet; use sysinfo::{CpuRefreshKind, RefreshKind, System}; @@ -25,10 +26,10 @@ pub fn get_cpu( values: filtered_values!( fields, [ - (InfoField::CpuName, cpu.brand().to_owned()), + (InfoField::CpuName, cpu.brand()), (InfoField::CpuUsage, cpu.cpu_usage().to_string()), (InfoField::CpuFrequency, cpu.frequency().to_string()), - (InfoField::CpuVendor, cpu.vendor_id().to_string()), + (InfoField::CpuVendor, cpu.vendor_id()), (InfoField::CpuArch, System::cpu_arch()), ] ), diff --git a/src/system/disk.rs b/src/system/disk.rs index 7ea1d75..0f6a8e6 100644 --- a/src/system/disk.rs +++ b/src/system/disk.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use crate::util::convert_to_readable_unity; use sysinfo::Disks; @@ -32,9 +33,7 @@ pub fn get_disk( [ ( InfoField::DiskName, - disk.name().to_os_string().into_string().map_err(|_| { - FetchInfoError::error("Failed to convert disk name to String") - })? + disk.name().to_os_string().into_string().ok() ), ( InfoField::DiskAvailableSpace, diff --git a/src/system/disks.rs b/src/system/disks.rs index 84c849b..01ce3df 100644 --- a/src/system/disks.rs +++ b/src/system/disks.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use crate::util::convert_to_readable_unity; use sysinfo::{DiskRefreshKind, Disks}; diff --git a/src/system/host.rs b/src/system/host.rs index 84e7aef..5f5d58e 100644 --- a/src/system/host.rs +++ b/src/system/host.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use whoami::fallible::hostname; use whoami::username; @@ -15,7 +16,7 @@ pub fn get_hostname( fields, [ (InfoField::Username, username()), - (InfoField::Hostname, hostname().unwrap_or_default()), + (InfoField::Hostname, hostname().ok()), ] ), })) diff --git a/src/system/kernel.rs b/src/system/kernel.rs index 93338e1..a1f007d 100644 --- a/src/system/kernel.rs +++ b/src/system/kernel.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use sysinfo::System; pub fn get_kernel( @@ -13,10 +14,7 @@ pub fn get_kernel( values: filtered_values!( fields, [ - ( - InfoField::KernelVersion, - System::kernel_version().ok_or_else(FetchInfoError::missing)? - ), + (InfoField::KernelVersion, System::kernel_version()), (InfoField::KernelLongVersion, System::kernel_long_version()), ] ), diff --git a/src/system/loadavg.rs b/src/system/loadavg.rs index dfe525b..8481a6f 100644 --- a/src/system/loadavg.rs +++ b/src/system/loadavg.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use sysinfo::System; pub fn get_loadavg( diff --git a/src/system/memory.rs b/src/system/memory.rs index 0b3b82d..1137deb 100644 --- a/src/system/memory.rs +++ b/src/system/memory.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use crate::util::convert_to_readable_unity; use sysinfo::{MemoryRefreshKind, RefreshKind, System}; diff --git a/src/system/mod.rs b/src/system/mod.rs index 850c074..9200fdf 100755 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -11,27 +11,13 @@ pub mod host; pub mod kernel; pub mod loadavg; pub mod memory; +pub mod motherboard; +pub mod product; pub mod uptime; pub type InfoFunction = fn(fn(&str) -> &str, &[InfoField], &Config) -> Result; -#[macro_export] -macro_rules! filtered_values { - ($fields:expr, [ $( ($field:expr, $value_expr:expr) ),* $(,)? ]) => {{ - let mut info: Vec = Vec::new(); - $( - if $fields.contains(&$field) { - info.push(InfoValue { - field: $field, - value: $value_expr, - }); - } - )* - info - }}; -} - #[derive(Deserialize, Clone, Copy, Debug, Decode, Encode, PartialEq, Eq, Hash)] #[serde(rename_all = "snake_case")] pub enum InfoKind { @@ -43,6 +29,8 @@ pub enum InfoKind { Kernel, Loadavg, Memory, + Motherboard, + Product, Uptime, } @@ -109,6 +97,22 @@ impl InfoKind { InfoField::MemorySwapTotal, InfoField::MemorySwapUsage, ], + Self::Motherboard => &[ + InfoField::MotherboardAssetTag, + InfoField::MotherboardName, + InfoField::MotherboardSerialNumber, + InfoField::MotherboardVendorName, + InfoField::MotherboardVersion, + ], + Self::Product => &[ + InfoField::ProductFamily, + InfoField::ProductName, + InfoField::ProductSerialNumber, + InfoField::ProductStockKeepingUnit, + InfoField::ProductUuid, + InfoField::ProductVendorName, + InfoField::ProductVersion, + ], Self::Uptime => &[InfoField::Uptime], } } @@ -121,9 +125,11 @@ impl InfoKind { Self::Disks => "{disks_used_space} / {disks_total_space}", Self::Host => "{username}@{hostname}", Self::Kernel => "{kernel_long_version}", + Self::Loadavg => "{loadavg_one}, {loadavg_five}, {loadavg_fifteen}", Self::Memory => "{memory_used} / {memory_total}", + Self::Motherboard => "{motherboard_name} {motherboard_version}", + Self::Product => "{product_name} {product_version}", Self::Uptime => "{uptime}", - Self::Loadavg => "{loadavg_one}, {loadavg_five}, {loadavg_fifteen}", } } @@ -135,9 +141,11 @@ impl InfoKind { Self::Disks => "disks", Self::Host => "host", Self::Kernel => "kernel", + Self::Loadavg => "loadavg", Self::Memory => "memory", + Self::Motherboard => "motherboard", + Self::Product => "host", Self::Uptime => "uptime", - Self::Loadavg => "loadavg", } } } @@ -183,6 +191,9 @@ pub enum InfoField { Hostname, KernelVersion, KernelLongVersion, + LoadAvgOne, + LoadAvgFive, + LoadAvgFifteen, MemoryAvailable, MemoryFree, MemoryTotal, @@ -190,11 +201,20 @@ pub enum InfoField { MemorySwapFree, MemorySwapTotal, MemorySwapUsage, + MotherboardAssetTag, + MotherboardName, + MotherboardSerialNumber, + MotherboardVendorName, + MotherboardVersion, + ProductFamily, + ProductName, + ProductSerialNumber, + ProductStockKeepingUnit, + ProductUuid, + ProductVendorName, + ProductVersion, Uptime, Username, - LoadAvgOne, - LoadAvgFive, - LoadAvgFifteen, } impl InfoField { @@ -239,6 +259,9 @@ impl InfoField { Self::Hostname => "hostname", Self::KernelVersion => "kernel_version", Self::KernelLongVersion => "kernel_long_version", + Self::LoadAvgOne => "loadavg_one", + Self::LoadAvgFive => "loadavg_five", + Self::LoadAvgFifteen => "loadavg_fifteen", Self::MemoryAvailable => "memory_available", Self::MemoryFree => "memory_free", Self::MemoryTotal => "memory_total", @@ -246,11 +269,20 @@ impl InfoField { Self::MemorySwapFree => "memory_swap_free", Self::MemorySwapTotal => "memory_swap_total", Self::MemorySwapUsage => "memory_swap_usage", + Self::MotherboardAssetTag => "motherboard_asset_tag", + Self::MotherboardName => "motherboard_name", + Self::MotherboardSerialNumber => "motherboard_serial_number", + Self::MotherboardVendorName => "motherboard_vendor_name", + Self::MotherboardVersion => "motherboard_version", + Self::ProductFamily => "product_family", + Self::ProductName => "product_name", + Self::ProductSerialNumber => "product_serial_number", + Self::ProductStockKeepingUnit => "product_stock_keeping_unit", + Self::ProductUuid => "product_uuid", + Self::ProductVendorName => "product_vendor_name", + Self::ProductVersion => "product_version", Self::Uptime => "uptime", Self::Username => "username", - Self::LoadAvgOne => "loadavg_one", - Self::LoadAvgFive => "loadavg_five", - Self::LoadAvgFifteen => "loadavg_fifteen", } } } diff --git a/src/system/motherboard.rs b/src/system/motherboard.rs new file mode 100644 index 0000000..1c8452c --- /dev/null +++ b/src/system/motherboard.rs @@ -0,0 +1,29 @@ +use crate::config::Config; +use crate::error::FetchInfoError; +use crate::filtered_values; +use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; +use sysinfo::Motherboard; + +pub fn get_motherboard( + _languages_func: fn(&str) -> &str, + fields: &[InfoField], + _config: &Config, +) -> Result { + let motherboard = Motherboard::new().ok_or_else(FetchInfoError::missing)?; + Ok(InfoResult::Single(InfoGroup { + values: filtered_values!( + fields, + [ + (InfoField::MotherboardVendorName, motherboard.vendor_name()), + (InfoField::MotherboardVersion, motherboard.version()), + ( + InfoField::MotherboardSerialNumber, + motherboard.serial_number() + ), + (InfoField::MotherboardName, motherboard.name()), + (InfoField::MotherboardAssetTag, motherboard.asset_tag()) + ] + ), + })) +} diff --git a/src/system/product.rs b/src/system/product.rs new file mode 100644 index 0000000..1b4d7eb --- /dev/null +++ b/src/system/product.rs @@ -0,0 +1,211 @@ +use crate::config::Config; +use crate::error::FetchInfoError; +use crate::filtered_values; +use crate::system::InfoValue; +use crate::system::{InfoField, InfoGroup, InfoResult}; +use crate::util::ToOptionString; +use sysinfo::Product; + +pub fn get_product( + _languages_func: fn(&str) -> &str, + fields: &[InfoField], + _config: &Config, +) -> Result { + Ok(InfoResult::Single(InfoGroup { + values: filtered_values!( + fields, + [ + (InfoField::ProductFamily, Product::family()), + (InfoField::ProductName, { + #[cfg(target_os = "macos")] + { + Product::name().map(|name| { + product_name_from_module_name(&name) + .map(str::to_owned) + .unwrap_or_else(|| name) + }) + } + #[cfg(not(target_os = "macos"))] + { + Product::name() + } + }), + (InfoField::ProductSerialNumber, Product::serial_number()), + ( + InfoField::ProductStockKeepingUnit, + Product::stock_keeping_unit() + ), + (InfoField::ProductUuid, Product::uuid()), + (InfoField::ProductVendorName, Product::vendor_name()), + (InfoField::ProductVersion, Product::version()), + ] + ), + })) +} + +#[cfg(target_os = "macos")] +// Based on: +// FastFetch: https://github.com/fastfetch-cli/fastfetch/blob/dev/src/detection/host/host_mac.c +// Macbook Pro: https://support.apple.com/en-us/HT201300 +// Macbook Air: https://support.apple.com/en-us/HT201862 +// Mac mini: https://support.apple.com/en-us/HT201894 +// iMac: https://support.apple.com/en-us/HT201634 +// Mac Pro: https://support.apple.com/en-us/HT202888 +// Mac Studio: https://support.apple.com/en-us/HT213073 +pub(crate) fn product_name_from_module_name(model: &str) -> Option<&str> { + if let Some(suffix) = model.strip_prefix("MacBookPro") { + Some(match suffix { + "18,3" | "18,4" => "MacBook Pro (14-inch, 2021)", + "18,1" | "18,2" => "MacBook Pro (16-inch, 2021)", + "17,1" => "MacBook Pro (13-inch, M1, 2020)", + "16,3" => "MacBook Pro (13-inch, 2020, Two Thunderbolt 3 ports)", + "16,2" => "MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports)", + "16,4" | "16,1" => "MacBook Pro (16-inch, 2019)", + "15,4" => "MacBook Pro (13-inch, 2019, Two Thunderbolt 3 ports)", + "15,3" => "MacBook Pro (15-inch, 2019)", + "15,2" => "MacBook Pro (13-inch, 2018/2019, Four Thunderbolt 3 ports)", + "15,1" => "MacBook Pro (15-inch, 2018/2019)", + "14,3" => "MacBook Pro (15-inch, 2017)", + "14,2" => "MacBook Pro (13-inch, 2017, Four Thunderbolt 3 ports)", + "14,1" => "MacBook Pro (13-inch, 2017, Two Thunderbolt 3 ports)", + "13,3" => "MacBook Pro (15-inch, 2016)", + "13,2" => "MacBook Pro (13-inch, 2016, Four Thunderbolt 3 ports)", + "13,1" => "MacBook Pro (13-inch, 2016, Two Thunderbolt 3 ports)", + "12,1" => "MacBook Pro (Retina, 13-inch, Early 2015)", + "11,4" | "11,5" => "MacBook Pro (Retina, 15-inch, Mid 2015)", + "11,2" | "11,3" => "MacBook Pro (Retina, 15-inch, Late 2013/Mid 2014)", + "11,1" => "MacBook Pro (Retina, 13-inch, Late 2013/Mid 2014)", + "10,2" => "MacBook Pro (Retina, 13-inch, Late 2012/Early 2013)", + "10,1" => "MacBook Pro (Retina, 15-inch, Mid 2012/Early 2013)", + "9,2" => "MacBook Pro (13-inch, Mid 2012)", + "9,1" => "MacBook Pro (15-inch, Mid 2012)", + "8,3" => "MacBook Pro (17-inch, 2011)", + "8,2" => "MacBook Pro (15-inch, 2011)", + "8,1" => "MacBook Pro (13-inch, 2011)", + "7,1" => "MacBook Pro (13-inch, Mid 2010)", + "6,2" => "MacBook Pro (15-inch, Mid 2010)", + "6,1" => "MacBook Pro (17-inch, Mid 2010)", + "5,5" => "MacBook Pro (13-inch, Mid 2009)", + "5,3" => "MacBook Pro (15-inch, Mid 2009)", + "5,2" => "MacBook Pro (17-inch, Mid/Early 2009)", + "5,1" => "MacBook Pro (15-inch, Late 2008)", + "4,1" => "MacBook Pro (17/15-inch, Early 2008)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("MacBookAir") { + Some(match suffix { + "10,1" => "MacBook Air (M1, 2020)", + "9,1" => "MacBook Air (Retina, 13-inch, 2020)", + "8,2" => "MacBook Air (Retina, 13-inch, 2019)", + "8,1" => "MacBook Air (Retina, 13-inch, 2018)", + "7,2" => "MacBook Air (13-inch, Early 2015/2017)", + "7,1" => "MacBook Air (11-inch, Early 2015)", + "6,2" => "MacBook Air (13-inch, Mid 2013/Early 2014)", + "6,1" => "MacBook Air (11-inch, Mid 2013/Early 2014)", + "5,2" => "MacBook Air (13-inch, Mid 2012)", + "5,1" => "MacBook Air (11-inch, Mid 2012)", + "4,2" => "MacBook Air (13-inch, Mid 2011)", + "4,1" => "MacBook Air (11-inch, Mid 2011)", + "3,2" => "MacBook Air (13-inch, Late 2010)", + "3,1" => "MacBook Air (11-inch, Late 2010)", + "2,1" => "MacBook Air (Mid 2009)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("Macmini") { + Some(match suffix { + "9,1" => "Mac mini (M1, 2020)", + "8,1" => "Mac mini (2018)", + "7,1" => "Mac mini (Mid 2014)", + "6,1" | "6,2" => "Mac mini (Late 2012)", + "5,1" | "5,2" => "Mac mini (Mid 2011)", + "4,1" => "Mac mini (Mid 2010)", + "3,1" => "Mac mini (Early/Late 2009)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("MacBook") { + Some(match suffix { + "10,1" => "MacBook (Retina, 12-inch, 2017)", + "9,1" => "MacBook (Retina, 12-inch, Early 2016)", + "8,1" => "MacBook (Retina, 12-inch, Early 2015)", + "7,1" => "MacBook (13-inch, Mid 2010)", + "6,1" => "MacBook (13-inch, Late 2009)", + "5,2" => "MacBook (13-inch, Early/Mid 2009)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("MacPro") { + Some(match suffix { + "7,1" => "Mac Pro (2019)", + "6,1" => "Mac Pro (Late 2013)", + "5,1" => "Mac Pro (Mid 2010 - Mid 2012)", + "4,1" => "Mac Pro (Early 2009)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("Mac") { + Some(match suffix { + "16,13" => "MacBook Air (15-inch, M4, 2025)", + "16,12" => "MacBook Air (13-inch, M4, 2025)", + "16,11" | "16,10" => "Mac Mini (2024)", + "16,9" => "Mac Studio (M4 Max, 2025)", + "16,3" => "iMac (24-inch, 2024, Four Thunderbolt / USB 4 ports)", + "16,2" => "iMac (24-inch, 2024, Two Thunderbolt / USB 4 ports)", + "16,1" => "MacBook Pro (14-inch, 2024, Three Thunderbolt 4 ports)", + "16,6" | "16,8" => "MacBook Pro (14-inch, 2024, Three Thunderbolt 5 ports)", + "16,7" | "16,5" => "MacBook Pro (16-inch, 2024, Three Thunderbolt 5 ports)", + "15,14" => "Mac Studio (M3 Ultra, 2025)", + "15,13" => "MacBook Air (15-inch, M3, 2024)", + "15,12" => "MacBook Air (13-inch, M3, 2024)", + "15,3" => "MacBook Pro (14-inch, Nov 2023, Two Thunderbolt / USB 4 ports)", + "15,4" => "iMac (24-inch, 2023, Two Thunderbolt / USB 4 ports)", + "15,5" => "iMac (24-inch, 2023, Two Thunderbolt / USB 4 ports, Two USB 3 ports)", + "15,6" | "15,8" | "15,10" => { + "MacBook Pro (14-inch, Nov 2023, Three Thunderbolt 4 ports)" + } + "15,7" | "15,9" | "15,11" => { + "MacBook Pro (16-inch, Nov 2023, Three Thunderbolt 4 ports)" + } + "14,15" => "MacBook Air (15-inch, M2, 2023)", + "14,14" => "Mac Studio (M2 Ultra, 2023, Two Thunderbolt 4 front ports)", + "14,13" => "Mac Studio (M2 Max, 2023, Two USB-C front ports)", + "14,8" => "Mac Pro (2023)", + "14,6" | "14,10" => "MacBook Pro (16-inch, 2023)", + "14,5" | "14,9" => "MacBook Pro (14-inch, 2023)", + "14,3" => "Mac mini (M2, 2023, Two Thunderbolt 4 ports)", + "14,12" => "Mac mini (M2, 2023, Four Thunderbolt 4 ports)", + "14,7" => "MacBook Pro (13-inch, M2, 2022)", + "14,2" => "MacBook Air (M2, 2022)", + "13,1" => "Mac Studio (M1 Max, 2022, Two USB-C front ports)", + "13,2" => "Mac Studio (M1 Ultra, 2022, Two Thunderbolt 4 front ports)", + _ => return None, + }) + } else if let Some(suffix) = model.strip_prefix("iMac") { + Some(match suffix { + "21,1" => "iMac (24-inch, M1, 2021, Two Thunderbolt / USB 4 ports, Two USB 3 ports)", + "21,2" => "iMac (24-inch, M1, 2021, Two Thunderbolt / USB 4 ports)", + "20,1" | "20,2" => "iMac (Retina 5K, 27-inch, 2020)", + "19,1" => "iMac (Retina 5K, 27-inch, 2019)", + "19,2" => "iMac (Retina 4K, 21.5-inch, 2019)", + "Pro1,1" => "iMac Pro (2017)", + "18,3" => "iMac (Retina 5K, 27-inch, 2017)", + "18,2" => "iMac (Retina 4K, 21.5-inch, 2017)", + "18,1" => "iMac (21.5-inch, 2017)", + "17,1" => "iMac (Retina 5K, 27-inch, Late 2015)", + "16,2" => "iMac (Retina 4K, 21.5-inch, Late 2015)", + "16,1" => "iMac (21.5-inch, Late 2015)", + "15,1" => "iMac (Retina 5K, 27-inch, Late 2014 - Mid 2015)", + "14,4" => "iMac (21.5-inch, Mid 2014)", + "14,2" => "iMac (27-inch, Late 2013)", + "14,1" => "iMac (21.5-inch, Late 2013)", + "13,2" => "iMac (27-inch, Late 2012)", + "13,1" => "iMac (21.5-inch, Late 2012)", + "12,2" => "iMac (27-inch, Mid 2011)", + "12,1" => "iMac (21.5-inch, Mid 2011)", + "11,3" => "iMac (27-inch, Mid 2010)", + "11,2" => "iMac (21.5-inch, Mid 2010)", + "10,1" => "iMac (27/21.5-inch, Late 2009)", + "9,1" => "iMac (24/20-inch, Early 2009)", + _ => return None, + }) + } else { + None + } +} diff --git a/src/system/uptime.rs b/src/system/uptime.rs index 6a332d2..58bb850 100644 --- a/src/system/uptime.rs +++ b/src/system/uptime.rs @@ -2,6 +2,7 @@ use crate::config::Config; use crate::error::FetchInfoError; use crate::filtered_values; use crate::system::{InfoField, InfoGroup, InfoResult, InfoValue}; +use crate::util::ToOptionString; use crate::util::format_time; use sysinfo::System; @@ -16,7 +17,6 @@ pub fn get_uptime( [( InfoField::Uptime, format_time(System::uptime(), languages_func) - .ok_or_else(FetchInfoError::missing)? ),] ), })) diff --git a/src/translations/english.rs b/src/translations/english.rs index 40b4725..e25fca1 100755 --- a/src/translations/english.rs +++ b/src/translations/english.rs @@ -19,6 +19,7 @@ pub fn english(key: &str) -> &'static str { "terminal" => "Terminal", "terminal-font" => "Font", "memory" => "Memory", + "motherboard" => "Motherboard", "cpu" => "CPU", "gpu" => "GPU", "network" => "Network", diff --git a/src/translations/french.rs b/src/translations/french.rs index 8eed0ae..987b98d 100755 --- a/src/translations/french.rs +++ b/src/translations/french.rs @@ -19,6 +19,7 @@ pub fn french(key: &str) -> &'static str { "terminal" => "Terminal", "terminal-font" => "Police", "memory" => "Mémoire", + "motherboard" => "Carte mère", "cpu" => "CPU", "gpu" => "GPU", "network" => "Réseau", diff --git a/src/util/filtered_values.rs b/src/util/filtered_values.rs new file mode 100644 index 0000000..17435ec --- /dev/null +++ b/src/util/filtered_values.rs @@ -0,0 +1,45 @@ +pub trait ToOptionString { + fn to_option_string(self) -> Option; +} + +impl ToOptionString for Option { + fn to_option_string(self) -> Option { + self + } +} + +impl ToOptionString for Option<&str> { + fn to_option_string(self) -> Option { + self.map(str::to_owned) + } +} + +impl ToOptionString for String { + fn to_option_string(self) -> Option { + Some(self) + } +} + +impl ToOptionString for &str { + fn to_option_string(self) -> Option { + Some(self.to_string()) + } +} + +#[macro_export] +macro_rules! filtered_values { + ($fields:expr, [ $( ($field:expr, $value_expr:expr) ),* $(,)? ]) => {{ + let mut info: Vec = Vec::new(); + $( + if $fields.contains(&$field) { + if let Some(value) = $value_expr.to_option_string() { + info.push(InfoValue { + field: $field, + value, + }); + } + } + )* + info + }}; +} diff --git a/src/util/mod.rs b/src/util/mod.rs index 5014659..e6acc90 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,4 +1,7 @@ pub mod colored; +mod filtered_values; + +pub use filtered_values::ToOptionString; use crate::error::FetchInfoError; #[cfg(feature = "image")]