dysk_cli/
table.rs

1use {
2    crate::{
3        Args,
4        col::Col,
5    },
6    lfs_core::*,
7    termimad::{
8        CompoundStyle,
9        MadSkin,
10        ProgressBar,
11        crossterm::style::Color::*,
12        minimad::{
13            self,
14            OwningTemplateExpander,
15            TableBuilder,
16        },
17    },
18};
19
20// those colors are chosen to be "redish" for used, "greenish" for available
21// and, most importantly, to work on both white and black backgrounds. If you
22// find a better combination, please show me.
23static USED_COLOR: u8 = 209;
24static AVAI_COLOR: u8 = 65;
25static SIZE_COLOR: u8 = 172;
26
27static BAR_WIDTH: usize = 5;
28static INODES_BAR_WIDTH: usize = 5;
29
30pub fn print(
31    mounts: &[&Mount],
32    color: bool,
33    args: &Args,
34) {
35    if args.cols.is_empty() {
36        return;
37    }
38    let units = args.units;
39    let mut expander = OwningTemplateExpander::new();
40    expander.set_default("");
41    for mount in mounts {
42        let sub = expander
43            .sub("rows")
44            .set(
45                "id",
46                mount
47                    .info
48                    .id
49                    .as_ref()
50                    .map_or("".to_string(), |i| i.to_string()),
51            )
52            .set("dev", mount.info.dev)
53            .set("filesystem", &mount.info.fs)
54            .set("disk", mount.disk.as_ref().map_or("", |d| d.disk_type()))
55            .set("type", &mount.info.fs_type)
56            .set("mount-point", mount.info.mount_point.to_string_lossy())
57            .set("mount-options", mount.info.options_string())
58            .set_option("uuid", mount.uuid.as_ref())
59            .set_option("part_uuid", mount.part_uuid.as_ref())
60            .set_option(
61                "compress-level",
62                mount.info.option_value("compress"),
63            );
64        if let Some(label) = &mount.fs_label {
65            sub.set("label", label);
66        }
67        if mount.is_remote() {
68            sub.set("remote", "x");
69        }
70        if let Some(stats) = mount.stats() {
71            let use_share = stats.use_share();
72            let free_share = 1.0 - use_share;
73            sub.set("size", units.fmt(stats.size()))
74                .set("used", units.fmt(stats.used()))
75                .set("use-percents", format!("{:.0}%", 100.0 * use_share))
76                .set_md("bar", progress_bar_md(use_share, BAR_WIDTH, args.ascii))
77                .set("free", units.fmt(stats.available()))
78                .set("free-percents", format!("{:.0}%", 100.0 * free_share));
79            if let Some(inodes) = &stats.inodes {
80                let iuse_share = inodes.use_share();
81                sub.set("inodes", inodes.files)
82                    .set("iused", inodes.used())
83                    .set("iuse-percents", format!("{:.0}%", 100.0 * iuse_share))
84                    .set_md(
85                        "ibar",
86                        progress_bar_md(iuse_share, INODES_BAR_WIDTH, args.ascii),
87                    )
88                    .set("ifree", inodes.favail);
89            }
90        } else if mount.is_unreachable() {
91            sub.set("use-error", "unreachable");
92        }
93    }
94    let mut skin = if color {
95        make_colored_skin()
96    } else {
97        MadSkin::no_style()
98    };
99    if args.ascii {
100        skin.limit_to_ascii();
101    }
102
103    let mut tbl = TableBuilder::default();
104    for col in args.cols.cols() {
105        tbl.col(
106            minimad::Col::new(
107                col.title(),
108                match col {
109                    Col::Id => "${id}",
110                    Col::Dev => "${dev}",
111                    Col::Filesystem => "${filesystem}",
112                    Col::Label => "${label}",
113                    Col::Disk => "${disk}",
114                    Col::Type => "${type}",
115                    Col::Remote => "${remote}",
116                    Col::Used => "~~${used}~~",
117                    Col::Use => "~~${use-percents}~~ ${bar}~~${use-error}~~",
118                    Col::UsePercent => "~~${use-percents}~~",
119                    Col::Free => "*${free}*",
120                    Col::FreePercent => "*${free-percents}*",
121                    Col::Size => "**${size}**",
122                    Col::InodesFree => "*${ifree}*",
123                    Col::InodesUsed => "~~${iused}~~",
124                    Col::InodesUse => "~~${iuse-percents}~~ ${ibar}",
125                    Col::InodesUsePercent => "~~${iuse-percents}~~",
126                    Col::InodesCount => "**${inodes}**",
127                    Col::MountPoint => "${mount-point}",
128                    Col::Uuid => "${uuid}",
129                    Col::PartUuid => "${part_uuid}",
130                    Col::MountOptions => "${mount-options}",
131                    Col::CompressLevel => "${compress-level}",
132                },
133            )
134            .align_content(col.content_align())
135            .align_header(col.header_align()),
136        );
137    }
138
139    skin.print_owning_expander_md(&expander, &tbl);
140}
141
142fn make_colored_skin() -> MadSkin {
143    MadSkin {
144        bold: CompoundStyle::with_fg(AnsiValue(SIZE_COLOR)), // size
145        inline_code: CompoundStyle::with_fgbg(AnsiValue(USED_COLOR), AnsiValue(AVAI_COLOR)), // use bar
146        strikeout: CompoundStyle::with_fg(AnsiValue(USED_COLOR)),                            // use%
147        italic: CompoundStyle::with_fg(AnsiValue(AVAI_COLOR)), // available
148        ..Default::default()
149    }
150}
151
152fn progress_bar_md(
153    share: f64,
154    bar_width: usize,
155    ascii: bool,
156) -> String {
157    if ascii {
158        let count = (share * bar_width as f64).round() as usize;
159        let bar: String = "".repeat(count);
160        let no_bar: String = "-".repeat(bar_width - count);
161        format!("~~{}~~*{}*", bar, no_bar)
162    } else {
163        let pb = ProgressBar::new(share as f32, bar_width);
164        format!("`{:<width$}`", pb, width = bar_width)
165    }
166}