mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 19:47:45 +00:00
Merge pull request #3513 from cakebaker/portability_headers
df: implement POSIX conform header line
This commit is contained in:
commit
c212f4a556
4 changed files with 147 additions and 19 deletions
|
@ -170,6 +170,15 @@ pub(crate) enum BlockSize {
|
||||||
Bytes(u64),
|
Bytes(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BlockSize {
|
||||||
|
/// Returns the associated value
|
||||||
|
pub(crate) fn as_u64(&self) -> u64 {
|
||||||
|
match *self {
|
||||||
|
Self::Bytes(n) => n,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for BlockSize {
|
impl Default for BlockSize {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
if env::var("POSIXLY_CORRECT").is_ok() {
|
if env::var("POSIXLY_CORRECT").is_ok() {
|
||||||
|
|
|
@ -12,6 +12,7 @@ mod filesystem;
|
||||||
mod table;
|
mod table;
|
||||||
|
|
||||||
use blocks::{HumanReadable, SizeFormat};
|
use blocks::{HumanReadable, SizeFormat};
|
||||||
|
use table::HeaderMode;
|
||||||
use uucore::display::Quotable;
|
use uucore::display::Quotable;
|
||||||
use uucore::error::{UError, UResult, USimpleError};
|
use uucore::error::{UError, UResult, USimpleError};
|
||||||
use uucore::fsext::{read_fs_list, MountInfo};
|
use uucore::fsext::{read_fs_list, MountInfo};
|
||||||
|
@ -72,6 +73,7 @@ struct Options {
|
||||||
show_all_fs: bool,
|
show_all_fs: bool,
|
||||||
size_format: SizeFormat,
|
size_format: SizeFormat,
|
||||||
block_size: BlockSize,
|
block_size: BlockSize,
|
||||||
|
header_mode: HeaderMode,
|
||||||
|
|
||||||
/// Optional list of filesystem types to include in the output table.
|
/// Optional list of filesystem types to include in the output table.
|
||||||
///
|
///
|
||||||
|
@ -99,6 +101,7 @@ impl Default for Options {
|
||||||
show_all_fs: Default::default(),
|
show_all_fs: Default::default(),
|
||||||
block_size: Default::default(),
|
block_size: Default::default(),
|
||||||
size_format: Default::default(),
|
size_format: Default::default(),
|
||||||
|
header_mode: Default::default(),
|
||||||
include: Default::default(),
|
include: Default::default(),
|
||||||
exclude: Default::default(),
|
exclude: Default::default(),
|
||||||
show_total: Default::default(),
|
show_total: Default::default(),
|
||||||
|
@ -176,6 +179,21 @@ impl Options {
|
||||||
),
|
),
|
||||||
ParseSizeError::ParseFailure(s) => OptionsError::InvalidBlockSize(s),
|
ParseSizeError::ParseFailure(s) => OptionsError::InvalidBlockSize(s),
|
||||||
})?,
|
})?,
|
||||||
|
header_mode: {
|
||||||
|
if matches.is_present(OPT_HUMAN_READABLE_BINARY)
|
||||||
|
|| matches.is_present(OPT_HUMAN_READABLE_DECIMAL)
|
||||||
|
{
|
||||||
|
HeaderMode::HumanReadable
|
||||||
|
} else if matches.is_present(OPT_PORTABILITY) {
|
||||||
|
HeaderMode::PosixPortability
|
||||||
|
// is_present() doesn't work here, it always returns true because OPT_OUTPUT has
|
||||||
|
// default values and hence is always present
|
||||||
|
} else if matches.occurrences_of(OPT_OUTPUT) > 0 {
|
||||||
|
HeaderMode::Output
|
||||||
|
} else {
|
||||||
|
HeaderMode::Default
|
||||||
|
}
|
||||||
|
},
|
||||||
size_format: {
|
size_format: {
|
||||||
if matches.is_present(OPT_HUMAN_READABLE_BINARY) {
|
if matches.is_present(OPT_HUMAN_READABLE_BINARY) {
|
||||||
SizeFormat::HumanReadable(HumanReadable::Binary)
|
SizeFormat::HumanReadable(HumanReadable::Binary)
|
||||||
|
|
|
@ -289,6 +289,23 @@ impl<'a> RowFormatter<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A HeaderMode defines what header labels should be shown.
|
||||||
|
pub(crate) enum HeaderMode {
|
||||||
|
Default,
|
||||||
|
// the user used -h or -H
|
||||||
|
HumanReadable,
|
||||||
|
// the user used -P
|
||||||
|
PosixPortability,
|
||||||
|
// the user used --output
|
||||||
|
Output,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for HeaderMode {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The data of the header row.
|
/// The data of the header row.
|
||||||
struct Header {}
|
struct Header {}
|
||||||
|
|
||||||
|
@ -302,15 +319,22 @@ impl Header {
|
||||||
for column in &options.columns {
|
for column in &options.columns {
|
||||||
let header = match column {
|
let header = match column {
|
||||||
Column::Source => String::from("Filesystem"),
|
Column::Source => String::from("Filesystem"),
|
||||||
Column::Size => match options.size_format {
|
Column::Size => match options.header_mode {
|
||||||
SizeFormat::HumanReadable(_) => String::from("Size"),
|
HeaderMode::HumanReadable => String::from("Size"),
|
||||||
SizeFormat::StaticBlockSize => {
|
HeaderMode::PosixPortability => {
|
||||||
format!("{}-blocks", options.block_size)
|
format!("{}-blocks", options.block_size.as_u64())
|
||||||
}
|
}
|
||||||
|
_ => format!("{}-blocks", options.block_size),
|
||||||
},
|
},
|
||||||
Column::Used => String::from("Used"),
|
Column::Used => String::from("Used"),
|
||||||
Column::Avail => String::from("Available"),
|
Column::Avail => match options.header_mode {
|
||||||
Column::Pcent => String::from("Use%"),
|
HeaderMode::HumanReadable | HeaderMode::Output => String::from("Avail"),
|
||||||
|
_ => String::from("Available"),
|
||||||
|
},
|
||||||
|
Column::Pcent => match options.header_mode {
|
||||||
|
HeaderMode::PosixPortability => String::from("Capacity"),
|
||||||
|
_ => String::from("Use%"),
|
||||||
|
},
|
||||||
Column::Target => String::from("Mounted on"),
|
Column::Target => String::from("Mounted on"),
|
||||||
Column::Itotal => String::from("Inodes"),
|
Column::Itotal => String::from("Inodes"),
|
||||||
Column::Iused => String::from("IUsed"),
|
Column::Iused => String::from("IUsed"),
|
||||||
|
@ -428,7 +452,7 @@ mod tests {
|
||||||
|
|
||||||
use crate::blocks::{HumanReadable, SizeFormat};
|
use crate::blocks::{HumanReadable, SizeFormat};
|
||||||
use crate::columns::Column;
|
use crate::columns::Column;
|
||||||
use crate::table::{Header, Row, RowFormatter};
|
use crate::table::{Header, HeaderMode, Row, RowFormatter};
|
||||||
use crate::{BlockSize, Options};
|
use crate::{BlockSize, Options};
|
||||||
|
|
||||||
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
|
const COLUMNS_WITH_FS_TYPE: [Column; 7] = [
|
||||||
|
@ -548,37 +572,49 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_header_with_human_readable_binary() {
|
fn test_human_readable_header() {
|
||||||
let options = Options {
|
let options = Options {
|
||||||
size_format: SizeFormat::HumanReadable(HumanReadable::Binary),
|
header_mode: HeaderMode::HumanReadable,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_eq!(
|
||||||
|
Header::get_headers(&options),
|
||||||
|
vec!("Filesystem", "Size", "Used", "Avail", "Use%", "Mounted on")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_posix_portability_header() {
|
||||||
|
let options = Options {
|
||||||
|
header_mode: HeaderMode::PosixPortability,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Header::get_headers(&options),
|
Header::get_headers(&options),
|
||||||
vec!(
|
vec!(
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"1024-blocks",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Available",
|
||||||
"Use%",
|
"Capacity",
|
||||||
"Mounted on"
|
"Mounted on"
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_header_with_human_readable_si() {
|
fn test_output_header() {
|
||||||
let options = Options {
|
let options = Options {
|
||||||
size_format: SizeFormat::HumanReadable(HumanReadable::Decimal),
|
header_mode: HeaderMode::Output,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Header::get_headers(&options),
|
Header::get_headers(&options),
|
||||||
vec!(
|
vec!(
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"1K-blocks",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Avail",
|
||||||
"Use%",
|
"Use%",
|
||||||
"Mounted on"
|
"Mounted on"
|
||||||
)
|
)
|
||||||
|
|
|
@ -73,7 +73,7 @@ fn test_df_output() {
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"Size",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Avail",
|
||||||
"Capacity",
|
"Capacity",
|
||||||
"Use%",
|
"Use%",
|
||||||
"Mounted",
|
"Mounted",
|
||||||
|
@ -84,7 +84,7 @@ fn test_df_output() {
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"Size",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Avail",
|
||||||
"Use%",
|
"Use%",
|
||||||
"Mounted",
|
"Mounted",
|
||||||
"on",
|
"on",
|
||||||
|
@ -107,7 +107,7 @@ fn test_df_output_overridden() {
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"Size",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Avail",
|
||||||
"Capacity",
|
"Capacity",
|
||||||
"Use%",
|
"Use%",
|
||||||
"Mounted",
|
"Mounted",
|
||||||
|
@ -118,7 +118,7 @@ fn test_df_output_overridden() {
|
||||||
"Filesystem",
|
"Filesystem",
|
||||||
"Size",
|
"Size",
|
||||||
"Used",
|
"Used",
|
||||||
"Available",
|
"Avail",
|
||||||
"Use%",
|
"Use%",
|
||||||
"Mounted",
|
"Mounted",
|
||||||
"on",
|
"on",
|
||||||
|
@ -134,6 +134,46 @@ fn test_df_output_overridden() {
|
||||||
assert_eq!(actual, expected);
|
assert_eq!(actual, expected);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_default_headers() {
|
||||||
|
let expected = if cfg!(target_os = "macos") {
|
||||||
|
vec![
|
||||||
|
"Filesystem",
|
||||||
|
"1K-blocks",
|
||||||
|
"Used",
|
||||||
|
"Available",
|
||||||
|
"Capacity",
|
||||||
|
"Use%",
|
||||||
|
"Mounted",
|
||||||
|
"on",
|
||||||
|
]
|
||||||
|
} else {
|
||||||
|
vec![
|
||||||
|
"Filesystem",
|
||||||
|
"1K-blocks",
|
||||||
|
"Used",
|
||||||
|
"Available",
|
||||||
|
"Use%",
|
||||||
|
"Mounted",
|
||||||
|
"on",
|
||||||
|
]
|
||||||
|
};
|
||||||
|
let output = new_ucmd!().succeeds().stdout_move_str();
|
||||||
|
let actual = output.lines().take(1).collect::<Vec<&str>>()[0];
|
||||||
|
let actual = actual.split_whitespace().collect::<Vec<_>>();
|
||||||
|
assert_eq!(actual, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_precedence_of_human_readable_header_over_output_header() {
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["-H", "--output=size"])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
let header = output.lines().next().unwrap().to_string();
|
||||||
|
assert_eq!(header.trim(), "Size");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_total_option_with_single_dash() {
|
fn test_total_option_with_single_dash() {
|
||||||
// These should fail because `-total` should have two dashes,
|
// These should fail because `-total` should have two dashes,
|
||||||
|
@ -443,6 +483,31 @@ fn test_block_size_with_suffix() {
|
||||||
assert_eq!(get_header("1GB"), "1GB-blocks");
|
assert_eq!(get_header("1GB"), "1GB-blocks");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_block_size_in_posix_portability_mode() {
|
||||||
|
fn get_header(block_size: &str) -> String {
|
||||||
|
let output = new_ucmd!()
|
||||||
|
.args(&["-P", "-B", block_size])
|
||||||
|
.succeeds()
|
||||||
|
.stdout_move_str();
|
||||||
|
output
|
||||||
|
.lines()
|
||||||
|
.next()
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
.split_whitespace()
|
||||||
|
.nth(1)
|
||||||
|
.unwrap()
|
||||||
|
.to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(get_header("1024"), "1024-blocks");
|
||||||
|
assert_eq!(get_header("1K"), "1024-blocks");
|
||||||
|
assert_eq!(get_header("1KB"), "1000-blocks");
|
||||||
|
assert_eq!(get_header("1M"), "1048576-blocks");
|
||||||
|
assert_eq!(get_header("1MB"), "1000000-blocks");
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_too_large_block_size() {
|
fn test_too_large_block_size() {
|
||||||
fn run_command(size: &str) {
|
fn run_command(size: &str) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue