1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//! Parallel directory scanner and snapshot pipeline behind the
//! [`rdirstat`](https://github.com/AndyGybels/rdirstat) TUI and GUI.
//!
//! `rdirstat-core` walks a filesystem in parallel, accumulates per-directory
//! sizes, and exposes the running state via [`UiSnapshot`]s suitable for
//! consumption by an event-loop-driven UI. Both the terminal frontend
//! ([`rdirstat`](https://crates.io/crates/rdirstat)) and the desktop frontend
//! ([`rdirstat-gui`](https://crates.io/crates/rdirstat-gui)) are built on
//! exactly this engine — no logic is duplicated between them.
//!
//! ## At a glance
//!
//! ```text
//! start_scan() ──► walker thread ──► ScanState (live, atomic + Mutex)
//! │
//! └─► spawn_snapshot_thread()
//! ─► UiSnapshot stream
//! ─► your UI
//! ```
//!
//! ## Quick start
//!
//! ```no_run
//! use std::path::PathBuf;
//! use std::sync::Arc;
//! use rdirstat_core::{ScanState, start_scan};
//!
//! let state = ScanState::new();
//! start_scan(PathBuf::from("/tmp"), Arc::clone(&state));
//!
//! // Block until the scan finishes (real frontends would poll a snapshot
//! // channel from their event loop instead).
//! while state.is_scanning() {
//! std::thread::sleep(std::time::Duration::from_millis(100));
//! }
//!
//! println!("scanned {} files, {} bytes",
//! state.files_scanned(),
//! state.total_bytes.load(std::sync::atomic::Ordering::Relaxed));
//! ```
//!
//! ## Design properties worth knowing
//!
//! - **Allocated size, not logical size.** [`util::allocated_size`] returns
//! `m.blocks() * 512` on Unix, matching `du`'s default output. Sparse files
//! report their real on-disk footprint; tiny files account for cluster
//! rounding.
//! - **Inode deduplication.** Hardlinks and macOS firmlinks (where `/Users`
//! and `/System/Volumes/Data/Users` alias the same inodes) are counted
//! exactly once, regardless of how many paths point at them.
//! - **Honest "complete" status.** [`ScanState::is_completed`] only returns
//! `true` when every file in a directory's subtree has been counted — not
//! the moment the walker finishes listing the directory's immediate
//! children. `dir_sizes[D]` is the final total at the moment `D` is marked
//! complete.
//! - **Snapshot-driven UI.** Frontends never read [`ScanState`] directly while
//! rendering. They consume cloned [`UiSnapshot`]s on a 100 ms cadence — see
//! [`spawn_snapshot_thread`] — which makes the UI loop lock-free.
//!
//! ## Module map
//!
//! | Module | Purpose |
//! |---|---|
//! | [`scan`] | Parallel walker, [`ScanState`], inode dedup, post-order completion |
//! | [`snapshot`] | [`UiSnapshot`] type and the 100 ms snapshot pump |
//! | [`app`] | [`AppState`] — directory navigation + scan controller for frontends |
//! | [`model`] | [`DirEntry`] — one row in a directory listing |
//! | [`mounts`] | [`list_mounts`] for drive-picker UIs |
//! | [`util`] | [`allocated_size`], [`format_size`], [`strip_unc_prefix`] |
//! | [`logging`] | Append-only file logger used by the binaries |
pub use ;
pub use ;
pub use ;
pub use ;
pub use DirEntry;
pub use AppState;
pub use ;