From dd75ec4aa783a32937087e8d188a554d0512fec6 Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Thu, 3 May 2018 00:02:23 +0100 Subject: [PATCH 1/3] wip: code to remove duplicate arguments Also handle case where an argument is a substring of another argument --- src/display.rs | 23 +++++++++++---- src/main.rs | 7 +++-- src/tests.rs | 10 ++++++- src/utils/mod.rs | 74 +++++++++++++++++++++++++++++++++++++++++++----- 4 files changed, 97 insertions(+), 17 deletions(-) diff --git a/src/display.rs b/src/display.rs index 49131c7..513f4fe 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,5 +1,6 @@ extern crate ansi_term; +use std::collections::HashSet; use self::ansi_term::Colour::Fixed; static UNITS: [char; 4] = ['T', 'G', 'M', 'K']; @@ -8,15 +9,18 @@ pub fn draw_it( permissions: bool, short_paths: bool, depth: Option, - base_dirs: Vec, + base_dirs: HashSet, to_display: Vec<(String, u64)>, ) -> () { if !permissions { eprintln!("Did not have permissions for all directories"); } + let mut found = HashSet::new(); - for f in base_dirs { - display_node(f, &to_display, true, short_paths, depth, "") + for &(ref k, _) in to_display.iter() { + if base_dirs.contains(k) { + display_node(&k, &mut found, &to_display, true, short_paths, depth, "") + } } } @@ -30,19 +34,25 @@ fn get_size(nodes: &Vec<(String, u64)>, node_to_print: &String) -> Option { } fn display_node>( - node_to_print: String, + node_to_print: &String, + found: &mut HashSet, to_display: &Vec<(String, u64)>, is_biggest: bool, short_paths: bool, depth: Option, indentation_str: S, ) { + if found.contains(node_to_print) { + return + } + found.insert(node_to_print.to_string()); + let new_depth = match depth { None => None, Some(0) => return, Some(d) => Some(d - 1), }; - match get_size(to_display, &node_to_print) { + match get_size(to_display, node_to_print) { None => println!("Can not find path: {}", node_to_print), Some(size) => { let mut is = indentation_str.into(); @@ -83,7 +93,8 @@ fn display_node>( } display_node( - k.to_string(), + k, + found, to_display, is_biggest, short_paths, diff --git a/src/main.rs b/src/main.rs index f64e707..966935e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ extern crate walkdir; use self::display::draw_it; use clap::{App, AppSettings, Arg}; -use utils::{find_big_ones, get_dir_tree, sort}; +use utils::{find_big_ones, get_dir_tree, sort, simplify_dir_names}; mod display; mod utils; @@ -46,7 +46,7 @@ fn main() { .arg(Arg::with_name("inputs").multiple(true)) .get_matches(); - let filenames = { + let target_dirs = { match options.values_of("inputs") { None => vec!["."], Some(r) => r.collect(), @@ -82,7 +82,8 @@ fn main() { let use_apparent_size = options.is_present("display_apparent_size"); let use_full_path = options.is_present("display_full_paths"); - let (permissions, nodes, top_level_names) = get_dir_tree(&filenames, use_apparent_size); + let simplified_dirs = simplify_dir_names(target_dirs); + let (permissions, nodes, top_level_names) = get_dir_tree(simplified_dirs, use_apparent_size); let sorted_data = sort(nodes); let biggest_ones = { if depth.is_none() { diff --git a/src/tests.rs b/src/tests.rs index fcc59d7..7e5e728 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -28,6 +28,15 @@ pub fn test_main_long_paths() { .unwrap(); } +#[test] +pub fn test_main_multi_arg() { + assert_cli::Assert::main_binary() + .with_args(&["src/test_dir/many/", "src/test_dir/", "src/test_dir"]) + .stdout() + .is(main_output(true)) + .unwrap(); +} + #[cfg(target_os = "macos")] fn main_output(short_paths: bool) -> String { format!( @@ -271,4 +280,3 @@ fn recursive_sym_link_output(dir: &str, link_name: &str) -> String { ) } -// TODO: add test for bad path diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 2af7d3d..f133e15 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -7,25 +7,49 @@ use walkdir::WalkDir; mod platform; use self::platform::*; +pub fn simplify_dir_names(filenames: Vec<&str>) -> HashSet { + let mut top_level_names: HashSet = HashSet::new(); + + for t in filenames { + let top_level_name = strip_end_slashes(t); + let mut can_add = true; + let mut to_remove: Vec = Vec::new(); + + for tt in top_level_names.iter() { + let temp = tt.to_string(); + if top_level_name.starts_with(&temp) { + can_add = false; + } else if tt.starts_with(&top_level_name) { + to_remove.push(temp); + } + } + for tr in to_remove { + top_level_names.remove(&tr); + } + if can_add { + top_level_names.insert(top_level_name); + } + } + + top_level_names +} + pub fn get_dir_tree( - filenames: &Vec<&str>, + top_level_names: HashSet, apparent_size: bool, -) -> (bool, HashMap, Vec) { +) -> (bool, HashMap, HashSet) { let mut permissions = 0; let mut inodes: HashSet<(u64, u64)> = HashSet::new(); let mut data: HashMap = HashMap::new(); - let mut top_level_names = Vec::new(); - for b in filenames { - let top_level_name = strip_end_slashes(b); + for b in top_level_names.iter() { examine_dir( - &top_level_name, + &b, apparent_size, &mut inodes, &mut data, &mut permissions, ); - top_level_names.push(top_level_name); } (permissions == 0, data, top_level_names) } @@ -100,3 +124,39 @@ pub fn find_big_ones<'a>(new_l: Vec<(String, u64)>, max_to_show: usize) -> Vec<( new_l } } + + +mod tests { + #[allow(unused_imports)] + use super::*; + + #[test] + fn test_simplify_dir() { + let mut correct = HashSet::new(); + correct.insert("a".to_string()); + assert!(simplify_dir_names(vec!["a"]) == correct); + } + + #[test] + fn test_simplify_dir_rm_subdir() { + let mut correct = HashSet::new(); + correct.insert("a/b".to_string()); + assert!(simplify_dir_names(vec!["a/b", "a/b/c", "a/b/d/f"]) == correct); + } + + #[test] + fn test_simplify_dir_duplicates() { + let mut correct = HashSet::new(); + correct.insert("a/b".to_string()); + correct.insert("c".to_string()); + assert!(simplify_dir_names(vec!["a/b", "a/b//", "c", "c/"]) == correct); + } + #[test] + fn test_simplify_dir_rm_subdir_and_not_substrings() { + let mut correct = HashSet::new(); + correct.insert("a/b".to_string()); + correct.insert("c/a/b".to_string()); + correct.insert("b".to_string()); + assert!(simplify_dir_names(vec!["a/b", "c/a/b/", "b"]) == correct); + } +} \ No newline at end of file From ce0e14bf00276cabb5af7ca1f0b082cb4c01e849 Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Wed, 9 May 2018 10:58:32 +0100 Subject: [PATCH 2/3] =?UTF-8?q?Tweak=20output=20-=20the=20root=20node=20no?= =?UTF-8?q?w=20has=20a:=20=E2=94=80=E2=94=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/display.rs | 3 ++- src/tests.rs | 54 +++++++++++++++++++++++++------------------------- 2 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/display.rs b/src/display.rs index 513f4fe..6966394 100644 --- a/src/display.rs +++ b/src/display.rs @@ -19,7 +19,7 @@ pub fn draw_it( for &(ref k, _) in to_display.iter() { if base_dirs.contains(k) { - display_node(&k, &mut found, &to_display, true, short_paths, depth, "") + display_node(&k, &mut found, &to_display, true, short_paths, depth, "─┬") } } } @@ -64,6 +64,7 @@ fn display_node>( is = is.replace("└──", " "); is = is.replace("├──", "│ "); is = is.replace("├─┬", "│ "); + is = is.replace("─┬", " "); let printable_node_slashes = node_to_print.matches('/').count(); diff --git a/src/tests.rs b/src/tests.rs index 7e5e728..648fa12 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -44,21 +44,21 @@ fn main_output(short_paths: bool) -> String { {} {} {}", - format_string("src/test_dir", true, short_paths, " 4.0K", ""), - format_string("src/test_dir/many", true, short_paths, " 4.0K", "└─┬",), + format_string("src/test_dir", true, short_paths, " 4.0K", "─┬"), + format_string("src/test_dir/many", true, short_paths, " 4.0K", " └─┬",), format_string( "src/test_dir/many/hello_file", true, short_paths, " 4.0K", - " ├──", + " ├──", ), format_string( "src/test_dir/many/a_file", false, short_paths, " 0B", - " └──", + " └──", ), ) } @@ -70,21 +70,21 @@ fn main_output(short_paths: bool) -> String { {} {} {}", - format_string("src/test_dir", true, short_paths, " 12K", ""), - format_string("src/test_dir/many", true, short_paths, " 8.0K", "└─┬",), + format_string("src/test_dir", true, short_paths, " 12K", "─┬"), + format_string("src/test_dir/many", true, short_paths, " 8.0K", " └─┬",), format_string( "src/test_dir/many/hello_file", true, short_paths, " 4.0K", - " ├──", + " ├──", ), format_string( "src/test_dir/many/a_file", false, short_paths, " 0B", - " └──", + " └──", ), ) } @@ -98,7 +98,7 @@ pub fn test_apparent_size() { true, true, " 6B", - " ├──", + " ├──", ), ); @@ -149,9 +149,9 @@ fn soft_sym_link_output(dir: &str, file_path: &str, link_name: &str) -> String { "{} {} {}", - format_string(dir, true, true, " 8.0K", ""), - format_string(file_path, true, true, " 4.0K", "├──",), - format_string(link_name, false, true, " 4.0K", "└──",), + format_string(dir, true, true, " 8.0K", "─┬"), + format_string(file_path, true, true, " 4.0K", " ├──",), + format_string(link_name, false, true, " 4.0K", " └──",), ) } @@ -161,9 +161,9 @@ fn soft_sym_link_output(dir: &str, file_path: &str, link_name: &str) -> String { "{} {} {}", - format_string(dir, true, true, " 8.0K", ""), - format_string(file_path, true, true, " 4.0K", "├──",), - format_string(link_name, false, true, " 0B", "└──",), + format_string(dir, true, true, " 8.0K", "─┬"), + format_string(file_path, true, true, " 4.0K", " ├──",), + format_string(link_name, false, true, " 0B", " └──",), ) } @@ -209,14 +209,14 @@ fn hard_link_output(dir_s: &str, file_path_s: &str, link_name_s: &str) -> (Strin let r = format!( "{} {}", - format_string(dir_s, true, true, " 4.0K", ""), - format_string(file_path_s, true, true, " 4.0K", "└──") + format_string(dir_s, true, true, " 4.0K", "─┬"), + format_string(file_path_s, true, true, " 4.0K", " └──") ); let r2 = format!( "{} {}", - format_string(dir_s, true, true, " 4.0K", ""), - format_string(link_name_s, true, true, " 4.0K", "└──") + format_string(dir_s, true, true, " 4.0K", "─┬"), + format_string(link_name_s, true, true, " 4.0K", " └──") ); (r, r2) } @@ -226,14 +226,14 @@ fn hard_link_output(dir_s: &str, file_path_s: &str, link_name_s: &str) -> (Strin let r = format!( "{} {}", - format_string(dir_s, true, true, " 8.0K", ""), - format_string(file_path_s, true, true, " 4.0K", "└──") + format_string(dir_s, true, true, " 8.0K", "─┬"), + format_string(file_path_s, true, true, " 4.0K", " └──") ); let r2 = format!( "{} {}", - format_string(dir_s, true, true, " 8.0K", ""), - format_string(link_name_s, true, true, " 4.0K", "└──") + format_string(dir_s, true, true, " 8.0K", "─┬"), + format_string(link_name_s, true, true, " 4.0K", " └──") ); (r, r2) } @@ -266,8 +266,8 @@ fn recursive_sym_link_output(dir: &str, link_name: &str) -> String { format!( "{} {}", - format_string(dir, true, true, " 4.0K", ""), - format_string(link_name, true, true, " 4.0K", "└──",), + format_string(dir, true, true, " 4.0K", "─┬"), + format_string(link_name, true, true, " 4.0K", " └──",), ) } #[cfg(target_os = "linux")] @@ -275,8 +275,8 @@ fn recursive_sym_link_output(dir: &str, link_name: &str) -> String { format!( "{} {}", - format_string(dir, true, true, " 4.0K", ""), - format_string(link_name, true, true, " 0B", "└──",), + format_string(dir, true, true, " 4.0K", "─┬"), + format_string(link_name, true, true, " 0B", " └──",), ) } From 51561994c588f74890f04ec056fdedb8ba4c6cc5 Mon Sep 17 00:00:00 2001 From: "andy.boot" Date: Wed, 9 May 2018 11:27:25 +0100 Subject: [PATCH 3/3] Break up display_node function slightly Also: run rustformat --- src/display.rs | 52 +++++++++++++++++++++++++++--------------------- src/main.rs | 2 +- src/utils/mod.rs | 11 ++-------- 3 files changed, 32 insertions(+), 33 deletions(-) diff --git a/src/display.rs b/src/display.rs index 6966394..ac9a6ab 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,7 +1,7 @@ extern crate ansi_term; -use std::collections::HashSet; use self::ansi_term::Colour::Fixed; +use std::collections::HashSet; static UNITS: [char; 4] = ['T', 'G', 'M', 'K']; @@ -43,7 +43,7 @@ fn display_node>( indentation_str: S, ) { if found.contains(node_to_print) { - return + return; } found.insert(node_to_print.to_string()); @@ -55,39 +55,25 @@ fn display_node>( match get_size(to_display, node_to_print) { None => println!("Can not find path: {}", node_to_print), Some(size) => { - let mut is = indentation_str.into(); + let is = indentation_str.into(); let ntp: &str = node_to_print.as_ref(); print_this_node(ntp, size, is_biggest, short_paths, is.as_ref()); + let new_indent_str = clean_indentation_string(is); - is = is.replace("└─┬", " "); - is = is.replace("└──", " "); - is = is.replace("├──", "│ "); - is = is.replace("├─┬", "│ "); - is = is.replace("─┬", " "); - - let printable_node_slashes = node_to_print.matches('/').count(); - - let mut num_siblings = to_display.iter().fold(0, |a, b| { - if b.0.starts_with(ntp) && b.0.matches('/').count() == printable_node_slashes + 1 { - a + 1 - } else { - a - } - }); + let num_slashes = node_to_print.matches('/').count(); + let mut num_siblings = count_siblings(to_display, num_slashes, ntp); let mut is_biggest = true; for &(ref k, _) in to_display.iter() { - if k.starts_with(ntp) && k.matches('/').count() == printable_node_slashes + 1 { + if k.starts_with(ntp) && k.matches('/').count() == num_slashes + 1 { num_siblings -= 1; let mut has_children = false; if new_depth.is_none() || new_depth.unwrap() != 1 { for &(ref k2, _) in to_display.iter() { let kk: &str = k.as_ref(); - if k2.starts_with(kk) - && k2.matches('/').count() == printable_node_slashes + 2 - { + if k2.starts_with(kk) && k2.matches('/').count() == num_slashes + 2 { has_children = true; } } @@ -100,7 +86,7 @@ fn display_node>( is_biggest, short_paths, new_depth, - is.to_string() + get_tree_chars(num_siblings, has_children), + new_indent_str.to_string() + get_tree_chars(num_siblings, has_children), ); is_biggest = false; } @@ -109,6 +95,26 @@ fn display_node>( } } +fn clean_indentation_string>(s: S) -> String { + let mut is = s.into(); + is = is.replace("└─┬", " "); + is = is.replace("└──", " "); + is = is.replace("├──", "│ "); + is = is.replace("├─┬", "│ "); + is = is.replace("─┬", " "); + is +} + +fn count_siblings(to_display: &Vec<(String, u64)>, num_slashes: usize, ntp: &str) -> u64 { + to_display.iter().fold(0, |a, b| { + if b.0.starts_with(ntp) && b.0.matches('/').count() == num_slashes + 1 { + a + 1 + } else { + a + } + }) +} + fn get_tree_chars(num_siblings: u64, has_children: bool) -> &'static str { if num_siblings == 0 { if has_children { diff --git a/src/main.rs b/src/main.rs index 966935e..61e613d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,7 +5,7 @@ extern crate walkdir; use self::display::draw_it; use clap::{App, AppSettings, Arg}; -use utils::{find_big_ones, get_dir_tree, sort, simplify_dir_names}; +use utils::{find_big_ones, get_dir_tree, simplify_dir_names, sort}; mod display; mod utils; diff --git a/src/utils/mod.rs b/src/utils/mod.rs index f133e15..a276b11 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -43,13 +43,7 @@ pub fn get_dir_tree( let mut data: HashMap = HashMap::new(); for b in top_level_names.iter() { - examine_dir( - &b, - apparent_size, - &mut inodes, - &mut data, - &mut permissions, - ); + examine_dir(&b, apparent_size, &mut inodes, &mut data, &mut permissions); } (permissions == 0, data, top_level_names) } @@ -125,7 +119,6 @@ pub fn find_big_ones<'a>(new_l: Vec<(String, u64)>, max_to_show: usize) -> Vec<( } } - mod tests { #[allow(unused_imports)] use super::*; @@ -159,4 +152,4 @@ mod tests { correct.insert("b".to_string()); assert!(simplify_dir_names(vec!["a/b", "c/a/b/", "b"]) == correct); } -} \ No newline at end of file +}