use {
crate::order::Order,
lfs_core::Mount,
std::{
cmp::Ordering,
fmt,
str::FromStr,
},
termimad::minimad::Alignment,
};
macro_rules! col_enum {
(@just_variant $variant:ident $discarded:ident) => {
Col::$variant
};
($($variant:ident $name:literal $($alias:literal)* : $title:literal $($def:ident)*,)*) => {
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Col {
$($variant,)*
}
pub static ALL_COLS: &[Col] = &[
$(Col::$variant,)*
];
pub static DEFAULT_COLS: &[Col] = &[
$(
$(col_enum!(@just_variant $variant $def),)*
)*
];
impl FromStr for Col {
type Err = ParseColError;
fn from_str(s: &str) -> Result<Self, ParseColError> {
match s {
$(
$name => Ok(Self::$variant),
$(
$alias => Ok(Self::$variant),
)*
)*
_ => Err(ParseColError::new(s)),
}
}
}
impl fmt::Display for Col {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
$(
Self::$variant => write!(f, "{}", self.title()),
)*
}
}
}
impl Col {
pub fn name(self) -> &'static str {
match self {
$(
Self::$variant => $name,
)*
}
}
pub fn title(self) -> &'static str {
match self {
$(
Self::$variant => $title,
)*
}
}
pub fn aliases(self) -> &'static [&'static str] {
match self {
$(
Self::$variant => &[$($alias,)*],
)*
}
}
pub fn is_default(self) -> bool {
DEFAULT_COLS.contains(&self)
}
}
};
}
col_enum!(
Id "id": "id",
Dev "dev" "device" "device_id": "dev",
Filesystem "fs" "filesystem": "filesystem" default,
Label "label": "label",
Type "type": "type" default,
Remote "remote" "rem": "remote",
Disk "disk" "dsk": "disk" default,
Used "used": "used" default,
Use "use": "use" default,
UsePercent "use_percent": "use%",
Free "free": "free" default,
Size "size": "size" default,
InodesUsed "inodes_used" "iused": "used inodes",
InodesUse "inodes" "ino" "inodes_use" "iuse": "inodes",
InodesUsePercent "inodes_use_percent" "iuse_percent": "inodes%",
InodesFree "inodes_free" "ifree": "free inodes",
InodesCount "inodes_total" "inodes_count" "itotal": "inodes total",
MountPoint "mount" "mount_point" "mp": "mount point" default,
);
impl Col {
pub fn header_align(self) -> Alignment {
match self {
Self::Label => Alignment::Left,
Self::MountPoint => Alignment::Left,
_ => Alignment::Center,
}
}
pub fn content_align(self) -> Alignment {
match self {
Self::Id => Alignment::Right,
Self::Dev => Alignment::Center,
Self::Filesystem => Alignment::Left,
Self::Label => Alignment::Left,
Self::Type => Alignment::Center,
Self::Remote => Alignment::Center,
Self::Disk => Alignment::Center,
Self::Used => Alignment::Right,
Self::Use => Alignment::Right,
Self::UsePercent => Alignment::Right,
Self::Free => Alignment::Right,
Self::Size => Alignment::Right,
Self::InodesUsed => Alignment::Right,
Self::InodesUse => Alignment::Right,
Self::InodesUsePercent => Alignment::Right,
Self::InodesFree => Alignment::Right,
Self::InodesCount => Alignment::Right,
Self::MountPoint => Alignment::Left,
}
}
pub fn description(self) -> &'static str {
match self {
Self::Id => "mount point id",
Self::Dev => "device id",
Self::Filesystem => "filesystem",
Self::Label => "volume label",
Self::Type => "filesystem type",
Self::Remote => "whether it's a remote filesystem",
Self::Disk => "storage type",
Self::Used => "size used",
Self::Use => "usage graphical view",
Self::UsePercent => "percentage of blocks used",
Self::Free => "free bytes",
Self::Size => "total size",
Self::InodesUsed => "number of inodes used",
Self::InodesUse => "graphical view of inodes usage",
Self::InodesUsePercent => "percentage of inodes used",
Self::InodesFree => "number of free inodes",
Self::InodesCount => "total count of inodes",
Self::MountPoint => "mount point",
}
}
pub fn comparator(self) -> impl for<'a, 'b> FnMut(&'a Mount, &'b Mount) -> Ordering {
match self {
Self::Id => |a: &Mount, b: &Mount| a.info.id.cmp(&b.info.id),
Self::Dev => |a: &Mount, b: &Mount| a.info.dev.cmp(&b.info.dev),
Self::Filesystem => |a: &Mount, b: &Mount| a.info.fs.cmp(&b.info.fs),
Self::Label => |a: &Mount, b: &Mount| match (&a.fs_label, &b.fs_label) {
(Some(a), Some(b)) => a.cmp(b),
(Some(_), None) => Ordering::Less,
(None, Some(_)) => Ordering::Greater,
(None, None) => Ordering::Equal,
},
Self::Type => |a: &Mount, b: &Mount| a.info.fs_type.cmp(&b.info.fs_type),
Self::Remote => |a: &Mount, b: &Mount| a.info.is_remote().cmp(&b.info.is_remote()),
Self::Disk => |a: &Mount, b: &Mount| match (&a.disk, &b.disk) {
(Some(a), Some(b)) => a.disk_type().to_lowercase().cmp(&b.disk_type().to_lowercase()),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::Used => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
(Some(a), Some(b)) => a.used().cmp(&b.used()),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::Use | Self::UsePercent => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
(Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::Free => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
(Some(a), Some(b)) => a.available().cmp(&b.available()),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::Size => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
(Some(a), Some(b)) => a.size().cmp(&b.size()),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::InodesUsed => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
(Some(a), Some(b)) => a.used().cmp(&b.used()),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::InodesUsePercent | Self::InodesUse => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
(Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::InodesFree => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
(Some(a), Some(b)) => a.favail.cmp(&b.favail),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::InodesCount => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
(Some(a), Some(b)) => a.files.cmp(&b.files),
(Some(_), None) => Ordering::Greater,
(None, Some(_)) => Ordering::Less,
(None, None) => Ordering::Equal,
},
Self::MountPoint => |a: &Mount, b: &Mount| a.info.mount_point.cmp(&b.info.mount_point),
}
}
pub fn default_sort_order(self) -> Order {
match self {
Self::Id => Order::Asc,
Self::Dev => Order::Asc,
Self::Filesystem => Order::Asc,
Self::Label => Order::Asc,
Self::Type => Order::Asc,
Self::Remote => Order::Desc,
Self::Disk => Order::Asc,
Self::Used => Order::Asc,
Self::Use => Order::Desc,
Self::UsePercent => Order::Asc,
Self::Free => Order::Asc,
Self::Size => Order::Desc,
Self::InodesUsed => Order::Asc,
Self::InodesUse => Order::Asc,
Self::InodesUsePercent => Order::Asc,
Self::InodesFree => Order::Asc,
Self::InodesCount => Order::Asc,
Self::MountPoint => Order::Asc,
}
}
pub fn default_sort_col() -> Self {
Self::Size
}
}
#[derive(Debug)]
pub struct ParseColError {
pub raw: String,
}
impl ParseColError {
pub fn new<S: Into<String>>(s: S) -> Self {
Self { raw: s.into() }
}
}
impl fmt::Display for ParseColError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{:?} can't be parsed as a column; use 'dysk --list-cols' to see all column names",
self.raw,
)
}
}
impl std::error::Error for ParseColError {}