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 MountOptions "options" "mount_options": "mount options",
109 CompressLevel "compress" "compress_level": "compress",
110);
111
112impl Col {
113 pub fn header_align(self) -> Alignment {
114 match self {
115 Self::Label => Alignment::Left,
116 Self::MountPoint => Alignment::Left,
117 Self::MountOptions => Alignment::Left,
118 _ => Alignment::Center,
119 }
120 }
121 pub fn content_align(self) -> Alignment {
122 match self {
123 Self::Id => Alignment::Right,
124 Self::Dev => Alignment::Center,
125 Self::Filesystem => Alignment::Left,
126 Self::Label => Alignment::Left,
127 Self::Type => Alignment::Center,
128 Self::Remote => Alignment::Center,
129 Self::Disk => Alignment::Center,
130 Self::Used => Alignment::Right,
131 Self::Use => Alignment::Right,
132 Self::UsePercent => Alignment::Right,
133 Self::Free => Alignment::Right,
134 Self::FreePercent => Alignment::Right,
135 Self::Size => Alignment::Right,
136 Self::InodesUsed => Alignment::Right,
137 Self::InodesUse => Alignment::Right,
138 Self::InodesUsePercent => Alignment::Right,
139 Self::InodesFree => Alignment::Right,
140 Self::InodesCount => Alignment::Right,
141 Self::MountPoint => Alignment::Left,
142 Self::Uuid => Alignment::Left,
143 Self::PartUuid => Alignment::Left,
144 Self::MountOptions => Alignment::Left,
145 Self::CompressLevel => Alignment::Center,
146 }
147 }
148 pub fn description(self) -> &'static str {
149 match self {
150 Self::Id => "mount point id",
151 Self::Dev => "device id",
152 Self::Filesystem => "filesystem",
153 Self::Label => "volume label",
154 Self::Type => "filesystem type",
155 Self::Remote => "whether it's a remote filesystem",
156 Self::Disk => "storage type",
157 Self::Used => "size used",
158 Self::Use => "usage graphical view",
159 Self::UsePercent => "percentage of blocks used",
160 Self::Free => "free bytes",
161 Self::FreePercent => "percentage of free blocks",
162 Self::Size => "total size",
163 Self::InodesUsed => "number of inodes used",
164 Self::InodesUse => "graphical view of inodes usage",
165 Self::InodesUsePercent => "percentage of inodes used",
166 Self::InodesFree => "number of free inodes",
167 Self::InodesCount => "total count of inodes",
168 Self::MountPoint => "mount point",
169 Self::Uuid => "filesystem UUID",
170 Self::PartUuid => "partition UUID",
171 Self::MountOptions => "mount options (linux only)",
172 Self::CompressLevel => "compress algo/level",
173 }
174 }
175 pub fn comparator(self) -> impl for<'a, 'b> FnMut(&'a Mount, &'b Mount) -> Ordering {
176 match self {
177 Self::Id => |a: &Mount, b: &Mount| a.info.id.cmp(&b.info.id),
178 Self::Dev => |a: &Mount, b: &Mount| a.info.dev.cmp(&b.info.dev),
179 Self::Filesystem => |a: &Mount, b: &Mount| a.info.fs.cmp(&b.info.fs),
180 Self::Label => |a: &Mount, b: &Mount| match (&a.fs_label, &b.fs_label) {
181 (Some(a), Some(b)) => a.cmp(b),
182 (Some(_), None) => Ordering::Less,
183 (None, Some(_)) => Ordering::Greater,
184 (None, None) => Ordering::Equal,
185 },
186 Self::Type => |a: &Mount, b: &Mount| a.info.fs_type.cmp(&b.info.fs_type),
187 Self::Remote => |a: &Mount, b: &Mount| a.is_remote().cmp(&b.is_remote()),
188 Self::Disk => |a: &Mount, b: &Mount| match (&a.disk, &b.disk) {
189 (Some(a), Some(b)) => a
190 .disk_type()
191 .to_lowercase()
192 .cmp(&b.disk_type().to_lowercase()),
193 (Some(_), None) => Ordering::Greater,
194 (None, Some(_)) => Ordering::Less,
195 (None, None) => Ordering::Equal,
196 },
197 Self::Used => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
198 (Some(a), Some(b)) => a.used().cmp(&b.used()),
199 (Some(_), None) => Ordering::Greater,
200 (None, Some(_)) => Ordering::Less,
201 (None, None) => Ordering::Equal,
202 },
203 Self::Use | Self::UsePercent => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
204 (Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
208 (Some(_), None) => Ordering::Greater,
209 (None, Some(_)) => Ordering::Less,
210 (None, None) => Ordering::Equal,
211 },
212 Self::Free => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
213 (Some(a), Some(b)) => a.available().cmp(&b.available()),
214 (Some(_), None) => Ordering::Greater,
215 (None, Some(_)) => Ordering::Less,
216 (None, None) => Ordering::Equal,
217 },
218 Self::FreePercent => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
219 (Some(a), Some(b)) => b.use_share().partial_cmp(&a.use_share()).unwrap(),
220 (Some(_), None) => Ordering::Greater,
221 (None, Some(_)) => Ordering::Less,
222 (None, None) => Ordering::Equal,
223 },
224 Self::Size => |a: &Mount, b: &Mount| match (&a.stats(), &b.stats()) {
225 (Some(a), Some(b)) => a.size().cmp(&b.size()),
226 (Some(_), None) => Ordering::Greater,
227 (None, Some(_)) => Ordering::Less,
228 (None, None) => Ordering::Equal,
229 },
230 Self::InodesUsed => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
231 (Some(a), Some(b)) => a.used().cmp(&b.used()),
232 (Some(_), None) => Ordering::Greater,
233 (None, Some(_)) => Ordering::Less,
234 (None, None) => Ordering::Equal,
235 },
236 Self::InodesUsePercent | Self::InodesUse => {
237 |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
238 (Some(a), Some(b)) => a.use_share().partial_cmp(&b.use_share()).unwrap(),
240 (Some(_), None) => Ordering::Greater,
241 (None, Some(_)) => Ordering::Less,
242 (None, None) => Ordering::Equal,
243 }
244 }
245 Self::InodesFree => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
246 (Some(a), Some(b)) => a.favail.cmp(&b.favail),
247 (Some(_), None) => Ordering::Greater,
248 (None, Some(_)) => Ordering::Less,
249 (None, None) => Ordering::Equal,
250 },
251 Self::InodesCount => |a: &Mount, b: &Mount| match (&a.inodes(), &b.inodes()) {
252 (Some(a), Some(b)) => a.files.cmp(&b.files),
253 (Some(_), None) => Ordering::Greater,
254 (None, Some(_)) => Ordering::Less,
255 (None, None) => Ordering::Equal,
256 },
257 Self::MountPoint => |a: &Mount, b: &Mount| a.info.mount_point.cmp(&b.info.mount_point),
258 Self::Uuid => |a: &Mount, b: &Mount| match (&a.uuid, &b.uuid) {
259 (Some(a), Some(b)) => a.cmp(b),
260 (Some(_), None) => Ordering::Less,
261 (None, Some(_)) => Ordering::Greater,
262 (None, None) => Ordering::Equal,
263 },
264 Self::PartUuid => |a: &Mount, b: &Mount| match (&a.part_uuid, &b.part_uuid) {
265 (Some(a), Some(b)) => a.cmp(b),
266 (Some(_), None) => Ordering::Less,
267 (None, Some(_)) => Ordering::Greater,
268 (None, None) => Ordering::Equal,
269 },
270 Self::MountOptions => |a: &Mount, b: &Mount| a.info.options_string()
271 .cmp(&b.info.options_string()),
272 Self::CompressLevel => |a: &Mount, b: &Mount| match (a.info.option_value("compress"), b.info.option_value("compress")) {
273 (Some(a), Some(b)) => a.cmp(b),
274 (Some(_), None) => Ordering::Less,
275 (None, Some(_)) => Ordering::Greater,
276 (None, None) => Ordering::Equal,
277 },
278 }
279 }
280 pub fn default_sort_order(self) -> Order {
281 match self {
282 Self::Id => Order::Asc,
283 Self::Dev => Order::Asc,
284 Self::Filesystem => Order::Asc,
285 Self::Label => Order::Asc,
286 Self::Type => Order::Asc,
287 Self::Remote => Order::Desc,
288 Self::Disk => Order::Asc,
289 Self::Used => Order::Asc,
290 Self::Use => Order::Desc,
291 Self::UsePercent => Order::Asc,
292 Self::Free => Order::Asc,
293 Self::FreePercent => Order::Desc,
294 Self::Size => Order::Desc,
295 Self::InodesUsed => Order::Asc,
296 Self::InodesUse => Order::Asc,
297 Self::InodesUsePercent => Order::Asc,
298 Self::InodesFree => Order::Asc,
299 Self::InodesCount => Order::Asc,
300 Self::MountPoint => Order::Asc,
301 Self::Uuid => Order::Asc,
302 Self::PartUuid => Order::Asc,
303 Self::MountOptions => Order::Asc,
304 Self::CompressLevel => Order::Asc,
305 }
306 }
307 pub fn default_sort_col() -> Self {
308 Self::Size
309 }
310}
311
312#[derive(Debug)]
313pub struct ParseColError {
314 pub raw: String,
316}
317impl ParseColError {
318 pub fn new<S: Into<String>>(s: S) -> Self {
319 Self { raw: s.into() }
320 }
321}
322impl fmt::Display for ParseColError {
323 fn fmt(
324 &self,
325 f: &mut fmt::Formatter<'_>,
326 ) -> fmt::Result {
327 write!(
328 f,
329 "{:?} can't be parsed as a column; use 'dysk --list-cols' to see all column names",
330 self.raw,
331 )
332 }
333}
334impl std::error::Error for ParseColError {}