du: add -l/--long flag, remove -a/--all flag (#14407)

# Description
Closes:  #14387 
~To make it happen, just need to added `-l` flag to `du`, and pass it to
`DirBuilder`, `DirInfo`, `FileInfo`
Then tweak `impl From<DirInfo> for Value` and `impl From<FileInfo> for
Value` impl.~

---

Edit: this PR is going to:
1. Exclude directories and files columns by default
2. Added `-l/--long` flag to output directories and files columns
3. When running `du`, it will output the files as well. Previously it
doesn't output the size of file.

To make it happen, just need to added `-r` flag to `du`, and pass it to
`DirBuilder`, `DirInfo`, `FileInfo`
Then tweak `impl From<DirInfo> for Value` and `impl From<FileInfo> for
Value` impl.

And rename some variables.

# User-Facing Changes
`du` is no longer output `directories` and `file` columns by default,
added `-r` flag will show `directories` column, `-f` flag will show
`files` column.

```nushell
> du nushell
╭───┬────────────────────────────────────┬──────────┬──────────╮
│ # │                path                │ apparent │ physical │
├───┼────────────────────────────────────┼──────────┼──────────┤
│ 0 │ /home/windsoilder/projects/nushell │ 34.6 GiB │ 34.7 GiB │
├───┼────────────────────────────────────┼──────────┼──────────┤
│ # │                path                │ apparent │ physical │
╰───┴────────────────────────────────────┴──────────┴──────────╯
> du nushell --recursive --files # It outputs two more columns, `directories` and `files`, but the output is too long to paste here.
```
# Tests + Formatting
Added 1 test

# After Submitting
NaN
This commit is contained in:
Wind 2024-12-11 01:22:56 +08:00 committed by GitHub
parent 8f9aa1a250
commit dff6268d66
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 84 additions and 42 deletions

View file

@ -12,8 +12,8 @@ pub struct Du;
#[derive(Deserialize, Clone, Debug)] #[derive(Deserialize, Clone, Debug)]
pub struct DuArgs { pub struct DuArgs {
path: Option<Spanned<NuGlob>>, path: Option<Spanned<NuGlob>>,
all: bool,
deref: bool, deref: bool,
long: bool,
exclude: Option<Spanned<NuGlob>>, exclude: Option<Spanned<NuGlob>>,
#[serde(rename = "max-depth")] #[serde(rename = "max-depth")]
max_depth: Option<Spanned<i64>>, max_depth: Option<Spanned<i64>>,
@ -49,6 +49,11 @@ impl Command for Du {
"Dereference symlinks to their targets for size", "Dereference symlinks to their targets for size",
Some('r'), Some('r'),
) )
.switch(
"long",
"Get underlying directories and files for each entry",
Some('l'),
)
.named( .named(
"exclude", "exclude",
SyntaxShape::GlobPattern, SyntaxShape::GlobPattern,
@ -94,8 +99,8 @@ impl Command for Du {
}); });
} }
} }
let all = call.has_flag(engine_state, stack, "all")?;
let deref = call.has_flag(engine_state, stack, "deref")?; let deref = call.has_flag(engine_state, stack, "deref")?;
let long = call.has_flag(engine_state, stack, "long")?;
let exclude = call.get_flag(engine_state, stack, "exclude")?; let exclude = call.get_flag(engine_state, stack, "exclude")?;
#[allow(deprecated)] #[allow(deprecated)]
let current_dir = current_dir(engine_state, stack)?; let current_dir = current_dir(engine_state, stack)?;
@ -111,8 +116,8 @@ impl Command for Du {
None => { None => {
let args = DuArgs { let args = DuArgs {
path: None, path: None,
all,
deref, deref,
long,
exclude, exclude,
max_depth, max_depth,
min_size, min_size,
@ -127,8 +132,8 @@ impl Command for Du {
for p in paths { for p in paths {
let args = DuArgs { let args = DuArgs {
path: Some(p), path: Some(p),
all,
deref, deref,
long,
exclude: exclude.clone(), exclude: exclude.clone(),
max_depth, max_depth,
min_size, min_size,
@ -174,7 +179,6 @@ fn du_for_one_pattern(
}) })
})?; })?;
let include_files = args.all;
let mut paths = match args.path { let mut paths = match args.path {
Some(p) => nu_engine::glob_from(&p, current_dir, span, None), Some(p) => nu_engine::glob_from(&p, current_dir, span, None),
// The * pattern should never fail. // The * pattern should never fail.
@ -188,17 +192,10 @@ fn du_for_one_pattern(
None, None,
), ),
} }
.map(|f| f.1)? .map(|f| f.1)?;
.filter(move |p| {
if include_files {
true
} else {
matches!(p, Ok(f) if f.is_dir())
}
});
let all = args.all;
let deref = args.deref; let deref = args.deref;
let long = args.long;
let max_depth = args.max_depth.map(|f| f.item as u64); let max_depth = args.max_depth.map(|f| f.item as u64);
let min_size = args.min_size.map(|f| f.item as u64); let min_size = args.min_size.map(|f| f.item as u64);
@ -207,7 +204,7 @@ fn du_for_one_pattern(
min: min_size, min: min_size,
deref, deref,
exclude, exclude,
all, long,
}; };
let mut output: Vec<Value> = vec![]; let mut output: Vec<Value> = vec![];
@ -216,7 +213,7 @@ fn du_for_one_pattern(
Ok(a) => { Ok(a) => {
if a.is_dir() { if a.is_dir() {
output.push(DirInfo::new(a, &params, max_depth, span, signals)?.into()); output.push(DirInfo::new(a, &params, max_depth, span, signals)?.into());
} else if let Ok(v) = FileInfo::new(a, deref, span) { } else if let Ok(v) = FileInfo::new(a, deref, span, params.long) {
output.push(v.into()); output.push(v.into());
} }
} }

View file

@ -9,7 +9,7 @@ pub struct DirBuilder {
pub min: Option<u64>, pub min: Option<u64>,
pub deref: bool, pub deref: bool,
pub exclude: Option<Pattern>, pub exclude: Option<Pattern>,
pub all: bool, pub long: bool,
} }
impl DirBuilder { impl DirBuilder {
@ -18,14 +18,14 @@ impl DirBuilder {
min: Option<u64>, min: Option<u64>,
deref: bool, deref: bool,
exclude: Option<Pattern>, exclude: Option<Pattern>,
all: bool, long: bool,
) -> DirBuilder { ) -> DirBuilder {
DirBuilder { DirBuilder {
tag, tag,
min, min,
deref, deref,
exclude, exclude,
all, long,
} }
} }
} }
@ -39,6 +39,7 @@ pub struct DirInfo {
blocks: u64, blocks: u64,
path: PathBuf, path: PathBuf,
tag: Span, tag: Span,
long: bool,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -47,10 +48,16 @@ pub struct FileInfo {
size: u64, size: u64,
blocks: Option<u64>, blocks: Option<u64>,
tag: Span, tag: Span,
long: bool,
} }
impl FileInfo { impl FileInfo {
pub fn new(path: impl Into<PathBuf>, deref: bool, tag: Span) -> Result<Self, ShellError> { pub fn new(
path: impl Into<PathBuf>,
deref: bool,
tag: Span,
long: bool,
) -> Result<Self, ShellError> {
let path = path.into(); let path = path.into();
let m = if deref { let m = if deref {
std::fs::metadata(&path) std::fs::metadata(&path)
@ -67,6 +74,7 @@ impl FileInfo {
blocks: block_size, blocks: block_size,
size: d.len(), size: d.len(),
tag, tag,
long,
}) })
} }
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
@ -92,6 +100,7 @@ impl DirInfo {
blocks: 0, blocks: 0,
tag: params.tag, tag: params.tag,
path, path,
long: params.long,
}; };
match std::fs::metadata(&s.path) { match std::fs::metadata(&s.path) {
@ -154,13 +163,13 @@ impl DirInfo {
.as_ref() .as_ref()
.map_or(true, |x| !x.matches_path(&f)); .map_or(true, |x| !x.matches_path(&f));
if include { if include {
match FileInfo::new(f, params.deref, self.tag) { match FileInfo::new(f, params.deref, self.tag, self.long) {
Ok(file) => { Ok(file) => {
let inc = params.min.map_or(true, |s| file.size >= s); let inc = params.min.map_or(true, |s| file.size >= s);
if inc { if inc {
self.size += file.size; self.size += file.size;
self.blocks += file.blocks.unwrap_or(0); self.blocks += file.blocks.unwrap_or(0);
if params.all { if params.long {
self.files.push(file); self.files.push(file);
} }
} }
@ -197,16 +206,27 @@ impl From<DirInfo> for Value {
// }) // })
// } // }
Value::record( if d.long {
record! { Value::record(
"path" => Value::string(d.path.display().to_string(), d.tag), record! {
"apparent" => Value::filesize(d.size as i64, d.tag), "path" => Value::string(d.path.display().to_string(), d.tag),
"physical" => Value::filesize(d.blocks as i64, d.tag), "apparent" => Value::filesize(d.size as i64, d.tag),
"directories" => value_from_vec(d.dirs, d.tag), "physical" => Value::filesize(d.blocks as i64, d.tag),
"files" => value_from_vec(d.files, d.tag) "directories" => value_from_vec(d.dirs, d.tag),
}, "files" => value_from_vec(d.files, d.tag)
d.tag, },
) d.tag,
)
} else {
Value::record(
record! {
"path" => Value::string(d.path.display().to_string(), d.tag),
"apparent" => Value::filesize(d.size as i64, d.tag),
"physical" => Value::filesize(d.blocks as i64, d.tag),
},
d.tag,
)
}
} }
} }
@ -215,16 +235,27 @@ impl From<FileInfo> for Value {
// cols.push("errors".into()); // cols.push("errors".into());
// vals.push(Value::nothing(Span::unknown())); // vals.push(Value::nothing(Span::unknown()));
Value::record( if f.long {
record! { Value::record(
"path" => Value::string(f.path.display().to_string(), f.tag), record! {
"apparent" => Value::filesize(f.size as i64, f.tag), "path" => Value::string(f.path.display().to_string(), f.tag),
"physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag), "apparent" => Value::filesize(f.size as i64, f.tag),
"directories" => Value::nothing(Span::unknown()), "physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag),
"files" => Value::nothing(Span::unknown()), "directories" => Value::nothing(Span::unknown()),
}, "files" => Value::nothing(Span::unknown()),
f.tag, },
) f.tag,
)
} else {
Value::record(
record! {
"path" => Value::string(f.path.display().to_string(), f.tag),
"apparent" => Value::filesize(f.size as i64, f.tag),
"physical" => Value::filesize(f.blocks.unwrap_or(0) as i64, f.tag),
},
f.tag,
)
}
} }
} }

View file

@ -100,3 +100,17 @@ fn du_with_multiple_path() {
let actual = nu!(cwd: "tests/fixtures", "du ...[] | length"); let actual = nu!(cwd: "tests/fixtures", "du ...[] | length");
assert_eq!(actual.out, "0"); assert_eq!(actual.out, "0");
} }
#[test]
fn test_du_output_columns() {
let actual = nu!(
cwd: "tests/fixtures/formats",
"du -m 1 | columns | str join ','"
);
assert_eq!(actual.out, "path,apparent,physical");
let actual = nu!(
cwd: "tests/fixtures/formats",
"du -m 1 -l | columns | str join ','"
);
assert_eq!(actual.out, "path,apparent,physical,directories,files");
}