diff options
author | Sebastian Thiel <sthiel@thoughtworks.com> | 2019-06-03 10:37:17 +0530 |
---|---|---|
committer | Sebastian Thiel <sthiel@thoughtworks.com> | 2019-06-03 10:39:31 +0530 |
commit | 1f482aab49a9094234d422b3599858e909c3f164 (patch) | |
tree | 7d83e447905e4b6f8f16b37997d31bb8e74b0c13 /src/interactive | |
parent | 0cc49f5cbbd383f57a2f628711cabf36a38de2c0 (diff) |
refactor
Diffstat (limited to 'src/interactive')
-rw-r--r-- | src/interactive/app.rs | 174 | ||||
-rw-r--r-- | src/interactive/mod.rs | 2 | ||||
-rw-r--r-- | src/interactive/traverse.rs | 169 |
3 files changed, 173 insertions, 172 deletions
diff --git a/src/interactive/app.rs b/src/interactive/app.rs index 2cfc830..758efeb 100644 --- a/src/interactive/app.rs +++ b/src/interactive/app.rs @@ -1,42 +1,10 @@ -use crate::{ByteFormat, WalkOptions, WalkResult}; +use crate::{interactive::Traversal, ByteFormat, WalkOptions, WalkResult}; use failure::Error; -use petgraph::{prelude::NodeIndex, Directed, Direction, Graph}; -use std::{ - ffi::OsString, - io, - path::PathBuf, - time::{Duration, Instant}, -}; +use std::{io, path::PathBuf}; use termion::input::{Keys, TermReadEventsAndRaw}; use tui::widgets::Widget; use tui::{backend::Backend, Terminal}; -pub type TreeIndexType = u32; -pub type TreeIndex = NodeIndex<TreeIndexType>; -pub type Tree = Graph<EntryData, (), Directed, TreeIndexType>; - -#[derive(Eq, PartialEq, Debug, Default)] -pub struct EntryData { - pub name: OsString, - /// The entry's size in bytes. If it's a directory, the size is the aggregated file size of all children - pub size: u64, - /// If set, the item meta-data could not be obtained - pub metadata_io_error: bool, -} - -/// The result of the previous filesystem traversal -#[derive(Default, Debug)] -pub struct Traversal { - /// A tree representing the entire filestem traversal - pub tree: Tree, - /// The top-level node of the tree. - pub root_index: TreeIndex, - /// Amount of files or directories we have seen during the filesystem traversal - pub entries_traversed: u64, - /// Total amount of IO errors encountered when traversing the filesystem - pub io_errors: u64, -} - /// Options to configure how we display things #[derive(Clone, Copy)] pub struct DisplayOptions { @@ -49,150 +17,12 @@ impl From<WalkOptions> for DisplayOptions { } } -impl Traversal { - pub fn from_walk( - options: WalkOptions, - input: Vec<PathBuf>, - mut update: impl FnMut(&Traversal) -> Result<(), Error>, - ) -> Result<Traversal, Error> { - fn set_size_or_panic(tree: &mut Tree, node_idx: TreeIndex, current_size_at_depth: u64) { - tree.node_weight_mut(node_idx) - .expect("node for parent index we just retrieved") - .size = current_size_at_depth; - } - 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<u64>) -> u64 { - v.pop().expect("sizes per level to be in sync with graph") - } - - let mut t = { - let mut tree = Tree::new(); - let root_index = tree.add_node(EntryData::default()); - Traversal { - tree, - root_index, - ..Default::default() - } - }; - - 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 = 0; - let mut previous_depth = 0; - - let mut last_checked = Instant::now(); - - const INITIAL_CHECK_INTERVAL: usize = 500; - let mut check_instant_every = INITIAL_CHECK_INTERVAL; - let mut last_seen_eid; - - for path in input.into_iter() { - last_seen_eid = 0; - for (eid, entry) in options - .iter_from_path(path.as_ref()) - .into_iter() - .enumerate() - { - t.entries_traversed += 1; - let mut data = EntryData::default(); - match entry { - Ok(entry) => { - data.name = entry.file_name; - let file_size = match entry.metadata { - Some(Ok(ref m)) if !m.is_dir() => m.len(), - Some(Ok(_)) => 0, - Some(Err(_)) => { - t.io_errors += 1; - data.metadata_io_error = true; - 0 - } - None => unreachable!( - "we ask for metadata, so we at least have Some(Err(..))). Issue in jwalk?" - ), - }; - - 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; - parent_node_idx = previous_node_idx; - } - (n, p) if n < p => { - for _ in n..p { - set_size_or_panic( - &mut t.tree, - parent_node_idx, - current_size_at_depth, - ); - current_size_at_depth += - pop_or_panic(&mut sizes_per_depth_level); - parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx); - } - current_size_at_depth += file_size; - set_size_or_panic( - &mut t.tree, - parent_node_idx, - current_size_at_depth, - ); - } - _ => { - current_size_at_depth += file_size; - } - }; - - data.size = file_size; - let entry_index = t.tree.add_node(data); - - t.tree.add_edge(parent_node_idx, entry_index, ()); - previous_node_idx = entry_index; - previous_depth = entry.depth; - } - Err(_) => t.io_errors += 1, - } - - if eid != 0 - && eid % check_instant_every == 0 - && last_checked.elapsed() >= GUI_REFRESH_RATE - { - let now = Instant::now(); - let elapsed = (now - last_checked).as_millis() as f64; - check_instant_every = (INITIAL_CHECK_INTERVAL as f64 - * ((eid - last_seen_eid) as f64 / INITIAL_CHECK_INTERVAL as f64) - * (GUI_REFRESH_RATE.as_millis() as f64 / elapsed)) - as usize; - last_seen_eid = eid; - last_checked = now; - - update(&t)?; - } - } - } - - sizes_per_depth_level.push(current_size_at_depth); - current_size_at_depth = 0; - 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); - parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx); - } - set_size_or_panic(&mut t.tree, t.root_index, current_size_at_depth); - - Ok(t) - } -} - /// State and methods representing the interactive disk usage analyser for the terminal pub struct TerminalApp { pub traversal: Traversal, pub display: DisplayOptions, } -const GUI_REFRESH_RATE: Duration = Duration::from_millis(100); - impl TerminalApp { fn draw<B>(&self, terminal: &mut Terminal<B>) -> Result<(), Error> where diff --git a/src/interactive/mod.rs b/src/interactive/mod.rs index 2268008..84130f8 100644 --- a/src/interactive/mod.rs +++ b/src/interactive/mod.rs @@ -1,4 +1,6 @@ mod app; +mod traverse; mod widgets; pub use self::app::*; +pub use self::traverse::*; diff --git a/src/interactive/traverse.rs b/src/interactive/traverse.rs new file mode 100644 index 0000000..ecc0098 --- /dev/null +++ b/src/interactive/traverse.rs @@ -0,0 +1,169 @@ +use crate::WalkOptions; +use failure::Error; +use petgraph::graph::NodeIndex; +use petgraph::{Directed, Direction, Graph}; +use std::{ffi::OsString, path::PathBuf, time::Duration, time::Instant}; + +pub type TreeIndexType = u32; +pub type TreeIndex = NodeIndex<TreeIndexType>; +pub type Tree = Graph<EntryData, (), Directed, TreeIndexType>; + +#[derive(Eq, PartialEq, Debug, Default)] +pub struct EntryData { + pub name: OsString, + /// The entry's size in bytes. If it's a directory, the size is the aggregated file size of all children + pub size: u64, + /// If set, the item meta-data could not be obtained + pub metadata_io_error: bool, +} + +const REFRESH_RATE: Duration = Duration::from_millis(100); + +/// The result of the previous filesystem traversal +#[derive(Default, Debug)] +pub struct Traversal { + /// A tree representing the entire filestem traversal + pub tree: Tree, + /// The top-level node of the tree. + pub root_index: TreeIndex, + /// Amount of files or directories we have seen during the filesystem traversal + pub entries_traversed: u64, + /// Total amount of IO errors encountered when traversing the filesystem + pub io_errors: u64, +} + +impl Traversal { + pub fn from_walk( + options: WalkOptions, + input: Vec<PathBuf>, + mut update: impl FnMut(&Traversal) -> Result<(), Error>, + ) -> Result<Traversal, Error> { + fn set_size_or_panic(tree: &mut Tree, node_idx: TreeIndex, current_size_at_depth: u64) { + tree.node_weight_mut(node_idx) + .expect("node for parent index we just retrieved") + .size = current_size_at_depth; + } + 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<u64>) -> u64 { + v.pop().expect("sizes per level to be in sync with graph") + } + + let mut t = { + let mut tree = Tree::new(); + let root_index = tree.add_node(EntryData::default()); + Traversal { + tree, + root_index, + ..Default::default() + } + }; + + 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 = 0; + let mut previous_depth = 0; + + let mut last_checked = Instant::now(); + + const INITIAL_CHECK_INTERVAL: usize = 500; + let mut check_instant_every = INITIAL_CHECK_INTERVAL; + let mut last_seen_eid; + + for path in input.into_iter() { + last_seen_eid = 0; + for (eid, entry) in options + .iter_from_path(path.as_ref()) + .into_iter() + .enumerate() + { + t.entries_traversed += 1; + let mut data = EntryData::default(); + match entry { + Ok(entry) => { + data.name = entry.file_name; + let file_size = match entry.metadata { + Some(Ok(ref m)) if !m.is_dir() => m.len(), + Some(Ok(_)) => 0, + Some(Err(_)) => { + t.io_errors += 1; + data.metadata_io_error = true; + 0 + } + None => unreachable!( + "we ask for metadata, so we at least have Some(Err(..))). Issue in jwalk?" + ), + }; + + 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; + parent_node_idx = previous_node_idx; + } + (n, p) if n < p => { + for _ in n..p { + set_size_or_panic( + &mut t.tree, + parent_node_idx, + current_size_at_depth, + ); + current_size_at_depth += + pop_or_panic(&mut sizes_per_depth_level); + parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx); + } + current_size_at_depth += file_size; + set_size_or_panic( + &mut t.tree, + parent_node_idx, + current_size_at_depth, + ); + } + _ => { + current_size_at_depth += file_size; + } + }; + + data.size = file_size; + let entry_index = t.tree.add_node(data); + + t.tree.add_edge(parent_node_idx, entry_index, ()); + previous_node_idx = entry_index; + previous_depth = entry.depth; + } + Err(_) => t.io_errors += 1, + } + + if eid != 0 + && eid % check_instant_every == 0 + && last_checked.elapsed() >= REFRESH_RATE + { + let now = Instant::now(); + let elapsed = (now - last_checked).as_millis() as f64; + check_instant_every = (INITIAL_CHECK_INTERVAL as f64 + * ((eid - last_seen_eid) as f64 / INITIAL_CHECK_INTERVAL as f64) + * (REFRESH_RATE.as_millis() as f64 / elapsed)) + as usize; + last_seen_eid = eid; + last_checked = now; + + update(&t)?; + } + } + } + + sizes_per_depth_level.push(current_size_at_depth); + current_size_at_depth = 0; + 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); + parent_node_idx = parent_or_panic(&mut t.tree, parent_node_idx); + } + set_size_or_panic(&mut t.tree, t.root_index, current_size_at_depth); + + Ok(t) + } +} |