Skip to content

Commit b62eb09

Browse files
committed
refactor: Use --sort mechanism with printing (until buffer full)
1 parent 676be36 commit b62eb09

3 files changed

Lines changed: 49 additions & 36 deletions

File tree

src/cli.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ pub struct Opts {
547547
///
548548
/// Amount of time in milliseconds to buffer, before streaming the search
549549
/// results to the console.
550-
#[arg(long, hide = true, value_parser = parse_millis)]
550+
#[arg(long, hide = true, value_parser = parse_millis, conflicts_with("sort"))]
551551
pub max_buffer_time: Option<Duration>,
552552

553553
/// Sort search results by the given key before printing or executing commands.
@@ -560,7 +560,7 @@ pub struct Opts {
560560
hide_short_help = true,
561561
help = "Sort results by: path, size, created, or modified",
562562
long_help,
563-
conflicts_with("list_details")
563+
conflicts_with_all(&["list_details", "max_buffer_time"])
564564
)]
565565
pub sort: Option<SortKey>,
566566

src/main.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use std::env;
1717
use std::io::IsTerminal;
1818
use std::path::Path;
1919
use std::sync::Arc;
20+
use std::time::Duration;
2021

2122
use anyhow::{Context, Result, anyhow, bail};
2223
use clap::{CommandFactory, Parser};
@@ -273,7 +274,11 @@ fn construct_config(mut opts: Opts, pattern_regexps: &[String]) -> Result<Config
273274
min_depth: opts.min_depth(),
274275
prune: opts.prune,
275276
threads: opts.threads().get(),
276-
max_buffer_time: opts.max_buffer_time,
277+
max_buffer_time: match opts.sort {
278+
// If sorting is enabled, the set max_buffer_time to practically infinity.
279+
Some(_) => Some(Duration::from_secs(u64::MAX / 2)),
280+
None => opts.max_buffer_time,
281+
},
277282
ls_colors,
278283
hyperlink,
279284
interactive_terminal,

src/walk.rs

Lines changed: 41 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use crate::filesystem;
2424
use crate::output;
2525

2626
/// The receiver thread can either be buffering results or directly streaming to the console.
27-
#[derive(PartialEq)]
27+
#[derive(Copy, Clone, PartialEq)]
2828
enum ReceiverMode {
2929
/// Receiver is still buffering in order to sort the results, if the search finishes fast
3030
/// enough.
@@ -241,6 +241,7 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
241241
self.stream()?;
242242
}
243243
Err(RecvTimeoutError::Disconnected) => {
244+
// Note: This branch is called when all the results are walked/exhausted.
244245
return self.stop();
245246
}
246247
}
@@ -280,9 +281,22 @@ impl<'a, W: Write> ReceiverBuffer<'a, W> {
280281

281282
/// Stop looping.
282283
fn stop(&mut self) -> Result<(), ExitCode> {
283-
if self.mode == ReceiverMode::Buffering {
284-
self.buffer.sort();
285-
self.stream()?;
284+
match (self.mode, self.config.sort_key) {
285+
(ReceiverMode::Buffering, None) => {
286+
self.buffer.sort();
287+
self.stream()?;
288+
}
289+
(ReceiverMode::Buffering, Some(sort_key)) => {
290+
sort_dir_entry_results(&mut self.buffer, sort_key);
291+
self.stream()?;
292+
}
293+
(ReceiverMode::Streaming, None) => {}
294+
(ReceiverMode::Streaming, Some(_)) => {
295+
// We force Buffering mode in Config construction if --sort is set by setting the timeout to almost infinity.
296+
unreachable!(
297+
"--sort cannot work in Streaming mode. Buffering mode is forced in Config construction."
298+
);
299+
}
286300
}
287301

288302
if self.config.quiet {
@@ -674,36 +688,30 @@ impl WorkerState {
674688
}
675689
}
676690

691+
fn dir_entry_key_fn(sort_key: SortKey) -> Box<dyn Fn(&DirEntry) -> Option<SortKeyValue>> {
692+
match sort_key {
693+
SortKey::Path => Box::new(|e| Some(SortKeyValue::Path(e.path().to_path_buf()))),
694+
SortKey::Size => Box::new(|e| e.metadata().map(|m| SortKeyValue::Size(m.len()))),
695+
SortKey::Created => {
696+
Box::new(|e| e.metadata().map(|m| SortKeyValue::Time(m.created().ok())))
697+
}
698+
SortKey::Modified => {
699+
Box::new(|e| e.metadata().map(|m| SortKeyValue::Time(m.modified().ok())))
700+
}
701+
}
702+
}
703+
704+
fn sort_dir_entry_results(results: &mut [DirEntry], sort_key: SortKey) {
705+
let key_fn = dir_entry_key_fn(sort_key);
706+
results.sort_by_cached_key(|e| key_fn(e));
707+
}
708+
677709
fn sort_worker_results(results: &mut [WorkerResult], sort_key: SortKey) {
678-
// Build the key extractor once, based on sort_key.
679-
// Returns None for errors, pushing them to the end naturally via Ord on Option.
680-
let key_fn: Box<dyn Fn(&WorkerResult) -> Option<SortKeyValue>> = match sort_key {
681-
SortKey::Path => Box::new(|r| match r {
682-
WorkerResult::Entry(e) => Some(SortKeyValue::Path(e.path().to_path_buf())),
683-
WorkerResult::Error(_) => None,
684-
}),
685-
SortKey::Size => Box::new(|r| match r {
686-
WorkerResult::Entry(e) => Some(SortKeyValue::Size(
687-
e.metadata().map(|m| m.len()).unwrap_or(0),
688-
)),
689-
WorkerResult::Error(_) => None,
690-
}),
691-
SortKey::Created => Box::new(|r| match r {
692-
WorkerResult::Entry(e) => Some(SortKeyValue::Time(
693-
e.metadata().and_then(|m| m.created().ok()),
694-
)),
695-
WorkerResult::Error(_) => None,
696-
}),
697-
SortKey::Modified => Box::new(|r| match r {
698-
WorkerResult::Entry(e) => Some(SortKeyValue::Time(
699-
e.metadata().and_then(|m| m.modified().ok()),
700-
)),
701-
WorkerResult::Error(_) => None,
702-
}),
703-
};
704-
705-
// Use sort_by_cached_key to avoid recomputing the key for each comparison.
706-
results.sort_by_cached_key(|r| key_fn(r));
710+
let key_fn = dir_entry_key_fn(sort_key);
711+
results.sort_by_cached_key(|r| match r {
712+
WorkerResult::Entry(e) => key_fn(e),
713+
WorkerResult::Error(_) => None,
714+
});
707715
}
708716

709717
/// Comparable key values, one variant per SortKey.

0 commit comments

Comments
 (0)