diff --git a/src/common/disk.rs b/src/common/disk.rs index 1fe004f11..4adaf7273 100644 --- a/src/common/disk.rs +++ b/src/common/disk.rs @@ -4,6 +4,7 @@ use std::ffi::OsStr; use std::fmt; use std::path::Path; +use crate::common::impl_get_set::impl_get_set; use crate::DiskUsage; /// Struct containing a disk information. @@ -133,7 +134,9 @@ impl Disk { self.inner.is_read_only() } - /// Updates the disk' information. + /// Updates the disk' information with everything loaded. + /// + /// Equivalent to [`Disk::refresh_specifics`]`(`[`DiskRefreshKind::everything`]`())`. /// /// ```no_run /// use sysinfo::Disks; @@ -144,7 +147,21 @@ impl Disk { /// } /// ``` pub fn refresh(&mut self) -> bool { - self.inner.refresh() + self.refresh_specifics(DiskRefreshKind::everything()) + } + + /// Updates the disk's information corresponding to the given [`DiskRefreshKind`]. + /// + /// ```no_run + /// use sysinfo::{Disks, DiskRefreshKind}; + /// + /// let mut disks = Disks::new_with_refreshed_list(); + /// for disk in disks.list_mut() { + /// disk.refresh_specifics(DiskRefreshKind::new()); + /// } + /// ``` + pub fn refresh_specifics(&mut self, refreshes: DiskRefreshKind) -> bool { + self.inner.refresh_specifics(refreshes) } /// Returns number of bytes read and written by the disk @@ -244,7 +261,8 @@ impl Disks { } /// Creates a new [`Disks`][crate::Disks] type with the disk list loaded. - /// It is a combination of [`Disks::new`] and [`Disks::refresh_list`]. + /// + /// Equivalent to [`Disks::new_with_refreshed_list_specifics`]`(`[`DiskRefreshKind::everything`]`())`. /// /// ```no_run /// use sysinfo::Disks; @@ -255,8 +273,24 @@ impl Disks { /// } /// ``` pub fn new_with_refreshed_list() -> Self { + Self::new_with_refreshed_list_specifics(DiskRefreshKind::everything()) + } + + /// Creates a new [`Disks`][crate::Disks] type with the disk list loaded + /// and refreshed according to the given [`DiskRefreshKind`]. It is a combination of + /// [`Disks::new`] and [`Disks::refresh_list_specifics`]. + /// + /// ```no_run + /// use sysinfo::{Disks, DiskRefreshKind}; + /// + /// let mut disks = Disks::new_with_refreshed_list_specifics(DiskRefreshKind::new()); + /// for disk in disks.list() { + /// println!("{disk:?}"); + /// } + /// ``` + pub fn new_with_refreshed_list_specifics(refreshes: DiskRefreshKind) -> Self { let mut disks = Self::new(); - disks.refresh_list(); + disks.refresh_list_specifics(refreshes); disks } @@ -291,6 +325,13 @@ impl Disks { /// Refreshes the listed disks' information. /// + /// Equivalent to [`Disks::refresh_specifics`]`(`[`DiskRefreshKind::everything`]`())`. + pub fn refresh(&mut self) { + self.refresh_specifics(DiskRefreshKind::everything()); + } + + /// Refreshes the listed disks' information according to the given [`DiskRefreshKind`]. + /// /// ⚠️ If a disk is added or removed, this method won't take it into account. Use /// [`Disks::refresh_list`] instead. /// @@ -304,30 +345,45 @@ impl Disks { /// // We wait some time...? /// disks.refresh(); /// ``` - pub fn refresh(&mut self) { - self.inner.refresh(); + pub fn refresh_specifics(&mut self, refreshes: DiskRefreshKind) { + self.inner.refresh_specifics(refreshes); } /// The disk list will be emptied then completely recomputed. /// + /// Equivalent to [`Disks::refresh_list_specifics`]`(`[`DiskRefreshKind::everything`]`())`. + /// + /// ```no_run + /// use sysinfo::Disks; + /// + /// let mut disks = Disks::new(); + /// disks.refresh_list(); + /// ``` + pub fn refresh_list(&mut self) { + self.refresh_list_specifics(DiskRefreshKind::everything()); + } + + /// The disk list will be emptied then completely recomputed according to the given + /// [`DiskRefreshKind`]. + /// /// ## Linux /// /// ⚠️ On Linux, the [NFS](https://en.wikipedia.org/wiki/Network_File_System) file /// systems are ignored and the information of a mounted NFS **cannot** be obtained - /// via [`Disks::refresh_list`]. This is due to the fact that I/O function - /// `statvfs` used by [`Disks::refresh_list`] is blocking and + /// via [`Disks::refresh_list_specifics`]. This is due to the fact that I/O function + /// `statvfs` used by [`Disks::refresh_list_specifics`] is blocking and /// [may hang](https://github.com/GuillaumeGomez/sysinfo/pull/876) in some cases, /// requiring to call `systemctl stop` to terminate the NFS service from the remote /// server in some cases. /// /// ```no_run - /// use sysinfo::Disks; + /// use sysinfo::{Disks, DiskRefreshKind}; /// /// let mut disks = Disks::new(); - /// disks.refresh_list(); + /// disks.refresh_list_specifics(DiskRefreshKind::new()); /// ``` - pub fn refresh_list(&mut self) { - self.inner.refresh_list(); + pub fn refresh_list_specifics(&mut self, refreshes: DiskRefreshKind) { + self.inner.refresh_list_specifics(refreshes); } } @@ -376,3 +432,111 @@ impl fmt::Display for DiskKind { }) } } + +/// Used to determine what you want to refresh specifically on the [`Disk`] type. +/// +/// ⚠️ Just like all other refresh types, ruling out a refresh doesn't assure you that +/// the information won't be retrieved if the information is accessible without needing +/// extra computation. +/// +/// ```no_run +/// use sysinfo::{Disks, DiskRefreshKind}; +/// +/// let mut disks = Disks::new_with_refreshed_list_specifics(DiskRefreshKind::new()); +/// +/// for disk in disks.list() { +/// assert_eq!(disk.total_space(), 0); +/// } +/// ``` +#[derive(Clone, Copy, Debug)] +pub struct DiskRefreshKind { + kind: bool, + total_space: bool, + available_space: bool, + is_removable: bool, + is_read_only: bool, + usage: bool, +} + +impl Default for DiskRefreshKind { + fn default() -> Self { + Self { + kind: true, + total_space: false, + available_space: false, + is_removable: false, + is_read_only: false, + usage: false, + } + } +} + +impl DiskRefreshKind { + /// Creates a new `DiskRefreshKind` with every refresh *except kind* set to false. + /// + /// ``` + /// use sysinfo::DiskRefreshKind; + /// + /// let r = DiskRefreshKind::new(); + /// + /// assert_eq!(r.kind(), true); + /// assert_eq!(r.total_space(), false); + /// assert_eq!(r.available_space(), false); + /// assert_eq!(r.is_removable(), false); + /// assert_eq!(r.is_read_only(), false); + /// ``` + pub fn new() -> Self { + Self::default() + } + + /// Creates a new `DiskRefreshKind` with every refresh set to true. + /// + /// ``` + /// use sysinfo::DiskRefreshKind; + /// + /// let r = DiskRefreshKind::everything(); + /// + /// assert_eq!(r.kind(), true); + /// assert_eq!(r.total_space(), true); + /// assert_eq!(r.available_space(), true); + /// assert_eq!(r.is_removable(), true); + /// assert_eq!(r.is_read_only(), true); + /// ``` + pub fn everything() -> Self { + Self { + kind: true, + total_space: true, + available_space: true, + is_removable: true, + is_read_only: true, + usage: true, + } + } + + impl_get_set!(DiskRefreshKind, kind, with_kind, without_kind); + impl_get_set!( + DiskRefreshKind, + total_space, + with_total_space, + without_total_space + ); + impl_get_set!( + DiskRefreshKind, + available_space, + with_available_space, + without_available_space + ); + impl_get_set!( + DiskRefreshKind, + is_removable, + with_is_removable, + without_is_removable + ); + impl_get_set!( + DiskRefreshKind, + is_read_only, + with_is_read_only, + without_is_read_only + ); + impl_get_set!(DiskRefreshKind, usage, with_usage, without_usage); +} diff --git a/src/common/impl_get_set.rs b/src/common/impl_get_set.rs new file mode 100644 index 000000000..2df3616db --- /dev/null +++ b/src/common/impl_get_set.rs @@ -0,0 +1,173 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +macro_rules! impl_get_set { + ($ty_name:ident, $name:ident, $with:ident, $without:ident $(, $extra_doc:literal)? $(,)?) => { + #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.")] + $(#[doc = concat!(" +", $extra_doc, " +")])? + #[doc = concat!(" +``` +use sysinfo::", stringify!($ty_name), "; + +let r = ", stringify!($ty_name), "::new(); + +let r = r.with_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), true); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), false); +```")] + pub fn $name(&self) -> bool { + self.$name + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`. + +``` +use sysinfo::", stringify!($ty_name), "; + +let r = ", stringify!($ty_name), "::new(); + +let r = r.with_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), true); +```")] + #[must_use] + pub fn $with(mut self) -> Self { + self.$name = true; + self + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`. + +``` +use sysinfo::", stringify!($ty_name), "; + +let r = ", stringify!($ty_name), "::everything(); +assert_eq!(r.", stringify!($name), "(), true); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), false); +```")] + #[must_use] + pub fn $without(mut self) -> Self { + self.$name = false; + self + } + }; + + // To handle `UpdateKind`. + ($ty_name:ident, $name:ident, $with:ident, $without:ident, UpdateKind $(, $extra_doc:literal)? $(,)?) => { + #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.")] + $(#[doc = concat!(" +", $extra_doc, " +")])? + #[doc = concat!(" +``` +use sysinfo::{", stringify!($ty_name), ", UpdateKind}; + +let r = ", stringify!($ty_name), "::new(); +assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); + +let r = r.with_", stringify!($name), "(UpdateKind::OnlyIfNotSet); +assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); +```")] + pub fn $name(&self) -> UpdateKind { + self.$name + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind. + +``` +use sysinfo::{", stringify!($ty_name), ", UpdateKind}; + +let r = ", stringify!($ty_name), "::new(); +assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); + +let r = r.with_", stringify!($name), "(UpdateKind::OnlyIfNotSet); +assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); +```")] + #[must_use] + pub fn $with(mut self, kind: UpdateKind) -> Self { + self.$name = kind; + self + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `UpdateKind::Never`. + +``` +use sysinfo::{", stringify!($ty_name), ", UpdateKind}; + +let r = ", stringify!($ty_name), "::everything(); +assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); +```")] + #[must_use] + pub fn $without(mut self) -> Self { + self.$name = UpdateKind::Never; + self + } + }; + + // To handle `*RefreshKind`. + ($ty_name:ident, $name:ident, $with:ident, $without:ident, $typ:ty $(,)?) => { + #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind. + +``` +use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "}; + +let r = ", stringify!($ty_name), "::new(); +assert_eq!(r.", stringify!($name), "().is_some(), false); + +let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything()); +assert_eq!(r.", stringify!($name), "().is_some(), true); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "().is_some(), false); +```")] + pub fn $name(&self) -> Option<$typ> { + self.$name + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `Some(...)`. + +``` +use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "}; + +let r = ", stringify!($ty_name), "::new(); +assert_eq!(r.", stringify!($name), "().is_some(), false); + +let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything()); +assert_eq!(r.", stringify!($name), "().is_some(), true); +```")] + #[must_use] + pub fn $with(mut self, kind: $typ) -> Self { + self.$name = Some(kind); + self + } + + #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `None`. + +``` +use sysinfo::", stringify!($ty_name), "; + +let r = ", stringify!($ty_name), "::everything(); +assert_eq!(r.", stringify!($name), "().is_some(), true); + +let r = r.without_", stringify!($name), "(); +assert_eq!(r.", stringify!($name), "().is_some(), false); +```")] + #[must_use] + pub fn $without(mut self) -> Self { + self.$name = None; + self + } + }; +} + +pub(crate) use impl_get_set; diff --git a/src/common/mod.rs b/src/common/mod.rs index cbbf52131..b59ba48f3 100644 --- a/src/common/mod.rs +++ b/src/common/mod.rs @@ -4,6 +4,8 @@ pub(crate) mod component; #[cfg(feature = "disk")] pub(crate) mod disk; +#[cfg(any(feature = "system", feature = "disk"))] +pub(crate) mod impl_get_set; #[cfg(feature = "network")] pub(crate) mod network; #[cfg(feature = "system")] diff --git a/src/common/system.rs b/src/common/system.rs index 71c71e5b1..73fd41078 100644 --- a/src/common/system.rs +++ b/src/common/system.rs @@ -6,6 +6,7 @@ use std::fmt; use std::path::Path; use std::str::FromStr; +use crate::common::impl_get_set::impl_get_set; use crate::common::DiskUsage; use crate::{CpuInner, Gid, ProcessInner, SystemInner, Uid}; @@ -1686,178 +1687,6 @@ cfg_if! { } } -macro_rules! impl_get_set { - ($ty_name:ident, $name:ident, $with:ident, $without:ident $(, $extra_doc:literal)? $(,)?) => { - #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.")] - $(#[doc = concat!(" -", $extra_doc, " -")])? - #[doc = concat!(" -``` -use sysinfo::", stringify!($ty_name), "; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "(), false); - -let r = r.with_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), true); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), false); -```")] - pub fn $name(&self) -> bool { - self.$name - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `true`. - -``` -use sysinfo::", stringify!($ty_name), "; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "(), false); - -let r = r.with_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), true); -```")] - #[must_use] - pub fn $with(mut self) -> Self { - self.$name = true; - self - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `false`. - -``` -use sysinfo::", stringify!($ty_name), "; - -let r = ", stringify!($ty_name), "::everything(); -assert_eq!(r.", stringify!($name), "(), true); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), false); -```")] - #[must_use] - pub fn $without(mut self) -> Self { - self.$name = false; - self - } - }; - - // To handle `UpdateKind`. - ($ty_name:ident, $name:ident, $with:ident, $without:ident, UpdateKind $(, $extra_doc:literal)? $(,)?) => { - #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind.")] - $(#[doc = concat!(" -", $extra_doc, " -")])? - #[doc = concat!(" -``` -use sysinfo::{", stringify!($ty_name), ", UpdateKind}; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); - -let r = r.with_", stringify!($name), "(UpdateKind::OnlyIfNotSet); -assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); -```")] - pub fn $name(&self) -> UpdateKind { - self.$name - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind. - -``` -use sysinfo::{", stringify!($ty_name), ", UpdateKind}; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); - -let r = r.with_", stringify!($name), "(UpdateKind::OnlyIfNotSet); -assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); -```")] - #[must_use] - pub fn $with(mut self, kind: UpdateKind) -> Self { - self.$name = kind; - self - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `UpdateKind::Never`. - -``` -use sysinfo::{", stringify!($ty_name), ", UpdateKind}; - -let r = ", stringify!($ty_name), "::everything(); -assert_eq!(r.", stringify!($name), "(), UpdateKind::OnlyIfNotSet); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "(), UpdateKind::Never); -```")] - #[must_use] - pub fn $without(mut self) -> Self { - self.$name = UpdateKind::Never; - self - } - }; - - // To handle `*RefreshKind`. - ($ty_name:ident, $name:ident, $with:ident, $without:ident, $typ:ty $(,)?) => { - #[doc = concat!("Returns the value of the \"", stringify!($name), "\" refresh kind. - -``` -use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "}; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "().is_some(), false); - -let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything()); -assert_eq!(r.", stringify!($name), "().is_some(), true); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "().is_some(), false); -```")] - pub fn $name(&self) -> Option<$typ> { - self.$name - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `Some(...)`. - -``` -use sysinfo::{", stringify!($ty_name), ", ", stringify!($typ), "}; - -let r = ", stringify!($ty_name), "::new(); -assert_eq!(r.", stringify!($name), "().is_some(), false); - -let r = r.with_", stringify!($name), "(", stringify!($typ), "::everything()); -assert_eq!(r.", stringify!($name), "().is_some(), true); -```")] - #[must_use] - pub fn $with(mut self, kind: $typ) -> Self { - self.$name = Some(kind); - self - } - - #[doc = concat!("Sets the value of the \"", stringify!($name), "\" refresh kind to `None`. - -``` -use sysinfo::", stringify!($ty_name), "; - -let r = ", stringify!($ty_name), "::everything(); -assert_eq!(r.", stringify!($name), "().is_some(), true); - -let r = r.without_", stringify!($name), "(); -assert_eq!(r.", stringify!($name), "().is_some(), false); -```")] - #[must_use] - pub fn $without(mut self) -> Self { - self.$name = None; - self - } - }; -} - /// This enum allows you to specify when you want the related information to be updated. /// /// For example if you only want the [`Process::exe()`] information to be refreshed only if it's not diff --git a/src/lib.rs b/src/lib.rs index 9c12c2e6e..895df7f2d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,7 +71,7 @@ cfg_if! { #[cfg(feature = "component")] pub use crate::common::component::{Component, Components}; #[cfg(feature = "disk")] -pub use crate::common::disk::{Disk, DiskKind, Disks}; +pub use crate::common::disk::{Disk, DiskKind, DiskRefreshKind, Disks}; #[cfg(feature = "network")] pub use crate::common::network::{IpNetwork, MacAddr, NetworkData, Networks}; #[cfg(feature = "system")] diff --git a/src/unix/apple/disk.rs b/src/unix/apple/disk.rs index 2ac36ac17..51d3cb183 100644 --- a/src/unix/apple/disk.rs +++ b/src/unix/apple/disk.rs @@ -7,7 +7,7 @@ use crate::{ }, DiskUsage, }; -use crate::{Disk, DiskKind}; +use crate::{Disk, DiskKind, DiskRefreshKind}; use core_foundation_sys::array::CFArrayCreate; use core_foundation_sys::base::kCFAllocatorDefault; @@ -72,7 +72,7 @@ impl DiskInner { self.is_read_only } - pub(crate) fn refresh(&mut self) -> bool { + pub(crate) fn refresh_specifics(&mut self, _refreshes: DiskRefreshKind) -> bool { #[cfg(target_os = "macos")] let Some((read_bytes, written_bytes)) = self .bsd_name @@ -129,7 +129,7 @@ impl crate::DisksInner { } } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh_list_specifics(&mut self, _refreshes: DiskRefreshKind) { unsafe { // SAFETY: We don't keep any Objective-C objects around because we // don't make any direct Objective-C calls in this code. @@ -139,9 +139,9 @@ impl crate::DisksInner { } } - pub(crate) fn refresh(&mut self) { + pub(crate) fn refresh_specifics(&mut self, refreshes: DiskRefreshKind) { for disk in self.list_mut() { - disk.refresh(); + disk.refresh_specifics(refreshes); } } diff --git a/src/unix/freebsd/disk.rs b/src/unix/freebsd/disk.rs index ec841603e..32f48225c 100644 --- a/src/unix/freebsd/disk.rs +++ b/src/unix/freebsd/disk.rs @@ -1,5 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::{Disk, DiskKind, DiskUsage, DiskRefreshKind}; + use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::marker::PhantomData; @@ -75,7 +77,7 @@ impl DiskInner { self.is_read_only } - pub(crate) fn refresh(&mut self) -> bool { + pub(crate) fn refresh_specifics(&mut self, _refreshes: DiskRefreshKind) -> bool { refresh_disk(self) } @@ -96,8 +98,8 @@ impl crate::DisksInner { } } - pub(crate) fn refresh_list(&mut self) { - unsafe { get_all_list(&mut self.disks, true); } + pub(crate) fn refresh_list_specifics(&mut self, _refreshes: DiskRefreshKind) { + unsafe { get_all_list(&mut self.disks, true) } } pub(crate) fn list(&self) -> &[Disk] { @@ -108,7 +110,7 @@ impl crate::DisksInner { &mut self.disks } - pub(crate) fn refresh(&mut self) { + pub(crate) fn refresh_specifics(&mut self, refreshes: DiskRefreshKind) { unsafe { get_all_list(&mut self.disks, false); } } } diff --git a/src/unix/linux/disk.rs b/src/unix/linux/disk.rs index 7e2a0156c..0665ff6c2 100644 --- a/src/unix/linux/disk.rs +++ b/src/unix/linux/disk.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::utils::{get_all_utf8_data, to_cpath}; -use crate::{Disk, DiskKind, DiskUsage}; +use crate::{Disk, DiskKind, DiskUsage, DiskRefreshKind}; use libc::statvfs; use std::collections::HashMap; @@ -37,6 +37,8 @@ macro_rules! cast { pub(crate) struct DiskInner { type_: DiskKind, device_name: OsString, + // Potential future surprise: right now, this field is only needed by usage-related code, + // so it is only populated if DiskRefreshKind::usage() is true. actual_device_name: String, file_system: OsString, mount_point: PathBuf, @@ -83,35 +85,36 @@ impl DiskInner { self.is_read_only } - pub(crate) fn refresh(&mut self) -> bool { - self.efficient_refresh(&disk_stats()) + pub(crate) fn refresh_specifics(&mut self, refresh_kind: DiskRefreshKind) -> bool { + self.efficient_refresh(refresh_kind, &disk_stats(&refresh_kind)) } - fn efficient_refresh(&mut self, procfs_disk_stats: &HashMap) -> bool { - let Some((read_bytes, written_bytes)) = procfs_disk_stats - .get(&self.actual_device_name) - .map(|stat| (stat.sectors_read * SECTOR_SIZE, stat.sectors_written * SECTOR_SIZE)) - else { - sysinfo_debug!("Failed to update disk i/o stats"); - return false; + fn efficient_refresh(&mut self, refresh_kind: DiskRefreshKind, procfs_disk_stats: &HashMap) -> bool { + if refresh_kind.usage() { + let Some((read_bytes, written_bytes)) = procfs_disk_stats + .get(&self.actual_device_name) + .map(|stat| (stat.sectors_read * SECTOR_SIZE, stat.sectors_written * SECTOR_SIZE)) + else { + sysinfo_debug!("Failed to update disk i/o stats"); + return false; + }; + + self.old_read_bytes = self.read_bytes; + self.old_written_bytes = self.written_bytes; + self.read_bytes = read_bytes; + self.written_bytes = written_bytes; + } + + match unsafe { load_statvfs_values(&self.mount_point, refresh_kind) } { + Some((total, available, is_read_only)) => { + self.total_space = total; + self.available_space = available; + self.is_read_only = is_read_only; + }, + None => return false, }; - self.old_read_bytes = self.read_bytes; - self.old_written_bytes = self.written_bytes; - self.read_bytes = read_bytes; - self.written_bytes = written_bytes; - - unsafe { - let mut stat: statvfs = mem::zeroed(); - let mount_point_cpath = to_cpath(&self.mount_point); - if retry_eintr!(statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat)) == 0 { - let tmp = cast!(stat.f_bsize).saturating_mul(cast!(stat.f_bavail)); - self.available_space = cast!(tmp); - true - } else { - false - } - } + true } pub(crate) fn usage(&self) -> DiskUsage { @@ -131,17 +134,18 @@ impl crate::DisksInner { } } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh_list_specifics(&mut self, refresh_kind: DiskRefreshKind) { get_all_list( &mut self.disks, &get_all_utf8_data("/proc/mounts", 16_385).unwrap_or_default(), + refresh_kind, ) } - pub(crate) fn refresh(&mut self) { - let procfs_disk_stats = disk_stats(); + pub(crate) fn refresh_specifics(&mut self, refresh_kind: DiskRefreshKind) { + let procfs_disk_stats = disk_stats(&refresh_kind); for disk in self.list_mut() { - disk.inner.efficient_refresh(&procfs_disk_stats); + disk.inner.efficient_refresh(refresh_kind, &procfs_disk_stats); } } @@ -173,63 +177,96 @@ fn get_actual_device_name(device: &OsStr) -> String { .unwrap_or_default() } -fn new_disk( - device_name: &OsStr, +unsafe fn load_statvfs_values( mount_point: &Path, - file_system: &OsStr, - removable_entries: &[PathBuf], - procfs_disk_stats: &HashMap, -) -> Option { - let mount_point_cpath = to_cpath(mount_point); - let type_ = find_type_for_device_name(device_name); - let mut total = 0; - let mut available = 0; - let mut is_read_only = false; - unsafe { + refresh_kind: DiskRefreshKind, +) -> Option<(u64, u64, bool)> { + if refresh_kind.total_space() || refresh_kind.available_space() || refresh_kind.is_read_only() { + let mount_point_cpath = to_cpath(mount_point); let mut stat: statvfs = mem::zeroed(); if retry_eintr!(statvfs(mount_point_cpath.as_ptr() as *const _, &mut stat)) == 0 { let bsize = cast!(stat.f_bsize); let blocks = cast!(stat.f_blocks); let bavail = cast!(stat.f_bavail); - total = bsize.saturating_mul(blocks); - available = bsize.saturating_mul(bavail); - is_read_only = (stat.f_flag & libc::ST_RDONLY) != 0; - } - if total == 0 { - return None; + let total = bsize.saturating_mul(blocks); + let available = bsize.saturating_mul(bavail); + let is_read_only = (stat.f_flag & libc::ST_RDONLY) != 0; + + if total == 0 { + None + } else { + Some((total, available, is_read_only)) + } + } else { + None } - let mount_point = mount_point.to_owned(); - let is_removable = removable_entries - .iter() - .any(|e| e.as_os_str() == device_name); - - let actual_device_name = get_actual_device_name(device_name); - - let (read_bytes, written_bytes) = procfs_disk_stats - .get(&actual_device_name) - .map(|stat| (stat.sectors_read * SECTOR_SIZE, stat.sectors_written * SECTOR_SIZE)) - .unwrap_or_default(); - - Some(Disk { - inner: DiskInner { - type_, - device_name: device_name.to_owned(), - actual_device_name, - file_system: file_system.to_owned(), - mount_point, - total_space: cast!(total), - available_space: cast!(available), - is_removable, - is_read_only, - old_read_bytes: 0, - old_written_bytes: 0, - read_bytes, - written_bytes, - }, - }) + } else { + Some((Default::default(), Default::default(), Default::default())) } } +fn new_disk( + device_name: &OsStr, + mount_point: &Path, + file_system: &OsStr, + removable_entries: &[PathBuf], + procfs_disk_stats: &HashMap, + refresh_kind: DiskRefreshKind, +) -> Option { + let type_ = if refresh_kind.kind() { + find_type_for_device_name(device_name) + } else { + // TODO: discuss the representation of "you opted out of refreshing the DiskKind" + DiskKind::Unknown(-1) + }; + + let (total_space, available_space, is_read_only) = match unsafe { load_statvfs_values(mount_point, refresh_kind) } { + Some((total_space, available_space, is_read_only)) => (total_space, available_space, is_read_only), + None => return None, + }; + + let is_removable = if refresh_kind.is_removable() { + removable_entries + .iter() + .any(|e| e.as_os_str() == device_name) + } else { + Default::default() + }; + + let actual_device_name = if refresh_kind.usage() { + get_actual_device_name(device_name) + } else { + Default::default() + }; + + let (read_bytes, written_bytes) = if refresh_kind.usage() { + procfs_disk_stats + .get(&actual_device_name) + .map(|stat| (stat.sectors_read * SECTOR_SIZE, stat.sectors_written * SECTOR_SIZE)) + .unwrap_or_default() + } else { + (Default::default(), Default::default()) + }; + + Some(Disk { + inner: DiskInner { + type_, + device_name: device_name.to_owned(), + actual_device_name, + file_system: file_system.to_owned(), + mount_point: mount_point.to_owned(), + total_space, + available_space, + is_removable, + is_read_only, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes, + written_bytes, + }, + }) +} + #[allow(clippy::manual_range_contains)] fn find_type_for_device_name(device_name: &OsStr) -> DiskKind { // The format of devices are as follows: @@ -301,7 +338,7 @@ fn find_type_for_device_name(device_name: &OsStr) -> DiskKind { } } -fn get_all_list(container: &mut Vec, content: &str) { +fn get_all_list(container: &mut Vec, content: &str, refresh_kind: DiskRefreshKind) { container.clear(); // The goal of this array is to list all removable devices (the ones whose name starts with // "usb-"). @@ -322,7 +359,7 @@ fn get_all_list(container: &mut Vec, content: &str) { _ => Vec::new(), }; - let procfs_disk_stats = disk_stats(); + let procfs_disk_stats = disk_stats(&refresh_kind); for disk in content .lines() @@ -376,6 +413,7 @@ fn get_all_list(container: &mut Vec, content: &str) { fs_vfstype.as_ref(), &removable_entries, &procfs_disk_stats, + refresh_kind, ) }) { @@ -432,14 +470,18 @@ impl DiskStat { } } -fn disk_stats() -> HashMap { - let path = "/proc/diskstats"; - match fs::read_to_string(path) { - Ok(content) => disk_stats_inner(&content), - Err(_error) => { - sysinfo_debug!("failed to read {path:?}: {_error:?}"); - HashMap::new() +fn disk_stats(refresh_kind: &DiskRefreshKind) -> HashMap { + if refresh_kind.usage() { + let path = "/proc/diskstats"; + match fs::read_to_string(path) { + Ok(content) => disk_stats_inner(&content), + Err(_error) => { + sysinfo_debug!("failed to read {path:?}: {_error:?}"); + HashMap::new() + } } + } else { + Default::default() } } diff --git a/src/unknown/disk.rs b/src/unknown/disk.rs index 7ab253b47..7a4b97d54 100644 --- a/src/unknown/disk.rs +++ b/src/unknown/disk.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{Disk, DiskKind, DiskUsage}; +use crate::{Disk, DiskKind, DiskRefreshKind, DiskUsage}; use std::{ffi::OsStr, path::Path}; @@ -39,7 +39,7 @@ impl DiskInner { false } - pub(crate) fn refresh(&mut self) -> bool { + pub(crate) fn refresh_specifics(&mut self, _refreshes: DiskRefreshKind) -> bool { true } @@ -65,11 +65,11 @@ impl DisksInner { self.disks } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh_list_specifics(&mut self, _refreshes: DiskRefreshKind) { // Does nothing. } - pub(crate) fn refresh(&mut self) { + pub(crate) fn refresh_specifics(&mut self, _refreshes: DiskRefreshKind) { // Does nothing. } diff --git a/src/windows/disk.rs b/src/windows/disk.rs index 5d635c396..0a6848f06 100644 --- a/src/windows/disk.rs +++ b/src/windows/disk.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::utils::HandleWrapper; -use crate::{Disk, DiskKind, DiskUsage}; +use crate::{Disk, DiskKind, DiskUsage, DiskRefreshKind}; use std::ffi::{c_void, OsStr, OsString}; use std::mem::size_of; @@ -167,7 +167,7 @@ impl DiskInner { self.is_read_only } - pub(crate) fn refresh(&mut self) -> bool { + pub(crate) fn refresh_specifics(&mut self, _refreshes: DiskRefreshKind) -> bool { let Some((read_bytes, written_bytes)) = get_disk_io(&self.device_path, None) else { sysinfo_debug!("Failed to update disk i/o stats"); return false; @@ -220,15 +220,15 @@ impl DisksInner { self.disks } - pub(crate) fn refresh_list(&mut self) { + pub(crate) fn refresh_list_specifics(&mut self, _refreshes: DiskRefreshKind) { unsafe { self.disks = get_list(); } } - pub(crate) fn refresh(&mut self) { + pub(crate) fn refresh_specifics(&mut self, refreshes: DiskRefreshKind) { for disk in self.list_mut() { - disk.refresh(); + disk.refresh_specifics(refreshes); } }