mirror of
https://github.com/uutils/coreutils
synced 2024-11-16 09:48:03 +00:00
df: use Options.columns field to control output
Replace the `Options.show_fs_type` and `Options.show_inode_instead` fields with the more general `Options.columns`, a `Vec` of `Column` variants representing the exact sequence of columns to display in the output. This makes `Options` able to represent arbitrary output column display configurations.
This commit is contained in:
parent
2e8945ba7f
commit
0b07ecdad2
3 changed files with 210 additions and 80 deletions
109
src/uu/df/src/columns.rs
Normal file
109
src/uu/df/src/columns.rs
Normal file
|
@ -0,0 +1,109 @@
|
|||
// * 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 itotal iused iavail ipcent pcent squashfs
|
||||
use crate::{OPT_INODES, OPT_PRINT_TYPE};
|
||||
use clap::ArgMatches;
|
||||
|
||||
/// The columns in the output table produced by `df`.
|
||||
///
|
||||
/// The [`Row`] struct has a field corresponding to each of the
|
||||
/// variants of this enumeration.
|
||||
///
|
||||
/// [`Row`]: crate::table::Row
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
pub(crate) enum Column {
|
||||
/// The source of the mount point, usually a device.
|
||||
Source,
|
||||
|
||||
/// Total number of blocks.
|
||||
Size,
|
||||
|
||||
/// Number of used blocks.
|
||||
Used,
|
||||
|
||||
/// Number of available blocks.
|
||||
Avail,
|
||||
|
||||
/// Percentage of blocks used out of total number of blocks.
|
||||
Pcent,
|
||||
|
||||
/// The mount point.
|
||||
Target,
|
||||
|
||||
/// Total number of inodes.
|
||||
Itotal,
|
||||
|
||||
/// Number of used inodes.
|
||||
Iused,
|
||||
|
||||
/// Number of available inodes.
|
||||
Iavail,
|
||||
|
||||
/// Percentage of inodes used out of total number of inodes.
|
||||
Ipcent,
|
||||
|
||||
/// The filename given as a command-line argument.
|
||||
File,
|
||||
|
||||
/// The filesystem type, like "ext4" or "squashfs".
|
||||
Fstype,
|
||||
|
||||
/// Percentage of bytes available to non-privileged processes.
|
||||
#[cfg(target_os = "macos")]
|
||||
Capacity,
|
||||
}
|
||||
|
||||
impl Column {
|
||||
/// Convert from command-line arguments to sequence of columns.
|
||||
///
|
||||
/// The set of columns that will appear in the output table can be
|
||||
/// specified by command-line arguments. This function converts
|
||||
/// those arguments to a [`Vec`] of [`Column`] variants.
|
||||
pub(crate) fn from_matches(matches: &ArgMatches) -> Vec<Self> {
|
||||
match (
|
||||
matches.is_present(OPT_PRINT_TYPE),
|
||||
matches.is_present(OPT_INODES),
|
||||
) {
|
||||
(false, false) => vec![
|
||||
Self::Source,
|
||||
Self::Size,
|
||||
Self::Used,
|
||||
Self::Avail,
|
||||
#[cfg(target_os = "macos")]
|
||||
Self::Capacity,
|
||||
Self::Pcent,
|
||||
Self::Target,
|
||||
],
|
||||
(false, true) => vec![
|
||||
Self::Source,
|
||||
Self::Itotal,
|
||||
Self::Iused,
|
||||
Self::Iavail,
|
||||
Self::Ipcent,
|
||||
Self::Target,
|
||||
],
|
||||
(true, false) => vec![
|
||||
Self::Source,
|
||||
Self::Fstype,
|
||||
Self::Size,
|
||||
Self::Used,
|
||||
Self::Avail,
|
||||
#[cfg(target_os = "macos")]
|
||||
Self::Capacity,
|
||||
Self::Pcent,
|
||||
Self::Target,
|
||||
],
|
||||
(true, true) => vec![
|
||||
Self::Source,
|
||||
Self::Fstype,
|
||||
Self::Itotal,
|
||||
Self::Iused,
|
||||
Self::Iavail,
|
||||
Self::Ipcent,
|
||||
Self::Target,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
// that was distributed with this source code.
|
||||
// spell-checker:ignore itotal iused iavail ipcent pcent tmpfs squashfs
|
||||
mod blocks;
|
||||
mod columns;
|
||||
mod table;
|
||||
|
||||
use uucore::error::{UResult, USimpleError};
|
||||
|
@ -25,6 +26,7 @@ use std::iter::FromIterator;
|
|||
use std::path::Path;
|
||||
|
||||
use crate::blocks::{block_size_from_matches, BlockSize};
|
||||
use crate::columns::Column;
|
||||
use crate::table::{DisplayRow, Header, Row};
|
||||
|
||||
static ABOUT: &str = "Show information about the file system on which each FILE resides,\n\
|
||||
|
@ -66,18 +68,37 @@ struct FsSelector {
|
|||
/// Most of these parameters control which rows and which columns are
|
||||
/// displayed. The `block_size` determines the units to use when
|
||||
/// displaying numbers of bytes or inodes.
|
||||
#[derive(Default)]
|
||||
struct Options {
|
||||
show_local_fs: bool,
|
||||
show_all_fs: bool,
|
||||
show_listed_fs: bool,
|
||||
show_fs_type: bool,
|
||||
show_inode_instead: bool,
|
||||
block_size: BlockSize,
|
||||
fs_selector: FsSelector,
|
||||
|
||||
/// Whether to show a final row comprising the totals for each column.
|
||||
show_total: bool,
|
||||
/// Sequence of columns to display in the output table.
|
||||
columns: Vec<Column>,
|
||||
}
|
||||
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
show_local_fs: Default::default(),
|
||||
show_all_fs: Default::default(),
|
||||
show_listed_fs: Default::default(),
|
||||
block_size: Default::default(),
|
||||
fs_selector: Default::default(),
|
||||
show_total: Default::default(),
|
||||
columns: vec![
|
||||
Column::Source,
|
||||
Column::Size,
|
||||
Column::Used,
|
||||
Column::Avail,
|
||||
Column::Pcent,
|
||||
Column::Target,
|
||||
],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum OptionsError {
|
||||
|
@ -103,12 +124,11 @@ impl Options {
|
|||
show_local_fs: matches.is_present(OPT_LOCAL),
|
||||
show_all_fs: matches.is_present(OPT_ALL),
|
||||
show_listed_fs: false,
|
||||
show_fs_type: matches.is_present(OPT_PRINT_TYPE),
|
||||
show_inode_instead: matches.is_present(OPT_INODES),
|
||||
block_size: block_size_from_matches(matches)
|
||||
.map_err(|_| OptionsError::InvalidBlockSize)?,
|
||||
fs_selector: FsSelector::from(matches),
|
||||
show_total: matches.is_present(OPT_TOTAL),
|
||||
columns: Column::from_matches(matches),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// *
|
||||
// * For the full copyright and license information, please view the LICENSE
|
||||
// * file that was distributed with this source code.
|
||||
// spell-checker:ignore tmpfs
|
||||
// spell-checker:ignore tmpfs Pcent Itotal Iused Iavail Ipcent
|
||||
//! The filesystem usage data table.
|
||||
//!
|
||||
//! A table comprises a header row ([`Header`]) and a collection of
|
||||
|
@ -11,6 +11,7 @@
|
|||
//! [`DisplayRow`] implements [`std::fmt::Display`].
|
||||
use number_prefix::NumberPrefix;
|
||||
|
||||
use crate::columns::Column;
|
||||
use crate::{BlockSize, Filesystem, Options};
|
||||
use uucore::fsext::{FsUsage, MountInfo};
|
||||
|
||||
|
@ -225,56 +226,37 @@ impl<'a> DisplayRow<'a> {
|
|||
Some(x) => format!("{:.0}%", (100.0 * x).ceil()),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)?;
|
||||
for column in &self.options.columns {
|
||||
match column {
|
||||
Column::Source => write!(f, "{0: <16} ", self.row.fs_device)?,
|
||||
Column::Size => write!(f, "{0: >12} ", self.scaled(self.row.bytes)?)?,
|
||||
Column::Used => write!(f, "{0: >12} ", self.scaled(self.row.bytes_used)?)?,
|
||||
Column::Avail => write!(f, "{0: >12} ", self.scaled(self.row.bytes_free)?)?,
|
||||
Column::Pcent => {
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.bytes_usage))?;
|
||||
}
|
||||
Column::Target => write!(f, "{0: <16}", self.row.fs_mount)?,
|
||||
Column::Itotal => write!(f, "{0: >12} ", self.scaled(self.row.inodes)?)?,
|
||||
Column::Iused => write!(f, "{0: >12} ", self.scaled(self.row.inodes_used)?)?,
|
||||
Column::Iavail => write!(f, "{0: >12} ", self.scaled(self.row.inodes_free)?)?,
|
||||
Column::Ipcent => {
|
||||
write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?;
|
||||
}
|
||||
// TODO Implement this.
|
||||
Column::File => {}
|
||||
Column::Fstype => write!(f, "{0: <5} ", self.row.fs_type)?,
|
||||
#[cfg(target_os = "macos")]
|
||||
Column::Capacity => write!(
|
||||
f,
|
||||
"{0: >12} ",
|
||||
DisplayRow::percentage(self.row.bytes_capacity)
|
||||
)?,
|
||||
}
|
||||
if self.options.show_inode_instead {
|
||||
self.fmt_inodes(f)?;
|
||||
} else {
|
||||
self.fmt_bytes(f)?;
|
||||
}
|
||||
write!(f, "{0: <16}", self.row.fs_mount)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -296,29 +278,29 @@ impl<'a> Header<'a> {
|
|||
|
||||
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 {
|
||||
// `Display` is implemented for `BlockSize`, but `Display`
|
||||
// only works when formatting an object into an empty
|
||||
// format, `{}`. So we use `format!()` first to create the
|
||||
// string, then use `write!()` to align the string and pad
|
||||
// with spaces.
|
||||
write!(f, "{0: >12} ", format!("{}", self.options.block_size))?;
|
||||
write!(f, "{0: >12} ", "Used")?;
|
||||
write!(f, "{0: >12} ", "Available")?;
|
||||
for column in &self.options.columns {
|
||||
match column {
|
||||
Column::Source => write!(f, "{0: <16} ", "Filesystem")?,
|
||||
// `Display` is implemented for `BlockSize`, but
|
||||
// `Display` only works when formatting an object into
|
||||
// an empty format, `{}`. So we use `format!()` first
|
||||
// to create the string, then use `write!()` to align
|
||||
// the string and pad with spaces.
|
||||
Column::Size => write!(f, "{0: >12} ", format!("{}", self.options.block_size))?,
|
||||
Column::Used => write!(f, "{0: >12} ", "Used")?,
|
||||
Column::Avail => write!(f, "{0: >12} ", "Available")?,
|
||||
Column::Pcent => write!(f, "{0: >5} ", "Use%")?,
|
||||
Column::Target => write!(f, "{0: <16} ", "Mounted on")?,
|
||||
Column::Itotal => write!(f, "{0: >12} ", "Inodes")?,
|
||||
Column::Iused => write!(f, "{0: >12} ", "IUsed")?,
|
||||
Column::Iavail => write!(f, "{0: >12} ", "IFree")?,
|
||||
Column::Ipcent => write!(f, "{0: >5} ", "IUse%")?,
|
||||
Column::File => write!(f, "{0: <16}", "File")?,
|
||||
Column::Fstype => write!(f, "{0: <5} ", "Type")?,
|
||||
#[cfg(target_os = "macos")]
|
||||
write!(f, "{0: >12} ", "Capacity")?;
|
||||
write!(f, "{0: >5} ", "Use%")?;
|
||||
Column::Capacity => write!(f, "{0: >12} ", "Capacity")?,
|
||||
}
|
||||
}
|
||||
write!(f, "{0: <16} ", "Mounted on")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -326,9 +308,28 @@ impl fmt::Display for Header<'_> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::columns::Column;
|
||||
use crate::table::{DisplayRow, Header, Row};
|
||||
use crate::{BlockSize, Options};
|
||||
|
||||
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
|
||||
Column::Source,
|
||||
Column::Fstype,
|
||||
Column::Size,
|
||||
Column::Used,
|
||||
Column::Avail,
|
||||
Column::Pcent,
|
||||
Column::Target,
|
||||
];
|
||||
const COLUMNS_WITH_INODES: [Column; 6] = [
|
||||
Column::Source,
|
||||
Column::Itotal,
|
||||
Column::Iused,
|
||||
Column::Iavail,
|
||||
Column::Ipcent,
|
||||
Column::Target,
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn test_header_display() {
|
||||
let options = Default::default();
|
||||
|
@ -341,7 +342,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_header_display_fs_type() {
|
||||
let options = Options {
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
|
@ -353,7 +354,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_header_display_inode() {
|
||||
let options = Options {
|
||||
show_inode_instead: true,
|
||||
columns: COLUMNS_WITH_INODES.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
assert_eq!(
|
||||
|
@ -431,8 +432,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_row_display_fs_type() {
|
||||
let options = Options {
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
block_size: BlockSize::Bytes(1),
|
||||
show_fs_type: true,
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -462,8 +463,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_row_display_inodes() {
|
||||
let options = Options {
|
||||
columns: COLUMNS_WITH_INODES.to_vec(),
|
||||
block_size: BlockSize::Bytes(1),
|
||||
show_inode_instead: true,
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -494,7 +495,7 @@ mod tests {
|
|||
fn test_row_display_human_readable_si() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::HumanReadableDecimal,
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
@ -525,7 +526,7 @@ mod tests {
|
|||
fn test_row_display_human_readable_binary() {
|
||||
let options = Options {
|
||||
block_size: BlockSize::HumanReadableBinary,
|
||||
show_fs_type: true,
|
||||
columns: COLUMNS_WITH_FS_TYPE.to_vec(),
|
||||
..Default::default()
|
||||
};
|
||||
let row = Row {
|
||||
|
|
Loading…
Reference in a new issue