From 04548bbaead8ce7a1e7991540e65be79026ac941 Mon Sep 17 00:00:00 2001 From: Gints Polis Date: Sun, 14 Jun 2026 10:47:03 +0300 Subject: [PATCH 1/2] #30 make output sorted by timestamp --- src/output_results/mod.rs | 26 ++++++++++++++++++++------ tests/errors.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/src/output_results/mod.rs b/src/output_results/mod.rs index dd72fe1..cfa3fc1 100644 --- a/src/output_results/mod.rs +++ b/src/output_results/mod.rs @@ -120,11 +120,13 @@ pub fn output_results( continue; } - let partials: Result>>> = ranges + type Partial = (Vec>, Vec<(DateTime, String)>); + let partials: Result> = ranges .par_iter() - .map(|range| -> Result>> { + .map(|range| -> Result { let mut local_aggregators: Vec> = aggregators.iter().map(|a| a.boxed_clone()).collect(); + let mut printed: Vec<(DateTime, String)> = Vec::new(); let slice = &bytes[range.clone()]; @@ -142,6 +144,7 @@ pub fn output_results( &filter_container, &mut local_aggregators, converted_args.print_details, + &mut printed, )?; record_start = offset; } @@ -156,18 +159,28 @@ pub fn output_results( &filter_container, &mut local_aggregators, converted_args.print_details, + &mut printed, )?; } - Ok(local_aggregators) + Ok((local_aggregators, printed)) }) .collect(); debug!("Finished output in: {:?}", timing.elapsed()); let partials = partials?; - for partial in partials { - for (i, aggregator) in partial.into_iter().enumerate() { + let mut printed: Vec<(DateTime, String)> = Vec::new(); + for (partial_aggregators, partial_printed) in partials { + for (i, aggregator) in partial_aggregators.into_iter().enumerate() { aggregators[i].merge_box(aggregator.as_ref()); } + printed.extend(partial_printed); + } + if converted_args.print_details { + // Stable sort keeps records with equal timestamps in file order. + printed.sort_by(|a, b| a.0.cmp(&b.0)); + for (_, text) in printed { + println!("{text}"); + } } for agg in &mut *aggregators { agg.print(); @@ -192,6 +205,7 @@ fn filter_record( filters: &FilterContainer, local_aggregators: &mut Vec>, print: bool, + printed: &mut Vec<(DateTime, String)>, ) -> Result<()> { for filter in &filters.filters { if !filter.matches(record, &filters.format) { @@ -238,7 +252,7 @@ fn filter_record( )?; if print { - println!("{text}"); + printed.push((log_time_local, text.to_string())); } Ok(()) } diff --git a/tests/errors.rs b/tests/errors.rs index adc3815..62e7c81 100644 --- a/tests/errors.rs +++ b/tests/errors.rs @@ -191,6 +191,44 @@ fn simple_filter_with_hist_subcommand() -> Result<(), Box Ok(()) } +#[test] +fn error_output_sorted_by_timestamp() -> Result<(), Box> { + let mut cmd = Command::new(cargo::cargo_bin!("pgweasel")); + + // The records in debian_default2.log are deliberately out of order in the + // file, so a correct sort must reorder them by leading timestamp. + let output = cmd + .args(["err", "./tests/files/debian_default2.log"]) + .assert() + .success() + .get_output() + .stdout + .clone(); + let stdout = String::from_utf8(output)?; + + // Collect the leading "YYYY-MM-DD HH:MM:SS.mmm" timestamp of every record. + let timestamps: Vec<&str> = stdout + .lines() + .filter(|line| line.len() >= 23 && line.as_bytes()[4] == b'-') + .map(|line| &line[..23]) + .collect(); + + assert!( + timestamps.len() > 1, + "expected multiple error records, got {}", + timestamps.len() + ); + + let mut sorted = timestamps.clone(); + sorted.sort(); + assert_eq!( + timestamps, sorted, + "error output was not sorted by timestamp" + ); + + Ok(()) +} + #[test] fn non_existing_file() -> Result<(), Box> { let mut cmd = Command::new(cargo::cargo_bin!("pgweasel")); From e43f8e82fca7f67c9f27cb3e22eb2820ffa5503b Mon Sep 17 00:00:00 2001 From: Gints Polis Date: Sun, 14 Jun 2026 10:52:11 +0300 Subject: [PATCH 2/2] sort_by -> sort_by_key --- src/output_results/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/output_results/mod.rs b/src/output_results/mod.rs index cfa3fc1..81284c3 100644 --- a/src/output_results/mod.rs +++ b/src/output_results/mod.rs @@ -177,7 +177,7 @@ pub fn output_results( } if converted_args.print_details { // Stable sort keeps records with equal timestamps in file order. - printed.sort_by(|a, b| a.0.cmp(&b.0)); + printed.sort_by_key(|entry| entry.0); for (_, text) in printed { println!("{text}"); }