d248f7d17b
derive(Default) from applets
150 lines
5.0 KiB
Rust
150 lines
5.0 KiB
Rust
use clap::{Arg, ArgAction, ArgMatches, Command};
|
|
use mount::MntEntries;
|
|
use shitbox::Cmd;
|
|
use size_display::Size;
|
|
use std::{error::Error, fmt, io, path::PathBuf};
|
|
|
|
#[derive(Debug)]
|
|
pub struct Df;
|
|
|
|
impl Cmd for Df {
|
|
fn cli(&self) -> Command {
|
|
Command::new("df")
|
|
.about("report free disk space")
|
|
.author("Nathan Fisher")
|
|
.version(env!("CARGO_PKG_VERSION"))
|
|
.disable_help_flag(true)
|
|
.args([
|
|
Arg::new("kilobytes")
|
|
.help("Use 1024-byte units, instead of the default 512-byte units, when writing space figures.")
|
|
.short('k')
|
|
.long("kilobytes")
|
|
.action(ArgAction::SetTrue),
|
|
Arg::new("human")
|
|
.help("print sizes in powers of 1024 (e.g., 1023M)")
|
|
.short('h')
|
|
.long("human-readable")
|
|
.conflicts_with("kilobytes")
|
|
.action(ArgAction::SetTrue),
|
|
Arg::new("file")
|
|
.value_name("FILE")
|
|
.num_args(1..)
|
|
])
|
|
}
|
|
|
|
fn run(&self, matches: &ArgMatches) -> Result<(), Box<dyn Error>> {
|
|
let units = if matches.get_flag("kilobytes") {
|
|
Units::Kilo
|
|
} else if matches.get_flag("human") {
|
|
Units::Natural
|
|
} else {
|
|
Units::Posix
|
|
};
|
|
println!("Filesystem {units} Used Avail Capacity Mounted on");
|
|
if let Some(files) = matches.get_many::<String>("file") {
|
|
for f in files {
|
|
let entry = MntEntries::new("/proc/mounts")?
|
|
.find(|e| e.dir.as_str() == f)
|
|
.ok_or(io::Error::new(io::ErrorKind::Other, "no such filesystem"))?;
|
|
print_stats(&entry.fsname, &entry.dir, units)?;
|
|
}
|
|
} else {
|
|
let entries = MntEntries::new("/proc/mounts")?.filter(|x| {
|
|
x.fstype != "proc"
|
|
&& x.fstype != "sysfs"
|
|
&& x.fstype != "securityfs"
|
|
&& x.fstype != "cgroup2"
|
|
&& x.fstype != "pstore"
|
|
&& x.fstype != "bpf"
|
|
&& x.fstype != "autofs"
|
|
&& x.fstype != "hugetlbfs"
|
|
&& x.fstype != "tracefs"
|
|
&& x.fstype != "debugfs"
|
|
&& x.fstype != "configfs"
|
|
&& x.fstype != "devpts"
|
|
&& x.fstype != "efivarfs"
|
|
&& x.fstype != "ramfs"
|
|
&& x.fstype != "binfmt_misc"
|
|
&& x.fstype != "fuse.gvfsd-fuse"
|
|
&& x.fstype != "fuse.portal"
|
|
&& x.fstype != "mqueue"
|
|
&& x.fstype != "fusectl"
|
|
});
|
|
for e in entries {
|
|
print_stats(&e.fsname, &e.dir, units)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn path(&self) -> Option<shitbox::Path> {
|
|
Some(shitbox::Path::UsrBin)
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, PartialEq)]
|
|
enum Units {
|
|
Posix,
|
|
Kilo,
|
|
Natural,
|
|
}
|
|
|
|
impl fmt::Display for Units {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Self::Posix => write!(f, "512-blocks"),
|
|
Self::Kilo => write!(f, " 1K-blocks"),
|
|
Self::Natural => write!(f, " Size"),
|
|
}
|
|
}
|
|
}
|
|
|
|
fn print_stats(fs: &str, mntpt: &str, units: Units) -> Result<(), Box<dyn Error>> {
|
|
let p = PathBuf::from(mntpt);
|
|
if p.exists() {
|
|
let st = stat::statfs(mntpt)?;
|
|
let bs = match units {
|
|
Units::Posix => st.f_frsize / 512,
|
|
Units::Kilo => st.f_frsize / 1024,
|
|
Units::Natural => st.f_frsize,
|
|
};
|
|
let total = st.f_blocks * bs;
|
|
let avail = st.f_bfree * bs;
|
|
let used = total - avail;
|
|
let mut capacity = if total > 0 { (used * 100) / total } else { 0 };
|
|
if used * 100 != capacity * (used + avail) {
|
|
capacity += 1;
|
|
}
|
|
if units == Units::Natural {
|
|
print_human(fs, mntpt, total, avail, used, capacity);
|
|
return Ok(());
|
|
}
|
|
println!("{fs:<12} {total:>9} {used:>9} {avail:>9} {capacity:>7}% {mntpt}");
|
|
Ok(())
|
|
} else {
|
|
Err(io::Error::new(io::ErrorKind::Other, "fs does not exist").into())
|
|
}
|
|
}
|
|
|
|
fn print_human(fs: &str, mntpt: &str, total: u64, avail: u64, used: u64, capacity: u64) {
|
|
// Unfortunately these strings must be printed separately due to the way the
|
|
// library formats size display, as 0 will be a shorter string due to having
|
|
// no suffix, throwing off column alignment
|
|
print!("{fs:<12}");
|
|
if total > 0 {
|
|
print!("{:>9.1}", Size(total));
|
|
} else {
|
|
print!("{total:>10}");
|
|
}
|
|
if used > 0 {
|
|
print!("{:>9.1}", Size(used));
|
|
} else {
|
|
print!("{used:>10}");
|
|
}
|
|
if avail > 0 {
|
|
println!("{:9.1} {capacity:>7}% {mntpt}", Size(avail));
|
|
} else {
|
|
println!("{avail:>10} {capacity:>7}% {mntpt}");
|
|
}
|
|
}
|