diff options
author | Piotr Wach <pwach@bloomberg.net> | 2024-01-08 22:00:44 +0000 |
---|---|---|
committer | Piotr Wach <pwach@bloomberg.net> | 2024-01-08 22:00:44 +0000 |
commit | 9eaa96144bc72de6515c30fc32961a2807b247c7 (patch) | |
tree | 50f6e3b5937e6e2cf9c79d8450da63c50075bbf0 /src | |
parent | b3236dcb3db927f3709e9355b218f42327a66a99 (diff) |
New Traversal
Diffstat (limited to 'src')
-rw-r--r-- | src/interactive/app/app_state.rs | 72 | ||||
-rw-r--r-- | src/interactive/app/eventloop.rs | 282 | ||||
-rw-r--r-- | src/interactive/app/terminal_app.rs | 63 | ||||
-rw-r--r-- | src/interactive/app/tests/utils.rs | 7 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/traverse.rs | 303 |
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 |