diff options
author | Sebastian Thiel <sebastian.thiel@icloud.com> | 2024-01-09 09:53:50 +0100 |
---|---|---|
committer | Sebastian Thiel <sebastian.thiel@icloud.com> | 2024-01-09 13:53:34 +0100 |
commit | 30da672a83c1063eb6f4c5483cb47f5d69c1dc35 (patch) | |
tree | a41a506e9f848e0986c8d7ef7340c6732c2d0107 /src | |
parent | c4efba87179636afeb26e472353a029a4030086c (diff) |
refactor
- avoid `app_` prefix in `app` module.
- cleanup root-index logic a little
Diffstat (limited to 'src')
-rw-r--r-- | src/interactive/app/eventloop.rs | 65 | ||||
-rw-r--r-- | src/interactive/app/handlers.rs | 4 | ||||
-rw-r--r-- | src/interactive/app/mod.rs | 4 | ||||
-rw-r--r-- | src/interactive/app/state.rs (renamed from src/interactive/app/app_state.rs) | 11 | ||||
-rw-r--r-- | src/interactive/app/terminal.rs (renamed from src/interactive/app/terminal_app.rs) | 25 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_readonly.rs | 4 | ||||
-rw-r--r-- | src/interactive/app/tests/journeys_with_writes.rs | 11 | ||||
-rw-r--r-- | src/interactive/app/tests/utils.rs | 6 | ||||
-rw-r--r-- | src/interactive/widgets/glob.rs | 2 | ||||
-rw-r--r-- | src/interactive/widgets/main.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 2 | ||||
-rw-r--r-- | src/traverse.rs | 37 |
12 files changed, 76 insertions, 97 deletions
diff --git a/src/interactive/app/eventloop.rs b/src/interactive/app/eventloop.rs index ad326a0..cd91a27 100644 --- a/src/interactive/app/eventloop.rs +++ b/src/interactive/app/eventloop.rs @@ -1,7 +1,7 @@ use crate::interactive::{ app::navigation::Navigation, - app_state::FocussedPane, sorted_entries, + state::FocussedPane, widgets::{glob_search, MainWindow, MainWindowProps}, CursorDirection, CursorMode, DisplayOptions, MarkEntryMode, }; @@ -10,14 +10,14 @@ use crossbeam::channel::Receiver; use crosstermion::crossterm::event::{KeyCode, KeyEvent, KeyEventKind, KeyModifiers}; use crosstermion::input::Event; use dua::{ - traverse::{EntryData, RunningTraversal, Traversal, TraversalProcessingEvent}, + traverse::{BackgroundTraversal, EntryData, Traversal}, WalkOptions, WalkResult, }; use std::path::PathBuf; use tui::backend::Backend; use tui_react::Terminal; -use super::app_state::{AppState, Cursor, ProcessingResult}; +use super::state::{AppState, Cursor}; use super::tree_view::TreeView; impl AppState { @@ -70,8 +70,10 @@ impl AppState { walk_options: &WalkOptions, input: Vec<PathBuf>, ) -> Result<()> { - let running_traversal = RunningTraversal::start(traversal.root_index, walk_options, input)?; - self.running_traversal = Some(running_traversal); + let background_traversal = + BackgroundTraversal::start(traversal.root_index, walk_options, input)?; + self.navigation_mut().view_root = traversal.root_index; + self.active_traversal = Some(background_traversal); Ok(()) } @@ -90,6 +92,7 @@ impl AppState { Ok(()) } + /// This method ends once the user quits the application or there are no more inputs to process. pub fn process_events<B>( &mut self, window: &mut MainWindow, @@ -97,7 +100,7 @@ impl AppState { display: &mut DisplayOptions, terminal: &mut Terminal<B>, events: Receiver<Event>, - ) -> Result<ProcessingResult> + ) -> Result<WalkResult> where B: Backend, { @@ -119,47 +122,43 @@ impl AppState { display: &mut DisplayOptions, terminal: &mut Terminal<B>, events: &Receiver<Event>, - ) -> Result<Option<ProcessingResult>> + ) -> Result<Option<WalkResult>> where B: Backend, { - if let Some(running_traversal) = &mut self.running_traversal { + if let Some(active_traversal) = &mut self.active_traversal { crossbeam::select! { recv(events) -> event => { let Ok(event) = event else { - return Ok(Some(ProcessingResult::ExitRequested(WalkResult { num_errors: 0 }))); + return Ok(Some(WalkResult { num_errors: 0 })); }; - let result = self.process_terminal_event( + let res = self.process_terminal_event( window, traversal, display, terminal, event)?; - if let Some(processing_result) = result { - return Ok(Some(processing_result)); + if let Some(res) = res { + return Ok(Some(res)); } }, - recv(&running_traversal.event_rx) -> event => { + recv(&active_traversal.event_rx) -> event => { let Ok(event) = event else { return Ok(None); }; - let result = running_traversal.process_event(traversal, event); - if result != TraversalProcessingEvent::None { - if result == TraversalProcessingEvent::Finished { - self.is_scanning = false; - self.running_traversal = None; + if let Some(is_finished) = active_traversal.integrate_traversal_event(traversal, event) { + if is_finished { + self.active_traversal = None; } self.update_state(traversal); self.refresh_screen(window, traversal, display, terminal)?; - } + }; } } } else { let Ok(event) = events.recv() else { - return Ok(Some(ProcessingResult::ExitRequested(WalkResult { - num_errors: 0, - }))); + return Ok(Some(WalkResult { num_errors: 0 })); }; let result = self.process_terminal_event(window, traversal, display, terminal, event)?; @@ -171,17 +170,13 @@ impl AppState { } fn update_state(&mut self, traversal: &Traversal) { - let received_events = self.received_event; - if !received_events { - self.navigation_mut().view_root = traversal.root_index; - } self.entries = sorted_entries( &traversal.tree, self.navigation().view_root, self.sorting, self.glob_root(), ); - if !received_events { + if !self.received_events { self.navigation_mut().selected = self.entries.first().map(|b| b.index); } self.reset_message(); // force "scanning" to appear @@ -194,7 +189,7 @@ impl AppState { display: &mut DisplayOptions, terminal: &mut Terminal<B>, event: Event, - ) -> Result<Option<ProcessingResult>> + ) -> Result<Option<WalkResult>> where B: Backend, { @@ -203,8 +198,8 @@ impl AppState { let key = match event { Event::Key(key) if key.kind != KeyEventKind::Release => { - if key.code != KeyCode::Char('\r') { - self.received_event = true; + if key != refresh_key() { + self.received_events = true; } key } @@ -231,9 +226,9 @@ impl AppState { } Char('?') if !glob_focussed => self.toggle_help_pane(window), Char('c') if key.modifiers.contains(KeyModifiers::CONTROL) && !glob_focussed => { - return Ok(Some(ProcessingResult::ExitRequested(WalkResult { + return Ok(Some(WalkResult { num_errors: tree_view.traversal.io_errors, - }))) + })) } Char('q') if !glob_focussed => { if let Some(result) = self.handle_quit(&mut tree_view, window) { @@ -367,16 +362,16 @@ impl AppState { &mut self, tree_view: &mut TreeView<'_>, window: &mut MainWindow, - ) -> Option<std::result::Result<ProcessingResult, anyhow::Error>> { + ) -> Option<std::result::Result<WalkResult, anyhow::Error>> { use FocussedPane::*; match self.focussed { Main => { if self.glob_navigation.is_some() { self.handle_glob_quit(tree_view, window); } else { - return Some(Ok(ProcessingResult::ExitRequested(WalkResult { + return Some(Ok(WalkResult { num_errors: tree_view.traversal.io_errors, - }))); + })); } } Mark => self.focussed = Main, diff --git a/src/interactive/app/handlers.rs b/src/interactive/app/handlers.rs index 611c2a0..d6b6582 100644 --- a/src/interactive/app/handlers.rs +++ b/src/interactive/app/handlers.rs @@ -9,7 +9,7 @@ use std::{fs, io, path::PathBuf}; use tui::backend::Backend; use tui_react::Terminal; -use super::app_state::{AppState, FocussedPane::*}; +use super::state::{AppState, FocussedPane::*}; #[derive(Copy, Clone)] pub enum CursorMode { @@ -146,7 +146,7 @@ impl AppState { } pub fn reset_message(&mut self) { - if self.is_scanning { + if self.active_traversal.is_some() { self.message = Some("-> scanning <-".into()); } else { self.message = None; diff --git a/src/interactive/app/mod.rs b/src/interactive/app/mod.rs index f4f6d3e..355d587 100644 --- a/src/interactive/app/mod.rs +++ b/src/interactive/app/mod.rs @@ -1,11 +1,11 @@ -pub mod app_state; mod bytevis; mod common; mod eventloop; mod handlers; pub mod input; mod navigation; -pub mod terminal_app; +pub mod state; +pub mod terminal; pub mod tree_view; pub use bytevis::*; diff --git a/src/interactive/app/app_state.rs b/src/interactive/app/state.rs index 3d32f80..9157da1 100644 --- a/src/interactive/app/app_state.rs +++ b/src/interactive/app/state.rs @@ -1,4 +1,4 @@ -use dua::{traverse::RunningTraversal, WalkResult}; +use dua::traverse::BackgroundTraversal; use super::{navigation::Navigation, EntryDataBundle, SortMode}; @@ -26,11 +26,6 @@ pub struct AppState { pub sorting: SortMode, pub message: Option<String>, pub focussed: FocussedPane, - pub is_scanning: bool, - pub received_event: bool, - pub running_traversal: Option<RunningTraversal>, -} - -pub enum ProcessingResult { - ExitRequested(WalkResult), + pub received_events: bool, + pub active_traversal: Option<BackgroundTraversal>, } diff --git a/src/interactive/app/terminal_app.rs b/src/interactive/app/terminal.rs index ae1c130..2109e00 100644 --- a/src/interactive/app/terminal_app.rs +++ b/src/interactive/app/terminal.rs @@ -12,10 +12,7 @@ use tui_react::Terminal; use crate::interactive::widgets::MainWindow; -use super::{ - app_state::{AppState, ProcessingResult}, - sorted_entries, DisplayOptions, -}; +use super::{sorted_entries, state::AppState, DisplayOptions}; /// State and methods representing the interactive disk usage analyser for the terminal pub struct TerminalApp { @@ -41,10 +38,7 @@ impl TerminalApp { let display = DisplayOptions::new(byte_format); let window = MainWindow::default(); - let mut state = AppState { - is_scanning: false, - ..Default::default() - }; + let mut state = AppState::default(); let traversal = { let mut tree = Tree::new(); @@ -93,15 +87,13 @@ impl TerminalApp { where B: Backend, { - match self.state.process_events( + self.state.process_events( &mut self.window, &mut self.traversal, &mut self.display, terminal, events, - )? { - ProcessingResult::ExitRequested(res) => Ok(res), - } + ) } } @@ -120,18 +112,15 @@ mod tests { where B: Backend, { - while self.state.running_traversal.is_some() { - match self.state.process_event( + while self.state.active_traversal.is_some() { + if let Some(res) = self.state.process_event( &mut self.window, &mut self.traversal, &mut self.display, terminal, &events, )? { - Some(ProcessingResult::ExitRequested(res)) => { - return Ok(res); - } - _ => {} + return Ok(res); } } Ok(WalkResult { num_errors: 0 }) diff --git a/src/interactive/app/tests/journeys_readonly.rs b/src/interactive/app/tests/journeys_readonly.rs index 28e0529..b6dc579 100644 --- a/src/interactive/app/tests/journeys_readonly.rs +++ b/src/interactive/app/tests/journeys_readonly.rs @@ -40,8 +40,8 @@ fn simple_user_journey_read_only() -> Result<()> { ); assert!( - !app.state.is_scanning, - "it will not think it is still scanning" + app.state.active_traversal.is_none(), + "it will not think it is still scanning as there is no traversal" ); let first_selected_path = OsString::from(format!("{}/{}", FIXTURE_PATH, long_root)); diff --git a/src/interactive/app/tests/journeys_with_writes.rs b/src/interactive/app/tests/journeys_with_writes.rs index 52431da..cbe18f1 100644 --- a/src/interactive/app/tests/journeys_with_writes.rs +++ b/src/interactive/app/tests/journeys_with_writes.rs @@ -28,13 +28,10 @@ fn basic_user_journey_with_deletion() -> Result<()> { // When selecting the marker window and pressing the combination to delete entries app.process_events( &mut terminal, - into_events( - vec![ - Event::Key(KeyCode::Tab.into()), - Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)), - ] - .into_iter(), - ), + into_events([ + Event::Key(KeyCode::Tab.into()), + Event::Key(KeyEvent::new(KeyCode::Char('r'), KeyModifiers::CONTROL)), + ]), )?; assert!( app.window.mark_pane.is_none(), diff --git a/src/interactive/app/tests/utils.rs b/src/interactive/app/tests/utils.rs index 8f7bf97..a54fa8f 100644 --- a/src/interactive/app/tests/utils.rs +++ b/src/interactive/app/tests/utils.rs @@ -19,12 +19,14 @@ use std::{ use tui::backend::TestBackend; use tui_react::Terminal; -use crate::interactive::{app::tests::FIXTURE_PATH, terminal_app::TerminalApp}; +use crate::interactive::{app::tests::FIXTURE_PATH, terminal::TerminalApp}; pub fn into_events<'a>(events: impl IntoIterator<Item = Event> + 'a) -> Receiver<Event> { let (key_send, key_receive) = crossbeam::channel::unbounded(); for event in events { - _ = key_send.send(event); + key_send + .send(event) + .expect("event is stored in the channel for later retrieval"); } key_receive } diff --git a/src/interactive/widgets/glob.rs b/src/interactive/widgets/glob.rs index 9f385a2..bafc69e 100644 --- a/src/interactive/widgets/glob.rs +++ b/src/interactive/widgets/glob.rs @@ -18,7 +18,7 @@ use tui_react::{draw_text_nowrap_fn, Terminal}; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; -use crate::interactive::app_state::Cursor; +use crate::interactive::state::Cursor; pub struct GlobPaneProps { pub border_style: Style, diff --git a/src/interactive/widgets/main.rs b/src/interactive/widgets/main.rs index 3df375a..cd3c330 100644 --- a/src/interactive/widgets/main.rs +++ b/src/interactive/widgets/main.rs @@ -1,5 +1,5 @@ use crate::interactive::{ - app_state::{AppState, Cursor, FocussedPane}, + state::{AppState, Cursor, FocussedPane}, widgets::{ Entries, EntriesProps, Footer, FooterProps, GlobPane, GlobPaneProps, Header, HelpPane, HelpPaneProps, MarkPane, MarkPaneProps, COLOR_MARKED, diff --git a/src/main.rs b/src/main.rs index 2743377..105cf58 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use std::{fs, io, io::Write, path::PathBuf, process}; #[cfg(feature = "tui-crossplatform")] use crate::interactive::input::input_channel; #[cfg(feature = "tui-crossplatform")] -use crate::interactive::terminal_app::TerminalApp; +use crate::interactive::terminal::TerminalApp; mod crossdev; #[cfg(feature = "tui-crossplatform")] diff --git a/src/traverse.rs b/src/traverse.rs index 3b6b291..6c24788 100644 --- a/src/traverse.rs +++ b/src/traverse.rs @@ -131,7 +131,8 @@ pub enum TraversalEvent { Finished(u64), } -pub struct RunningTraversal { +/// An in-progress traversal which exposes newly obtained entries +pub struct BackgroundTraversal { walk_options: WalkOptions, previous_node_idx: TreeIndex, parent_node_idx: TreeIndex, @@ -143,19 +144,14 @@ pub struct RunningTraversal { pub event_rx: Receiver<TraversalEvent>, } -#[derive(PartialEq)] -pub enum TraversalProcessingEvent { - None, - UpdateIsReady, - Finished, -} - -impl RunningTraversal { +impl BackgroundTraversal { + /// Start a background thread to perform the actual tree walk, and dispatch the results + /// as events to be received on [BackgroundTraversal::event_rx]. pub fn start( root_idx: TreeIndex, walk_options: &WalkOptions, input: Vec<PathBuf>, - ) -> anyhow::Result<RunningTraversal> { + ) -> anyhow::Result<BackgroundTraversal> { let (entry_tx, entry_rx) = crossbeam::channel::bounded(100); std::thread::Builder::new() .name("dua-fs-walk-dispatcher".to_string()) @@ -210,11 +206,18 @@ impl RunningTraversal { }) } - pub fn process_event( + /// Integrate `event` into traversal `t` so its information is represented by it. + /// This builds the traversal tree from a directory-walk. + /// + /// Returns + /// * `Some(true)` if the traversal is finished + /// * `Some(false)` if the caller may update its state after throttling kicked in + /// * `None` - the event was written into the traversal, but there is nothing else to do + pub fn integrate_traversal_event( &mut self, t: &mut Traversal, event: TraversalEvent, - ) -> TraversalProcessingEvent { + ) -> Option<bool> { match event { TraversalEvent::Entry(entry, root_path, device_id) => { t.entries_traversed += 1; @@ -334,10 +337,8 @@ impl RunningTraversal { } } - if let Some(throttle) = &self.throttle { - if throttle.can_update() { - return TraversalProcessingEvent::UpdateIsReady; - } + if self.throttle.as_ref().map_or(false, |t| t.can_update()) { + return Some(false); } } TraversalEvent::Finished(io_errors) => { @@ -371,10 +372,10 @@ impl RunningTraversal { t.total_bytes = Some(root_size); t.elapsed = Some(t.start.elapsed()); - return TraversalProcessingEvent::Finished; + return Some(true); } } - TraversalProcessingEvent::None + None } } |