summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Thiel <sthiel@thoughtworks.com>2019-06-03 09:21:22 +0530
committerSebastian Thiel <sthiel@thoughtworks.com>2019-06-03 09:21:22 +0530
commit7eb857467c6d2603129edbaea636ef0d118fa064 (patch)
tree8fad44d65c091fadd2903a2d3c6f4ba8a9bd6bd2
parentb919c501a249dcf626e390d496faf6d31a9e71ac (diff)
bytes formatting for interactive + footer
-rw-r--r--src/aggregate.rs2
-rw-r--r--src/common.rs83
-rw-r--r--src/interactive/app.rs33
-rw-r--r--src/interactive/widgets.rs70
-rw-r--r--src/main.rs4
-rw-r--r--tests/interactive.rs4
6 files changed, 138 insertions, 58 deletions
diff --git a/src/aggregate.rs b/src/aggregate.rs
index e6f52f6..6b616e8 100644
--- a/src/aggregate.rs
+++ b/src/aggregate.rs
@@ -113,7 +113,7 @@ fn write_path<C: fmt::Display>(
writeln!(
out,
"{byte_color}{:>10}{byte_color_reset}\t{path_color}{}{path_color_reset}{}",
- options.format_bytes(num_bytes),
+ options.byte_format.display(num_bytes),
path.as_ref().display(),
if num_errors == 0 {
Cow::Borrowed("")
diff --git a/src/common.rs b/src/common.rs
index 0ed32f4..86e5ac4 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -3,6 +3,7 @@ use std::fmt;
use std::path::Path;
/// Specifies a way to format bytes
+#[derive(Clone, Copy)]
pub enum ByteFormat {
/// metric format, based on 1000.
Metric,
@@ -12,8 +13,54 @@ pub enum ByteFormat {
Bytes,
}
+impl ByteFormat {
+ pub fn display(&self, bytes: u64) -> ByteFormatDisplay {
+ ByteFormatDisplay {
+ format: *self,
+ bytes,
+ }
+ }
+}
+
+pub struct ByteFormatDisplay {
+ format: ByteFormat,
+ bytes: u64,
+}
+
+impl fmt::Display for ByteFormatDisplay {
+ fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+ use byte_unit::Byte;
+ use ByteFormat::*;
+
+ let binary = match self.format {
+ Bytes => return write!(f, "{} b", self.bytes),
+ Binary => true,
+ Metric => false,
+ };
+ let b = Byte::from_bytes(self.bytes as u128)
+ .get_appropriate_unit(binary)
+ .format(2);
+ let mut splits = b.split(' ');
+ match (splits.next(), splits.next()) {
+ (Some(bytes), Some(unit)) => write!(
+ f,
+ "{:>8} {:>unit_width$}",
+ bytes,
+ unit,
+ unit_width = match self.format {
+ Binary => 3,
+ Metric => 2,
+ _ => 2,
+ }
+ ),
+ _ => f.write_str(&b),
+ }
+ }
+}
+
/// Identify the kind of sorting to apply during filesystem iteration
-pub enum Sorting {
+#[derive(Clone)]
+pub enum TraversalSorting {
None,
AlphabeticalByFileName,
}
@@ -51,49 +98,23 @@ where
}
/// Configures a filesystem walk, including output and formatting options.
+#[derive(Clone)]
pub struct WalkOptions {
/// The amount of threads to use. Refer to [`WalkDir::num_threads()`](https://docs.rs/jwalk/0.4.0/jwalk/struct.WalkDir.html#method.num_threads)
/// for more information.
pub threads: usize,
pub byte_format: ByteFormat,
pub color: Color,
- pub sorting: Sorting,
+ pub sorting: TraversalSorting,
}
impl WalkOptions {
- pub(crate) fn format_bytes(&self, b: u64) -> String {
- use byte_unit::Byte;
- use ByteFormat::*;
- let binary = match self.byte_format {
- Bytes => return format!("{} b", b),
- Binary => true,
- Metric => false,
- };
- let b = Byte::from_bytes(b as u128)
- .get_appropriate_unit(binary)
- .format(2);
- let mut splits = b.split(' ');
- match (splits.next(), splits.next()) {
- (Some(bytes), Some(unit)) => format!(
- "{:>8} {:>unit_width$}",
- bytes,
- unit,
- unit_width = match self.byte_format {
- Binary => 3,
- Metric => 2,
- _ => 2,
- }
- ),
- _ => b,
- }
- }
-
pub(crate) fn iter_from_path(&self, path: &Path) -> WalkDir {
WalkDir::new(path)
.preload_metadata(true)
.sort(match self.sorting {
- Sorting::None => false,
- Sorting::AlphabeticalByFileName => true,
+ TraversalSorting::None => false,
+ TraversalSorting::AlphabeticalByFileName => true,
})
.skip_hidden(false)
.num_threads(self.threads)
diff --git a/src/interactive/app.rs b/src/interactive/app.rs
index f36ab66..0d3cfc8 100644
--- a/src/interactive/app.rs
+++ b/src/interactive/app.rs
@@ -1,4 +1,4 @@
-use crate::{WalkOptions, WalkResult};
+use crate::{ByteFormat, WalkOptions, WalkResult};
use failure::Error;
use petgraph::{prelude::NodeIndex, Directed, Direction, Graph};
use std::{
@@ -37,18 +37,26 @@ pub struct Traversal {
pub io_errors: u64,
}
+/// Options to configure how we display things
+#[derive(Clone, Copy)]
+pub struct DisplayOptions {
+ pub byte_format: ByteFormat,
+}
+
+impl From<WalkOptions> for DisplayOptions {
+ fn from(WalkOptions { byte_format, .. }: WalkOptions) -> Self {
+ DisplayOptions { byte_format }
+ }
+}
+
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,
- parent_node_idx: TreeIndex,
- current_size_at_depth: u64,
- ) {
- tree.node_weight_mut(parent_node_idx)
+ 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;
}
@@ -199,7 +207,7 @@ impl TerminalApp {
for key in keys.filter_map(Result::ok) {
match key {
- Ctrl('c') | Char('\n') => break,
+ Ctrl('c') | Char('\n') | Char('q') => break,
_ => dbg!(&key),
};
}
@@ -216,11 +224,16 @@ impl TerminalApp {
where
B: Backend,
{
+ let display_options: DisplayOptions = options.clone().into();
Ok(TerminalApp {
- traversal: Traversal::from_walk(options, input, |traversal| {
+ traversal: Traversal::from_walk(options, input, move |traversal| {
terminal.draw(|mut f| {
let full_screen = f.size();
- super::widgets::InitWindow { traversal }.render(&mut f, full_screen)
+ super::widgets::InitWindow {
+ traversal,
+ display: display_options,
+ }
+ .render(&mut f, full_screen)
})?;
Ok(())
})?,
diff --git a/src/interactive/widgets.rs b/src/interactive/widgets.rs
index c1cf0b9..915044c 100644
--- a/src/interactive/widgets.rs
+++ b/src/interactive/widgets.rs
@@ -1,5 +1,6 @@
-use super::{Traversal, Tree, TreeIndex};
-use petgraph::Direction;
+use super::{DisplayOptions, Traversal, Tree, TreeIndex};
+use tui::layout::{Constraint, Direction, Layout};
+use tui::style::{Color, Style};
use tui::{
buffer::Buffer,
layout::{Corner, Rect},
@@ -9,36 +10,81 @@ use tui::{
pub struct Entries<'a> {
pub tree: &'a Tree,
pub root: TreeIndex,
+ pub display: DisplayOptions,
}
pub struct InitWindow<'a> {
pub traversal: &'a Traversal,
+ pub display: DisplayOptions,
+}
+
+fn get_size_or_panic(tree: &Tree, node_idx: TreeIndex) -> u64 {
+ tree.node_weight(node_idx)
+ .expect("node should always be retrievable with valid index")
+ .size
}
impl<'a> Widget for InitWindow<'a> {
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
- let Self {
- traversal: Traversal {
- tree, root_index, ..
- },
- } = self;
+ let regions = Layout::default()
+ .direction(Direction::Vertical)
+ .constraints([Constraint::Max(256), Constraint::Length(1)].as_ref())
+ .split(area);
+ let (entries, footer) = (regions[0], regions[1]);
Entries {
- tree: tree,
- root: *root_index,
+ tree: &self.traversal.tree,
+ root: self.traversal.root_index,
+ display: self.display,
}
- .draw(area, buf);
+ .draw(entries, buf);
+
+ let bg_color = Color::White;
+ let text_color = Color::Black;
+ let margin = 1;
+ self.background(footer, buf, bg_color);
+ buf.set_stringn(
+ footer.x + margin,
+ footer.y,
+ format!(
+ "Total disk usage: {}",
+ format!(
+ "{}",
+ self.display.byte_format.display(get_size_or_panic(
+ &self.traversal.tree,
+ self.traversal.root_index
+ ))
+ )
+ .trim()
+ ),
+ (footer.width - margin) as usize,
+ Style {
+ fg: text_color,
+ bg: bg_color,
+ ..Default::default()
+ },
+ )
}
}
impl<'a> Widget for Entries<'a> {
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
- let Self { tree, root } = self;
+ use petgraph::Direction;
+ let Self {
+ tree,
+ root,
+ display,
+ } = self;
List::new(
tree.neighbors_directed(*root, Direction::Outgoing)
.filter_map(|w| {
tree.node_weight(w).map(|w| {
Text::Raw(
- format!("{} | ----- | {}", w.size, w.name.to_string_lossy()).into(),
+ format!(
+ "{} | ----- | {}",
+ display.byte_format.display(w.size),
+ w.name.to_string_lossy()
+ )
+ .into(),
)
})
}),
diff --git a/src/main.rs b/src/main.rs
index 7eddb14..0ff205f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -4,7 +4,7 @@ extern crate structopt;
use structopt::StructOpt;
-use dua::{ByteFormat, Color, Sorting};
+use dua::{ByteFormat, Color, TraversalSorting};
use failure::{Error, ResultExt};
use failure_tools::ok_or_exit;
use std::{fs, io, io::Write, path::PathBuf, process};
@@ -28,7 +28,7 @@ fn run() -> Result<(), Error> {
} else {
Color::None
},
- sorting: Sorting::None,
+ sorting: TraversalSorting::None,
};
let res = match opt.command {
Some(Interactive { input }) => {
diff --git a/tests/interactive.rs b/tests/interactive.rs
index 14684e4..159f05e 100644
--- a/tests/interactive.rs
+++ b/tests/interactive.rs
@@ -1,6 +1,6 @@
mod app {
use dua::interactive::{EntryData, TerminalApp, Tree, TreeIndexType};
- use dua::{ByteFormat, Color, Sorting, WalkOptions};
+ use dua::{ByteFormat, Color, TraversalSorting, WalkOptions};
use failure::Error;
use petgraph::prelude::NodeIndex;
use pretty_assertions::assert_eq;
@@ -51,7 +51,7 @@ mod app {
threads: 1,
byte_format: ByteFormat::Metric,
color: Color::None,
- sorting: Sorting::AlphabeticalByFileName,
+ sorting: TraversalSorting::AlphabeticalByFileName,
},
vec![input],
)?;