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