Skip to content

Commit 9b7795e

Browse files
committed
chore: development v0.2.146 - comprehensive testing complete [auto-commit]
1 parent 130bdac commit 9b7795e

File tree

14 files changed

+250
-43
lines changed

14 files changed

+250
-43
lines changed

CHANGELOG.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2323
- Cleaned up all TTAPI references from justfile and build scripts
2424
- Updated justfile header and recipes for UFFS
2525

26-
## [0.2.145] - 2026-01-27
26+
## [0.2.146] - 2026-01-27
2727

2828
### Added
2929
- Baseline CI validation for modernization effort
@@ -46,7 +46,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4646
### Fixed
4747
- Various MFT parsing edge cases
4848

49-
[Unreleased]: https://github.com/githubrobbi/UltraFastFileSearch/compare/v0.2.145...HEAD
50-
[0.2.145]: https://github.com/githubrobbi/UltraFastFileSearch/compare/v0.2.114...v0.2.145
49+
[Unreleased]: https://github.com/githubrobbi/UltraFastFileSearch/compare/v0.2.146...HEAD
50+
[0.2.146]: https://github.com/githubrobbi/UltraFastFileSearch/compare/v0.2.114...v0.2.146
5151
[0.2.114]: https://github.com/githubrobbi/UltraFastFileSearch/releases/tag/v0.2.114
5252

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ exclude = [
3939
# Workspace Package Metadata (inherited by all crates)
4040
# ─────────────────────────────────────────────────────────────────────────────
4141
[workspace.package]
42-
version = "0.2.145"
42+
version = "0.2.146"
4343
edition = "2024"
4444
rust-version = "1.85"
4545
license = "MPL-2.0 OR LicenseRef-UFFS-Commercial"

LOG/2026_01_29_22_00_CHANGELOG_HEALING.md

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,3 +130,70 @@ let (name, parent_frs, _namespace) = match primary_name {
130130

131131
### CI Pipeline Status
132132

133+
- Run 5: v0.2.145 - PASSED ✅
134+
135+
---
136+
137+
## Fix 3: Remove Debug Prints from Normal Flow
138+
139+
### What Failed
140+
141+
Debug `eprintln!` statements were polluting stdout/stderr during normal operation:
142+
```
143+
[DEBUG] read_all_index: ENTER volume=D
144+
[DEBUG] read_all_index: INSIDE spawn_blocking volume=D
145+
[DEBUG] read_all_index: read_mft_index_internal done
146+
[DEBUG] search_dataframe: before load_or_build_dataframe_cached drive=D
147+
[DEBUG] search_dataframe: after load_or_build_dataframe_cached
148+
```
149+
150+
These were left over from debugging and should only appear when tracing is enabled.
151+
152+
### The Fix
153+
154+
Converted `eprintln!("[DEBUG] ...")` to proper `tracing::trace!()` calls:
155+
- `crates/uffs-mft/src/reader.rs`: 4 debug prints in `read_all_index`
156+
- `crates/uffs-cli/src/commands.rs`: 2 debug prints in `search_dataframe`
157+
158+
---
159+
160+
## Fix 4: Match C++ Output Format Exactly
161+
162+
### What Was Different
163+
164+
The Rust CLI output didn't match C++ output format exactly:
165+
1. Missing "Drives?" line AFTER the CSV data (not before)
166+
2. Missing "MMMmmm that was FAST" message when search completes in <= 1 second
167+
3. "Finished" message format was slightly different
168+
169+
### C++ Output Format (stdout)
170+
171+
```
172+
"Path","Name","Path Only",...,"Attributes"
173+
174+
"G:\","","G:\",609893968,...
175+
...
176+
"G:\last\file.txt",...
177+
178+
Drives? 1 G:
179+
180+
MMMmmm that was FAST ... maybe your searchstring was wrong? *
181+
Search path. E.g. 'C:/' or 'C:\Prog**'
182+
```
183+
184+
### C++ Output Format (stderr)
185+
186+
```
187+
Finished in 0 s
188+
189+
```
190+
191+
### The Fix
192+
193+
Modified `crates/uffs-cli/src/commands.rs`:
194+
1. Added "Drives?" line AFTER the CSV data (to stdout, format: `\nDrives? \t{count}\t{drive_list}\n\n`)
195+
2. Added "MMMmmm that was FAST" message when elapsed <= 1 second (to stdout)
196+
3. Updated "Finished" message format to match C++ exactly (to stderr: `\nFinished \tin {secs} s\n`)
197+
198+
### CI Pipeline Status
199+

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ Traditional file search tools (including `os.walk`, `FindFirstFile`, etc.) work
2121

2222
**UFFS reads the MFT directly** - once - and queries it in memory using Polars DataFrames. This is like reading the entire phonebook once instead of looking up each name individually.
2323

24-
### Benchmark Results (v0.2.145)
24+
### Benchmark Results (v0.2.146)
2525

2626
| Drive Type | Records | Time | Throughput |
2727
|------------|---------|------|------------|
@@ -33,7 +33,7 @@ Traditional file search tools (including `os.walk`, `FindFirstFile`, etc.) work
3333

3434
| Comparison | Records | Time | Notes |
3535
|------------|---------|------|-------|
36-
| **UFFS v0.2.145** | **18.7 Million** | **~142 seconds** | All disks, fast mode |
36+
| **UFFS v0.2.146** | **18.7 Million** | **~142 seconds** | All disks, fast mode |
3737
| UFFS v0.1.30 | 18.7 Million | ~315 seconds | Baseline |
3838
| Everything | 19 Million | 178 seconds | All disks |
3939
| WizFile | 6.5 Million | 299 seconds | Single HDD |

crates/uffs-cli/src/commands.rs

Lines changed: 69 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,17 @@ pub async fn search(
405405
// Index path is the default (fast, cached) - DataFrame path is fallback
406406
let use_index_path = should_use_index_path(mode, index.as_ref(), multi_drives.as_ref());
407407

408+
// Determine drives to search (needed for C++ compatible "Drives?" output)
409+
#[cfg(windows)]
410+
let drives_to_search: Vec<char> = if let Some(ref drives) = multi_drives {
411+
drives.clone()
412+
} else if let Some(drive) = single_drive.or_else(|| filters.parsed.drive()) {
413+
vec![drive]
414+
} else {
415+
// No drive specified - search ALL available NTFS drives
416+
uffs_mft::detect_ntfs_drives()
417+
};
418+
408419
// Handle raw MFT file input (cross-platform debugging)
409420
let mut results = if let Some(mft_path) = mft_file.as_ref() {
410421
info!(path = %mft_path.display(), "📂 Loading from raw MFT file");
@@ -420,20 +431,10 @@ pub async fn search(
420431
info!("🚀 Using fast cached MftIndex query path");
421432
#[cfg(windows)]
422433
{
423-
// Determine drives to search
424-
let drives_to_search: Vec<char> = if let Some(ref drives) = multi_drives {
425-
drives.clone()
426-
} else if let Some(drive) = single_drive.or_else(|| filters.parsed.drive()) {
427-
vec![drive]
428-
} else {
429-
// No drive specified - search ALL available NTFS drives
430-
let all_drives = uffs_mft::detect_ntfs_drives();
431-
if all_drives.is_empty() {
432-
bail!("No NTFS drives found on this system");
433-
}
434-
info!(drives = ?all_drives, "No drive specified - searching all NTFS drives");
435-
all_drives
436-
};
434+
// Use pre-computed drives_to_search
435+
if drives_to_search.is_empty() {
436+
bail!("No NTFS drives found on this system");
437+
}
437438

438439
if drives_to_search.len() == 1 {
439440
// Single drive - use existing function
@@ -482,9 +483,32 @@ pub async fn search(
482483
no_bitmap,
483484
)
484485
.await;
485-
// Print timing after streaming completes
486+
487+
// C++ compatibility: Print "Drives?" and "MMMmmm" to stdout AFTER the data
486488
let elapsed = start_time.elapsed();
487-
eprintln!("Finished in {} s", elapsed.as_secs());
489+
let secs = elapsed.as_secs();
490+
491+
#[allow(clippy::print_stdout)] // Intentional: C++ compatibility requires stdout
492+
if !drives_to_search.is_empty() {
493+
let drive_list: String = drives_to_search
494+
.iter()
495+
.map(|d| format!("{d}:"))
496+
.collect::<Vec<_>>()
497+
.join("|");
498+
// C++ format: "\nDrives? \t{count}\t{drive_list}\n\n"
499+
println!("\nDrives? \t{}\t{drive_list}\n", drives_to_search.len());
500+
}
501+
502+
#[allow(clippy::print_stdout)] // Intentional: C++ compatibility requires stdout
503+
if secs <= 1 {
504+
println!(
505+
"MMMmmm that was FAST ... maybe your searchstring was wrong?\t{pattern}\n\
506+
Search path. E.g. 'C:/' or 'C:\\Prog**' "
507+
);
508+
}
509+
510+
// C++ compatibility: "Finished in X s" (to stderr)
511+
eprintln!("\nFinished \tin {secs} s\n");
488512
return result;
489513
}
490514
}
@@ -554,8 +578,34 @@ pub async fn search(
554578
eprintln!("=== TOTAL: {total_ms} ms ===");
555579
}
556580

581+
// C++ compatibility: Print "Drives?" and "MMMmmm" to stdout AFTER the data
582+
// These go to stdout (not stderr) to match C++ behavior
557583
let secs = elapsed.as_secs();
558-
eprintln!("Finished in {secs} s");
584+
585+
#[cfg(windows)]
586+
#[allow(clippy::print_stdout)] // Intentional: C++ compatibility requires stdout
587+
if !drives_to_search.is_empty() {
588+
let drive_list: String = drives_to_search
589+
.iter()
590+
.map(|d| format!("{d}:"))
591+
.collect::<Vec<_>>()
592+
.join("|");
593+
// C++ format: "\nDrives? \t{count}\t{drive_list}\n\n"
594+
println!("\nDrives? \t{}\t{drive_list}\n", drives_to_search.len());
595+
}
596+
597+
// C++ compatibility: "MMMmmm that was FAST" message when elapsed <= 1 second
598+
// (to stdout)
599+
#[allow(clippy::print_stdout)] // Intentional: C++ compatibility requires stdout
600+
if secs <= 1 {
601+
println!(
602+
"MMMmmm that was FAST ... maybe your searchstring was wrong?\t{pattern}\n\
603+
Search path. E.g. 'C:/' or 'C:\\Prog**' "
604+
);
605+
}
606+
607+
// C++ compatibility: "Finished in X s" (to stderr)
608+
eprintln!("\nFinished \tin {secs} s\n");
559609

560610
info!(count = results.height(), "Search complete");
561611
Ok(())
@@ -824,9 +874,7 @@ async fn load_and_filter_data(
824874
if let Some(drive_letter) = effective_drive {
825875
// Single drive search with proper path resolution
826876
let t_read = std::time::Instant::now();
827-
eprintln!(
828-
"[DEBUG] search_dataframe: before load_or_build_dataframe_cached drive={drive_letter}"
829-
);
877+
tracing::trace!(drive = %drive_letter, "search_dataframe: before load_or_build_dataframe_cached");
830878

831879
// Use cached DataFrame path for performance (Windows only)
832880
#[cfg(windows)]
@@ -835,7 +883,7 @@ async fn load_and_filter_data(
835883
.await
836884
.with_context(|| format!("Failed to read MFT for drive {drive_letter}:"))?;
837885

838-
eprintln!("[DEBUG] search_dataframe: after load_or_build_dataframe_cached");
886+
tracing::trace!(drive = %drive_letter, "search_dataframe: after load_or_build_dataframe_cached");
839887

840888
// Non-Windows: read directly (no caching)
841889
#[cfg(not(windows))]

0 commit comments

Comments
 (0)