1use {
2 crate::order::Order,
3 lfs_core::Mount,
4 std::{
5 cmp::Ordering,
6 fmt,
7 str::FromStr,
8 },
9 termimad::minimad::Alignment,
10};
11
12macro_rules! col_enum {
13 (@just_variant $variant:ident $discarded:ident) => {
14 Col::$variant
15 };
16 ($($variant:ident $name:literal $($alias:literal)* : $title:literal $($def:ident)*,)*) => {
17 #[derive(Debug, Clone, Copy, PartialEq)]
19 pub enum Col {
20 $($variant,)*
21 }
22 pub static ALL_COLS: &[Col] = &[
23 $(Col::$variant,)*
24 ];
25 pub static DEFAULT_COLS: &[Col] = &[
26 $(
27 $(col_enum!(@just_variant $variant $def),)*
28 )*
29 ];
30 impl FromStr for Col {
31 type Err = ParseColError;
32 fn from_str(s: &str) -> Result<Self, ParseColError> {
33 match s {
34 $(
35 $name => Ok(Self::$variant),
36 $(
37 $alias => Ok(Self::$variant),
38 )*
39 )*
40 _ => Err(ParseColError::new(s)),
41 }
42 }
43 }
44 impl fmt::Display for Col {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 match self {
47 $(
48 Self::$variant => write!(f, "{}", self.title()),
49 )*
50 }
51 }
52 }
53 impl Col {
54 pub fn name(self) -> &'static str {
55 match self {
56 $(
57 Self::$variant => $name,
58 )*
59 }
60 }
61 pub fn title(self) -> &'static str {
62 match self {
63 $(
64 Self::$variant => $title,
65 )*
66 }
67 }
68 pub fn aliases(self) -> &'static [&'static str] {
69 match self {
70 $(
71 Self::$variant => &[$($alias,)*],
72 )*
73 }
74 }
75 pub fn is_default(self) -> bool {
76 DEFAULT_COLS.contains(&self)
77 }
78 }
79 };
80}
81
82col_enum!(
85 Id "id": "id",
88 Dev "dev" "device" "device_id": "dev",
89 Filesystem "fs" "filesystem": "filesystem" default,
90 Label "label": "label",
91 Type "type": "type" default,
92 Remote "remote" "rem": "remote",
93 Disk "disk" "dsk": "disk" default,
94 Used "used": "used" default,
95 Use "use": "use" default,
96 UsePercent "use_percent": "use%",
97 Free "free": "free" default,
98 FreePercent "free_percent": "free%",
99 Size "size": "size" default,
100 InodesUsed "inodes_used" "iused": "used inodes",
101 InodesUse "inodes" "ino" "inodes_use" "iuse": "inodes",
102 InodesUsePercent "inodes_use_percent" "iuse_percent": "inodes%",
103 InodesFree "inodes_free" "ifree": "free inodes",
104 InodesCount "inodes_total" "inodes_count" "itotal": "inodes total",
105 MountPoint "mount" "mount_point" "mp": "mount point" default,
106 Uuid "uuid": "UUID",
107 PartUuid "partuuid" "part_uuid": "PARTUUID",
108);
109
110impl Col {
111 pub fn header_align(self) -> Alignment {
112 match self {
113 Self::Label => Alignment::Left,
114 Self::MountPoint => Alignment::Left,
115 _ => Alignment::Center,
116 }
117 }
118 pub fn content_align(self) -> Alignment {
119 match self {
120 Self::Id => Alignment::Right,
121 Self::Dev => Alignment::Center,
122 Self::Filesystem => Alignment::Left,
123 Self::Label => Alignment::Left,
124 Self::Type => Alignment::Center,
125 Self::Remote => Alignment::Center,
126 Self::Disk => Alignment::Center,
127 Self::Used => Alignment::Right,
128 Self::Use => Alignment::Right,
129 Self::UsePercent => Alignment::Right,
130 Self::Free => Alignment::Right,
131 Self::FreePercent => Alignment::Right,
132 Self::Size => Alignment::Right,
133 Self::InodesUsed => Alignment::Right,
134 Self::InodesUse => Alignment::Right,
135 Self::InodesUsePercent => Alignment::Right,
136 Self::InodesFree => Alignment::Right,
137 Self::InodesCount => Alignment::Right,
138 Self::MountPoint => Alignment::Left,
139 Self::Uuid => Alignment::Left,
140 Self::PartUuid => Alignment::Left,
141 }
142 }
143 pub fn description(self) -> &'static str {
144 match self {
145 Self::Id => "mount point id",
146 Self::Dev => "device id",
147 Self::Filesystem => "filesystem",
148 Self::Label => "volume label",
149 Self::Type => "filesystem type",
150 Self::Remote => "whether it's a remote filesystem",
151 Self::Disk => "storage type",
152 Self::Used => "size used",
153 Self::Use => "usage graphical view",
154 Self::UsePercent => "percentage of blocks used",
155 Self::Free => "free bytes",
156 Self::FreePercent => "percentage of free blocks",
157 Self::Size => "total size",
158 Self::InodesUsed => "number of inodes used",
159 Self::InodesUse => "graphical view of inodes usage",
160 Self::InodesUsePercent => "percentage of inodes used",
161 Self::InodesFree => "number of free inodes",
162 Self::InodesCount => "total count of inodes",
163 Self::MountPoint => "mount point",
164 Self::Uuid => "filesystem UUID",
165 Self::PartUuid => "partition UUID",
166 }
167 }
168 pub fn comparator(self) -> impl for<'a, 'b> FnMut(&'a Mount, &'b Mount) -> Ordering {
169 match self {
170 Self::Id => |a: &Mount, b: &Mount| a.info.id.cmp(&b.info.id),
171 Self::Dev => |a: &Mount, b: &Mount| a.info.dev.cmp(&b.info.dev),
172 Self::Filesystem => |a: &Mount, b: &Mount| a.info.fs.cmp(&b.info.fs),
173 Self::Label => |a: &Mount, b: &Mount| match (&a.fs_label, &b.fs_label) {
174 (Some(a), Some(b)) => a.cmp(b),
175 (Some(_), None) => Ordering::Less,
176 (None, Some(_)) => Ordering::Greater,
177 (None, None) => Ordering::Equal,
178 },
179 Self::Type => |a: &Mount, b: &Mount| a.info.fs_type.cmp(&b.info.fs_type),
180 Self::Remote => |a: &Mount, b: &Mount| a.info.is_remote().cmp(&b.info.is_remote()),
181 Self::Disk => |a: &Mount, b: &Mount| match (&a.disk, &b.disk) {
182 (Some(a), Some(b)) => a.disk_type().to_lowercase().cmp(&b.disk_type().to_lowercase()),
183 (Some(_), None) => Ordering::Greater,
184 (None, Some(_)) => Ordering::Less,
185 (None, None) => Ordering::Equal,
186 },
187 Self::Used => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
188 (Some(a), Some(b)) => a.used().cmp(&b.used()),
189 (Some(_), None) => Ordering::Greater,
190 (None, Some(_)) => Ordering::Less,
191 (None, None) => Ordering::Equal,
192 },
193 Self::Use | Self::UsePercent => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
194 (Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
198 (Some(_), None) => Ordering::Greater,
199 (None, Some(_)) => Ordering::Less,
200 (None, None) => Ordering::Equal,
201 },
202 Self::Free => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
203 (Some(a), Some(b)) => a.available().cmp(&b.available()),
204 (Some(_), None) => Ordering::Greater,
205 (None, Some(_)) => Ordering::Less,
206 (None, None) => Ordering::Equal,
207 },
208 Self::FreePercent => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
209 (Some(a), Some(b)) => b.use_share().partial_cmp(&a.use_share()).unwrap(),
210 (Some(_), None) => Ordering::Greater,
211 (None, Some(_)) => Ordering::Less,
212 (None, None) => Ordering::Equal,
213 },
214 Self::Size => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
215 (Some(a), Some(b)) => a.size().cmp(&b.size()),
216 (Some(_), None) => Ordering::Greater,
217 (None, Some(_)) => Ordering::Less,
218 (None, None) => Ordering::Equal,
219 },
220 Self::InodesUsed => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
221 (Some(a), Some(b)) => a.used().cmp(&b.used()),
222 (Some(_), None) => Ordering::Greater,
223 (None, Some(_)) => Ordering::Less,
224 (None, None) => Ordering::Equal,
225 },
226 Self::InodesUsePercent | Self::InodesUse => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
227 (Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
229 (Some(_), None) => Ordering::Greater,
230 (None, Some(_)) => Ordering::Less,
231 (None, None) => Ordering::Equal,
232 },
233 Self::InodesFree => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
234 (Some(a), Some(b)) => a.favail.cmp(&b.favail),
235 (Some(_), None) => Ordering::Greater,
236 (None, Some(_)) => Ordering::Less,
237 (None, None) => Ordering::Equal,
238 },
239 Self::InodesCount => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
240 (Some(a), Some(b)) => a.files.cmp(&b.files),
241 (Some(_), None) => Ordering::Greater,
242 (None, Some(_)) => Ordering::Less,
243 (None, None) => Ordering::Equal,
244 },
245 Self::MountPoint => |a: &Mount, b: &Mount| a.info.mount_point.cmp(&b.info.mount_point),
246 Self::Uuid => |a: &Mount, b: &Mount| match (&a.uuid, &b.uuid) {
247 (Some(a), Some(b)) => a.cmp(b),
248 (Some(_), None) => Ordering::Less,
249 (None, Some(_)) => Ordering::Greater,
250 (None, None) => Ordering::Equal,
251 },
252 Self::PartUuid => |a: &Mount, b: &Mount| match (&a.part_uuid, &b.part_uuid) {
253 (Some(a), Some(b)) => a.cmp(b),
254 (Some(_), None) => Ordering::Less,
255 (None, Some(_)) => Ordering::Greater,
256 (None, None) => Ordering::Equal,
257 },
258 }
259 }
260 pub fn default_sort_order(self) -> Order {
261 match self {
262 Self::Id => Order::Asc,
263 Self::Dev => Order::Asc,
264 Self::Filesystem => Order::Asc,
265 Self::Label => Order::Asc,
266 Self::Type => Order::Asc,
267 Self::Remote => Order::Desc,
268 Self::Disk => Order::Asc,
269 Self::Used => Order::Asc,
270 Self::Use => Order::Desc,
271 Self::UsePercent => Order::Asc,
272 Self::Free => Order::Asc,
273 Self::FreePercent => Order::Desc,
274 Self::Size => Order::Desc,
275 Self::InodesUsed => Order::Asc,
276 Self::InodesUse => Order::Asc,
277 Self::InodesUsePercent => Order::Asc,
278 Self::InodesFree => Order::Asc,
279 Self::InodesCount => Order::Asc,
280 Self::MountPoint => Order::Asc,
281 Self::Uuid => Order::Asc,
282 Self::PartUuid => Order::Asc,
283 }
284 }
285 pub fn default_sort_col() -> Self {
286 Self::Size
287 }
288}
289
290
291#[derive(Debug)]
292pub struct ParseColError {
293 pub raw: String,
295}
296impl ParseColError {
297 pub fn new<S: Into<String>>(s: S) -> Self {
298 Self { raw: s.into() }
299 }
300}
301impl fmt::Display for ParseColError {
302 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
303 write!(
304 f,
305 "{:?} can't be parsed as a column; use 'dysk --list-cols' to see all column names",
306 self.raw,
307 )
308 }
309}
310impl std::error::Error for ParseColError {}
311