summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorPiotr Wach <pwach@bloomberg.net>2024-01-08 22:00:44 +0000
committerPiotr Wach <pwach@bloomberg.net>2024-01-08 22:00:44 +0000
commit9eaa96144bc72de6515c30fc32961a2807b247c7 (patch)
tree50f6e3b5937e6e2cf9c79d8450da63c50075bbf0 /src
parentb3236dcb3db927f3709e9355b218f42327a66a99 (diff)
New Traversal
Diffstat (limited to 'src')
-rw-r--r--src/interactive/app/app_state.rs72
-rw-r--r--src/interactive/app/eventloop.rs282
-rw-r--r--src/interactive/app/terminal_app.rs63
-rw-r--r--src/interactive/app/tests/utils.rs7
-rw-r--r--src/main.rs6
-rw-r--r--src/traverse.rs303
6 files changed, 373 insertions, 360 deletions
diff --git a/src/interactive/app/app_state.rs b/src/interactive/app/app_state.rs
index 8d245db..14164d2 100644
--- a/src/interactive/app/app_state.rs
+++ b/src/interactive/app/app_state.rs
@@ -2,7 +2,7 @@ use std::time::Duration;
use dua::{
inodefilter::InodeFilter,
- traverse::{Tree, TreeIndex},
+ traverse::{RunningTraversal, Tree, TreeIndex},
Throttle, WalkResult,
};
use petgraph::Direction;
@@ -34,76 +34,8 @@ pub struct AppState {
pub message: Option<String>,
pub focussed: FocussedPane,
pub is_scanning: bool,
- pub traversal_state: TraversalState,
-}
-
-#[derive(Default)]
-pub struct TraversalState {
- pub previous_node_idx: TreeIndex,
- pub parent_node_idx: TreeIndex,
- pub directory_info_per_depth_level: Vec<EntryInfo>,
- pub current_directory_at_depth: EntryInfo,
- pub previous_depth: usize,
- pub inodes: InodeFilter,
- pub throttle: Option<Throttle>,
pub received_event: bool,
-}
-
-impl TraversalState {
- pub fn new(root_idx: TreeIndex) -> Self {
- Self {
- previous_node_idx: root_idx,
- parent_node_idx: root_idx,
- directory_info_per_depth_level: Vec::new(),
- current_directory_at_depth: EntryInfo::default(),
- previous_depth: 0,
- inodes: InodeFilter::default(),
- throttle: Some(Throttle::new(Duration::from_millis(250), None)),
- received_event: false,
- }
- }
-}
-
-#[derive(Default, Copy, Clone)]
-pub struct EntryInfo {
- pub size: u128,
- pub entries_count: Option<u64>,
-}
-
-impl EntryInfo {
- pub 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,
- };
- }
-}
-
-pub 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;
-}
-
-pub 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")
-}
-
-pub fn pop_or_panic(v: &mut Vec<EntryInfo>) -> EntryInfo {
- v.pop().expect("sizes per level to be in sync with graph")
+ pub running_traversal: Option<RunningTraversal>,
}
pub enum ProcessingResult {
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs
index efb4cd1..594164d 100644
--- a/src/interactive/app/eventloop.rs
+++ b/src/interactive/app/eventloop.rs
@@ -9,23 +9,22 @@ use crate::{
},
};
use anyhow::Result;
-use crossbeam::channel::Receiver;
+use crossbeam::channel::{Receiver, Select};
use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use crosstermion::input::Event;
use dua::{
- traverse::{size_on_disk, EntryData, Traversal},
+ traverse::{size_on_disk, EntryData, ProcessEventResult, RunningTraversal, Traversal},
WalkOptions, WalkResult,
};
-use std::time::{SystemTime, UNIX_EPOCH};
+use std::{
+ path::PathBuf,
+ time::{SystemTime, UNIX_EPOCH},
+};
use tui::backend::Backend;
use tui_react::Terminal;
use super::app_state::{AppState, Cursor, ProcessingResult};
-use super::{
- app_state::{parent_or_panic, pop_or_panic, set_entry_info_or_panic, EntryInfo},
- terminal_app::TraversalEvent,
- tree_view::TreeView,
-};
+use super::tree_view::TreeView;
impl AppState {
pub fn navigation_mut(&mut self) -> &mut Navigation {
@@ -71,251 +70,92 @@ impl AppState {
result
}
+ pub fn traverse(
+ &mut self,
+ traversal: &Traversal,
+ walk_options: &WalkOptions,
+ input: Vec<PathBuf>,
+ ) -> Result<()> {
+ let running_traversal = RunningTraversal::new(traversal.root_index, walk_options, input)?;
+ self.running_traversal = Some(running_traversal);
+ Ok(())
+ }
+
+ fn refresh_screen<B>(
+ &mut self,
+ window: &mut MainWindow,
+ traversal: &mut Traversal,
+ display: &mut DisplayOptions,
+ terminal: &mut Terminal<B>,
+ ) -> Result<()>
+ where
+ B: Backend,
+ {
+ let tree_view = self.tree_view(traversal);
+ self.draw(window, &tree_view, *display, terminal)?;
+ Ok(())
+ }
+
pub fn process_events<B>(
&mut self,
window: &mut MainWindow,
traversal: &mut Traversal,
display: &mut DisplayOptions,
terminal: &mut Terminal<B>,
- walk_options: &WalkOptions,
events: Receiver<Event>,
- traversal_events: Receiver<TraversalEvent>,
) -> Result<ProcessingResult>
where
B: Backend,
{
- {
- let tree_view = self.tree_view(traversal);
- self.draw(window, &tree_view, *display, terminal)?;
- }
+ self.refresh_screen(window, traversal, display, terminal)?;
loop {
- crossbeam::select! {
- recv(events) -> event => {
- let Ok(event) = event else {
- continue;
- };
- let result = self.process_event(
- window,
- traversal,
- display,
- terminal,
- event)?;
- if let Some(processing_result) = result {
- return Ok(processing_result);
- }
- },
- recv(traversal_events) -> event => {
- let Ok(event) = event else {
- continue;
- };
- if self.process_traversal_event(traversal, walk_options, event) {
- self.update_state(traversal);
+ if let Some(running_traversal) = &mut self.running_traversal {
+ crossbeam::select! {
+ recv(events) -> event => {
+ let Ok(event) = event else {
+ continue;
+ };
let result = self.process_event(
window,
traversal,
display,
terminal,
- Event::Key(refresh_key()))?;
+ event)?;
if let Some(processing_result) = result {
return Ok(processing_result);
}
- }
- }
- }
- }
- }
-
- fn process_traversal_event<'a>(
- &mut self,
- t: &'a mut Traversal,
- walk_options: &'a WalkOptions,
- event: TraversalEvent,
- ) -> bool {
- match event {
- TraversalEvent::Entry(entry, root_path, device_id) => {
- t.entries_traversed += 1;
- let mut data = EntryData::default();
- match entry {
- Ok(entry) => {
- data.name = if entry.depth < 1 {
- (*root_path).clone()
- } else {
- entry.file_name.into()
+ },
+ recv(&running_traversal.event_rx) -> event => {
+ let Ok(event) = event else {
+ continue;
};
- let mut file_size = 0u128;
- let mut mtime: SystemTime = UNIX_EPOCH;
- match &entry.client_state {
- Some(Ok(ref m)) => {
- if !m.is_dir()
- && (walk_options.count_hard_links
- || self.traversal_state.inodes.add(m))
- && (walk_options.cross_filesystems
- || crossdev::is_same_device(device_id, m))
- {
- if walk_options.apparent_size {
- file_size = m.len() as u128;
- } else {
- file_size = size_on_disk(&entry.parent_path, &data.name, m)
- .unwrap_or_else(|_| {
- t.io_errors += 1;
- data.metadata_io_error = true;
- 0
- })
- as u128;
- }
- } else {
- data.entry_count = Some(0);
- data.is_dir = true;
- }
-
- match m.modified() {
- Ok(modified) => {
- mtime = modified;
- }
- Err(_) => {
- t.io_errors += 1;
- data.metadata_io_error = true;
- }
- }
- }
- Some(Err(_)) => {
- t.io_errors += 1;
- data.metadata_io_error = true;
+ let result = running_traversal.process_event(traversal, event);
+ if result != ProcessEventResult::NoOp {
+ if result == ProcessEventResult::Finished {
+ self.is_scanning = false;
+ self.running_traversal = None;
}
- None => {}
+ self.update_state(traversal);
+ self.refresh_screen(window, traversal, display, terminal)?;
}
-
- match (entry.depth, self.traversal_state.previous_depth) {
- (n, p) if n > p => {
- self.traversal_state
- .directory_info_per_depth_level
- .push(self.traversal_state.current_directory_at_depth);
- self.traversal_state.current_directory_at_depth = EntryInfo {
- size: file_size,
- entries_count: Some(1),
- };
- self.traversal_state.parent_node_idx =
- self.traversal_state.previous_node_idx;
- }
- (n, p) if n < p => {
- for _ in n..p {
- set_entry_info_or_panic(
- &mut t.tree,
- self.traversal_state.parent_node_idx,
- self.traversal_state.current_directory_at_depth,
- );
- let dir_info = pop_or_panic(
- &mut self.traversal_state.directory_info_per_depth_level,
- );
-
- self.traversal_state.current_directory_at_depth.size +=
- dir_info.size;
- self.traversal_state
- .current_directory_at_depth
- .add_count(&dir_info);
-
- self.traversal_state.parent_node_idx = parent_or_panic(
- &mut t.tree,
- self.traversal_state.parent_node_idx,
- );
- }
- self.traversal_state.current_directory_at_depth.size += file_size;
- *self
- .traversal_state
- .current_directory_at_depth
- .entries_count
- .get_or_insert(0) += 1;
- set_entry_info_or_panic(
- &mut t.tree,
- self.traversal_state.parent_node_idx,
- self.traversal_state.current_directory_at_depth,
- );
- }
- _ => {
- self.traversal_state.current_directory_at_depth.size += file_size;
- *self
- .traversal_state
- .current_directory_at_depth
- .entries_count
- .get_or_insert(0) += 1;
- }
- };
-
- data.mtime = mtime;
- data.size = file_size;
- let entry_index = t.tree.add_node(data);
-
- t.tree
- .add_edge(self.traversal_state.parent_node_idx, entry_index, ());
- self.traversal_state.previous_node_idx = entry_index;
- self.traversal_state.previous_depth = entry.depth;
- }
- Err(_) => {
- if self.traversal_state.previous_depth == 0 {
- data.name = (*root_path).clone();
- let entry_index = t.tree.add_node(data);
- t.tree
- .add_edge(self.traversal_state.parent_node_idx, entry_index, ());
- }
-
- t.io_errors += 1
- }
- }
-
- if let Some(throttle) = &self.traversal_state.throttle {
- if throttle.can_update() {
- return true;
}
}
- }
- TraversalEvent::Finished(io_errors) => {
- t.io_errors += io_errors;
-
- self.traversal_state.throttle = None;
- self.traversal_state
- .directory_info_per_depth_level
- .push(self.traversal_state.current_directory_at_depth);
- self.traversal_state.current_directory_at_depth = EntryInfo::default();
- for _ in 0..self.traversal_state.previous_depth {
- let dir_info =
- pop_or_panic(&mut self.traversal_state.directory_info_per_depth_level);
- self.traversal_state.current_directory_at_depth.size += dir_info.size;
- self.traversal_state
- .current_directory_at_depth
- .add_count(&dir_info);
-
- set_entry_info_or_panic(
- &mut t.tree,
- self.traversal_state.parent_node_idx,
- self.traversal_state.current_directory_at_depth,
- );
- self.traversal_state.parent_node_idx =
- parent_or_panic(&mut t.tree, self.traversal_state.parent_node_idx);
+ } else {
+ let Ok(event) = events.recv() else {
+ continue;
+ };
+ let result = self.process_event(window, traversal, display, terminal, event)?;
+ if let Some(processing_result) = result {
+ return Ok(processing_result);
}
- let root_size = t.recompute_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());
-
- self.is_scanning = false;
-
- return true;
}
}
- false
}
fn update_state(&mut self, traversal: &Traversal) {
- let received_events = self.traversal_state.received_event;
+ let received_events = self.received_event;
if !received_events {
self.navigation_mut().view_root = traversal.root_index;
}
@@ -348,7 +188,7 @@ impl AppState {
let key = match event {
Event::Key(key) if key.kind != KeyEventKind::Release => {
if key.code != KeyCode::Char('\r') {
- self.traversal_state.received_event = true;
+ self.received_event = true;
}
key
}
diff --git a/src/interactive/app/terminal_app.rs b/src/interactive/app/terminal_app.rs
index 537cb6f..5d57cf1 100644
--- a/src/interactive/app/terminal_app.rs
+++ b/src/interactive/app/terminal_app.rs
@@ -13,7 +13,7 @@ use tui_react::Terminal;
use crate::{crossdev, interactive::widgets::MainWindow};
use super::{
- app_state::{AppState, ProcessingResult, TraversalState},
+ app_state::{AppState, ProcessingResult},
sorted_entries, DisplayOptions,
};
@@ -26,14 +26,6 @@ pub struct TerminalApp {
pub walk_options: WalkOptions,
}
-pub type TraversalEntry =
- Result<jwalk::DirEntry<((), Option<Result<std::fs::Metadata, jwalk::Error>>)>, jwalk::Error>;
-
-pub enum TraversalEvent {
- Entry(TraversalEntry, Arc<PathBuf>, u64),
- Finished(u64),
-}
-
impl TerminalApp {
pub fn initialize<B>(
terminal: &mut Terminal<B>,
@@ -87,59 +79,16 @@ impl TerminalApp {
Ok(app)
}
- pub fn scan(&mut self, input: Vec<PathBuf>) -> Result<Receiver<TraversalEvent>> {
- self.state.traversal_state = TraversalState::new(self.traversal.root_index);
- self.state.is_scanning = true;
-
- let (entry_tx, entry_rx) = crossbeam::channel::bounded(100);
- std::thread::Builder::new()
- .name("dua-fs-walk-dispatcher".to_string())
- .spawn({
- let walk_options = self.walk_options.clone();
- let mut io_errors: u64 = 0;
- move || {
- for root_path in input.into_iter() {
- let device_id = match crossdev::init(root_path.as_ref()) {
- Ok(id) => id,
- Err(_) => {
- io_errors += 1;
- continue;
- }
- };
-
- let root_path = Arc::new(root_path);
- for entry in walk_options
- .iter_from_path(root_path.as_ref(), device_id)
- .into_iter()
- {
- if entry_tx
- .send(TraversalEvent::Entry(
- entry,
- Arc::clone(&root_path),
- device_id,
- ))
- .is_err()
- {
- // The channel is closed, this means the user has
- // requested to quit the app. Abort the walking.
- return;
- }
- }
- }
- if entry_tx.send(TraversalEvent::Finished(io_errors)).is_err() {
- log::error!("Failed to send TraversalEvents::Finished event");
- }
- }
- })?;
-
- Ok(entry_rx)
+ pub fn traverse(&mut self, input: Vec<PathBuf>) -> Result<()> {
+ self.state
+ .traverse(&self.traversal, &self.walk_options, input)?;
+ Ok(())
}
pub fn process_events<B>(
&mut self,
terminal: &mut Terminal<B>,
events: Receiver<Event>,
- traversal: Receiver<TraversalEvent>,
) -> Result<WalkResult>
where
B: Backend,
@@ -149,9 +98,7 @@ impl TerminalApp {
&mut self.traversal,
&mut self.display,
terminal,
- &self.walk_options,
events,
- traversal,
)? {
ProcessingResult::ExitRequested(res) => Ok(res),
}
diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs
index 6be28ab..133dd9a 100644
--- a/src/interactive/app/tests/utils.rs
+++ b/src/interactive/app/tests/utils.rs
@@ -19,10 +19,7 @@ use std::{
use tui::backend::TestBackend;
use tui_react::Terminal;
-use crate::interactive::{
- app::tests::FIXTURE_PATH,
- terminal_app::{TerminalApp, TraversalEvent},
-};
+use crate::interactive::{app::tests::FIXTURE_PATH, terminal_app::TerminalApp};
pub fn into_keys<'a>(
codes: impl IntoIterator<Item = KeyCode> + 'a,
@@ -191,7 +188,7 @@ pub fn initialized_app_and_terminal_with_closure(
let mut app = TerminalApp::initialize(&mut terminal, walk_options, ByteFormat::Metric)?;
let input_paths = fixture_paths.iter().map(|c| convert(c.as_ref())).collect();
- let traversal_rx = app.scan(input_paths)?;
+ let traversal_rx = app.traverse(input_paths)?;
Ok((terminal, app, traversal_rx))
}
diff --git a/src/main.rs b/src/main.rs
index 52abdc6..7e28c13 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -76,11 +76,9 @@ fn main() -> Result<()> {
let keys_rx = input_channel();
let mut app = TerminalApp::initialize(&mut terminal, walk_options, byte_format)?;
+ app.traverse(extract_paths_maybe_set_cwd(input, !opt.stay_on_filesystem)?)?;
- let traversal_rx =
- app.scan(extract_paths_maybe_set_cwd(input, !opt.stay_on_filesystem)?)?;
-
- let res = app.process_events(&mut terminal, keys_rx, traversal_rx);
+ let res = app.process_events(&mut terminal, keys_rx);
let res = res.map(|r| {
(
diff --git a/src/traverse.rs b/src/traverse.rs
index e1e4b9a..e83751c 100644
--- a/src/traverse.rs
+++ b/src/traverse.rs
@@ -1,5 +1,6 @@
-use crate::get_size_or_panic;
+use crate::{crossdev, get_size_or_panic, inodefilter::InodeFilter, Throttle, WalkOptions};
+use crossbeam::channel::Receiver;
use filesize::PathExt;
use petgraph::{graph::NodeIndex, stable_graph::StableGraph, Directed, Direction};
use std::{
@@ -7,7 +8,8 @@ use std::{
fs::Metadata,
io,
path::{Path, PathBuf},
- time::{SystemTime, UNIX_EPOCH},
+ sync::Arc,
+ time::{Duration, SystemTime, UNIX_EPOCH},
};
pub type TreeIndex = NodeIndex;
@@ -78,6 +80,303 @@ impl Traversal {
}
}
+#[derive(Default, Copy, Clone)]
+pub struct EntryInfo {
+ pub size: u128,
+ pub entries_count: Option<u64>,
+}
+
+impl EntryInfo {
+ pub 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,
+ };
+ }
+}
+
+pub 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;
+}
+
+pub 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")
+}
+
+pub fn pop_or_panic(v: &mut Vec<EntryInfo>) -> EntryInfo {
+ v.pop().expect("sizes per level to be in sync with graph")
+}
+
+pub type TraversalEntry =
+ Result<jwalk::DirEntry<((), Option<Result<std::fs::Metadata, jwalk::Error>>)>, jwalk::Error>;
+
+pub enum TraversalEvent {
+ Entry(TraversalEntry, Arc<PathBuf>, u64),
+ Finished(u64),
+}
+
+pub struct RunningTraversal {
+ walk_options: WalkOptions,
+ previous_node_idx: TreeIndex,
+ parent_node_idx: TreeIndex,
+ directory_info_per_depth_level: Vec<EntryInfo>,
+ current_directory_at_depth: EntryInfo,
+ previous_depth: usize,
+ inodes: InodeFilter,
+ throttle: Option<Throttle>,
+ pub event_rx: Receiver<TraversalEvent>,
+}
+
+#[derive(PartialEq)]
+pub enum ProcessEventResult {
+ NoOp,
+ UpdateIsReady,
+ Finished,
+}
+
+impl RunningTraversal {
+ pub fn new(
+ root_idx: TreeIndex,
+ walk_options: &WalkOptions,
+ input: Vec<PathBuf>,
+ ) -> anyhow::Result<RunningTraversal> {
+ let (entry_tx, entry_rx) = crossbeam::channel::bounded(100);
+ std::thread::Builder::new()
+ .name("dua-fs-walk-dispatcher".to_string())
+ .spawn({
+ let walk_options = walk_options.clone();
+ let mut io_errors: u64 = 0;
+ move || {
+ for root_path in input.into_iter() {
+ let device_id = match crossdev::init(root_path.as_ref()) {
+ Ok(id) => id,
+ Err(_) => {
+ io_errors += 1;
+ continue;
+ }
+ };
+
+ let root_path = Arc::new(root_path);
+ for entry in walk_options
+ .iter_from_path(root_path.as_ref(), device_id)
+ .into_iter()
+ {
+ if entry_tx
+ .send(TraversalEvent::Entry(
+ entry,
+ Arc::clone(&root_path),
+ device_id,
+ ))
+ .is_err()
+ {
+ // The channel is closed, this means the user has
+ // requested to quit the app. Abort the walking.
+ return;
+ }
+ }
+ }
+ if entry_tx.send(TraversalEvent::Finished(io_errors)).is_err() {
+ log::error!("Failed to send TraversalEvents::Finished event");
+ }
+ }
+ })?;
+
+ Ok(Self {
+ walk_options: walk_options.clone(),
+ previous_node_idx: root_idx,
+ parent_node_idx: root_idx,
+ directory_info_per_depth_level: Vec::new(),
+ current_directory_at_depth: EntryInfo::default(),
+ previous_depth: 0,
+ inodes: InodeFilter::default(),
+ throttle: Some(Throttle::new(Duration::from_millis(250), None)),
+ event_rx: entry_rx,
+ })
+ }
+
+ pub fn process_event<'a>(
+ &mut self,
+ t: &'a mut Traversal,
+ event: TraversalEvent,
+ ) -> ProcessEventResult {
+ match event {
+ TraversalEvent::Entry(entry, root_path, device_id) => {
+ t.entries_traversed += 1;
+ let mut data = EntryData::default();
+ match entry {
+ Ok(entry) => {
+ data.name = if entry.depth < 1 {
+ (*root_path).clone()
+ } else {
+ entry.file_name.into()
+ };
+
+ let mut file_size = 0u128;
+ let mut mtime: SystemTime = UNIX_EPOCH;
+ match &entry.client_state {
+ Some(Ok(ref m)) => {
+ if !m.is_dir()
+ && (self.walk_options.count_hard_links || self.inodes.add(m))
+ && (self.walk_options.cross_filesystems
+ || crossdev::is_same_device(device_id, m))
+ {
+ if self.walk_options.apparent_size {
+ file_size = m.len() as u128;
+ } else {
+ file_size = size_on_disk(&entry.parent_path, &data.name, m)
+ .unwrap_or_else(|_| {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ 0
+ })
+ as u128;
+ }
+ } else {
+ data.entry_count = Some(0);
+ data.is_dir = true;
+ }
+
+ match m.modified() {
+ Ok(modified) => {
+ mtime = modified;
+ }
+ Err(_) => {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ }
+ }
+ }
+ Some(Err(_)) => {
+ t.io_errors += 1;
+ data.metadata_io_error = true;
+ }
+ None => {}
+ }
+
+ match (entry.depth, self.previous_depth) {
+ (n, p) if n > p => {
+ self.directory_info_per_depth_level
+ .push(self.current_directory_at_depth);
+ self.current_directory_at_depth = EntryInfo {
+ size: file_size,
+ entries_count: Some(1),
+ };
+ self.parent_node_idx = self.previous_node_idx;
+ }
+ (n, p) if n < p => {
+ for _ in n..p {
+ set_entry_info_or_panic(
+ &mut t.tree,
+ self.parent_node_idx,
+ self.current_directory_at_depth,
+ );
+ let dir_info =
+ pop_or_panic(&mut self.directory_info_per_depth_level);
+
+ self.current_directory_at_depth.size += dir_info.size;
+ self.current_directory_at_depth.add_count(&dir_info);
+
+ self.parent_node_idx =
+ parent_or_panic(&mut t.tree, self.parent_node_idx);
+ }
+ self.current_directory_at_depth.size += file_size;
+ *self
+ .current_directory_at_depth
+ .entries_count
+ .get_or_insert(0) += 1;
+ set_entry_info_or_panic(
+ &mut t.tree,
+ self.parent_node_idx,
+ self.current_directory_at_depth,
+ );
+ }
+ _ => {
+ self.current_directory_at_depth.size += file_size;
+ *self
+ .current_directory_at_depth
+ .entries_count
+ .get_or_insert(0) += 1;
+ }
+ };
+
+ data.mtime = mtime;
+ data.size = file_size;
+ let entry_index = t.tree.add_node(data);
+
+ t.tree.add_edge(self.parent_node_idx, entry_index, ());
+ self.previous_node_idx = entry_index;
+ self.previous_depth = entry.depth;
+ }
+ Err(_) => {
+ if self.previous_depth == 0 {
+ data.name = (*root_path).clone();
+ let entry_index = t.tree.add_node(data);
+ t.tree.add_edge(self.parent_node_idx, entry_index, ());
+ }
+
+ t.io_errors += 1
+ }
+ }
+
+ if let Some(throttle) = &self.throttle {
+ if throttle.can_update() {
+ return ProcessEventResult::UpdateIsReady;
+ }
+ }
+ }
+ TraversalEvent::Finished(io_errors) => {
+ t.io_errors += io_errors;
+
+ self.throttle = None;
+ self.directory_info_per_depth_level
+ .push(self.current_directory_at_depth);
+ self.current_directory_at_depth = EntryInfo::default();
+ for _ in 0..self.previous_depth {
+ let dir_info = pop_or_panic(&mut self.directory_info_per_depth_level);
+ self.current_directory_at_depth.size += dir_info.size;
+ self.current_directory_at_depth.add_count(&dir_info);
+
+ set_entry_info_or_panic(
+ &mut t.tree,
+ self.parent_node_idx,
+ self.current_directory_at_depth,
+ );
+ self.parent_node_idx = parent_or_panic(&mut t.tree, self.parent_node_idx);
+ }
+ let root_size = t.recompute_root_size();
+ set_entry_info_or_panic(
+ &mut t.tree,
+ t.root_index