From ab717ce370242fc82660dc800e1203a7c5aa686b Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Mon, 21 Mar 2022 22:03:10 -0400 Subject: [PATCH 1/3] df: implement the File column Implement the "File" column in the `df` output table. Before this commit, a blank entry appeared in the "File" column for each row. After this commit, a "-" entry appears when `df` is run with no positional arguments and the filename appears when run with positional arguments. For example: $ touch a b c && df --output=target,file a b c Mounted on File / a / b / c --- src/uu/df/src/df.rs | 5 ++++- src/uu/df/src/filesystem.rs | 18 +++++++++++++--- src/uu/df/src/table.rs | 17 +++++++++++++-- tests/by-util/test_df.rs | 43 ++++++++++++++++++++++++++++++++++++- 4 files changed, 76 insertions(+), 7 deletions(-) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 934a40a3d..6dd5ad43d 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -243,7 +243,10 @@ fn get_all_filesystems(opt: &Options) -> Vec { // Convert each `MountInfo` into a `Filesystem`, which contains // both the mount information and usage information. - mounts.into_iter().filter_map(Filesystem::new).collect() + mounts + .into_iter() + .filter_map(|m| Filesystem::new(m, None)) + .collect() } /// For each path, get the filesystem that contains that path. diff --git a/src/uu/df/src/filesystem.rs b/src/uu/df/src/filesystem.rs index bd9ff34eb..00b810073 100644 --- a/src/uu/df/src/filesystem.rs +++ b/src/uu/df/src/filesystem.rs @@ -23,6 +23,13 @@ use uucore::fsext::{FsUsage, MountInfo}; /// space available on the filesystem and the amount of space used. #[derive(Debug, Clone)] pub(crate) struct Filesystem { + /// The file given on the command line if any. + /// + /// When invoking `df` with a positional argument, it displays + /// usage information for the filesystem that contains the given + /// file. If given, this field contains that filename. + pub file: Option, + /// Information about the mounted device, mount directory, and related options. pub mount_info: MountInfo, @@ -66,7 +73,7 @@ where impl Filesystem { // TODO: resolve uuid in `mount_info.dev_name` if exists - pub(crate) fn new(mount_info: MountInfo) -> Option { + pub(crate) fn new(mount_info: MountInfo, file: Option) -> Option { let _stat_path = if !mount_info.mount_dir.is_empty() { mount_info.mount_dir.clone() } else { @@ -84,7 +91,11 @@ impl Filesystem { let usage = FsUsage::new(statfs(_stat_path).ok()?); #[cfg(windows)] let usage = FsUsage::new(Path::new(&_stat_path)); - Some(Self { mount_info, usage }) + Some(Self { + mount_info, + usage, + file, + }) } /// Find and create the filesystem that best matches a given path. @@ -107,11 +118,12 @@ impl Filesystem { where P: AsRef, { + let file = path.as_ref().display().to_string(); let canonicalize = true; let mount_info = mount_info_from_path(mounts, path, canonicalize)?; // TODO Make it so that we do not need to clone the `mount_info`. let mount_info = (*mount_info).clone(); - Self::new(mount_info) + Self::new(mount_info, Some(file)) } } diff --git a/src/uu/df/src/table.rs b/src/uu/df/src/table.rs index 00fe31caf..a99adaa6c 100644 --- a/src/uu/df/src/table.rs +++ b/src/uu/df/src/table.rs @@ -24,6 +24,9 @@ use std::ops::AddAssign; /// A row comprises several pieces of information, including the /// filesystem device, the mountpoint, the number of bytes used, etc. pub(crate) struct Row { + /// The filename given on the command-line, if given. + file: Option, + /// Name of the device on which the filesystem lives. fs_device: String, @@ -73,6 +76,7 @@ pub(crate) struct Row { impl Row { pub(crate) fn new(source: &str) -> Self { Self { + file: None, fs_device: source.into(), fs_type: "-".into(), fs_mount: "-".into(), @@ -101,6 +105,7 @@ impl AddAssign for Row { let inodes = self.inodes + rhs.inodes; let inodes_used = self.inodes_used + rhs.inodes_used; *self = Self { + file: None, fs_device: "total".into(), fs_type: "-".into(), fs_mount: "-".into(), @@ -145,6 +150,7 @@ impl From for Row { .. } = fs.usage; Self { + file: fs.file, fs_device: dev_name, fs_type, fs_mount: mount_dir, @@ -246,8 +252,9 @@ impl fmt::Display for DisplayRow<'_> { Column::Ipcent => { write!(f, "{0: >5} ", DisplayRow::percentage(self.row.inodes_usage))?; } - // TODO Implement this. - Column::File => {} + Column::File => { + write!(f, "{0: <16}", self.row.file.as_ref().unwrap_or(&"-".into()))?; + } Column::Fstype => write!(f, "{0: <5} ", self.row.fs_type)?, #[cfg(target_os = "macos")] Column::Capacity => write!( @@ -406,6 +413,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -437,6 +445,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -468,6 +477,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -499,6 +509,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -530,6 +541,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), @@ -560,6 +572,7 @@ mod tests { ..Default::default() }; let row = Row { + file: Some("/path/to/file".to_string()), fs_device: "my_device".to_string(), fs_type: "my_type".to_string(), fs_mount: "my_mount".to_string(), diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 4ba76385c..24277890d 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -222,4 +222,45 @@ fn test_output_selects_columns() { ); } -// ToDO: more tests... +// TODO Fix the spacing. +#[test] +fn test_output_file_all_filesystems() { + // When run with no positional arguments, `df` lets "-" represent + // the "File" entry for each row. + let output = new_ucmd!() + .arg("--output=file") + .succeeds() + .stdout_move_str(); + let mut lines = output.lines(); + assert_eq!(lines.next().unwrap(), "File "); + for line in lines { + assert_eq!(line, "- "); + } +} + +// TODO Fix the spacing. +#[test] +fn test_output_file_specific_files() { + // Create three files. + let (at, mut ucmd) = at_and_ucmd!(); + at.touch("a"); + at.touch("b"); + at.touch("c"); + + // When run with positional arguments, the filesystems should + // appear in the "File" column. + let output = ucmd + .args(&["--output=file", "a", "b", "c"]) + .succeeds() + .stdout_move_str(); + let actual: Vec<&str> = output.lines().collect(); + assert_eq!( + actual, + vec![ + "File ", + "a ", + "b ", + "c " + ] + ); +} From a1f300e8a72cb8304cc29f246af54ebd4ea2f2cf Mon Sep 17 00:00:00 2001 From: Jeffrey Finkelstein Date: Sun, 27 Mar 2022 22:14:16 -0400 Subject: [PATCH 2/3] df: allow multiple occurrences of --output arg Allow multiple occurrences of the `--output` argument. For example, $ df --output=source --output=target | head -n1 Filesystem Mounted on --- src/uu/df/src/df.rs | 1 + tests/by-util/test_df.rs | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index 6dd5ad43d..ee2ea83a0 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -389,6 +389,7 @@ pub fn uu_app<'a>() -> Command<'a> { .long("output") .takes_value(true) .use_value_delimiter(true) + .multiple_occurrences(true) .possible_values(OUTPUT_FIELD_LIST) .default_missing_values(&OUTPUT_FIELD_LIST) .default_values(&["source", "size", "used", "avail", "pcent", "target"]) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 24277890d..4c87fc63e 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -222,6 +222,18 @@ fn test_output_selects_columns() { ); } +#[test] +fn test_output_multiple_occurrences() { + let output = new_ucmd!() + .args(&["--output=source", "--output=target"]) + .succeeds() + .stdout_move_str(); + assert_eq!( + output.lines().next().unwrap(), + "Filesystem Mounted on " + ); +} + // TODO Fix the spacing. #[test] fn test_output_file_all_filesystems() { From a68d77b8cf30de1e7946fb26671a8086d4b76ed2 Mon Sep 17 00:00:00 2001 From: Daniel Hofstetter Date: Mon, 28 Mar 2022 10:13:54 +0200 Subject: [PATCH 3/3] df: --output w/o "=" doesn't expect further args "df --output ." was treated as "df --output=." and hence "." was interpreted as a column name. With this commit, "." is treated as an argument on its own. Fixes #3324 --- src/uu/df/src/df.rs | 2 ++ tests/by-util/test_df.rs | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/src/uu/df/src/df.rs b/src/uu/df/src/df.rs index ee2ea83a0..525e2f086 100644 --- a/src/uu/df/src/df.rs +++ b/src/uu/df/src/df.rs @@ -388,6 +388,8 @@ pub fn uu_app<'a>() -> Command<'a> { Arg::new(OPT_OUTPUT) .long("output") .takes_value(true) + .min_values(0) + .require_equals(true) .use_value_delimiter(true) .multiple_occurrences(true) .possible_values(OUTPUT_FIELD_LIST) diff --git a/tests/by-util/test_df.rs b/tests/by-util/test_df.rs index 4c87fc63e..b178c40c0 100644 --- a/tests/by-util/test_df.rs +++ b/tests/by-util/test_df.rs @@ -80,6 +80,11 @@ fn test_output_option() { new_ucmd!().arg("--output=invalid_option").fails(); } +#[test] +fn test_output_option_without_equals_sign() { + new_ucmd!().arg("--output").arg(".").succeeds(); +} + #[test] fn test_type_option() { new_ucmd!().args(&["-t", "ext4", "-t", "ext3"]).succeeds();