use crate::model::MetricState;
use crate::units::{Percent, Rate};
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
pub enum FilesystemKind {
Physical,
Removable,
Network,
Virtual,
#[default]
Unknown,
}
impl FilesystemKind {
#[must_use]
pub const fn hidden_by_default(self) -> bool {
matches!(self, Self::Virtual)
}
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct FilesystemSnapshot {
pub mount_point: Box<str>,
pub device: Option<Box<str>>,
pub fs_type: Option<Box<str>>,
pub total_bytes: u64,
pub available_bytes: MetricState<u64>,
pub used_bytes: MetricState<u64>,
pub usage: MetricState<Percent>,
pub kind: FilesystemKind,
pub read_only: bool,
}
#[derive(Clone, Copy, Debug, Default, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DiskTotals {
pub read_bytes: u64,
pub write_bytes: u64,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct DiskSnapshot {
pub device: Box<str>,
pub model: Option<Box<str>>,
pub read: MetricState<Rate>,
pub write: MetricState<Rate>,
pub read_ops: MetricState<Rate>,
pub write_ops: MetricState<Rate>,
pub busy: MetricState<Percent>,
pub queue_length: MetricState<f32>,
pub totals: MetricState<DiskTotals>,
pub mount_points: Vec<Box<str>>,
}
impl DiskSnapshot {
#[must_use]
pub fn warming_up(device: Box<str>) -> Self {
Self {
device,
model: None,
read: MetricState::WarmingUp,
write: MetricState::WarmingUp,
read_ops: MetricState::WarmingUp,
write_ops: MetricState::WarmingUp,
busy: MetricState::WarmingUp,
queue_length: MetricState::WarmingUp,
totals: MetricState::WarmingUp,
mount_points: Vec::new(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn virtual_filesystems_are_hidden_by_default_and_real_ones_are_not() {
assert!(FilesystemKind::Virtual.hidden_by_default());
for kind in [
FilesystemKind::Physical,
FilesystemKind::Removable,
FilesystemKind::Network,
FilesystemKind::Unknown,
] {
assert!(!kind.hidden_by_default(), "{kind:?}");
}
}
#[test]
fn a_warming_up_device_reports_no_throughput_and_no_busy_percentage() {
let disk = DiskSnapshot::warming_up("nvme0n1".into());
assert!(disk.read.fresh().is_none());
assert!(disk.busy.fresh().is_none());
assert!(disk.mount_points.is_empty());
}
#[test]
fn capacity_and_throughput_live_in_separate_types() {
fn assert_fields<T>(_: &T) {}
let fs = FilesystemSnapshot {
mount_point: "/".into(),
device: Some("disk3s1s1".into()),
fs_type: Some("apfs".into()),
total_bytes: 494_384_795_648,
available_bytes: MetricState::Available(120_000_000_000),
used_bytes: MetricState::Available(374_384_795_648),
usage: Percent::ratio(374_384_795_648, 494_384_795_648)
.map_or(MetricState::Unsupported, MetricState::Available),
kind: FilesystemKind::Physical,
read_only: false,
};
assert_fields(&fs);
assert!(fs.usage.fresh().is_some());
let disk = DiskSnapshot::warming_up("disk0".into());
assert_fields(&disk);
}
}