mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-29 03:57:44 +00:00
Merge pull request #3083 from jfinkels/df-table-module
df: refactor data table into Row, Header structs
This commit is contained in:
commit
e76818bb19
3 changed files with 513 additions and 152 deletions
|
@ -5,8 +5,8 @@
|
||||||
//
|
//
|
||||||
// For the full copyright and license information, please view the LICENSE file
|
// For the full copyright and license information, please view the LICENSE file
|
||||||
// that was distributed with this source code.
|
// that was distributed with this source code.
|
||||||
|
mod table;
|
||||||
|
|
||||||
use uucore::error::UError;
|
|
||||||
use uucore::error::UResult;
|
use uucore::error::UResult;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use uucore::fsext::statfs_fn;
|
use uucore::fsext::statfs_fn;
|
||||||
|
@ -14,14 +14,11 @@ use uucore::fsext::{read_fs_list, FsUsage, MountInfo};
|
||||||
|
|
||||||
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
|
use clap::{crate_version, App, AppSettings, Arg, ArgMatches};
|
||||||
|
|
||||||
use number_prefix::NumberPrefix;
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::error::Error;
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fmt::Display;
|
|
||||||
use std::iter::FromIterator;
|
use std::iter::FromIterator;
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -29,6 +26,8 @@ use std::mem;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::table::{DisplayRow, Header, Row};
|
||||||
|
|
||||||
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
|
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
|
||||||
or all file systems by default.";
|
or all file systems by default.";
|
||||||
|
|
||||||
|
@ -58,6 +57,7 @@ struct FsSelector {
|
||||||
exclude: HashSet<String>,
|
exclude: HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
struct Options {
|
struct Options {
|
||||||
show_local_fs: bool,
|
show_local_fs: bool,
|
||||||
show_all_fs: bool,
|
show_all_fs: bool,
|
||||||
|
@ -236,64 +236,6 @@ fn filter_mount_list(vmi: Vec<MountInfo>, paths: &[String], opt: &Options) -> Ve
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert `value` to a human readable string based on `base`.
|
|
||||||
/// e.g. It returns 1G when value is 1 * 1024 * 1024 * 1024 and base is 1024.
|
|
||||||
/// Note: It returns `value` if `base` isn't positive.
|
|
||||||
fn human_readable(value: u64, base: i64) -> UResult<String> {
|
|
||||||
let base_str = match base {
|
|
||||||
d if d < 0 => value.to_string(),
|
|
||||||
|
|
||||||
// ref: [Binary prefix](https://en.wikipedia.org/wiki/Binary_prefix) @@ <https://archive.is/cnwmF>
|
|
||||||
// ref: [SI/metric prefix](https://en.wikipedia.org/wiki/Metric_prefix) @@ <https://archive.is/QIuLj>
|
|
||||||
1000 => match NumberPrefix::decimal(value as f64) {
|
|
||||||
NumberPrefix::Standalone(bytes) => bytes.to_string(),
|
|
||||||
NumberPrefix::Prefixed(prefix, bytes) => format!("{:.1}{}", bytes, prefix.symbol()),
|
|
||||||
},
|
|
||||||
|
|
||||||
1024 => match NumberPrefix::binary(value as f64) {
|
|
||||||
NumberPrefix::Standalone(bytes) => bytes.to_string(),
|
|
||||||
NumberPrefix::Prefixed(prefix, bytes) => format!("{:.1}{}", bytes, prefix.symbol()),
|
|
||||||
},
|
|
||||||
|
|
||||||
_ => return Err(DfError::InvalidBaseValue(base.to_string()).into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(base_str)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn use_size(free_size: u64, total_size: u64) -> String {
|
|
||||||
if total_size == 0 {
|
|
||||||
return String::from("-");
|
|
||||||
}
|
|
||||||
return format!(
|
|
||||||
"{:.0}%",
|
|
||||||
100f64 - 100f64 * (free_size as f64 / total_size as f64)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum DfError {
|
|
||||||
InvalidBaseValue(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for DfError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
DfError::InvalidBaseValue(s) => write!(f, "Internal error: Unknown base value {}", s),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for DfError {}
|
|
||||||
|
|
||||||
impl UError for DfError {
|
|
||||||
fn code(&self) -> i32 {
|
|
||||||
match self {
|
|
||||||
DfError::InvalidBaseValue(_) => 1,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let usage = usage();
|
let usage = usage();
|
||||||
|
@ -314,98 +256,16 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
|
|
||||||
let opt = Options::from(&matches);
|
let opt = Options::from(&matches);
|
||||||
|
|
||||||
let fs_list = filter_mount_list(read_fs_list(), &paths, &opt)
|
let mounts = read_fs_list();
|
||||||
|
let data: Vec<Row> = filter_mount_list(mounts, &paths, &opt)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(Filesystem::new)
|
.filter_map(Filesystem::new)
|
||||||
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
|
.filter(|fs| fs.usage.blocks != 0 || opt.show_all_fs || opt.show_listed_fs)
|
||||||
.collect::<Vec<_>>();
|
.map(Into::into)
|
||||||
|
.collect();
|
||||||
// set headers
|
println!("{}", Header::new(&opt));
|
||||||
let mut header = vec!["Filesystem"];
|
for row in data {
|
||||||
if opt.show_fs_type {
|
println!("{}", DisplayRow::new(row, &opt));
|
||||||
header.push("Type");
|
|
||||||
}
|
|
||||||
header.extend_from_slice(&if opt.show_inode_instead {
|
|
||||||
// spell-checker:disable-next-line
|
|
||||||
["Inodes", "Iused", "IFree", "IUses%"]
|
|
||||||
} else {
|
|
||||||
[
|
|
||||||
if opt.human_readable_base == -1 {
|
|
||||||
"1k-blocks"
|
|
||||||
} else {
|
|
||||||
"Size"
|
|
||||||
},
|
|
||||||
"Used",
|
|
||||||
"Available",
|
|
||||||
"Use%",
|
|
||||||
]
|
|
||||||
});
|
|
||||||
if cfg!(target_os = "macos") && !opt.show_inode_instead {
|
|
||||||
header.insert(header.len() - 1, "Capacity");
|
|
||||||
}
|
|
||||||
header.push("Mounted on");
|
|
||||||
|
|
||||||
for (idx, title) in header.iter().enumerate() {
|
|
||||||
if idx == 0 || idx == header.len() - 1 {
|
|
||||||
print!("{0: <16} ", title);
|
|
||||||
} else if opt.show_fs_type && idx == 1 {
|
|
||||||
print!("{0: <5} ", title);
|
|
||||||
} else if idx == header.len() - 2 {
|
|
||||||
print!("{0: >5} ", title);
|
|
||||||
} else {
|
|
||||||
print!("{0: >12} ", title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
println!();
|
|
||||||
for fs in &fs_list {
|
|
||||||
print!("{0: <16} ", fs.mount_info.dev_name);
|
|
||||||
if opt.show_fs_type {
|
|
||||||
print!("{0: <5} ", fs.mount_info.fs_type);
|
|
||||||
}
|
|
||||||
if opt.show_inode_instead {
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(fs.usage.files, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(fs.usage.files - fs.usage.ffree, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(fs.usage.ffree, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
print!(
|
|
||||||
"{0: >5} ",
|
|
||||||
format!(
|
|
||||||
"{0:.1}%",
|
|
||||||
100f64 - 100f64 * (fs.usage.ffree as f64 / fs.usage.files as f64)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let total_size = fs.usage.blocksize * fs.usage.blocks;
|
|
||||||
let free_size = fs.usage.blocksize * fs.usage.bfree;
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(total_size, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(total_size - free_size, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
print!(
|
|
||||||
"{0: >12} ",
|
|
||||||
human_readable(free_size, opt.human_readable_base)?
|
|
||||||
);
|
|
||||||
if cfg!(target_os = "macos") {
|
|
||||||
let used = fs.usage.blocks - fs.usage.bfree;
|
|
||||||
let blocks = used + fs.usage.bavail;
|
|
||||||
print!("{0: >12} ", use_size(used, blocks));
|
|
||||||
}
|
|
||||||
print!("{0: >5} ", use_size(free_size, total_size));
|
|
||||||
}
|
|
||||||
print!("{0: <16}", fs.mount_info.mount_dir);
|
|
||||||
println!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
501
src/uu/df/src/table.rs
Normal file
501
src/uu/df/src/table.rs
Normal file
|
@ -0,0 +1,501 @@
|
||||||
|
// * This file is part of the uutils coreutils package.
|
||||||
|
// *
|
||||||
|
// * For the full copyright and license information, please view the LICENSE
|
||||||
|
// * file that was distributed with this source code.
|
||||||
|
// spell-checker:ignore tmpfs
|
||||||
|
//! The filesystem usage data table.
|
||||||
|
//!
|
||||||
|
//! A table comprises a header row ([`Header`]) and a collection of
|
||||||
|
//! data rows ([`Row`]), one per filesystem. To display a [`Row`],
|
||||||
|
//! combine it with [`Options`] in the [`DisplayRow`] struct; the
|
||||||
|
//! [`DisplayRow`] implements [`std::fmt::Display`].
|
||||||
|
use number_prefix::NumberPrefix;
|
||||||
|
|
||||||
|
use crate::{Filesystem, Options};
|
||||||
|
use uucore::fsext::{FsUsage, MountInfo};
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// A row in the filesystem usage data table.
|
||||||
|
///
|
||||||
|
/// A row comprises several pieces of information, including the
|
||||||
|
/// filesystem device, the mountpoint, the number of bytes used, etc.
|
||||||
|
pub(crate) struct Row {
|
||||||
|
/// Name of the device on which the filesystem lives.
|
||||||
|
fs_device: String,
|
||||||
|
|
||||||
|
/// Type of filesystem (for example, `"ext4"`, `"tmpfs"`, etc.).
|
||||||
|
fs_type: String,
|
||||||
|
|
||||||
|
/// Path at which the filesystem is mounted.
|
||||||
|
fs_mount: String,
|
||||||
|
|
||||||
|
/// Total number of bytes in the filesystem regardless of whether they are used.
|
||||||
|
bytes: u64,
|
||||||
|
|
||||||
|
/// Number of used bytes.
|
||||||
|
bytes_used: u64,
|
||||||
|
|
||||||
|
/// Number of free bytes.
|
||||||
|
bytes_free: u64,
|
||||||
|
|
||||||
|
/// Percentage of bytes that are used, given as a float between 0 and 1.
|
||||||
|
///
|
||||||
|
/// If the filesystem has zero bytes, then this is `None`.
|
||||||
|
bytes_usage: Option<f64>,
|
||||||
|
|
||||||
|
/// Percentage of bytes that are available, given as a float between 0 and 1.
|
||||||
|
///
|
||||||
|
/// These are the bytes that are available to non-privileged processes.
|
||||||
|
///
|
||||||
|
/// If the filesystem has zero bytes, then this is `None`.
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Option<f64>,
|
||||||
|
|
||||||
|
/// Total number of inodes in the filesystem.
|
||||||
|
inodes: u64,
|
||||||
|
|
||||||
|
/// Number of used inodes.
|
||||||
|
inodes_used: u64,
|
||||||
|
|
||||||
|
/// Number of free inodes.
|
||||||
|
inodes_free: u64,
|
||||||
|
|
||||||
|
/// Percentage of inodes that are used, given as a float between 0 and 1.
|
||||||
|
///
|
||||||
|
/// If the filesystem has zero bytes, then this is `None`.
|
||||||
|
inodes_usage: Option<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Filesystem> for Row {
|
||||||
|
fn from(fs: Filesystem) -> Self {
|
||||||
|
let MountInfo {
|
||||||
|
dev_name,
|
||||||
|
fs_type,
|
||||||
|
mount_dir,
|
||||||
|
..
|
||||||
|
} = fs.mount_info;
|
||||||
|
let FsUsage {
|
||||||
|
blocksize,
|
||||||
|
blocks,
|
||||||
|
bfree,
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bavail,
|
||||||
|
files,
|
||||||
|
ffree,
|
||||||
|
..
|
||||||
|
} = fs.usage;
|
||||||
|
Self {
|
||||||
|
fs_device: dev_name,
|
||||||
|
fs_type,
|
||||||
|
fs_mount: mount_dir,
|
||||||
|
bytes: blocksize * blocks,
|
||||||
|
bytes_used: blocksize * (blocks - bfree),
|
||||||
|
bytes_free: blocksize * bfree,
|
||||||
|
bytes_usage: if blocks == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(((blocks - bfree) as f64) / blocks as f64)
|
||||||
|
},
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: if bavail == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(bavail as f64 / ((blocks - bfree + bavail) as f64))
|
||||||
|
},
|
||||||
|
inodes: files,
|
||||||
|
inodes_used: files - ffree,
|
||||||
|
inodes_free: ffree,
|
||||||
|
inodes_usage: if files == 0 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(ffree as f64 / files as f64)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A displayable wrapper around a [`Row`].
|
||||||
|
///
|
||||||
|
/// The `options` control how the information in the row gets displayed.
|
||||||
|
pub(crate) struct DisplayRow<'a> {
|
||||||
|
/// The data in this row.
|
||||||
|
row: Row,
|
||||||
|
|
||||||
|
/// Options that control how to display the data.
|
||||||
|
options: &'a Options,
|
||||||
|
// TODO We don't need all of the command-line options here. Some
|
||||||
|
// of the command-line options indicate which rows to include or
|
||||||
|
// exclude. Other command-line options indicate which columns to
|
||||||
|
// include or exclude. Still other options indicate how to format
|
||||||
|
// numbers. We could split the options up into those groups to
|
||||||
|
// reduce the coupling between this `table.rs` module and the main
|
||||||
|
// `df.rs` module.
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DisplayRow<'a> {
|
||||||
|
/// Instantiate this struct.
|
||||||
|
pub(crate) fn new(row: Row, options: &'a Options) -> Self {
|
||||||
|
Self { row, options }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a string giving the scaled version of the input number.
|
||||||
|
///
|
||||||
|
/// The scaling factor is defined in the `options` field.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If the scaling factor is not 1000, 1024, or a negative number.
|
||||||
|
fn scaled(&self, size: u64) -> Result<String, fmt::Error> {
|
||||||
|
// TODO The argument-parsing code should be responsible for
|
||||||
|
// ensuring that the `human_readable_base` number is
|
||||||
|
// positive. Then we could remove the `Err` case from this
|
||||||
|
// function.
|
||||||
|
//
|
||||||
|
// TODO We should not be using a negative number to indicate
|
||||||
|
// default behavior. The default behavior for `df` is to show
|
||||||
|
// sizes in blocks of 1K bytes each, so we should just do
|
||||||
|
// that.
|
||||||
|
//
|
||||||
|
// TODO Support arbitrary positive scaling factors (from the
|
||||||
|
// `--block-size` command-line argument).
|
||||||
|
let number_prefix = match self.options.human_readable_base {
|
||||||
|
1000 => NumberPrefix::decimal(size as f64),
|
||||||
|
1024 => NumberPrefix::binary(size as f64),
|
||||||
|
d if d < 0 => return Ok(size.to_string()),
|
||||||
|
_ => return Err(fmt::Error {}),
|
||||||
|
};
|
||||||
|
match number_prefix {
|
||||||
|
NumberPrefix::Standalone(bytes) => Ok(bytes.to_string()),
|
||||||
|
NumberPrefix::Prefixed(prefix, bytes) => Ok(format!("{:.1}{}", bytes, prefix.symbol())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a float between 0 and 1 into a percentage string.
|
||||||
|
///
|
||||||
|
/// If `None`, return the string `"-"` instead.
|
||||||
|
fn percentage(fraction: Option<f64>) -> String {
|
||||||
|
match fraction {
|
||||||
|
None => "-".to_string(),
|
||||||
|
Some(x) => format!("{:.0}%", 100.0 * x),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the bytes data for this row.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If there is a problem writing to `f`.
|
||||||
|
///
|
||||||
|
/// If the scaling factor is not 1000, 1024, or a negative number.
|
||||||
|
fn fmt_bytes(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?;
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?;
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{0: >12} ",
|
||||||
|
DisplayRow::percentage(self.row.bytes_capacity)
|
||||||
|
)?;
|
||||||
|
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write the inodes data for this row.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// If there is a problem writing to `f`.
|
||||||
|
///
|
||||||
|
/// If the scaling factor is not 1000, 1024, or a negative number.
|
||||||
|
fn fmt_inodes(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?;
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?;
|
||||||
|
write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?;
|
||||||
|
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DisplayRow<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{0: <16} ", self.row.fs_device)?;
|
||||||
|
if self.options.show_fs_type {
|
||||||
|
write!(f, "{0: <5} ", self.row.fs_type)?;
|
||||||
|
}
|
||||||
|
if self.options.show_inode_instead {
|
||||||
|
self.fmt_inodes(f)?;
|
||||||
|
} else {
|
||||||
|
self.fmt_bytes(f)?;
|
||||||
|
}
|
||||||
|
write!(f, "{0: <16}", self.row.fs_mount)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The header row.
|
||||||
|
///
|
||||||
|
/// The `options` control which columns are displayed.
|
||||||
|
pub(crate) struct Header<'a> {
|
||||||
|
/// Options that control which columns are displayed.
|
||||||
|
options: &'a Options,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Header<'a> {
|
||||||
|
/// Instantiate this struct.
|
||||||
|
pub(crate) fn new(options: &'a Options) -> Self {
|
||||||
|
Self { options }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Header<'_> {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{0: <16} ", "Filesystem")?;
|
||||||
|
if self.options.show_fs_type {
|
||||||
|
write!(f, "{0: <5} ", "Type")?;
|
||||||
|
}
|
||||||
|
if self.options.show_inode_instead {
|
||||||
|
write!(f, "{0: >12} ", "Inodes")?;
|
||||||
|
write!(f, "{0: >12} ", "IUsed")?;
|
||||||
|
write!(f, "{0: >12} ", "IFree")?;
|
||||||
|
write!(f, "{0: >5} ", "IUse%")?;
|
||||||
|
} else {
|
||||||
|
if self.options.human_readable_base == -1 {
|
||||||
|
write!(f, "{0: >12} ", "1k-blocks")?;
|
||||||
|
} else {
|
||||||
|
write!(f, "{0: >12} ", "Size")?;
|
||||||
|
};
|
||||||
|
write!(f, "{0: >12} ", "Used")?;
|
||||||
|
write!(f, "{0: >12} ", "Available")?;
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
write!(f, "{0: >12} ", "Capacity")?;
|
||||||
|
write!(f, "{0: >5} ", "Use%")?;
|
||||||
|
}
|
||||||
|
write!(f, "{0: <16} ", "Mounted on")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
|
||||||
|
use crate::table::{DisplayRow, Header, Row};
|
||||||
|
use crate::Options;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_display() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::new(&options).to_string(),
|
||||||
|
"Filesystem 1k-blocks Used Available Use% Mounted on "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_display_fs_type() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
show_fs_type: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::new(&options).to_string(),
|
||||||
|
"Filesystem Type 1k-blocks Used Available Use% Mounted on "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_display_inode() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
show_inode_instead: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::new(&options).to_string(),
|
||||||
|
"Filesystem Inodes IUsed IFree IUse% Mounted on "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_display_human_readable_binary() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: 1024,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::new(&options).to_string(),
|
||||||
|
"Filesystem Size Used Available Use% Mounted on "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_header_display_human_readable_si() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: 1000,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::new(&options).to_string(),
|
||||||
|
"Filesystem Size Used Available Use% Mounted on "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_display() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let row = Row {
|
||||||
|
fs_device: "my_device".to_string(),
|
||||||
|
fs_type: "my_type".to_string(),
|
||||||
|
fs_mount: "my_mount".to_string(),
|
||||||
|
|
||||||
|
bytes: 100,
|
||||||
|
bytes_used: 25,
|
||||||
|
bytes_free: 75,
|
||||||
|
bytes_usage: Some(0.25),
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Some(0.5),
|
||||||
|
|
||||||
|
inodes: 10,
|
||||||
|
inodes_used: 2,
|
||||||
|
inodes_free: 8,
|
||||||
|
inodes_usage: Some(0.2),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
DisplayRow::new(row, &options).to_string(),
|
||||||
|
"my_device 100 25 75 25% my_mount "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_display_fs_type() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
show_fs_type: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let row = Row {
|
||||||
|
fs_device: "my_device".to_string(),
|
||||||
|
fs_type: "my_type".to_string(),
|
||||||
|
fs_mount: "my_mount".to_string(),
|
||||||
|
|
||||||
|
bytes: 100,
|
||||||
|
bytes_used: 25,
|
||||||
|
bytes_free: 75,
|
||||||
|
bytes_usage: Some(0.25),
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Some(0.5),
|
||||||
|
|
||||||
|
inodes: 10,
|
||||||
|
inodes_used: 2,
|
||||||
|
inodes_free: 8,
|
||||||
|
inodes_usage: Some(0.2),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
DisplayRow::new(row, &options).to_string(),
|
||||||
|
"my_device my_type 100 25 75 25% my_mount "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_display_inodes() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: -1,
|
||||||
|
show_inode_instead: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let row = Row {
|
||||||
|
fs_device: "my_device".to_string(),
|
||||||
|
fs_type: "my_type".to_string(),
|
||||||
|
fs_mount: "my_mount".to_string(),
|
||||||
|
|
||||||
|
bytes: 100,
|
||||||
|
bytes_used: 25,
|
||||||
|
bytes_free: 75,
|
||||||
|
bytes_usage: Some(0.25),
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Some(0.5),
|
||||||
|
|
||||||
|
inodes: 10,
|
||||||
|
inodes_used: 2,
|
||||||
|
inodes_free: 8,
|
||||||
|
inodes_usage: Some(0.2),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
DisplayRow::new(row, &options).to_string(),
|
||||||
|
"my_device 10 2 8 20% my_mount "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_display_human_readable_si() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: 1000,
|
||||||
|
show_fs_type: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let row = Row {
|
||||||
|
fs_device: "my_device".to_string(),
|
||||||
|
fs_type: "my_type".to_string(),
|
||||||
|
fs_mount: "my_mount".to_string(),
|
||||||
|
|
||||||
|
bytes: 4000,
|
||||||
|
bytes_used: 1000,
|
||||||
|
bytes_free: 3000,
|
||||||
|
bytes_usage: Some(0.25),
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Some(0.5),
|
||||||
|
|
||||||
|
inodes: 10,
|
||||||
|
inodes_used: 2,
|
||||||
|
inodes_free: 8,
|
||||||
|
inodes_usage: Some(0.2),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
DisplayRow::new(row, &options).to_string(),
|
||||||
|
"my_device my_type 4.0k 1.0k 3.0k 25% my_mount "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_row_display_human_readable_binary() {
|
||||||
|
let options = Options {
|
||||||
|
human_readable_base: 1024,
|
||||||
|
show_fs_type: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let row = Row {
|
||||||
|
fs_device: "my_device".to_string(),
|
||||||
|
fs_type: "my_type".to_string(),
|
||||||
|
fs_mount: "my_mount".to_string(),
|
||||||
|
|
||||||
|
bytes: 4096,
|
||||||
|
bytes_used: 1024,
|
||||||
|
bytes_free: 3072,
|
||||||
|
bytes_usage: Some(0.25),
|
||||||
|
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
bytes_capacity: Some(0.5),
|
||||||
|
|
||||||
|
inodes: 10,
|
||||||
|
inodes_used: 2,
|
||||||
|
inodes_free: 8,
|
||||||
|
inodes_usage: Some(0.2),
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
DisplayRow::new(row, &options).to_string(),
|
||||||
|
"my_device my_type 4.0Ki 1.0Ki 3.0Ki 25% my_mount "
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -567,7 +567,7 @@ impl FsUsage {
|
||||||
// Total number of file nodes (inodes) on the file system.
|
// Total number of file nodes (inodes) on the file system.
|
||||||
files: 0, // Not available on windows
|
files: 0, // Not available on windows
|
||||||
// Total number of free file nodes (inodes).
|
// Total number of free file nodes (inodes).
|
||||||
ffree: 4096, // Meaningless on Windows
|
ffree: 0, // Meaningless on Windows
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue