summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorSebastian Thiel <sebastian.thiel@icloud.com>2023-12-09 08:45:52 +0100
committerSebastian Thiel <sebastian.thiel@icloud.com>2023-12-09 10:07:52 +0100
commit8439ba703d7f16b2a8f5bd0348b63b26a5fbe689 (patch)
tree15a12cb7119970aeee571944eb0a6a59f939a2dd /src
parent9fb3113d788ff746873bd67f6ed508ec1fcf1b02 (diff)
assure sorting of entry-counts takes files into consideration.
Those don't have a count in that sense, and that needs to be visible when sorting. Now files, which naturally have no count, will sort alphabetically instead which seems most natural, as they are technically out of scope.
Diffstat (limited to 'src')
-rw-r--r--src/interactive/app/common.rs11
-rw-r--r--src/interactive/app/tests/utils.rs2
-rw-r--r--src/interactive/widgets/entries.rs19
-rw-r--r--src/traverse.rs60
4 files changed, 55 insertions, 37 deletions
diff --git a/src/interactive/app/common.rs b/src/interactive/app/common.rs
index 2eb784e..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)]
@@ -53,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| {
@@ -71,8 +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 => l.data.entry_count.cmp(&r.data.entry_count),
- CountDescending => r.data.entry_count.cmp(&l.data.entry_count),
+ CountAscending => cmp_count(l, r),
+ CountDescending => cmp_count(l, r).reverse(),
})
.collect()
}
diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs
index 2ded843..aa89926 100644
--- a/src/interactive/app/tests/utils.rs
+++ b/src/interactive/app/tests/utils.rs
@@ -297,7 +297,7 @@ pub fn make_add_node(
let n = t.add_node(EntryData {
name: PathBuf::from(name),
size,
- entry_count,
+ entry_count: (entry_count > 0).then_some(entry_count),
..Default::default()
});
if let Some(from) = maybe_from_idx {
diff --git a/src/interactive/widgets/entries.rs b/src/interactive/widgets/entries.rs
index 2e1062d..3d46a6e 100644
--- a/src/interactive/widgets/entries.rs
+++ b/src/interactive/widgets/entries.rs
@@ -104,7 +104,6 @@ impl Entries {
if show_count_column(sort_mode) {
columns.push(count_column(
entry_data.entry_count,
- *is_dir,
column_style(Column::Count, *sort_mode, text_style),
));
}
@@ -245,18 +244,18 @@ fn mtime_column(entry_mtime: SystemTime, style: Style) -> Span<'static> {
Span::styled(format!("{:>20}", formatted_time), style)
}
-fn count_column(entry_count: u64, is_dir: bool, style: Style) -> Span<'static> {
- let count_in_units = human_format::Formatter::new()
- .with_decimals(0)
- .with_separator("")
- .format(entry_count as f64);
+fn count_column(entry_count: Option<u64>, style: Style) -> Span<'static> {
Span::styled(
format!(
"{:>4}",
- if is_dir {
- count_in_units
- } else {
- "".to_string()
+ match entry_count {
+ Some(count) => {
+ human_format::Formatter::new()
+ .with_decimals(0)
+ .with_separator("")
+ .format(count as f64)
+ }
+ None => "".to_string(),
}
),
style,
diff --git a/src/traverse.rs b/src/traverse.rs
index 3d54784..157ce2c 100644
--- a/src/traverse.rs
+++ b/src/traverse.rs
@@ -19,7 +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: u64,
+ pub entry_count: Option<u64>,
/// If set, the item meta-data could not be obtained
pub metadata_io_error: bool,
}
@@ -30,7 +30,7 @@ impl Default for EntryData {
name: PathBuf::default(),
size: u128::default(),
mtime: UNIX_EPOCH,
- entry_count: u64::default(),
+ entry_count: None,
metadata_io_error: bool::default(),
}
}
@@ -74,17 +74,27 @@ impl Traversal {
mut update: impl FnMut(&mut Traversal) -> Result<bool>,
) -> Result<Option<Traversal>> {
#[derive(Default, Copy, Clone)]
- struct DirectoryInfo {
+ struct EntryInfo {
size: u128,
- entries_count: u64,
+ entries_count: Option<u64>,
}
- fn set_directory_info_or_panic(
+ 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,
- DirectoryInfo {
+ EntryInfo {
size,
entries_count,
- }: DirectoryInfo,
+ }: EntryInfo,
) {
let node = tree
.node_weight_mut(node_idx)
@@ -97,7 +107,7 @@ impl Traversal {
.next()
.expect("every node in the iteration has a parent")
}
- fn pop_or_panic(v: &mut Vec<DirectoryInfo>) -> DirectoryInfo {
+ fn pop_or_panic(v: &mut Vec<EntryInfo>) -> EntryInfo {
v.pop().expect("sizes per level to be in sync with graph")
}
@@ -117,7 +127,7 @@ impl Traversal {
let (mut previous_node_idx, mut parent_node_idx) = (t.root_index, t.root_index);
let mut directory_info_per_depth_level = Vec::new();
- let mut current_directory_at_depth = DirectoryInfo::default();
+ let mut current_directory_at_depth = EntryInfo::default();
let mut previous_depth = 0;
let mut inodes = InodeFilter::default();
@@ -179,6 +189,8 @@ impl Traversal {
})
as u128;
}
+ } else {
+ data.entry_count = Some(0);
}
match m.modified() {
@@ -201,30 +213,30 @@ impl Traversal {
match (entry.depth, previous_depth) {
(n, p) if n > p => {
directory_info_per_depth_level.push(current_directory_at_depth);
- current_directory_at_depth = DirectoryInfo {
+ current_directory_at_depth = EntryInfo {
size: file_size,
- entries_count: 1,
+ entries_count: Some(1),
};
parent_node_idx = previous_node_idx;
}
(n, p) if n < p => {
for _ in n..p {
- set_directory_info_or_panic(
+ set_entry_info_or_panic(
&mut t.tree,
parent_node_idx,
current_directory_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.entries_count +=
- dir_info.entries_count;
+ current_directory_at_depth.add_count(&dir_info);
parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx);
}
current_directory_at_depth.size += file_size;
- current_directory_at_depth.entries_count += 1;
- set_directory_info_or_panic(
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
+ set_entry_info_or_panic(
&mut t.tree,
parent_node_idx,
current_directory_at_depth,
@@ -232,7 +244,7 @@ impl Traversal {
}
_ => {
current_directory_at_depth.size += file_size;
- current_directory_at_depth.entries_count += 1;
+ *current_directory_at_depth.entries_count.get_or_insert(0) += 1;
}
};
@@ -262,22 +274,22 @@ impl Traversal {
}
directory_info_per_depth_level.push(current_directory_at_depth);
- current_directory_at_depth = DirectoryInfo::default();
+ current_directory_at_depth = EntryInfo::default();
for _ in 0..previous_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.entries_count += dir_info.entries_count;
+ current_directory_at_depth.add_count(&dir_info);
- set_directory_info_or_panic(&mut t.tree, parent_node_idx, current_directory_at_depth);
+ 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_directory_info_or_panic(
+ set_entry_info_or_panic(
&mut t.tree,
t.root_index,
- DirectoryInfo {
+ EntryInfo {
size: root_size,
- entries_count: t.entries_traversed,
+ entries_count: (t.entries_traversed > 0).then_some(t.entries_traversed),
},
);
t.total_bytes = Some(root_size);
@@ -302,7 +314,7 @@ mod tests {
fn size_of_entry_data() {
assert_eq!(
std::mem::size_of::<EntryData>(),
- if cfg!(target_os = "macos") { 80 } else { 72 },
+ 80,
"the size of this should not change unexpectedly as it affects overall memory consumption"
);
}