dysk_cli/
table.rs

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