From c99bcf93e8eb78d13762364b688ccfd83aecb206 Mon Sep 17 00:00:00 2001 From: Peter de Kraker Date: Sun, 16 Feb 2025 23:21:01 +0100 Subject: [PATCH 1/5] Working, but rough version of SATA link info --- Cargo.toml | 1 + src/utils/drive.rs | 47 +++++++++++++-- src/utils/gpu/mod.rs | 4 +- src/utils/link.rs | 137 ++++++++++++++++++++++++++++++++++++++----- 4 files changed, 166 insertions(+), 23 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1f037cb..6fd5340 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ nix = { version = "0.29.0", default-features = false, features = [ num_cpus = "1.16.0" nvml-wrapper = "0.10.0" paste = "1.0.15" +path-dedot = "3.1.1" plotters = { version = "0.3.7", default-features = false, features = [ "area_series", ] } diff --git a/src/utils/drive.rs b/src/utils/drive.rs index ea2739a..869c7b7 100644 --- a/src/utils/drive.rs +++ b/src/utils/drive.rs @@ -1,7 +1,11 @@ +use super::units::convert_storage; +use crate::i18n::{i18n, i18n_f}; +use crate::utils::link::{Link, LinkData, SataSpeed}; use anyhow::{bail, Context, Result}; use gtk::gio::{Icon, ThemedIcon}; use lazy_regex::{lazy_regex, Lazy, Regex}; use log::trace; +use path_dedot::ParseDot; use process_data::pci_slot::PciSlot; use std::{ collections::HashMap, @@ -10,10 +14,6 @@ use std::{ str::FromStr, }; -use super::units::convert_storage; -use crate::i18n::{i18n, i18n_f}; -use crate::utils::link::{Link, PcieLink}; - const PATH_SYSFS: &str = "/sys/block"; static RE_DRIVE: Lazy = lazy_regex!( @@ -317,7 +317,7 @@ impl Drive { pub fn link(&self) -> Result { match self.drive_type { DriveType::Nvme => self.link_for_nvme(), - _ => bail!("unsupported drive type"), + _ => self.link_for_sata(), } } @@ -328,10 +328,45 @@ impl Drive { .map(|x| x.trim().to_string()) .context("Could not find PCIe address in sysfs for nvme")?, )?; - let pcie_link = PcieLink::from_pci_slot(pci_slot)?; + let pcie_link = LinkData::from_pci_slot(pci_slot)?; Ok(Link::Pcie(pcie_link)) } + fn link_for_sata(&self) -> Result { + let symlink = std::fs::read_link(&self.sysfs_path) + .context("Could not read sysfs_path as symblink")? + .to_string_lossy() + .to_string(); + // ../../devices/pci0000:40/0000:40:08.3/0000:47:00.0/ata25/host24/target24:0:0/24:0:0:0/block/sda + + let regex = Regex::new(r"(^.+?/ata\d+)/").context("Could not parse regex for ata link")?; + let ata_path_match = regex + .captures(&symlink) + .context("No ata match found, probably no ata device")? + .get(1) + .context("TODO")? + .as_str(); + + let ata_path = Path::new(&self.sysfs_path).join("..").join(&ata_path_match); + let dedotted_path = ata_path.parse_dot()?.clone(); + let sub_dirs = std::fs::read_dir(dedotted_path).context("Could not read ata path")?; + let link_name = sub_dirs + .filter(|x| { + x.as_ref() + .unwrap() + .file_name() + .to_string_lossy() + .starts_with("link") + }) + .next() + .context("No ata link name found")?? + .file_name(); + //Move this to Link + Ok(Link::Sata(LinkData::from_sata_link( + link_name.to_string_lossy().as_ref(), + )?)) + } + /// Returns the World-Wide Identification of the drive /// /// # Errors diff --git a/src/utils/gpu/mod.rs b/src/utils/gpu/mod.rs index 2aaba96..d9a99f1 100644 --- a/src/utils/gpu/mod.rs +++ b/src/utils/gpu/mod.rs @@ -16,7 +16,7 @@ use std::{ }; use self::{amd::AmdGpu, intel::IntelGpu, nvidia::NvidiaGpu, other::OtherGpu}; -use crate::utils::link::{Link, PcieLink}; +use crate::utils::link::{Link, LinkData}; use crate::{ i18n::i18n, utils::{pci::Device, read_uevent}, @@ -532,7 +532,7 @@ impl Gpu { pub fn link(&self) -> Result { if let GpuIdentifier::PciSlot(pci_slot) = self.gpu_identifier() { - let pcie_link = PcieLink::from_pci_slot(pci_slot)?; + let pcie_link = LinkData::from_pci_slot(pci_slot)?; Ok(Link::Pcie(pcie_link)) } else { bail!("Could not retrieve PciSlot from Gpu"); diff --git a/src/utils/link.rs b/src/utils/link.rs index 0f4b490..2b00ceb 100644 --- a/src/utils/link.rs +++ b/src/utils/link.rs @@ -1,4 +1,5 @@ use crate::i18n::i18n; +use crate::utils::link::SataSpeed::{Sata150, Sata300, Sata600}; use anyhow::{anyhow, bail, Context, Error, Result}; use process_data::pci_slot::PciSlot; use std::fmt::{Display, Formatter}; @@ -7,17 +8,17 @@ use std::str::FromStr; #[derive(Debug, Default)] pub enum Link { - Pcie(PcieLink), + Pcie(LinkData), + Sata(LinkData), #[default] Unknown, } #[derive(Debug)] -pub struct PcieLink { - pub current: Result, - pub max: Result, +pub struct LinkData { + pub current: Result, + pub max: Result, } - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct PcieLinkData { pub speed: PcieSpeed, @@ -33,9 +34,15 @@ pub enum PcieSpeed { Pcie50, Pcie60, } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum SataSpeed { + Sata150, + Sata300, + Sata600, +} -impl PcieLink { - pub fn from_pci_slot(pci_slot: PciSlot) -> Result { +impl LinkData { + pub fn from_pci_slot(pci_slot: PciSlot) -> Result { let pcie_dir = format!("/sys/bus/pci/devices/{pci_slot}/"); let pcie_folder = Path::new(pcie_dir.as_str()); if pcie_folder.exists() { @@ -66,7 +73,7 @@ impl PcieLink { } else { Err(anyhow!("Could not parse max PCIe link")) }; - Ok(PcieLink { current, max }) + Ok(Self { current, max }) } } @@ -80,7 +87,12 @@ impl PcieLinkData { } } -impl Display for PcieLink { +impl Display for LinkData +where + T: Display, + T: Copy, + T: PartialEq, +{ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { if let Ok(current) = self.current { let has_different_max = { @@ -101,6 +113,29 @@ impl Display for PcieLink { } } +impl LinkData { + pub fn from_sata_link(sata_link: &str) -> Result { + let ata_link_path = Path::new("/sys/class/ata_link").join(sata_link); + if std::fs::exists(&ata_link_path)? { + let current_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd")) + .map(|x| x.trim().to_string()) + .context("Could not read sata_spd")?; + let max_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd_max")) + .map(|x| x.trim().to_string()) + .context("Could not read sata_spd_max"); + + let current = SataSpeed::from_str(¤t_sata_speed_raw); + let max = if let Ok(max_sata_speed_raw) = max_sata_speed_raw { + SataSpeed::from_str(&max_sata_speed_raw) + } else { + Err(anyhow::anyhow!("Could not read sata_spd_max")) + }; + return Ok(Self { current, max }); + } + bail!("ata link path not found for '{}'", sata_link); + } +} + impl Display for PcieLinkData { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{} ×{}", self.speed, self.width) @@ -135,11 +170,37 @@ impl FromStr for PcieSpeed { "16.0 GT/s PCIe" => Ok(PcieSpeed::Pcie40), "32.0 GT/s PCIe" => Ok(PcieSpeed::Pcie50), "64.0 GT/s PCIe" => Ok(PcieSpeed::Pcie60), - _ => Err(anyhow!("Could not parse PCIe speed")), + _ => Err(anyhow!("Could not parse PCIe speed: '{s}'")), } } } +impl FromStr for SataSpeed { + type Err = Error; + + fn from_str(s: &str) -> std::result::Result { + match s { + "1.5 Gbps" => Ok(Sata150), + "3.0 Gbps" => Ok(Sata300), + "6.0 Gbps" => Ok(Sata600), + _ => Err(anyhow!("Could not parse SATA speed: '{s}'")), + } + } +} + +impl Display for SataSpeed { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + match self { + Sata150 => "SATA-150", + Sata300 => "SATA-300", + Sata600 => "SATA-600", + } + ) + } +} impl Display for Link { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!( @@ -147,6 +208,7 @@ impl Display for Link { "{}", match self { Link::Pcie(data) => data.to_string(), + Link::Sata(data) => data.to_string(), Link::Unknown => i18n("N/A"), } ) @@ -155,7 +217,7 @@ impl Display for Link { #[cfg(test)] mod test { - use crate::utils::link::{PcieLink, PcieLinkData, PcieSpeed}; + use crate::utils::link::{LinkData, PcieLinkData, PcieSpeed, SataSpeed}; use anyhow::anyhow; use std::collections::HashMap; use std::str::FromStr; @@ -233,7 +295,7 @@ mod test { let map = get_test_pcie_link_data(); for link_data in map.keys() { - let input = PcieLink { + let input = LinkData { current: Ok(link_data.clone()), max: Ok(link_data.clone()), }; @@ -248,7 +310,7 @@ mod test { let map = get_test_pcie_link_data(); for link_data in map.keys() { - let input = PcieLink { + let input = LinkData { current: Ok(link_data.clone()), max: Err(anyhow!("No max")), }; @@ -265,7 +327,7 @@ mod test { for current_data in map.keys() { for max_data in map.keys() { if current_data != max_data { - let input = PcieLink { + let input = LinkData { current: Ok(current_data.clone()), max: Ok(max_data.clone()), }; @@ -280,7 +342,7 @@ mod test { #[test] fn display_pcie_link_different_max_2() { - let input = PcieLink { + let input = LinkData { current: Ok(PcieLinkData { speed: PcieSpeed::Pcie40, width: 8, @@ -341,4 +403,49 @@ mod test { ), ]) } + + #[test] + fn parse_sata_link_speeds() { + let map = HashMap::from([ + ("1.5 Gbps", SataSpeed::Sata150), + ("3.0 Gbps", SataSpeed::Sata300), + ("6.0 Gbps", SataSpeed::Sata600), + ]); + + for input in map.keys() { + let result = SataSpeed::from_str(input); + assert!(result.is_ok(), "Could not parse SATA speed for '{}'", input); + let expected = map[input]; + pretty_assertions::assert_eq!(expected, result.unwrap()); + } + } + + #[test] + fn parse_sata_link_speeds_failure() { + let invalid = vec!["4.0 Gbps", "SOMETHING_ELSE", ""]; + + for input in invalid { + let result = SataSpeed::from_str(input); + assert!( + result.is_err(), + "Could parse SATA speed for '{}' while we don't expect that", + input + ); + } + } + + #[test] + fn display_sata_link_speeds() { + let map = HashMap::from([ + (SataSpeed::Sata150, "SATA-150"), + (SataSpeed::Sata300, "SATA-300"), + (SataSpeed::Sata600, "SATA-600"), + ]); + + for input in map.keys() { + let result = input.to_string(); + let expected = map[input]; + pretty_assertions::assert_str_eq!(expected, result); + } + } } From e088218a10e2e8de900ce8b78cc10a23de9f6371 Mon Sep 17 00:00:00 2001 From: Peter de Kraker Date: Wed, 19 Feb 2025 19:22:08 +0100 Subject: [PATCH 2/5] Use DriveSlot and cleanup --- src/utils/drive.rs | 103 ++++++++++++++++++++++++++++--------------- src/utils/gpu/mod.rs | 2 +- src/utils/link.rs | 10 +++-- 3 files changed, 75 insertions(+), 40 deletions(-) diff --git a/src/utils/drive.rs b/src/utils/drive.rs index 869c7b7..cb86edf 100644 --- a/src/utils/drive.rs +++ b/src/utils/drive.rs @@ -1,7 +1,7 @@ use super::units::convert_storage; use crate::i18n::{i18n, i18n_f}; -use crate::utils::link::{Link, LinkData, SataSpeed}; -use anyhow::{bail, Context, Result}; +use crate::utils::link::{Link, LinkData}; +use anyhow::{anyhow, Context, Result}; use gtk::gio::{Icon, ThemedIcon}; use lazy_regex::{lazy_regex, Lazy, Regex}; use log::trace; @@ -83,11 +83,26 @@ pub enum DriveType { Unknown, } +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] +pub enum DriveSlot { + Pcie(PciSlot), + Ata(AtaSlot), + #[default] + Unknown, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +pub struct AtaSlot { + pub ata_device: u8, + pub ata_link: u8, +} + #[derive(Debug, Clone, Default, Eq)] pub struct Drive { pub model: Option, pub drive_type: DriveType, pub block_device: String, + pub slot: DriveSlot, pub sysfs_path: PathBuf, } @@ -145,7 +160,7 @@ impl Drive { drive.block_device = block_device; drive.model = drive.model().ok().map(|model| model.trim().to_string()); drive.drive_type = drive.drive_type().unwrap_or_default(); - + drive.slot = drive.slot(); trace!("Created Drive object of {path:?}: {drive:?}"); drive @@ -315,56 +330,74 @@ impl Drive { /// Will return `Err` if there are errors during /// reading or parsing, or if the drive link type is not supported pub fn link(&self) -> Result { - match self.drive_type { - DriveType::Nvme => self.link_for_nvme(), - _ => self.link_for_sata(), + match self.slot { + DriveSlot::Pcie(slot) => Ok(Link::Pcie(LinkData::from_pci_slot(&slot)?)), + DriveSlot::Ata(slot) => Ok(Link::Sata(LinkData::from_ata_slot(&slot)?)), + _ => Err(anyhow!("unsupported drive connection type")), } } - fn link_for_nvme(&self) -> Result { - let pcie_address_path = self.sysfs_path.join("device").join("address"); - let pci_slot = PciSlot::from_str( - &std::fs::read_to_string(pcie_address_path) - .map(|x| x.trim().to_string()) - .context("Could not find PCIe address in sysfs for nvme")?, - )?; - let pcie_link = LinkData::from_pci_slot(pci_slot)?; - Ok(Link::Pcie(pcie_link)) + pub fn slot(&self) -> DriveSlot { + let pci_address_path = self.sysfs_path.join("device").join("address"); + let pci_address = std::fs::read_to_string(pci_address_path).map(|x| x.trim().to_string()); + if let Ok(pci_address) = pci_address { + if let Ok(pci_slot) = PciSlot::from_str(&pci_address) { + return DriveSlot::Pcie(pci_slot); + } + } + if let Ok(ata_slot) = self.ata_slot() { + return DriveSlot::Ata(ata_slot); + } + DriveSlot::Unknown } - fn link_for_sata(&self) -> Result { + fn ata_slot(&self) -> Result { let symlink = std::fs::read_link(&self.sysfs_path) - .context("Could not read sysfs_path as symblink")? + .context("Could not read sysfs_path as symlink")? .to_string_lossy() .to_string(); // ../../devices/pci0000:40/0000:40:08.3/0000:47:00.0/ata25/host24/target24:0:0/24:0:0:0/block/sda - let regex = Regex::new(r"(^.+?/ata\d+)/").context("Could not parse regex for ata link")?; - let ata_path_match = regex + let ata_regex = + Regex::new(r"(^.+?/ata(\d+))/").context("Could not parse regex for ata slot")?; + let ata_sub_path_match = ata_regex .captures(&symlink) - .context("No ata match found, probably no ata device")? - .get(1) - .context("TODO")? - .as_str(); + .context("No ata match found, probably no ata device")?; + let ata_sub_path = ata_sub_path_match.get(1).context("TODO")?.as_str(); - let ata_path = Path::new(&self.sysfs_path).join("..").join(&ata_path_match); + let ata_device = ata_sub_path_match + .get(2) + .context("could not match digits in ata")? + .as_str() + .parse::()?; + + let ata_path = Path::new(&self.sysfs_path).join("..").join(&ata_sub_path); let dedotted_path = ata_path.parse_dot()?.clone(); let sub_dirs = std::fs::read_dir(dedotted_path).context("Could not read ata path")?; - let link_name = sub_dirs - .filter(|x| { - x.as_ref() - .unwrap() + + let ata_link_regex = + Regex::new(r"(^link(\d+))$").context("Could not parse regex for ata link")?; + let ata_link = sub_dirs + .filter_map(|x| { + let file_name = x + .as_ref() + .unwrap() //TODO .file_name() .to_string_lossy() - .starts_with("link") + .to_string(); + if let Some(capture) = ata_link_regex.captures(&file_name) { + capture.get(2).unwrap().as_str().parse::().ok() + } else { + None + } }) .next() - .context("No ata link name found")?? - .file_name(); - //Move this to Link - Ok(Link::Sata(LinkData::from_sata_link( - link_name.to_string_lossy().as_ref(), - )?)) + .context("No ata link number found")?; + + Ok(AtaSlot { + ata_device, + ata_link, + }) } /// Returns the World-Wide Identification of the drive diff --git a/src/utils/gpu/mod.rs b/src/utils/gpu/mod.rs index d9a99f1..fa464cd 100644 --- a/src/utils/gpu/mod.rs +++ b/src/utils/gpu/mod.rs @@ -532,7 +532,7 @@ impl Gpu { pub fn link(&self) -> Result { if let GpuIdentifier::PciSlot(pci_slot) = self.gpu_identifier() { - let pcie_link = LinkData::from_pci_slot(pci_slot)?; + let pcie_link = LinkData::from_pci_slot(&pci_slot)?; Ok(Link::Pcie(pcie_link)) } else { bail!("Could not retrieve PciSlot from Gpu"); diff --git a/src/utils/link.rs b/src/utils/link.rs index 2b00ceb..894a758 100644 --- a/src/utils/link.rs +++ b/src/utils/link.rs @@ -1,4 +1,5 @@ use crate::i18n::i18n; +use crate::utils::drive::AtaSlot; use crate::utils::link::SataSpeed::{Sata150, Sata300, Sata600}; use anyhow::{anyhow, bail, Context, Error, Result}; use process_data::pci_slot::PciSlot; @@ -42,7 +43,7 @@ pub enum SataSpeed { } impl LinkData { - pub fn from_pci_slot(pci_slot: PciSlot) -> Result { + pub fn from_pci_slot(pci_slot: &PciSlot) -> Result { let pcie_dir = format!("/sys/bus/pci/devices/{pci_slot}/"); let pcie_folder = Path::new(pcie_dir.as_str()); if pcie_folder.exists() { @@ -114,8 +115,9 @@ where } impl LinkData { - pub fn from_sata_link(sata_link: &str) -> Result { - let ata_link_path = Path::new("/sys/class/ata_link").join(sata_link); + pub fn from_ata_slot(ata_slot: &AtaSlot) -> Result { + let ata_link_path = + Path::new("/sys/class/ata_link").join(format!("link{}", ata_slot.ata_link)); if std::fs::exists(&ata_link_path)? { let current_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd")) .map(|x| x.trim().to_string()) @@ -132,7 +134,7 @@ impl LinkData { }; return Ok(Self { current, max }); } - bail!("ata link path not found for '{}'", sata_link); + bail!("ata link path not found for '{:?}'", ata_slot); } } From 17f71b9ef8ccb63ea5841cf7eece22cf105df110 Mon Sep 17 00:00:00 2001 From: Peter de Kraker Date: Wed, 19 Feb 2025 19:28:34 +0100 Subject: [PATCH 3/5] Cleanup --- src/utils/drive.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/utils/drive.rs b/src/utils/drive.rs index cb86edf..e2c9cd8 100644 --- a/src/utils/drive.rs +++ b/src/utils/drive.rs @@ -85,7 +85,7 @@ pub enum DriveType { #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)] pub enum DriveSlot { - Pcie(PciSlot), + Pci(PciSlot), Ata(AtaSlot), #[default] Unknown, @@ -160,7 +160,7 @@ impl Drive { drive.block_device = block_device; drive.model = drive.model().ok().map(|model| model.trim().to_string()); drive.drive_type = drive.drive_type().unwrap_or_default(); - drive.slot = drive.slot(); + drive.slot = drive.slot().unwrap_or_default(); trace!("Created Drive object of {path:?}: {drive:?}"); drive @@ -331,26 +331,32 @@ impl Drive { /// reading or parsing, or if the drive link type is not supported pub fn link(&self) -> Result { match self.slot { - DriveSlot::Pcie(slot) => Ok(Link::Pcie(LinkData::from_pci_slot(&slot)?)), + DriveSlot::Pci(slot) => Ok(Link::Pcie(LinkData::from_pci_slot(&slot)?)), DriveSlot::Ata(slot) => Ok(Link::Sata(LinkData::from_ata_slot(&slot)?)), _ => Err(anyhow!("unsupported drive connection type")), } } - pub fn slot(&self) -> DriveSlot { + pub fn slot(&self) -> Result { + if let Ok(pci_slot) = self.pci_slot() { + Ok(DriveSlot::Pci(pci_slot)) + } else if let Ok(ata_slot) = self.ata_slot() { + Ok(DriveSlot::Ata(ata_slot)) + } else { + Err(anyhow!("unsupported drive slot type")) + } + } + + fn pci_slot(&self) -> Result { let pci_address_path = self.sysfs_path.join("device").join("address"); let pci_address = std::fs::read_to_string(pci_address_path).map(|x| x.trim().to_string()); if let Ok(pci_address) = pci_address { if let Ok(pci_slot) = PciSlot::from_str(&pci_address) { - return DriveSlot::Pcie(pci_slot); + return Ok(pci_slot); } } - if let Ok(ata_slot) = self.ata_slot() { - return DriveSlot::Ata(ata_slot); - } - DriveSlot::Unknown + Err(anyhow!("No PCI slot detected")) } - fn ata_slot(&self) -> Result { let symlink = std::fs::read_link(&self.sysfs_path) .context("Could not read sysfs_path as symlink")? From f137de0c64aec799035fa49617c63bbc2d15a5be Mon Sep 17 00:00:00 2001 From: Peter de Kraker Date: Wed, 19 Feb 2025 19:38:06 +0100 Subject: [PATCH 4/5] Cleanup --- src/utils/drive.rs | 10 +++++++--- src/utils/link.rs | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/utils/drive.rs b/src/utils/drive.rs index e2c9cd8..90e1280 100644 --- a/src/utils/drive.rs +++ b/src/utils/drive.rs @@ -357,6 +357,7 @@ impl Drive { } Err(anyhow!("No PCI slot detected")) } + fn ata_slot(&self) -> Result { let symlink = std::fs::read_link(&self.sysfs_path) .context("Could not read sysfs_path as symlink")? @@ -369,7 +370,10 @@ impl Drive { let ata_sub_path_match = ata_regex .captures(&symlink) .context("No ata match found, probably no ata device")?; - let ata_sub_path = ata_sub_path_match.get(1).context("TODO")?.as_str(); + let ata_sub_path = ata_sub_path_match + .get(1) + .context("No ata match found, probably no ata device")? + .as_str(); let ata_device = ata_sub_path_match .get(2) @@ -378,8 +382,8 @@ impl Drive { .parse::()?; let ata_path = Path::new(&self.sysfs_path).join("..").join(&ata_sub_path); - let dedotted_path = ata_path.parse_dot()?.clone(); - let sub_dirs = std::fs::read_dir(dedotted_path).context("Could not read ata path")?; + let dot_parsed_path = ata_path.parse_dot()?.clone(); + let sub_dirs = std::fs::read_dir(dot_parsed_path).context("Could not read ata path")?; let ata_link_regex = Regex::new(r"(^link(\d+))$").context("Could not parse regex for ata link")?; diff --git a/src/utils/link.rs b/src/utils/link.rs index 894a758..5b9defe 100644 --- a/src/utils/link.rs +++ b/src/utils/link.rs @@ -182,6 +182,7 @@ impl FromStr for SataSpeed { fn from_str(s: &str) -> std::result::Result { match s { + // https://en.wikipedia.org/wiki/SATA "1.5 Gbps" => Ok(Sata150), "3.0 Gbps" => Ok(Sata300), "6.0 Gbps" => Ok(Sata600), From 5ac675863b159eca035714ad52f10b49243e228d Mon Sep 17 00:00:00 2001 From: nokyan Date: Thu, 20 Feb 2025 15:56:23 +0100 Subject: [PATCH 5/5] More compact code and general cargo clippy fixes --- Cargo.lock | 10 ++++++++++ src/ui/window.rs | 18 ++++++----------- src/utils/battery.rs | 24 +++++++++++----------- src/utils/cpu.rs | 2 +- src/utils/drive.rs | 47 +++++++++++++++++++------------------------- src/utils/gpu/mod.rs | 2 +- src/utils/link.rs | 40 ++++++++++++++++++------------------- src/utils/memory.rs | 2 +- 8 files changed, 71 insertions(+), 74 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e946152..8bc954b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,6 +1186,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "path-dedot" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07ba0ad7e047712414213ff67533e6dd477af0a4e1d14fb52343e53d30ea9397" +dependencies = [ + "once_cell", +] + [[package]] name = "pin-project-lite" version = "0.2.15" @@ -1344,6 +1353,7 @@ dependencies = [ "num_cpus", "nvml-wrapper", "paste", + "path-dedot", "plotters", "plotters-cairo", "pretty_assertions", diff --git a/src/ui/window.rs b/src/ui/window.rs index 91dd198..6a93570 100644 --- a/src/ui/window.rs +++ b/src/ui/window.rs @@ -354,12 +354,9 @@ impl MainWindow { #[strong(rename_to = this)] self, move |_, key, _, _| { - match key { - gdk::Key::Control_L => { - debug!("Ctrl is being held, halting apps and processes updates"); - this.imp().pause_updates.set(true); - } - _ => (), + if key == gdk::Key::Control_L { + debug!("Ctrl is being held, halting apps and processes updates"); + this.imp().pause_updates.set(true); }; glib::Propagation::Proceed } @@ -368,12 +365,9 @@ impl MainWindow { #[weak] imp, move |_, key, _, _| { - match key { - gdk::Key::Control_L => { - debug!("Ctrl has been released, continuing apps and processes updates"); - imp.pause_updates.set(false); - } - _ => (), + if key == gdk::Key::Control_L { + debug!("Ctrl has been released, continuing apps and processes updates"); + imp.pause_updates.set(false); }; } )); diff --git a/src/utils/battery.rs b/src/utils/battery.rs index 5142209..eecb4aa 100644 --- a/src/utils/battery.rs +++ b/src/utils/battery.rs @@ -176,19 +176,21 @@ impl Battery { Ok(list) } - pub fn is_valid_power_supply(path: &PathBuf) -> bool { + pub fn is_valid_power_supply>(path: P) -> bool { + let path = path.as_ref(); + //type == "Battery" //scope != "Device" (HID device batteries) - let power_supply_type = std::fs::read_to_string(path.join("type")); - let power_supply_scope = std::fs::read_to_string(path.join("scope")); - if let Ok(power_supply_type) = power_supply_type { - if power_supply_type.trim() == "Battery" { - if power_supply_scope.is_err() || power_supply_scope.unwrap().trim() != "Device" { - return true; - } - } - } - false + + let power_supply_type_is_battery = std::fs::read_to_string(path.join("type")) + .map(|ps_type| ps_type.trim() == "Battery") + .unwrap_or_default(); + + let power_supply_scope_is_not_device = std::fs::read_to_string(path.join("scope")) + .map(|ps_scope| ps_scope.trim() != "Device") + .unwrap_or(true); + + power_supply_type_is_battery && power_supply_scope_is_not_device } pub fn from_sysfs>(sysfs_path: P) -> Battery { diff --git a/src/utils/cpu.rs b/src/utils/cpu.rs index 2dcc467..60a438a 100644 --- a/src/utils/cpu.rs +++ b/src/utils/cpu.rs @@ -295,7 +295,7 @@ fn parse_proc_stat>(stat: S) -> Vec> { .lines() .skip(1) .filter(|line| line.starts_with("cpu")) - .map(|line| parse_proc_stat_line(line)) + .map(parse_proc_stat_line) .collect() } diff --git a/src/utils/drive.rs b/src/utils/drive.rs index 90e1280..b491fff 100644 --- a/src/utils/drive.rs +++ b/src/utils/drive.rs @@ -1,7 +1,7 @@ use super::units::convert_storage; use crate::i18n::{i18n, i18n_f}; use crate::utils::link::{Link, LinkData}; -use anyhow::{anyhow, Context, Result}; +use anyhow::{bail, Context, Result}; use gtk::gio::{Icon, ThemedIcon}; use lazy_regex::{lazy_regex, Lazy, Regex}; use log::trace; @@ -20,6 +20,10 @@ static RE_DRIVE: Lazy = lazy_regex!( r" *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*) *(?P[0-9]*)" ); +static RE_ATA_LINK: Lazy = lazy_regex!(r"(^link(\d+))$"); + +static RE_ATA_SLOT: Lazy = lazy_regex!(r"(^.+?/ata(\d+))/"); + #[derive(Debug)] pub struct DriveData { pub inner: Drive, @@ -333,7 +337,7 @@ impl Drive { match self.slot { DriveSlot::Pci(slot) => Ok(Link::Pcie(LinkData::from_pci_slot(&slot)?)), DriveSlot::Ata(slot) => Ok(Link::Sata(LinkData::from_ata_slot(&slot)?)), - _ => Err(anyhow!("unsupported drive connection type")), + _ => bail!("unsupported drive connection type"), } } @@ -343,19 +347,16 @@ impl Drive { } else if let Ok(ata_slot) = self.ata_slot() { Ok(DriveSlot::Ata(ata_slot)) } else { - Err(anyhow!("unsupported drive slot type")) + bail!("unsupported drive slot type") } } fn pci_slot(&self) -> Result { let pci_address_path = self.sysfs_path.join("device").join("address"); - let pci_address = std::fs::read_to_string(pci_address_path).map(|x| x.trim().to_string()); - if let Ok(pci_address) = pci_address { - if let Ok(pci_slot) = PciSlot::from_str(&pci_address) { - return Ok(pci_slot); - } - } - Err(anyhow!("No PCI slot detected")) + let pci_address = + std::fs::read_to_string(pci_address_path).map(|x| x.trim().to_string())?; + + Ok(PciSlot::from_str(&pci_address)?) } fn ata_slot(&self) -> Result { @@ -365,11 +366,10 @@ impl Drive { .to_string(); // ../../devices/pci0000:40/0000:40:08.3/0000:47:00.0/ata25/host24/target24:0:0/24:0:0:0/block/sda - let ata_regex = - Regex::new(r"(^.+?/ata(\d+))/").context("Could not parse regex for ata slot")?; - let ata_sub_path_match = ata_regex + let ata_sub_path_match = RE_ATA_SLOT .captures(&symlink) .context("No ata match found, probably no ata device")?; + let ata_sub_path = ata_sub_path_match .get(1) .context("No ata match found, probably no ata device")? @@ -381,25 +381,18 @@ impl Drive { .as_str() .parse::()?; - let ata_path = Path::new(&self.sysfs_path).join("..").join(&ata_sub_path); + let ata_path = Path::new(&self.sysfs_path).join("..").join(ata_sub_path); let dot_parsed_path = ata_path.parse_dot()?.clone(); let sub_dirs = std::fs::read_dir(dot_parsed_path).context("Could not read ata path")?; - let ata_link_regex = - Regex::new(r"(^link(\d+))$").context("Could not parse regex for ata link")?; let ata_link = sub_dirs .filter_map(|x| { - let file_name = x - .as_ref() - .unwrap() //TODO - .file_name() - .to_string_lossy() - .to_string(); - if let Some(capture) = ata_link_regex.captures(&file_name) { - capture.get(2).unwrap().as_str().parse::().ok() - } else { - None - } + x.ok().and_then(|x| { + RE_ATA_LINK + .captures(&x.file_name().to_string_lossy()) + .and_then(|captures| captures.get(2)) + .and_then(|capture| capture.as_str().parse::().ok()) + }) }) .next() .context("No ata link number found")?; diff --git a/src/utils/gpu/mod.rs b/src/utils/gpu/mod.rs index fa464cd..3f7e393 100644 --- a/src/utils/gpu/mod.rs +++ b/src/utils/gpu/mod.rs @@ -29,7 +29,7 @@ pub const VID_AMD: u16 = 0x1002; pub const VID_INTEL: u16 = 0x8086; pub const VID_NVIDIA: u16 = 0x10DE; -const RE_CARD_ENUMARATOR: Lazy = lazy_regex!(r"(\d+)\/?$"); +static RE_CARD_ENUMARATOR: Lazy = lazy_regex!(r"(\d+)\/?$"); #[derive(Debug)] pub struct GpuData { diff --git a/src/utils/link.rs b/src/utils/link.rs index 5b9defe..2983162 100644 --- a/src/utils/link.rs +++ b/src/utils/link.rs @@ -4,7 +4,7 @@ use crate::utils::link::SataSpeed::{Sata150, Sata300, Sata600}; use anyhow::{anyhow, bail, Context, Error, Result}; use process_data::pci_slot::PciSlot; use std::fmt::{Display, Formatter}; -use std::path::{Path, PathBuf}; +use std::path::Path; use std::str::FromStr; #[derive(Debug, Default)] @@ -52,7 +52,9 @@ impl LinkData { bail!("Could not find PCIe address entry for {pci_slot}"); } - fn read_pcie_link_data(path: &PathBuf) -> Result { + fn read_pcie_link_data>(path: P) -> Result { + let path = path.as_ref(); + let current_pcie_speed_raw = std::fs::read_to_string(path.join("current_link_speed")) .map(|x| x.trim().to_string()) .context("Could not read current link speed")?; @@ -79,9 +81,10 @@ impl LinkData { } impl PcieLinkData { - pub fn parse(speed_raw: &str, width_raw: &str) -> Result { - let speed = PcieSpeed::from_str(speed_raw)?; + pub fn parse>(speed_raw: S, width_raw: S) -> Result { + let speed = PcieSpeed::from_str(speed_raw.as_ref())?; let width = width_raw + .as_ref() .parse::() .context("Could not parse PCIe width")?; Ok(PcieLinkData { speed, width }) @@ -118,23 +121,18 @@ impl LinkData { pub fn from_ata_slot(ata_slot: &AtaSlot) -> Result { let ata_link_path = Path::new("/sys/class/ata_link").join(format!("link{}", ata_slot.ata_link)); - if std::fs::exists(&ata_link_path)? { - let current_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd")) - .map(|x| x.trim().to_string()) - .context("Could not read sata_spd")?; - let max_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd_max")) - .map(|x| x.trim().to_string()) - .context("Could not read sata_spd_max"); - - let current = SataSpeed::from_str(¤t_sata_speed_raw); - let max = if let Ok(max_sata_speed_raw) = max_sata_speed_raw { - SataSpeed::from_str(&max_sata_speed_raw) - } else { - Err(anyhow::anyhow!("Could not read sata_spd_max")) - }; - return Ok(Self { current, max }); - } - bail!("ata link path not found for '{:?}'", ata_slot); + + let current_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd")) + .map(|x| x.trim().to_string()) + .context("Could not read sata_spd")?; + let max_sata_speed_raw = std::fs::read_to_string(ata_link_path.join("sata_spd_max")) + .map(|x| x.trim().to_string()) + .context("Could not read sata_spd_max"); + + let current = SataSpeed::from_str(¤t_sata_speed_raw); + let max = max_sata_speed_raw.and_then(|raw| SataSpeed::from_str(&raw)); + + Ok(Self { current, max }) } } diff --git a/src/utils/memory.rs b/src/utils/memory.rs index 130d11f..fa2e11b 100644 --- a/src/utils/memory.rs +++ b/src/utils/memory.rs @@ -241,7 +241,7 @@ impl MemoryDevice { .and_then(|regex| regex.captures(dmi)) .and_then(|captures| captures.get(1)) .and_then(|capture| capture.as_str().parse::().ok()) - .map_or(true, |int| int != 0); + != Some(0); let speed = if installed { Regex::new(&TEMPLATE_RE_CONFIGURED_SPEED_MTS.replace('%', &i))