diff --git a/src/display.rs b/src/display.rs index 4795c0b..e603667 100644 --- a/src/display.rs +++ b/src/display.rs @@ -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 { - 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, 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, - base_dirs: HashSet, - 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, + node: Node, is_biggest: bool, depth: Option, 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, - new_depth: Option, - 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, diff --git a/src/main.rs b/src/main.rs index 80b780d..d7948d5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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::::new(new_node); + parent_node.children.push(temp); } #[cfg(test)] diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 6263686..d4d89dc 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -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>, +} + pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet { let mut top_level_names: HashSet = 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) -> 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 }