diff options
author | Clement Tsang <34804052+ClementTsang@users.noreply.github.com> | 2023-12-28 04:00:58 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-27 23:00:58 -0500 |
commit | 22b40780ade874bed8682f0ffc31450458bd5659 (patch) | |
tree | c3c67626498aea1e14be0b47b9ef1fc637450cdd /src/app/data_harvester | |
parent | 28d2950d92f44f3b664fcaeede07e5ed4b30fd49 (diff) |
refactor: pull data collection out of nested folder structure (#1364)
* refactor: pull data collection out of nested folder structure
* fix sysinfo
* comment
Diffstat (limited to 'src/app/data_harvester')
47 files changed, 0 insertions, 4664 deletions
diff --git a/src/app/data_harvester/batteries.rs b/src/app/data_harvester/batteries.rs deleted file mode 100644 index 8c0e4a92..00000000 --- a/src/app/data_harvester/batteries.rs +++ /dev/null @@ -1,10 +0,0 @@ -//! Data collection for batteries. -//! -//! For Linux, macOS, Windows, FreeBSD, Dragonfly, and iOS, this is handled by the battery crate. - -cfg_if::cfg_if! { - if #[cfg(any(target_os = "windows", target_os = "macos", target_os = "linux", target_os = "freebsd", target_os = "dragonfly", target_os = "ios"))] { - pub mod battery; - pub use self::battery::*; - } -} diff --git a/src/app/data_harvester/batteries/battery.rs b/src/app/data_harvester/batteries/battery.rs deleted file mode 100644 index ec95ada2..00000000 --- a/src/app/data_harvester/batteries/battery.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! Uses the battery crate from svartalf. -//! Covers battery usage for: -//! - Linux 2.6.39+ -//! - MacOS 10.10+ -//! - iOS -//! - Windows 7+ -//! - FreeBSD -//! - DragonFlyBSD -//! -//! For more information, refer to the [starship_battery](https://github.com/starship/rust-battery) repo/docs. - -use starship_battery::{ - units::{power::watt, ratio::percent, time::second}, - Battery, Manager, State, -}; - -#[derive(Debug, Clone)] -pub struct BatteryHarvest { - pub charge_percent: f64, - pub secs_until_full: Option<i64>, - pub secs_until_empty: Option<i64>, - pub power_consumption_rate_watts: f64, - pub health_percent: f64, - pub state: State, -} - -pub fn refresh_batteries(manager: &Manager, batteries: &mut [Battery]) -> Vec<BatteryHarvest> { - batteries - .iter_mut() - .filter_map(|battery| { - if manager.refresh(battery).is_ok() { - Some(BatteryHarvest { - secs_until_full: { - let optional_time = battery.time_to_full(); - optional_time.map(|time| f64::from(time.get::<second>()) as i64) - }, - secs_until_empty: { - let optional_time = battery.time_to_empty(); - optional_time.map(|time| f64::from(time.get::<second>()) as i64) - }, - charge_percent: f64::from(battery.state_of_charge().get::<percent>()), - power_consumption_rate_watts: f64::from(battery.energy_rate().get::<watt>()), - health_percent: f64::from(battery.state_of_health().get::<percent>()), - state: battery.state(), - }) - } else { - None - } - }) - .collect::<Vec<_>>() -} diff --git a/src/app/data_harvester/cpu.rs b/src/app/data_harvester/cpu.rs deleted file mode 100644 index 843df161..00000000 --- a/src/app/data_harvester/cpu.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! Data collection for CPU usage and load average. - -pub mod sysinfo; -pub use self::sysinfo::*; - -pub type LoadAvgHarvest = [f32; 3]; - -#[derive(Debug, Clone, Copy)] -pub enum CpuDataType { - Avg, - Cpu(usize), -} - -#[derive(Debug, Clone)] -pub struct CpuData { - pub data_type: CpuDataType, - pub cpu_usage: f64, -} - -pub type CpuHarvest = Vec<CpuData>; - -pub type PastCpuWork = f64; -pub type PastCpuTotal = f64; diff --git a/src/app/data_harvester/cpu/sysinfo.rs b/src/app/data_harvester/cpu/sysinfo.rs deleted file mode 100644 index 0d79e347..00000000 --- a/src/app/data_harvester/cpu/sysinfo.rs +++ /dev/null @@ -1,39 +0,0 @@ -//! CPU stats through sysinfo. -//! Supports FreeBSD. - -use std::collections::VecDeque; - -use sysinfo::{CpuExt, LoadAvg, System, SystemExt}; - -use super::{CpuData, CpuDataType, CpuHarvest}; -use crate::app::data_harvester::cpu::LoadAvgHarvest; - -pub fn get_cpu_data_list(sys: &System, show_average_cpu: bool) -> crate::error::Result<CpuHarvest> { - let mut cpu_deque: VecDeque<_> = sys - .cpus() - .iter() - .enumerate() - .map(|(i, cpu)| CpuData { - data_type: CpuDataType::Cpu(i), - cpu_usage: cpu.cpu_usage() as f64, - }) - .collect(); - - if show_average_cpu { - let cpu = sys.global_cpu_info(); - - cpu_deque.push_front(CpuData { - data_type: CpuDataType::Avg, - cpu_usage: cpu.cpu_usage() as f64, - }) - } - - Ok(Vec::from(cpu_deque)) -} - -pub fn get_load_avg() -> crate::error::Result<LoadAvgHarvest> { - let sys = System::new(); - let LoadAvg { one, five, fifteen } = sys.load_average(); - - Ok([one as f32, five as f32, fifteen as f32]) -} diff --git a/src/app/data_harvester/disks.rs b/src/app/data_harvester/disks.rs deleted file mode 100644 index c4a66eff..00000000 --- a/src/app/data_harvester/disks.rs +++ /dev/null @@ -1,193 +0,0 @@ -//! Data collection about disks (e.g. I/O, usage, space). - -use cfg_if::cfg_if; -use hashbrown::HashMap; - -use crate::app::filter::Filter; - -cfg_if! { - if #[cfg(target_os = "freebsd")] { - mod freebsd; - #[cfg(feature = "zfs")] - mod io_counters; - #[cfg(feature = "zfs")] - mod zfs_io_counters; - #[cfg(feature = "zfs")] - pub use io_counters::IoCounters; - pub(crate) use self::freebsd::*; - } else if #[cfg(target_os = "windows")] { - mod windows; - pub(crate) use self::windows::*; - } else if #[cfg(target_os = "linux")] { - mod unix; - #[cfg(feature = "zfs")] - mod zfs_io_counters; - pub(crate) use self::unix::*; - } else if #[cfg(target_os = "macos")] { - mod unix; - pub(crate) use self::unix::*; - } else { - mod other; - pub(crate) use self::other::*; - } -} - -#[derive(Clone, Debug, Default)] -pub struct DiskHarvest { - pub name: String, - pub mount_point: String, - - /// Windows also contains an additional volume name field. - #[cfg(target_os = "windows")] - pub volume_name: Option<String>, - - // TODO: Maybe unify all these? - pub free_space: Option<u64>, - pub used_space: Option<u64>, - pub total_space: Option<u64>, -} - -#[derive(Clone, Debug)] -pub struct IoData { - pub read_bytes: u64, - pub write_bytes: u64, -} - -pub type IoHarvest = HashMap<String, Option<IoData>>; - -cfg_if! { - if #[cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))] { - mod io_counters; - pub use io_counters::IoCounters; - - /// Returns the I/O usage of certain mount points. - pub fn get_io_usage() -> anyhow::Result<IoHarvest> { - let mut io_hash: HashMap<String, Option<IoData>> = HashMap::new(); - - // TODO: Maybe rewrite this to not do a result of vec of result... - for io in io_stats()?.into_iter() { - let mount_point = io.device_name().to_string_lossy(); - - io_hash.insert( - mount_point.to_string(), - Some(IoData { - read_bytes: io.read_bytes(), - write_bytes: io.write_bytes(), - }), - ); - } - - Ok(io_hash) - } - } else if #[cfg(not(target_os = "freebsd"))] { - pub fn get_io_usage() -> anyhow::Result<IoHarvest> { - anyhow::bail!("Unsupported OS"); - } - } -} - -/// Whether to keep the current disk entry given the filters, disk name, and disk mount. -/// Precedence ordering in the case where name and mount filters disagree, "allow" -/// takes precedence over "deny". -/// -/// For implementation, we do this as follows: -/// -/// 1. Is the entry allowed through any filter? That is, does it match an entry in a -/// filter where `is_list_ignored` is `false`? If so, we always keep this entry. -/// 2. Is the entry denied through any filter? That is, does it match an entry in a -/// filter where `is_list_ignored` is `true`? If so, we always deny this entry. -/// 3. Anything else is allowed. -pub fn keep_disk_entry( - disk_name: &str, mount_point: &str, disk_filter: &Option<Filter>, mount_filter: &Option<Filter>, -) -> bool { - match (disk_filter, mount_filter) { - (Some(d), Some(m)) => match (d.is_list_ignored, m.is_list_ignored) { - (true, true) => !(d.has_match(disk_name) || m.has_match(mount_point)), - (true, false) => { - if m.has_match(mount_point) { - true - } else { - d.keep_entry(disk_name) - } - } - (false, true) => { - if d.has_match(disk_name) { - true - } else { - m.keep_entry(mount_point) - } - } - (false, false) => d.has_match(disk_name) || m.has_match(mount_point), - }, - (Some(d), None) => d.keep_entry(disk_name), - (None, Some(m)) => m.keep_entry(mount_point), - (None, None) => true, - } -} - -#[cfg(test)] -mod test { - use regex::Regex; - - use super::keep_disk_entry; - use crate::app::filter::Filter; - - fn run_filter(disk_filter: &Option<Filter>, mount_filter: &Option<Filter>) -> Vec<usize> { - let targets = [ - ("/dev/nvme0n1p1", "/boot"), - ("/dev/nvme0n1p2", "/"), - ("/dev/nvme0n1p3", "/home"), - ("/dev/sda1", "/mnt/test"), - ("/dev/sda2", "/mnt/boot"), - ]; - - targets - .into_iter() - .enumerate() - .filter_map(|(itx, (name, mount))| { - if keep_disk_entry(name, mount, disk_filter, mount_filter) { - Some(itx) - } else { - None - } - }) - .collect() - } - - #[test] - fn test_keeping_disk_entry() { - let disk_ignore = Some(Filter { - is_list_ignored: true, - list: vec![Regex::new("nvme").unwrap()], - }); - - let disk_keep = Some(Filter { - is_list_ignored: false, - list: vec![Regex::new("nvme").unwrap()], - }); - - let mount_ignore = Some(Filter { - is_list_ignored: true, - list: vec![Regex::new("boot").unwrap()], - }); - - let mount_keep = Some(Filter { - is_list_ignored: false, - list: vec![Regex::new("boot").unwrap()], - }); - - assert_eq!(run_filter(&None, &None), vec![0, 1, 2, 3, 4]); - - assert_eq!(run_filter(&disk_ignore, &None), vec![3, 4]); - assert_eq!(run_filter(&disk_keep, &None), vec![0, 1, 2]); - - assert_eq!(run_filter(&None, &mount_ignore), vec![1, 2, 3]); - assert_eq!(run_filter(&None, &mount_keep), vec![0, 4]); - - assert_eq!(run_filter(&disk_ignore, &mount_ignore), vec![3]); - assert_eq!(run_filter(&disk_keep, &mount_ignore), vec![0, 1, 2, 3]); - - assert_eq!(run_filter(&disk_ignore, &mount_keep), vec![0, 3, 4]); - assert_eq!(run_filter(&disk_keep, &mount_keep), vec![0, 1, 2, 4]); - } -} diff --git a/src/app/data_harvester/disks/freebsd.rs b/src/app/data_harvester/disks/freebsd.rs deleted file mode 100644 index 4c392ebc..00000000 --- a/src/app/data_harvester/disks/freebsd.rs +++ /dev/null @@ -1,94 +0,0 @@ -//! Disk stats for FreeBSD. - -use std::io; - -use serde::Deserialize; - -use super::{keep_disk_entry, DiskHarvest, IoHarvest}; - -use crate::{ - app::data_harvester::DataCollector, data_harvester::deserialize_xo, - data_harvester::disks::IoData, utils::error, -}; -use hashbrown::HashMap; - -#[derive(Deserialize, Debug, Default)] -#[serde(rename_all = "kebab-case")] -struct StorageSystemInformation { - filesystem: Vec<FileSystem>, -} - -#[derive(Deserialize, Debug)] -#[serde(rename_all = "kebab-case")] -struct FileSystem { - name: String, - total_blocks: u64, - used_blocks: u64, - available_blocks: u64, - mounted_on: String, -} - -pub fn get_io_usage() -> error::Result<IoHarvest> { - // TODO: Should this (and other I/O collectors) fail fast? In general, should collection ever fail fast? - #[allow(unused_mut)] - let mut io_harvest: HashMap<String, Option<IoData>> = - get_disk_info().map(|storage_system_information| { - storage_system_information - .filesystem - .into_iter() - .map(|disk| (disk.name, None)) - .collect() - })?; - - #[cfg(feature = "zfs")] - { - use crate::app::data_harvester::disks::zfs_io_counters; - if let Ok(zfs_io) = zfs_io_counters::zfs_io_stats() { - for io in zfs_io.into_iter() { - let mount_point = io.device_name().to_string_lossy(); - io_harvest.insert( - mount_point.to_string(), - Some(IoData { - read_bytes: io.read_bytes(), - write_bytes: io.write_bytes(), - }), - ); - } - } - } - Ok(io_harvest) -} - -pub fn get_disk_usage(collector: &DataCollector) -> error::Result<Vec<DiskHarvest>> { - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - let vec_disks: Vec<DiskHarvest> = get_disk_info().map(|storage_system_information| { - storage_system_information - .filesystem - .into_iter() - .filter_map(|disk| { - if keep_disk_entry(&disk.name, &disk.mounted_on, disk_filter, mount_filter) { - Some(DiskHarvest { - free_space: Some(disk.available_blocks * 1024), - used_space: Some(disk.used_blocks * 1024), - total_space: Some(disk.total_blocks * 1024), - mount_point: disk.mounted_on, - name: disk.name, - }) - } else { - None - } - }) - .collect() - })?; - - Ok(vec_disks) -} - -fn get_disk_info() -> io::Result<StorageSystemInformation> { - // TODO: Ideally we don't have to shell out to a new program. - let output = std::process::Command::new("df") - .args(["--libxo", "json", "-k", "-t", "ufs,msdosfs,zfs"]) - .output()?; - deserialize_xo("storage-system-information", &output.stdout) -} diff --git a/src/app/data_harvester/disks/io_counters.rs b/src/app/data_harvester/disks/io_counters.rs deleted file mode 100644 index 2b07fa5d..00000000 --- a/src/app/data_harvester/disks/io_counters.rs +++ /dev/null @@ -1,30 +0,0 @@ -use std::ffi::OsStr; - -#[derive(Debug, Default)] -pub struct IoCounters { - name: String, - read_bytes: u64, - write_bytes: u64, -} - -impl IoCounters { - pub fn new(name: String, read_bytes: u64, write_bytes: u64) -> Self { - Self { - name, - read_bytes, - write_bytes, - } - } - - pub(crate) fn device_name(&self) -> &OsStr { - OsStr::new(&self.name) - } - - pub(crate) fn read_bytes(&self) -> u64 { - self.read_bytes - } - - pub(crate) fn write_bytes(&self) -> u64 { - self.write_bytes - } -} diff --git a/src/app/data_harvester/disks/other.rs b/src/app/data_harvester/disks/other.rs deleted file mode 100644 index 6dac7e93..00000000 --- a/src/app/data_harvester/disks/other.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! Fallback disk info using sysinfo. - -use sysinfo::{DiskExt, SystemExt}; - -use crate::app::data_harvester::DataCollector; - -use super::{keep_disk_entry, DiskHarvest}; - -pub(crate) fn get_disk_usage(collector: &DataCollector) -> anyhow::Result<Vec<DiskHarvest>> { - let disks = collector.sys.disks(); - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - - Ok(disks - .iter() - .filter_map(|disk| { - let name = { - let name = disk.name(); - - if name.is_empty() { - "No Name".to_string() - } else { - name.to_os_string() - .into_string() - .unwrap_or_else(|_| "Name Unavailable".to_string()) - } - }; - - let mount_point = disk - .mount_point() - .as_os_str() - .to_os_string() - .into_string() - .unwrap_or_else(|_| "Mount Unavailable".to_string()); - - if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) { - let free_space = disk.available_space(); - let total_space = disk.total_space(); - let used_space = total_space - free_space; - - Some(DiskHarvest { - name, - mount_point, - free_space: Some(free_space), - used_space: Some(used_space), - total_space: Some(total_space), - }) - } else { - None - } - }) - .collect()) -} diff --git a/src/app/data_harvester/disks/unix.rs b/src/app/data_harvester/disks/unix.rs deleted file mode 100644 index e8ebaeeb..00000000 --- a/src/app/data_harvester/disks/unix.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! Disk stats for Unix-like systems that aren't supported through other means. Officially, -//! for now, this means Linux and macOS. - -mod file_systems; - -use file_systems::*; - -mod usage; -use usage::*; - -cfg_if::cfg_if! { - if #[cfg(target_os = "linux")] { - mod linux; - pub use linux::*; - } else if #[cfg(target_os = "macos")] { - mod other; - use other::*; - - mod macos; - pub use macos::*; - } else { - mod other; - use other::*; - } -} - -use super::{keep_disk_entry, DiskHarvest}; -use crate::app::data_harvester::DataCollector; - -/// Returns the disk usage of the mounted (and for now, physical) disks. -pub fn get_disk_usage(collector: &DataCollector) -> anyhow::Result<Vec<DiskHarvest>> { - let disk_filter = &collector.filters.disk_filter; - let mount_filter = &collector.filters.mount_filter; - let mut vec_disks: Vec<DiskHarvest> = Vec::new(); - - for partition in physical_partitions()? { - let name = partition.get_device_name(); - let mount_point = partition.mount_point().to_string_lossy().to_string(); - - // Precedence ordering in the case where name and mount filters disagree, "allow" takes precedence over "deny". - // - // For implementation, we do this as follows: - // 1. Is the entry allowed through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `false`? If so, we always keep this entry. - // 2. Is the entry denied through any filter? That is, does it match an entry in a filter where `is_list_ignored` is `true`? If so, we always deny this entry. - // 3. Anything else is allowed. - - if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) { - // The usage line can fail in some cases (for example, if you use Void Linux + LUKS, - // see https://github.com/ClementTsang/bottom/issues/419 for details). - if let Ok(usage) = partition.usage() { - let total = usage.total(); - - vec_disks.push(DiskHarvest { - free_space: Some(usage.free()), - used_space: Some(total - usage.available()), - total_space: Some(total), - mount_point, - name, - }); - } else { - vec_disks.push(DiskHarvest { - free_space: None, - used_space: None, - total_space: None, - mount_point, - name, - }); - } - } - } - - Ok(vec_disks) -} diff --git a/src/app/data_harvester/disks/unix/file_systems.rs b/src/app/data_harvester/disks/unix/file_systems.rs deleted file mode 100644 index 15bdc3ac..00000000 --- a/src/app/data_harvester/disks/unix/file_systems.rs +++ /dev/null @@ -1,185 +0,0 @@ -use std::str::FromStr; - -use crate::multi_eq_ignore_ascii_case; - -/// Known filesystems. Original list from -/// [heim](https://github.com/heim-rs/heim/blob/master/heim-disk/src/filesystem.rs). -/// -/// All physical filesystems should have their own enum element and all virtual filesystems will go into -/// the [`FileSystem::Other`] element. -#[derive(Debug, Eq, PartialEq, Hash, C |