Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

du: start printing output immediately #5552

Merged
merged 13 commits into from
Nov 25, 2023
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 152 additions & 40 deletions src/uu/du/src/du.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display};
use uucore::display::{print_verbatim, Quotable};
Expand Down Expand Up @@ -81,6 +83,7 @@
// TODO: Support Z & Y (currently limited by size of u64)
const UNITS: [(char, u32); 6] = [('E', 6), ('P', 5), ('T', 4), ('G', 3), ('M', 2), ('K', 1)];

#[derive(Clone)]
struct Options {
all: bool,
max_depth: Option<usize>,
Expand All @@ -93,7 +96,7 @@
verbose: bool,
}

#[derive(PartialEq)]
#[derive(PartialEq, Clone)]
enum Deref {
All,
Args(Vec<PathBuf>),
Expand All @@ -106,6 +109,7 @@
dev_id: u64,
}

#[derive(Clone)]
struct Stat {
path: PathBuf,
is_dir: bool,
Expand Down Expand Up @@ -289,6 +293,82 @@
}
}

struct StatPrinter {
matches: ArgMatches,
threshold: Option<Threshold>,
summarize: bool,
time_format_str: String,
line_ending: LineEnding,
options: Options,
}

impl StatPrinter {
fn new(
matches: ArgMatches,
threshold: Option<Threshold>,
summarize: bool,
time_format_str: String,
line_ending: LineEnding,
options: Options,
) -> StatPrinter {
StatPrinter {
matches,
threshold,
summarize,
time_format_str,
line_ending,
options,
}
}

fn print(
&self,
stat: &Stat,
depth: usize,
convert_size: &dyn Fn(u64) -> String,
) -> UResult<()> {
let size = choose_size(&self.matches, &stat);

if !self
.threshold
.map_or(false, |threshold| threshold.should_exclude(size))
&& self
.options
.max_depth
.map_or(true, |max_depth| depth <= max_depth)
{
if self.matches.contains_id(options::TIME) {
if !self.summarize || depth == 0 {
let tm = {
let secs = self
.matches
.get_one::<String>(options::TIME)
.map(|s| get_time_secs(s, &stat))
.transpose()?

Check warning on line 347 in src/uu/du/src/du.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/du/src/du.rs#L347

Added line #L347 was not covered by tests
.unwrap_or(stat.modified);
DateTime::<Local>::from(UNIX_EPOCH + Duration::from_secs(secs))
};
let time_str = tm.format(&self.time_format_str).to_string();
print!("{}\t{}\t", convert_size(size), time_str);
print_verbatim(&stat.path).unwrap();
print!("{}", self.line_ending);
}
} else if !self.summarize || depth == 0 {
print!("{}\t", convert_size(size));
print_verbatim(&stat.path).unwrap();
print!("{}", self.line_ending);
}
}

Ok(())
}
}

struct StatPrintInfo {
stat: Stat,
depth: usize,
}

// this takes `my_stat` to avoid having to stat files multiple times.
// XXX: this should use the impl Trait return type when it is stabilized
#[allow(clippy::cognitive_complexity)]
Expand All @@ -298,7 +378,8 @@
depth: usize,
seen_inodes: &mut HashSet<FileInfo>,
exclude: &[Pattern],
) -> Box<dyn DoubleEndedIterator<Item = Stat>> {
tx: &mpsc::Sender<StatPrintInfo>,
ceteece marked this conversation as resolved.
Show resolved Hide resolved
) -> UResult<Box<dyn DoubleEndedIterator<Item = Stat>>> {
let mut stats = vec![];
let mut futures = vec![];

Expand All @@ -309,7 +390,12 @@
show!(
e.map_err_context(|| format!("cannot read directory {}", my_stat.path.quote()))
);
return Box::new(iter::once(my_stat));
tx.send(StatPrintInfo {
stat: my_stat.clone(),
depth,
})
.unwrap();
ceteece marked this conversation as resolved.
Show resolved Hide resolved
return Ok(Box::new(iter::once(my_stat)));
}
};

Expand Down Expand Up @@ -360,12 +446,18 @@
depth + 1,
seen_inodes,
exclude,
));
tx,
)?);

Check warning on line 450 in src/uu/du/src/du.rs

View check run for this annotation

Codecov / codecov/patch

src/uu/du/src/du.rs#L450

Added line #L450 was not covered by tests
} else {
my_stat.size += this_stat.size;
my_stat.blocks += this_stat.blocks;
my_stat.inodes += 1;
if options.all {
tx.send(StatPrintInfo {
stat: this_stat.clone(),
ceteece marked this conversation as resolved.
Show resolved Hide resolved
depth,
})
.unwrap();
stats.push(this_stat);
}
}
Expand All @@ -390,8 +482,16 @@
.max_depth
.map_or(true, |max_depth| depth < max_depth)
}));

tx.send(StatPrintInfo {
stat: my_stat.clone(),
depth,
})
.unwrap();

stats.push(my_stat);
Box::new(stats.into_iter())

Ok(Box::new(stats.into_iter()))
}

fn convert_size_human(size: u64, multiplier: u64, _block_size: u64) -> String {
Expand Down Expand Up @@ -622,6 +722,44 @@

let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::NULL));

let stat_printer = StatPrinter::new(
matches.clone(),
threshold.clone(),
summarize,
time_format_str.to_owned(),
line_ending,
options.clone(),
);

let (tx, rx) = mpsc::channel::<StatPrintInfo>();

let printing_thread = thread::spawn({
let matchesx = matches.clone();

Check failure on line 737 in src/uu/du/src/du.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: Unknown word (matchesx) (file:'src/uu/du/src/du.rs', line:737)
let optionsx = options.clone();

Check failure on line 738 in src/uu/du/src/du.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: Unknown word (optionsx) (file:'src/uu/du/src/du.rs', line:738)
move || {
let convert_size_fn = get_convert_size_fn(&matchesx);

Check failure on line 740 in src/uu/du/src/du.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: Unknown word (matchesx) (file:'src/uu/du/src/du.rs', line:740)

let convert_size = |size: u64| {
if optionsx.inodes {

Check failure on line 743 in src/uu/du/src/du.rs

View workflow job for this annotation

GitHub Actions / Style/spelling (ubuntu-latest, feat_os_unix)

ERROR: Unknown word (optionsx) (file:'src/uu/du/src/du.rs', line:743)
size.to_string()
} else {
convert_size_fn(size, multiplier, block_size)
}
};
loop {
let stat_info = rx.recv();

match stat_info {
// TODO: actually use depth properly
Ok(stat_info) => stat_printer
.print(&stat_info.stat, stat_info.depth, &convert_size)
.unwrap(),
Err(_) => break,
}
}
}
});

let excludes = build_exclude_patterns(&matches)?;

let mut grand_total = 0;
Expand All @@ -647,43 +785,11 @@
if let Some(inode) = stat.inode {
seen_inodes.insert(inode);
}
let iter = du(stat, &options, 0, &mut seen_inodes, &excludes);
let iter = du(stat, &options, 0, &mut seen_inodes, &excludes, &tx)?;

// Sum up all the returned `Stat`s and display results
let (_, len) = iter.size_hint();
let len = len.unwrap();
for (index, stat) in iter.enumerate() {
let size = choose_size(&matches, &stat);

if threshold.map_or(false, |threshold| threshold.should_exclude(size)) {
continue;
}

if matches.contains_id(options::TIME) {
let tm = {
let secs = matches
.get_one::<String>(options::TIME)
.map(|s| get_time_secs(s, &stat))
.transpose()?
.unwrap_or(stat.modified);
DateTime::<Local>::from(UNIX_EPOCH + Duration::from_secs(secs))
};
if !summarize || index == len - 1 {
let time_str = tm.format(time_format_str).to_string();
print!("{}\t{}\t", convert_size(size), time_str);
print_verbatim(stat.path).unwrap();
print!("{line_ending}");
}
} else if !summarize || index == len - 1 {
print!("{}\t", convert_size(size));
print_verbatim(stat.path).unwrap();
print!("{line_ending}");
}
if options.total && index == (len - 1) {
// The last element will be the total size of the the path under
// path_string. We add it to the grand total.
grand_total += size;
}
if let Some(last_stat) = iter.last() {
grand_total += choose_size(&matches, &last_stat);
}
} else {
show_error!(
Expand All @@ -695,6 +801,12 @@
}
}

drop(tx);

printing_thread
.join()
.expect("Failed to join with printing thread.");

if options.total {
print!("{}\ttotal", convert_size(grand_total));
print!("{line_ending}");
Expand Down
Loading