summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2023-12-09 10:12:36 +0100
committerSebastian Thiel <sebastian.thiel@icloud.com>2023-12-09 10:12:36 +0100
commit45ccb7cb5a4765190ea6b8d02e0b29f63b1bd702 (patch)
tree15a12cb7119970aeee571944eb0a6a59f939a2dd
parentbf4da4e1c4444fb490f85516efc518bb238e1652 (diff)
parent8439ba703d7f16b2a8f5bd0348b63b26a5fbe689 (diff)
feat: Press `c` to sort by count of entries in a directory.
That way it's easy to spot places that have a lot of (possibly small) files, which otherwise would remain under the radar when sorting by size.
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml1
-rw-r--r--src/interactive/app/common.rs28
-rw-r--r--src/interactive/app/eventloop.rs1
-rw-r--r--src/interactive/app/handlers.rs5
-rw-r--r--src/interactive/app/tests/journeys_readonly.rs33
-rw-r--r--src/interactive/app/tests/utils.rs64
-rw-r--r--src/interactive/widgets/entries.rs89
-rw-r--r--src/interactive/widgets/footer.rs2
-rw-r--r--src/interactive/widgets/help.rs1
-rw-r--r--src/traverse.rs94
11 files changed, 234 insertions, 91 deletions
diff --git a/Cargo.lock b/Cargo.lock
index e3078b1..430c886 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -277,6 +277,7 @@ dependencies = [
"clap",
"crosstermion",
"filesize",
+ "human_format",
"itertools",
"jwalk",
"num_cpus",
@@ -361,6 +362,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
[[package]]
+name = "human_format"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "86cce260d758a9aa3d7c4b99d55c815a540f8a37514ba6046ab6be402a157cb0"
+
+[[package]]
name = "idna"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 89d6e1b..221c7ec 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -38,6 +38,7 @@ tui-react = { version = "0.19.0", optional = true }
open = { version = "5.0", optional = true }
wild = "2.0.4"
owo-colors = "3.5.0"
+human_format = "1.0.3"
[[bin]]
name="dua"
diff --git a/src/interactive/app/common.rs b/src/interactive/app/common.rs
index 805adc2..6b455a1 100644
--- a/src/interactive/app/common.rs
+++ b/src/interactive/app/common.rs
@@ -2,6 +2,7 @@ use crate::interactive::path_of;
use dua::traverse::{EntryData, Tree, TreeIndex};
use itertools::Itertools;
use petgraph::Direction;
+use std::cmp::Ordering;
use unicode_segmentation::UnicodeSegmentation;
#[derive(Default, Debug, Copy, Clone, PartialOrd, PartialEq, Eq)]
@@ -9,8 +10,10 @@ pub enum SortMode {
#[default]
SizeDescending,
SizeAscending,
- MTimeAscending,
MTimeDescending,
+ MTimeAscending,
+ CountDescending,
+ CountAscending,
}
impl SortMode {
@@ -19,18 +22,25 @@ impl SortMode {
*self = match self {
SizeDescending => SizeAscending,
SizeAscending => SizeDescending,
- MTimeAscending => SizeAscending,
- MTimeDescending => SizeDescending,
+ _ => SizeDescending,
}
}
pub fn toggle_mtime(&mut self) {
use SortMode::*;
*self = match self {
- SizeDescending => MTimeDescending,
- SizeAscending => MTimeAscending,
MTimeAscending => MTimeDescending,
MTimeDescending => MTimeAscending,
+ _ => MTimeDescending,
+ }
+ }
+
+ pub fn toggle_count(&mut self) {
+ use SortMode::*;
+ *self = match self {
+ CountAscending => CountDescending,
+ CountDescending => CountAscending,
+ _ => CountDescending,
}
}
}
@@ -44,6 +54,12 @@ pub struct EntryDataBundle {
pub fn sorted_entries(tree: &Tree, node_idx: TreeIndex, sorting: SortMode) -> Vec<EntryDataBundle> {
use SortMode::*;
+ fn cmp_count(l: &EntryDataBundle, r: &EntryDataBundle) -> Ordering {
+ l.data
+ .entry_count
+ .cmp(&r.data.entry_count)
+ .then_with(|| l.data.name.cmp(&r.data.name))
+ }
tree.neighbors_directed(node_idx, Direction::Outgoing)
.filter_map(|idx| {
tree.node_weight(idx).map(|w| {
@@ -62,6 +78,8 @@ pub fn sorted_entries(tree: &Tree, node_idx: TreeIndex, sorting: SortMode) -> Ve
SizeAscending => l.data.size.cmp(&r.data.size),
MTimeAscending => l.data.mtime.cmp(&r.data.mtime),
MTimeDescending => r.data.mtime.cmp(&l.data.mtime),
+ CountAscending => cmp_count(l, r),
+ CountDescending => cmp_count(l, r).reverse(),
})
.collect()
}
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index a8108bc..0502ddd 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -149,6 +149,7 @@ impl AppState {
Ctrl('d') | PageDown => self.change_entry_selection(CursorDirection::PageDown),
Char('s') => self.cycle_sorting(traversal),
Char('m') => self.cycle_mtime_sorting(traversal),
+ Char('c') => self.cycle_count_sorting(traversal),
Char('g') => display.byte_vis.cycle(),
_ => {}
},
diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs
index 4948c95..d3761e9 100644
--- a/src/interactive/app/handlers.rs
+++ b/src/interactive/app/handlers.rs
@@ -161,6 +161,11 @@ impl AppState {
self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
}
+ pub fn cycle_count_sorting(&mut self, traversal: &Traversal) {
+ self.sorting.toggle_count();
+ self.entries = sorted_entries(&traversal.tree, self.root, self.sorting);
+ }
+
pub fn reset_message(&mut self) {
if self.is_scanning {
self.message = Some("-> scanning <-".into());
diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs
index c220a37..7a1df0e 100644
--- a/src/interactive/app/tests/journeys_readonly.rs
+++ b/src/interactive/app/tests/journeys_readonly.rs
@@ -77,19 +77,26 @@ fn simple_user_journey_read_only() -> Result<()> {
SortMode::MTimeAscending,
"it sets the sort mode to ascending by mtime"
);
- // when hitting the S key
- app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ // when hitting the C key
+ app.process_events(&mut terminal, into_keys(b"c".iter()))?;
assert_eq!(
app.state.sorting,
- SortMode::SizeAscending,
- "it sets the sort mode to ascending by size"
+ SortMode::CountDescending,
+ "it sets the sort mode to descending by count"
+ );
+ // when hitting the C key again
+ app.process_events(&mut terminal, into_keys(b"c".iter()))?;
+ assert_eq!(
+ app.state.sorting,
+ SortMode::CountAscending,
+ "it sets the sort mode to ascending by count"
);
assert_eq!(
node_by_index(&app, app.state.entries[0].index),
node_by_name(&app, fixture_str(long_root)),
"it recomputes the cached entries"
);
- // when hitting the S key again
+ // when hitting the S key
app.process_events(&mut terminal, into_keys(b"s".iter()))?;
assert_eq!(
app.state.sorting,
@@ -97,6 +104,22 @@ fn simple_user_journey_read_only() -> Result<()> {
"it sets the sort mode to descending by size"
);
assert_eq!(
+ node_by_index(&app, app.state.entries[1].index),
+ node_by_name(&app, fixture_str(long_root)),
+ "it recomputes the cached entries"
+ );
+ // when hitting the S key again
+ app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ assert_eq!(
+ app.state.sorting,
+ SortMode::SizeAscending,
+ "it sets the sort mode to ascending by size"
+ );
+ // hit the S key again to get Descending - the rest depends on it
+ app.process_events(&mut terminal, into_keys(b"s".iter()))?;
+ assert_eq!(app.state.sorting, SortMode::SizeDescending,);
+
+ assert_eq!(
node_by_index(&app, app.state.entries[0].index),
node_by_name(&app, fixture_str(short_root)),
"it recomputes the cached entries"
diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs
index 7ca7382..aa89926 100644
--- a/src/interactive/app/tests/utils.rs
+++ b/src/interactive/app/tests/utils.rs
@@ -13,7 +13,6 @@ use std::{
fs::{copy, create_dir_all, remove_dir, remove_file},
io::ErrorKind,
path::{Path, PathBuf},
- time::UNIX_EPOCH,
};
use tui::backend::TestBackend;
use tui_react::Terminal;
@@ -224,32 +223,32 @@ pub fn sample_01_tree() -> Tree {
let root_size = 1259070;
#[cfg(windows)]
let root_size = 1259069;
- let rn = add_node("", root_size, None);
+ let rn = add_node("", root_size, 14, None);
{
- let sn = add_node(&fixture_str("sample-01"), root_size, Some(rn));
+ let sn = add_node(&fixture_str("sample-01"), root_size, 13, Some(rn));
{
- add_node(".hidden.666", 666, Some(sn));
- add_node("a", 256, Some(sn));
- add_node("b.empty", 0, Some(sn));
+ add_node(".hidden.666", 666, 0, Some(sn));
+ add_node("a", 256, 0, Some(sn));
+ add_node("b.empty", 0, 0, Some(sn));
#[cfg(not(windows))]
- add_node("c.lnk", 1, Some(sn));
+ add_node("c.lnk", 1, 0, Some(sn));
#[cfg(windows)]
- add_node("c.lnk", 0, Some(sn));
- let dn = add_node("dir", 1258024, Some(sn));
+ add_node("c.lnk", 0, 0, Some(sn));
+ let dn = add_node("dir", 1258024, 7, Some(sn));
{
- add_node("1000bytes", 1000, Some(dn));
- add_node("dir-a.1mb", 1_000_000, Some(dn));
- add_node("dir-a.kb", 1024, Some(dn));
- let en = add_node("empty-dir", 0, Some(dn));
+ add_node("1000bytes", 1000, 0, Some(dn));
+ add_node("dir-a.1mb", 1_000_000, 0, Some(dn));
+ add_node("dir-a.kb", 1024, 0, Some(dn));
+ let en = add_node("empty-dir", 0, 1, Some(dn));
{
- add_node(".gitkeep", 0, Some(en));
+ add_node(".gitkeep", 0, 0, Some(en));
}
- let sub = add_node("sub", 256_000, Some(dn));
+ let sub = add_node("sub", 256_000, 1, Some(dn));
{
- add_node("dir-sub-a.256kb", 256_000, Some(sub));
+ add_node("dir-sub-a.256kb", 256_000, 0, Some(sub));
}
}
- add_node("z123.b", 123, Some(sn));
+ add_node("z123.b", 123, 0, Some(sn));
}
}
}
@@ -261,27 +260,28 @@ pub fn sample_02_tree() -> Tree {
{
let mut add_node = make_add_node(&mut tree);
let root_size = 1540;
- let rn = add_node("", root_size, None);
+ let rn = add_node("", root_size, 10, None);
{
let sn = add_node(
Path::new(FIXTURE_PATH).join("sample-02").to_str().unwrap(),
root_size,
+ 9,
Some(rn),
);
{
- add_node("a", 256, Some(sn));
- add_node("b", 1, Some(sn));
- let dn = add_node("dir", 1283, Some(sn));
+ add_node("a", 256, 0, Some(sn));
+ add_node("b", 1, 0, Some(sn));
+ let dn = add_node("dir", 1283, 6, Some(sn));
{
- add_node("c", 257, Some(dn));
- add_node("d", 2, Some(dn));
- let en = add_node("empty-dir", 0, Some(dn));
+ add_node("c", 257, 0, Some(dn));
+ add_node("d", 2, 0, Some(dn));
+ let en = add_node("empty-dir", 0, 1, Some(dn));
{
- add_node(".gitkeep", 0, Some(en));
+ add_node(".gitkeep", 0, 0, Some(en));
}
- let sub = add_node("sub", 1024, Some(dn));
+ let sub = add_node("sub", 1024, 1, Some(dn));
{
- add_node("e", 1024, Some(sub));
+ add_node("e", 1024, 0, Some(sub));
}
}
}
@@ -290,13 +290,15 @@ pub fn sample_02_tree() -> Tree {
tree
}
-pub fn make_add_node(t: &mut Tree) -> impl FnMut(&str, u128, Option<NodeIndex>) -> NodeIndex + '_ {
- move |name, size, maybe_from_idx| {
+pub fn make_add_node(
+ t: &mut Tree,
+) -> impl FnMut(&str, u128, u64, Option<NodeIndex>) -> NodeIndex + '_ {
+ move |name, size, entry_count, maybe_from_idx| {
let n = t.add_node(EntryData {
name: PathBuf::from(name),
size,
- mtime: UNIX_EPOCH,
- metadata_io_error: false,
+ entry_count: (entry_count > 0).then_some(entry_count),
+ ..Default::default()
});
if let Some(from) = maybe_from_idx {
t.add_edge(from, n, ());
diff --git a/src/interactive/widgets/entries.rs b/src/interactive/widgets/entries.rs
index 4df8efb..3d46a6e 100644
--- a/src/interactive/widgets/entries.rs
+++ b/src/interactive/widgets/entries.rs
@@ -5,6 +5,7 @@ use crate::interactive::{
};
use chrono::DateTime;
use dua::traverse::{EntryData, Tree, TreeIndex};
+use human_format;
use itertools::Itertools;
use std::time::SystemTime;
use std::{borrow::Borrow, path::Path};
@@ -88,16 +89,24 @@ impl Entries {
let percentage_style = percentage_style(fraction, text_style);
let mut columns = Vec::new();
- if should_show_mtime_column(sort_mode) {
- columns.push(mtime_column(entry_data.mtime, *sort_mode, text_style));
+ if show_mtime_column(sort_mode) {
+ columns.push(mtime_column(
+ entry_data.mtime,
+ column_style(Column::MTime, *sort_mode, text_style),
+ ));
}
columns.push(bytes_column(
*display,
entry_data.size,
- *sort_mode,
- text_style,
+ column_style(Column::Bytes, *sort_mode, text_style),
));
columns.push(percentage_column(*display, fraction, percentage_style));
+ if show_count_column(sort_mode) {
+ columns.push(count_column(
+ entry_data.entry_count,
+ column_style(Column::Count, *sort_mode, text_style),
+ ));
+ }
columns.push(name_column(
&entry_data.name,
*is_dir,
@@ -229,18 +238,27 @@ fn columns_with_separators(columns: Vec<Span<'_>>, style: Style) -> Vec<Span<'_>
columns_with_separators
}
-fn mtime_column(entry_mtime: SystemTime, sort_mode: SortMode, style: Style) -> Span<'static> {
+fn mtime_column(entry_mtime: SystemTime, style: Style) -> Span<'static> {
let datetime = DateTime::<chrono::Utc>::from(entry_mtime);
let formatted_time = datetime.format("%d/%m/%Y %H:%M:%S").to_string();
+ Span::styled(format!("{:>20}", formatted_time), style)
+}
+
+fn count_column(entry_count: Option<u64>, style: Style) -> Span<'static> {
Span::styled(
- format!("{:>20}", formatted_time),
- Style {
- fg: match sort_mode {
- SortMode::SizeAscending | SortMode::SizeDescending => style.fg,
- SortMode::MTimeAscending | SortMode::MTimeDescending => Color::Green.into(),
- },
- ..style
- },
+ format!(
+ "{:>4}",
+ match entry_count {
+ Some(count) => {
+ human_format::Formatter::new()
+ .with_decimals(0)
+ .with_separator("")
+ .format(count as f64)
+ }
+ None => "".to_string(),
+ }
+ ),
+ style,
)
}
@@ -279,31 +297,48 @@ fn percentage_column(display: DisplayOptions, fraction: f32, style: Style) -> Sp
Span::styled(format!("{}", display.byte_vis.display(fraction)), style)
}
-fn bytes_column(
- display: DisplayOptions,
- entry_size: u128,
- sort_mode: SortMode,
- style: Style,
-) -> Span<'static> {
+fn bytes_column(display: DisplayOptions, entry_size: u128, style: Style) -> Span<'static> {
Span::styled(
format!(
"{:>byte_column_width$}",
display.byte_format.display(entry_size).to_string(), // we would have to impl alignment/padding ourselves otherwise...
byte_column_width = display.byte_format.width()
),
- Style {
- fg: match sort_mode {
- SortMode::SizeAscending | SortMode::SizeDescending => Color::Green.into(),
- SortMode::MTimeAscending | SortMode::MTimeDescending => style.fg,
- },
- ..style
- },
+ style,
)
}
-fn should_show_mtime_column(sort_mode: &SortMode) -> bool {
+#[derive(PartialEq)]
+enum Column {
+ Bytes,
+ MTime,
+ Count,
+}
+
+fn column_style(column: Column, sort_mode: SortMode, style: Style) -> Style {
+ Style {
+ fg: match (sort_mode, column) {
+ (SortMode::SizeAscending | SortMode::SizeDescending, Column::Bytes)
+ | (SortMode::MTimeAscending | SortMode::MTimeDescending, Column::MTime)
+ | (SortMode::CountAscending | SortMode::CountDescending, Column::Count) => {
+ Color::Green.into()
+ }
+ _ => style.fg,
+ },
+ ..style
+ }
+}
+
+fn show_mtime_column(sort_mode: &SortMode) -> bool {
matches!(
sort_mode,
SortMode::MTimeAscending | SortMode::MTimeDescending
)
}
+
+fn show_count_column(sort_mode: &SortMode) -> bool {
+ matches!(
+ sort_mode,
+ SortMode::CountAscending | SortMode::CountDescending
+ )
+}
diff --git a/src/interactive/widgets/footer.rs b/src/interactive/widgets/footer.rs
index 6e6413b..911a266 100644
--- a/src/interactive/widgets/footer.rs
+++ b/src/interactive/widgets/footer.rs
@@ -43,6 +43,8 @@ impl Footer {
SortMode::SizeDescending => "size descending",
SortMode::MTimeAscending => "modified ascending",
SortMode::MTimeDescending => "modified descending",
+ SortMode::CountAscending => "items ascending",
+ SortMode::CountDescending => "items descending",
},
match total_bytes {
Some(b) => format!("{}", format.display(*b)),
diff --git a/src/interactive/widgets/help.rs b/src/interactive/widgets/help.rs
index c1144c7..82d3725 100644
--- a/src/interactive/widgets/help.rs
+++ b/src/interactive/widgets/help.rs
@@ -134,6 +134,7 @@ impl HelpPane {
{
hotkey("s", "toggle sort by size ascending/descending", None);
hotkey("m", "toggle sort by mtime ascending/descending", None);
+ hotkey("c", "toggle sort by items ascending/descending", None);
hotkey(
"g",
"cycle through percentage display and bar options",
diff --git a/src/traverse.rs b/src/traverse.rs
index d0f5cc6..157ce2c 100644
--- a/src/traverse.rs
+++ b/src/traverse.rs
@@ -19,6 +19,7 @@ pub struct EntryData {
/// The entry's size in bytes. If it's a directory, the size is the aggregated file size of all children
pub size: u128,
pub mtime: SystemTime,
+ pub entry_count: Option<u64>,
/// If set, the item meta-data could not be obtained
pub metadata_io_error: bool,
}
@@ -29,6 +30,7 @@ impl Default for EntryData {
name: PathBuf::default(),
size: u128::default(),
mtime: UNIX_EPOCH,
+ entry_count: None,
metadata_io_error: bool::default(),
}
}
@@ -39,6 +41,7 @@ impl fmt::Debug for EntryData {
f.debug_struct("EntryData")
.field("name", &self.name)
.field("size", &self.size)
+ .field("entry_count", &self.entry_count)
// Skip mtime
.field("metadata_io_error", &self.metadata_io_error)
.finish()
@@ -70,17 +73,41 @@ impl Traversal {
input: Vec<PathBuf>,
mut update: impl FnMut(&mut Traversal) -> Result<bool>,
) -> Result<Option<Traversal>> {
- fn set_size_or_panic(tree: &mut Tree, node_idx: TreeIndex, current_size_at_depth: u128) {
- tree.node_weight_mut(node_idx)
- .expect("node for parent index we just retrieved")
- .size = current_size_at_depth;
+ #[derive(Default, Copy, Clone)]
+ struct EntryInfo {
+ size: u128,
+ entries_count: Option<u64>,
+ }
+ impl EntryInfo {
+ fn add_count(&mut self, other: &Self) {
+ self.entries_count = match (self.entries_count, other.entries_count) {
+ (Some(a), Some(b)) => Some(a + b),
+ (None, Some(b)) => Some(b),
+ (Some(a), None) => Some(a),
+ (None, None) => None,
+ };
+ }
+ }
+ fn set_entry_info_or_panic(
+ tree: &mut Tree,
+ node_idx: TreeIndex,
+ EntryInfo {
+ size,
+ entries_count,
+ }: EntryInfo,
+ ) {
+ let node = tree
+ .node_weight_mut(node_idx)
+ .expect("node for parent index we just retrieved");
+ node.size = size;
+ node.entry_count = entries_count;
}
fn parent_or_panic(tree: &mut Tree, parent_node_idx: TreeIndex) -> TreeIndex {
tree.neighbors_directed(parent_node_idx, Direction::Incoming)
.next()
.expect("every node in the iteration has a parent")
}
- fn pop_or_panic(v: &mut Vec<u128>) -> u128 {
+ fn pop_or_panic(v: &mut Vec<EntryInfo>) -> EntryInfo {
v.pop().expect("sizes per level to be in sync with graph")
}
@@ -99,8 +126,8 @@ impl Traversal {
};
let (mut previous_node_idx, mut parent_node_idx) = (t.root_index, t.root_index);
- let mut sizes_per_depth_level = Vec::new();
- let mut current_size_at_depth: u128 = 0;
+ let mut directory_info_per_depth_level = Vec::new();
+ let mut current_directory_at_depth = EntryInfo::default();
let mut previous_depth = 0;
let mut inodes = InodeFilter::default();
@@ -162,6 +189,8 @@ impl Traversal {
})
as u128;
}
+ } else {
+ data.entry_count = Some(0);
}
match m.modified() {
@@ -183,30 +212,39 @@ impl Traversal {
match (entry.depth, previous_depth) {
(n, p) if n > p => {
- sizes_per_depth_level.push(current_size_at_depth);
- current_size_at_depth = file_size;
+ directory_info_per_depth_level.push(current_directory_at_depth);
+ current_directory_at_depth = EntryInfo {
+ size: file_size,
+ entries_count: Some(1),
+ };
parent_node_idx = previous_node_idx;
}
(n, p) if n < p => {
for _ in n..p {
- set_size_or_panic(
+ set_entry_info_or_panic(
&mut t.tree,
parent_node_idx,
- current_size_at_depth,
+ current_directory_at_depth,
);
- current_size_at_depth +=
- pop_or_panic(&mut sizes_per_depth_level);
+ let dir_info =
+ pop_or_panic(&mut directory_info_per_depth_level);
+
+ current_directory_at_depth.size += dir_info.size;
+ current_directory_at_depth.add_count(&dir_info);
+
parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx);
}
- current_size_at_depth += file_size;
- set_size_or_panic(
+ current_directory_at_depth.size += file_size;
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
+ set_entry_info_or_panic(
&mut t.tree,
parent_node_idx,
- current_size_at_depth,
+ current_directory_at_depth,
);
}
_ => {
- current_size_at_depth += file_size;
+ current_directory_at_depth.size += file_size;
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
}
};
@@ -235,15 +273,25 @@ impl Traversal {
}
}
- sizes_per_depth_level.push(current_size_at_depth);
- current_size_at_depth = 0;
+ directory_info_per_depth_level.push(current_directory_at_depth);
+ current_directory_at_depth = EntryInfo::default();
for _ in 0..previous_depth {
- current_size_at_depth += pop_or_panic(&mut sizes_per_depth_level);
- set_size_or_panic(&mut t.tree, parent_node_idx, current_size_at_depth);
+ let dir_info = pop_or_panic(&mut directory_info_per_depth_level);
+ current_directory_at_depth.size += dir_info.size;
+ current_directory_at_depth.add_count(&dir_info);
+
+ set_entry_info_or_panic(&mut t.tree, parent_node_idx, current_directory_at_depth);
parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx);
}
let root_size = t.recompute_root_size();
- set_size_or_panic(&mut t.tree, t.root_index, root_size);
+ set_entry_info_or_panic(
+ &mut t.tree,
+ t.root_index,
+ EntryInfo {
+ size: root_size,
+ entries_count: (t.entries_traversed > 0).then_some(t.entries_traversed),
+ },
+ );
t.total_bytes = Some(root_size);
t.elapsed = Some(t.start.elapsed());
@@ -266,7 +314,7 @@ mod tests {
fn size_of_entry_data() {
assert_eq!(
std::mem::size_of::<EntryData>(),
- 64,
+ 80,
"the size of this should not change unexpectedly as it affects overall memory consumption"
);
}