mirror of
https://github.com/bootandy/dust.git
synced 2026-06-08 11:29:05 +03:00
Massive refactor
WIP Replace array of (string, int) pairs with tree of nodes. A tree of nodes more accurately represents the underlying file structure and hence is a better fit for the problem space. Regression: Reverse doesn't work in this commit. I suspect more methods can be simplifed and reduced.
This commit is contained in:
+27
-110
@@ -2,16 +2,13 @@ extern crate ansi_term;
|
||||
|
||||
use self::ansi_term::Colour::Fixed;
|
||||
use self::ansi_term::Style;
|
||||
use std::cmp::max;
|
||||
use std::collections::HashSet;
|
||||
use utils::{ensure_end_slash, strip_end_slash_including_root};
|
||||
use utils::Node;
|
||||
|
||||
static UNITS: [char; 4] = ['T', 'G', 'M', 'K'];
|
||||
|
||||
pub struct DisplayData {
|
||||
pub short_paths: bool,
|
||||
pub is_reversed: bool,
|
||||
pub to_display: Vec<(String, u64)>,
|
||||
}
|
||||
|
||||
impl DisplayData {
|
||||
@@ -63,128 +60,54 @@ impl DisplayData {
|
||||
num_siblings == max_siblings - 1
|
||||
}
|
||||
}
|
||||
|
||||
fn get_size(&self, node_to_print: &str) -> Option<u64> {
|
||||
for &(ref k, ref v) in self.to_display.iter() {
|
||||
if *k == *node_to_print {
|
||||
return Some(*v);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn count_siblings(&self, num_slashes: usize, ntp: &str) -> u64 {
|
||||
self.to_display.iter().fold(0, |a, b| {
|
||||
if b.0.starts_with(ntp) && b.0.as_str().matches('/').count() == num_slashes + 1 {
|
||||
a + 1
|
||||
} else {
|
||||
a
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn has_children(&self, new_depth: Option<u64>, ntp: &str, num_slashes: usize) -> bool {
|
||||
// this shouldn't be needed we should have already stripped
|
||||
if new_depth.is_none() || new_depth.unwrap() != 1 {
|
||||
for &(ref k2, _) in self.to_display.iter() {
|
||||
let ntp_with_slash = String::from(ntp.to_owned() + "/");
|
||||
if k2.starts_with(ntp_with_slash.as_str())
|
||||
&& k2.matches('/').count() == num_slashes + 1
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn draw_it(
|
||||
permissions: bool,
|
||||
depth: Option<u64>,
|
||||
base_dirs: HashSet<String>,
|
||||
display_data: &DisplayData,
|
||||
use_full_path: bool,
|
||||
is_reversed: bool,
|
||||
root_node: Node,
|
||||
) {
|
||||
if !permissions {
|
||||
eprintln!("Did not have permissions for all directories");
|
||||
}
|
||||
let first_tree_chars = display_data.get_first_chars();
|
||||
let mut found = HashSet::new();
|
||||
let display_data = DisplayData {
|
||||
short_paths: !use_full_path,
|
||||
is_reversed,
|
||||
};
|
||||
|
||||
for &(ref k, _) in display_data.to_display.iter() {
|
||||
if base_dirs.contains(k) {
|
||||
display_node(&k, &mut found, true, depth, first_tree_chars, display_data);
|
||||
}
|
||||
for c in root_node.children {
|
||||
let first_tree_chars = display_data.get_first_chars();
|
||||
display_node(*c, true, depth, first_tree_chars, &display_data)
|
||||
}
|
||||
}
|
||||
|
||||
fn display_node(
|
||||
node: &str,
|
||||
nodes_already_found: &mut HashSet<String>,
|
||||
node: Node,
|
||||
is_biggest: bool,
|
||||
depth: Option<u64>,
|
||||
indent: &str,
|
||||
display_data: &DisplayData,
|
||||
) {
|
||||
if nodes_already_found.contains(node) {
|
||||
return;
|
||||
}
|
||||
nodes_already_found.insert(node.to_string());
|
||||
|
||||
let new_depth = match depth {
|
||||
None => None,
|
||||
Some(0) => return,
|
||||
Some(d) => Some(d - 1),
|
||||
};
|
||||
let short = display_data.short_paths;
|
||||
print_this_node(&node, is_biggest, short, indent);
|
||||
|
||||
match display_data.get_size(node) {
|
||||
None => println!("Can not find path: {}", node),
|
||||
Some(size) => {
|
||||
let short_path = display_data.short_paths;
|
||||
// move this inside display_data?
|
||||
if !display_data.is_reversed {
|
||||
print_this_node(node, size, is_biggest, short_path, indent);
|
||||
}
|
||||
fan_out(node, nodes_already_found, new_depth, indent, display_data);
|
||||
if display_data.is_reversed {
|
||||
print_this_node(node, size, is_biggest, short_path, indent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut num_siblings = node.children.len() as u64;
|
||||
let max_sibling = num_siblings;
|
||||
let new_indent = clean_indentation_string(indent);
|
||||
|
||||
fn fan_out(
|
||||
node_to_print: &str,
|
||||
nodes_already_found: &mut HashSet<String>,
|
||||
new_depth: Option<u64>,
|
||||
indentation_str: &str,
|
||||
display_data: &DisplayData,
|
||||
) {
|
||||
let new_indent = clean_indentation_string(indentation_str);
|
||||
let num_slashes = strip_end_slash_including_root(node_to_print)
|
||||
.matches('/')
|
||||
.count();
|
||||
|
||||
let mut num_siblings = display_data.count_siblings(num_slashes, node_to_print);
|
||||
let max_siblings = num_siblings;
|
||||
|
||||
for &(ref k, _) in display_data.to_display.iter() {
|
||||
let temp = String::from(ensure_end_slash(node_to_print));
|
||||
if k.starts_with(temp.as_str()) && k.matches('/').count() == num_slashes + 1 {
|
||||
num_siblings -= 1;
|
||||
let has_children = display_data.has_children(new_depth, k, num_slashes + 1);
|
||||
let new_tree_chars =
|
||||
display_data.get_tree_chars(num_siblings, max_siblings, has_children);
|
||||
let biggest = display_data.biggest(num_siblings, max_siblings);
|
||||
display_node(
|
||||
k,
|
||||
nodes_already_found,
|
||||
biggest,
|
||||
new_depth,
|
||||
&*(new_indent.to_string() + new_tree_chars),
|
||||
display_data,
|
||||
);
|
||||
}
|
||||
for c in node.children {
|
||||
num_siblings -= 1;
|
||||
let chars = display_data.get_tree_chars(num_siblings, max_sibling, c.children.len() > 0);
|
||||
let is_biggest = display_data.biggest(num_siblings, max_sibling);
|
||||
let full_indent = (new_indent.clone() + chars);
|
||||
display_node(*c, is_biggest, new_depth, &*full_indent, display_data)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,18 +128,12 @@ fn clean_indentation_string(s: &str) -> String {
|
||||
is
|
||||
}
|
||||
|
||||
fn print_this_node(
|
||||
node_name: &str,
|
||||
size: u64,
|
||||
is_biggest: bool,
|
||||
short_paths: bool,
|
||||
indentation: &str,
|
||||
) {
|
||||
let pretty_size = format!("{:>5}", human_readable_number(size),);
|
||||
fn print_this_node(node: &Node, is_biggest: bool, short_paths: bool, indentation: &str) {
|
||||
let pretty_size = format!("{:>5}", human_readable_number(node.size),);
|
||||
println!(
|
||||
"{}",
|
||||
format_string(
|
||||
node_name,
|
||||
&*node.name,
|
||||
is_biggest,
|
||||
short_paths,
|
||||
pretty_size.as_ref(),
|
||||
@@ -224,7 +141,7 @@ fn print_this_node(
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// idea: squash these 2
|
||||
pub fn format_string(
|
||||
dir_name: &str,
|
||||
is_biggest: bool,
|
||||
|
||||
+38
-7
@@ -4,11 +4,10 @@ extern crate assert_cli;
|
||||
extern crate walkdir;
|
||||
|
||||
use self::display::draw_it;
|
||||
use self::display::DisplayData;
|
||||
use clap::{App, AppSettings, Arg};
|
||||
use utils::{
|
||||
compare_tuple_smallest_first, find_big_ones, get_dir_tree, simplify_dir_names, sort,
|
||||
trim_deep_ones,
|
||||
trim_deep_ones, Node,
|
||||
};
|
||||
|
||||
mod display;
|
||||
@@ -104,13 +103,45 @@ fn main() {
|
||||
if options.is_present("reverse") {
|
||||
biggest_ones.sort_by(compare_tuple_smallest_first);
|
||||
}
|
||||
let dd = DisplayData {
|
||||
short_paths: !use_full_path,
|
||||
is_reversed: options.is_present("reverse"),
|
||||
to_display: biggest_ones,
|
||||
let tree = build_tree(&biggest_ones);
|
||||
//println!("{:?}", tree);
|
||||
|
||||
draw_it(
|
||||
permissions,
|
||||
depth,
|
||||
use_full_path,
|
||||
options.is_present("reverse"),
|
||||
tree,
|
||||
);
|
||||
}
|
||||
|
||||
fn build_tree(biggest_ones: &Vec<(String, u64)>) -> Node {
|
||||
let mut top_parent = Node {
|
||||
name: "".to_string(),
|
||||
size: 0,
|
||||
children: vec![],
|
||||
};
|
||||
|
||||
draw_it(permissions, depth, simplified_dirs, &dd);
|
||||
// assume sorted order
|
||||
for b in biggest_ones.clone() {
|
||||
let n = Node {
|
||||
name: b.0,
|
||||
size: b.1,
|
||||
children: vec![],
|
||||
};
|
||||
recursively_build_tree(&mut top_parent, n)
|
||||
}
|
||||
top_parent
|
||||
}
|
||||
|
||||
fn recursively_build_tree(parent_node: &mut Node, new_node: Node) {
|
||||
for c in parent_node.children.iter_mut() {
|
||||
if new_node.name.starts_with(&c.name) {
|
||||
return recursively_build_tree(&mut *c, new_node);
|
||||
}
|
||||
}
|
||||
let temp = Box::<Node>::new(new_node);
|
||||
parent_node.children.push(temp);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
+11
-10
@@ -7,6 +7,13 @@ use walkdir::WalkDir;
|
||||
mod platform;
|
||||
use self::platform::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Node {
|
||||
pub name: String,
|
||||
pub size: u64,
|
||||
pub children: Vec<Box<Node>>,
|
||||
}
|
||||
|
||||
pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet<String> {
|
||||
let mut top_level_names: HashSet<String> = HashSet::new();
|
||||
|
||||
@@ -64,14 +71,6 @@ pub fn strip_end_slash(s: &str) -> String {
|
||||
new_name
|
||||
}
|
||||
|
||||
pub fn strip_end_slash_including_root(s: &str) -> String {
|
||||
let mut new_name = String::from(s);
|
||||
while new_name.ends_with('/') || new_name.ends_with("/.") {
|
||||
new_name.pop();
|
||||
}
|
||||
new_name
|
||||
}
|
||||
|
||||
fn examine_dir(
|
||||
top_dir: &str,
|
||||
apparent_size: bool,
|
||||
@@ -93,6 +92,7 @@ fn examine_dir(
|
||||
inodes.insert(inode_dev_pair);
|
||||
}
|
||||
}
|
||||
// This path and all its parent paths have their counter incremented
|
||||
let mut e_path = e.path().to_path_buf();
|
||||
loop {
|
||||
let path_name = e_path.to_string_lossy().to_string();
|
||||
@@ -112,7 +112,8 @@ fn examine_dir(
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn compare_tuple(a: &(String, u64), b: &(String, u64)) -> Ordering {
|
||||
|
||||
pub fn sort_by_size_first_name_second(a: &(String, u64), b: &(String, u64)) -> Ordering {
|
||||
let result = b.1.cmp(&a.1);
|
||||
if result == Ordering::Equal {
|
||||
a.0.cmp(&b.0)
|
||||
@@ -132,7 +133,7 @@ pub fn compare_tuple_smallest_first(a: &(String, u64), b: &(String, u64)) -> Ord
|
||||
|
||||
pub fn sort(data: HashMap<String, u64>) -> Vec<(String, u64)> {
|
||||
let mut new_l: Vec<(String, u64)> = data.iter().map(|(a, b)| (a.clone(), *b)).collect();
|
||||
new_l.sort_by(|a, b| compare_tuple(&a, &b));
|
||||
new_l.sort_by(|a, b| sort_by_size_first_name_second(&a, &b));
|
||||
new_l
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user