Skip to content

Commit b489e99

Browse files
authored
Merge pull request #288 from fox0/df
df: impl Mount::print
2 parents 089b87a + 6d84e22 commit b489e99

File tree

1 file changed

+169
-57
lines changed

1 file changed

+169
-57
lines changed

fs/df.rs

Lines changed: 169 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ use gettextrs::{bind_textdomain_codeset, gettext, setlocale, textdomain, LocaleC
1818
use plib::PROJECT_NAME;
1919
#[cfg(target_os = "macos")]
2020
use std::ffi::CStr;
21-
use std::ffi::CString;
22-
use std::io;
21+
use std::{cmp, ffi::CString, io};
2322

2423
#[derive(Parser)]
2524
#[command(version, about = gettext("df - report free storage space"))]
@@ -51,6 +50,133 @@ struct Args {
5150
files: Vec<String>,
5251
}
5352

53+
/// Display modes
54+
pub enum OutputMode {
55+
/// When both the -k and -P options are specified
56+
Posix,
57+
/// When the -P option is specified without the -k option
58+
PosixLegacy,
59+
/// The format of the default output from df is unspecified,
60+
/// but all space figures are reported in 512-byte units
61+
Unspecified,
62+
}
63+
64+
impl OutputMode {
65+
pub fn new(kilo: bool, portable: bool) -> Self {
66+
match (kilo, portable) {
67+
(true, true) => Self::Posix,
68+
(false, true) => Self::PosixLegacy,
69+
_ => Self::Unspecified,
70+
}
71+
}
72+
73+
pub fn get_block_size(&self) -> u64 {
74+
match self {
75+
OutputMode::Posix => 1024,
76+
OutputMode::PosixLegacy => 512,
77+
OutputMode::Unspecified => 512,
78+
}
79+
}
80+
}
81+
82+
pub struct Field {
83+
caption: String,
84+
width: usize,
85+
}
86+
87+
impl Field {
88+
pub fn new(caption: String, min_width: usize) -> Self {
89+
let width = cmp::max(caption.len(), min_width);
90+
Self { caption, width }
91+
}
92+
93+
pub fn print_header(&self) {
94+
print!("{: <width$} ", self.caption, width = self.width);
95+
}
96+
97+
pub fn print_header_align_right(&self) {
98+
print!("{: >width$} ", self.caption, width = self.width);
99+
}
100+
101+
pub fn print_string(&self, value: &String) {
102+
print!("{: <width$} ", value, width = self.width);
103+
}
104+
105+
pub fn print_u64(&self, value: u64) {
106+
print!("{: >width$} ", value, width = self.width);
107+
}
108+
109+
pub fn print_percentage(&self, value: u32) {
110+
print!("{: >width$}% ", value, width = self.width - 1);
111+
}
112+
}
113+
114+
pub struct Fields {
115+
pub mode: OutputMode,
116+
/// file system
117+
pub source: Field,
118+
/// FS size
119+
pub size: Field,
120+
/// FS size used
121+
pub used: Field,
122+
/// FS size available
123+
pub avail: Field,
124+
/// percent used
125+
pub pcent: Field,
126+
/// mount point
127+
pub target: Field,
128+
// /// specified file name
129+
// file: Field,
130+
}
131+
132+
impl Fields {
133+
pub fn new(mode: OutputMode) -> Self {
134+
let size_caption = format!("{}-{}", mode.get_block_size(), gettext("blocks"));
135+
Self {
136+
mode,
137+
source: Field::new(gettext("Filesystem"), 14),
138+
size: Field::new(size_caption, 10),
139+
used: Field::new(gettext("Used"), 10),
140+
avail: Field::new(gettext("Available"), 10),
141+
pcent: Field::new(gettext("Capacity"), 5),
142+
target: Field::new(gettext("Mounted on"), 0),
143+
}
144+
}
145+
146+
pub fn print_header(&self) {
147+
self.source.print_header();
148+
self.size.print_header_align_right();
149+
self.used.print_header_align_right();
150+
self.avail.print_header_align_right();
151+
self.pcent.print_header_align_right();
152+
self.target.print_header();
153+
println!();
154+
}
155+
156+
fn print_row(
157+
&self,
158+
fsname: &String,
159+
total: u64,
160+
used: u64,
161+
avail: u64,
162+
percentage_used: u32,
163+
target: &String,
164+
) {
165+
// The remaining output with -P shall consist of one line of information
166+
// for each specified file system. These lines shall be formatted as follows:
167+
// "%s %d %d %d %d%% %s\n", <file system name>, <total space>,
168+
// <space used>, <space free>, <percentage used>,
169+
// <file system root>
170+
self.source.print_string(fsname);
171+
self.size.print_u64(total);
172+
self.used.print_u64(used);
173+
self.avail.print_u64(avail);
174+
self.pcent.print_percentage(percentage_used);
175+
self.target.print_string(target);
176+
println!();
177+
}
178+
}
179+
54180
#[cfg(target_os = "macos")]
55181
fn to_cstr(array: &[libc::c_char]) -> &CStr {
56182
unsafe {
@@ -79,6 +205,39 @@ struct Mount {
79205
cached_statfs: libc::statfs,
80206
}
81207

208+
impl Mount {
209+
fn print(&self, fields: &Fields) {
210+
if !self.masked {
211+
return;
212+
}
213+
214+
let sf = self.cached_statfs;
215+
216+
let block_size = fields.mode.get_block_size();
217+
let blksz = sf.f_bsize as u64;
218+
219+
let total = (sf.f_blocks * blksz) / block_size;
220+
let avail = (sf.f_bavail * blksz) / block_size;
221+
let free = (sf.f_bfree * blksz) / block_size;
222+
let used = total - free;
223+
224+
// The percentage value shall be expressed as a positive integer,
225+
// with any fractional result causing it to be rounded to the next highest integer.
226+
let percentage_used = f64::from(used as u32) / f64::from((used + free) as u32);
227+
let percentage_used = percentage_used * 100.0;
228+
let percentage_used = percentage_used.ceil() as u32;
229+
230+
fields.print_row(
231+
&self.devname,
232+
total,
233+
used,
234+
avail,
235+
percentage_used,
236+
&self.dir,
237+
);
238+
}
239+
}
240+
82241
struct MountList {
83242
mounts: Vec<Mount>,
84243
has_masks: bool,
@@ -192,60 +351,6 @@ fn mask_fs_by_file(info: &mut MountList, filename: &str) -> io::Result<()> {
192351
Ok(())
193352
}
194353

195-
fn show_mount(args: &Args, block_size: u64, mount: &Mount) {
196-
let sf = &mount.cached_statfs;
197-
198-
let blksz = sf.f_bsize as u64;
199-
200-
let total = (sf.f_blocks * blksz) / block_size;
201-
let avail = (sf.f_bavail * blksz) / block_size;
202-
let free = (sf.f_bfree * blksz) / block_size;
203-
let used = total - free;
204-
205-
if total == 0 {
206-
return;
207-
}
208-
209-
let pct = ((total - avail) * 100) / total;
210-
211-
if args.portable {
212-
println!(
213-
"{:>20} {:>9} {:>9} {:>9} {:>7} {}",
214-
mount.devname, total, used, avail, pct, mount.dir
215-
);
216-
} else {
217-
println!(
218-
"{:>20} {:>9} {:>9} {:>9} {:>3} {}",
219-
mount.devname, total, used, avail, pct, mount.dir
220-
);
221-
}
222-
}
223-
224-
fn show_info(args: &Args, info: &MountList) {
225-
let block_size: u64 = match args.kilo {
226-
true => 1024,
227-
false => 512,
228-
};
229-
230-
if args.portable {
231-
println!(
232-
"Filesystem {:>4}-blocks Used Available Capacity Mounted on",
233-
block_size
234-
);
235-
} else {
236-
println!(
237-
"Filesystem {:>4}-blocks Used Available Use % Mounted on",
238-
block_size
239-
);
240-
}
241-
242-
for mount in &info.mounts {
243-
if mount.masked {
244-
show_mount(args, block_size, mount);
245-
}
246-
}
247-
}
248-
249354
fn main() -> Result<(), Box<dyn std::error::Error>> {
250355
// parse command line arguments
251356
let args = Args::parse();
@@ -265,7 +370,14 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
265370
}
266371

267372
info.ensure_masked();
268-
show_info(&args, &info);
373+
374+
let mode = OutputMode::new(args.kilo, args.portable);
375+
let fields = Fields::new(mode);
376+
fields.print_header();
377+
378+
for mount in &info.mounts {
379+
mount.print(&fields);
380+
}
269381

270382
Ok(())
271383
}

0 commit comments

Comments
 (0)