Skip to main content

rdirstat_core/
lib.rs

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