Skip to main content

disktree/
lib.rs

1//! Disk usage scanning for a terminal UI: find what is large, then delete it.
2//!
3//! This crate backs the `disktree` binary. The reusable part is [`index`], which
4//! walks a directory tree in parallel and returns it as a flat arena of
5//! [`Node`](index::Node)s addressed by index, with sizes and file counts already
6//! rolled up into every directory.
7//!
8//! ```
9//! use disktree::index::{ScanProgress, scan};
10//! # use std::fs;
11//! # let dir = std::env::temp_dir().join(format!("disktree_doc_{}", std::process::id()));
12//! # let _ = fs::remove_dir_all(&dir);
13//! # fs::create_dir_all(dir.join("logs")).unwrap();
14//! # fs::write(dir.join("logs").join("big.log"), vec![0u8; 4096]).unwrap();
15//! # fs::write(dir.join("small.txt"), vec![0u8; 10]).unwrap();
16//! let progress = ScanProgress::default();
17//! let tree = scan(dir.clone(), &progress);
18//!
19//! // The root aggregates its whole subtree.
20//! let root = &tree.nodes[tree.root];
21//! assert_eq!(root.n_files, 2);
22//! assert_eq!(root.size, 4106);
23//!
24//! // Children are indices into `tree.nodes`; sort them to find the biggest.
25//! let mut kids = root.children.clone();
26//! kids.sort_by_key(|&i| std::cmp::Reverse(tree.nodes[i].size));
27//!
28//! let biggest = kids[0];
29//! assert_eq!(tree.nodes[biggest].name, "logs");
30//! assert_eq!(tree.nodes[biggest].size, 4096);
31//! assert_eq!(tree.path_of(biggest), dir.join("logs"));
32//! # fs::remove_dir_all(&dir).unwrap();
33//! ```
34//!
35//! # What the numbers mean
36//!
37//! Sizes are **apparent** sizes — the logical length of a file, the same value
38//! [`std::fs::Metadata::len`] reports — not the space it occupies on disk. For
39//! compressed, sparse, or cloud-placeholder files the two differ, sometimes by a
40//! lot. Alternate data streams are not counted. Symlinks, junctions, and volume
41//! mount points appear as zero-byte entries and are never followed, so a target
42//! reached by several links is counted once, under its real path. A file with
43//! multiple hard links is counted once per link, because a directory walk cannot
44//! tell that two entries share one extent list.
45//!
46//! Directories that cannot be read are counted in [`Tree::errors`](index::Tree::errors)
47//! and contribute zero rather than aborting the scan.
48
49#![warn(missing_docs)]
50#![deny(unsafe_op_in_unsafe_fn)]
51
52pub mod index;
53
54#[cfg(windows)]
55pub mod mft;
56
57// The UI layer is an implementation detail of the binary: its signatures name
58// `ratatui` types, so exposing it as documented API would make every ratatui
59// version bump a breaking change for this crate.
60#[doc(hidden)]
61pub mod app;
62#[doc(hidden)]
63pub mod ui;