Skip to content

Commit 00457e6

Browse files
committed
Merge fix-f-drive-parity: fast-scan heuristic and pattern format fixes
2 parents 2f4f119 + 07a15c0 commit 00457e6

File tree

2 files changed

+47
-24
lines changed

2 files changed

+47
-24
lines changed

crates/uffs-cli/src/commands/output.rs

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ use uffs_core::{export_json, export_table};
2424
pub(super) struct CppFooterContext<'a> {
2525
/// Drive letters to include in the footer (e.g., `['C', 'D']`).
2626
pub(super) output_targets: &'a [char],
27-
/// Elapsed time since search started.
28-
pub(super) elapsed: Duration,
2927
/// Original search pattern string.
3028
pub(super) pattern: &'a str,
29+
/// Total result row count for fast-scan heuristic.
30+
pub(super) row_count: usize,
3131
}
3232

3333
/// Streaming output writer for multi-drive search.
@@ -896,18 +896,20 @@ pub(super) fn write_results(
896896
out: &str,
897897
output_config: &OutputConfig,
898898
output_targets: &[char],
899-
elapsed: Duration,
899+
_elapsed: Duration,
900900
pattern: &str,
901901
) -> Result<()> {
902902
let is_console = matches!(
903903
out.to_lowercase().as_str(),
904904
"console" | "con" | "term" | "terminal"
905905
);
906906

907+
let row_count = results.height();
908+
907909
let footer_ctx = CppFooterContext {
908910
output_targets,
909-
elapsed,
910911
pattern,
912+
row_count,
911913
};
912914

913915
if is_console {
@@ -947,7 +949,7 @@ pub(super) fn write_results(
947949
/// Append the legacy C++ drive footer for baseline-compatible custom output.
948950
///
949951
/// Uses CRLF line endings (`\r\n`) to match C++ baseline behavior.
950-
/// When `elapsed` is <= 1 second, appends the fast-scan message.
952+
/// When `row_count` is < 20,000, appends the fast-scan message.
951953
fn write_cpp_drive_footer<W: Write>(writer: &mut W, ctx: &CppFooterContext<'_>) -> Result<()> {
952954
if ctx.output_targets.is_empty() {
953955
return Ok(());
@@ -963,7 +965,7 @@ fn write_cpp_drive_footer<W: Write>(writer: &mut W, ctx: &CppFooterContext<'_>)
963965
)?;
964966
write!(writer, "\r\n")?;
965967

966-
if ctx.elapsed.as_secs() <= 1 {
968+
if ctx.row_count < 20_000 {
967969
write!(
968970
writer,
969971
"MMMmmm that was FAST ... maybe your searchstring was wrong?\t{pattern}\r\n",
@@ -1018,6 +1020,24 @@ mod tests {
10181020
.map_err(Into::into)
10191021
}
10201022

1023+
/// Create a large sample DataFrame (20,000+ rows) for testing slow-scan footer.
1024+
fn large_sample_df() -> Result<DataFrame> {
1025+
let count = 20_000;
1026+
let paths: Vec<String> = (0..count)
1027+
.map(|i| format!("C:\\Temp\\file{}.txt", i))
1028+
.collect();
1029+
let names: Vec<String> = (0..count).map(|i| format!("file{}.txt", i)).collect();
1030+
1031+
let path_refs: Vec<&str> = paths.iter().map(|s| s.as_str()).collect();
1032+
let name_refs: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
1033+
1034+
DataFrame::new_infer_height(vec![
1035+
Column::new("path".into(), &path_refs),
1036+
Column::new("name".into(), &name_refs),
1037+
])
1038+
.map_err(Into::into)
1039+
}
1040+
10211041
fn sample_index() -> MftIndex {
10221042
let mut index = MftIndex::new('C');
10231043
let root_name = index.add_name(".");
@@ -1100,14 +1120,14 @@ mod tests {
11001120
format: &str,
11011121
output_config: &OutputConfig,
11021122
output_targets: &[char],
1103-
elapsed: Duration,
1123+
_elapsed: Duration,
11041124
pattern: &str,
11051125
) -> Result<String> {
11061126
let mut output = Vec::new();
11071127
let footer_ctx = CppFooterContext {
11081128
output_targets,
1109-
elapsed,
11101129
pattern,
1130+
row_count: results.height(),
11111131
};
11121132
match format {
11131133
"json" => export_json(results, &mut output)?,
@@ -1126,14 +1146,14 @@ mod tests {
11261146
format: &str,
11271147
output_config: &OutputConfig,
11281148
output_targets: &[char],
1129-
elapsed: Duration,
1149+
_elapsed: Duration,
11301150
pattern: &str,
11311151
) -> Result<String> {
11321152
let mut output = Vec::new();
11331153
let footer_ctx = CppFooterContext {
11341154
output_targets,
1135-
elapsed,
11361155
pattern,
1156+
row_count: results.len(),
11371157
};
11381158
write_native_results_to(
11391159
index,
@@ -1204,14 +1224,17 @@ mod tests {
12041224
let written = fs::read_to_string(&path)?;
12051225
drop(fs::remove_file(&path));
12061226

1227+
// With 1 row (< 20,000), should include fast-scan message
12071228
assert_eq!(
12081229
written,
12091230
concat!(
12101231
"\"C:\\Temp\\file.txt\",\"file.txt\"\n",
12111232
"\r\n",
12121233
"\r\n",
12131234
"Drives? \t2\tC:|D:\r\n",
1214-
"\r\n"
1235+
"\r\n",
1236+
"MMMmmm that was FAST ... maybe your searchstring was wrong?\t*.txt\r\n",
1237+
"Search path. E.g. 'C:/' or 'C:\\Prog**' \r\n"
12151238
)
12161239
);
12171240
Ok(())
@@ -1392,7 +1415,7 @@ mod tests {
13921415
#[test]
13931416
fn test_cpp_footer_omits_fast_scan_message_when_elapsed_gt_1s() -> TestResult {
13941417
let path = temp_output_path("txt");
1395-
let results = sample_df()?;
1418+
let results = large_sample_df()?; // Use 20,000 rows to trigger slow scan
13961419
let output_config = OutputConfig::new()
13971420
.with_columns("path,name")
13981421
.with_header(false);
@@ -1410,16 +1433,14 @@ mod tests {
14101433
let written = fs::read_to_string(&path)?;
14111434
drop(fs::remove_file(&path));
14121435

1413-
assert_eq!(
1414-
written,
1415-
concat!(
1416-
"\"C:\\Temp\\file.txt\",\"file.txt\"\n",
1417-
"\r\n",
1418-
"\r\n",
1419-
"Drives? \t1\tG:\r\n",
1420-
"\r\n"
1421-
)
1422-
);
1436+
// Should NOT contain fast-scan message (row_count >= 20,000)
1437+
// Only check footer structure - first row will be "C:\Temp\file0.txt","file0.txt"
1438+
let lines: Vec<&str> = written.lines().collect();
1439+
let footer_start = lines.len().saturating_sub(4);
1440+
assert_eq!(lines[footer_start], "");
1441+
assert_eq!(lines[footer_start + 1], "");
1442+
assert_eq!(lines[footer_start + 2], "Drives? \t1\tG:");
1443+
assert_eq!(lines[footer_start + 3], "");
14231444
Ok(())
14241445
}
14251446
}

crates/uffs-cli/src/commands/search/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,12 @@ pub async fn search(
223223

224224
let t_output = std::time::Instant::now();
225225
let elapsed = start_time.elapsed();
226+
let row_count = native.results.len();
227+
let cpp_pattern = format!(">{}:{}", native.index.volume, pattern);
226228
let footer_ctx = crate::commands::output::CppFooterContext {
227229
output_targets: &output_targets,
228-
elapsed,
229-
pattern,
230+
pattern: &cpp_pattern,
231+
row_count,
230232
};
231233
write_native_results(
232234
&native.index,

0 commit comments

Comments
 (0)