diff --git a/src/dir_walker.rs b/src/dir_walker.rs index f40fc64..850a45c 100644 --- a/src/dir_walker.rs +++ b/src/dir_walker.rs @@ -34,7 +34,7 @@ pub fn walk_it(dirs: HashSet, walk_data: WalkData) -> (Vec, bool) let top_level_nodes: Vec<_> = dirs .into_iter() .filter_map(|d| { - let n = walk(d, &permissions_flag, &walk_data); + let n = walk(d, &permissions_flag, &walk_data, 0); match n { Some(n) => { let mut inodes: HashSet<(u64, u64)> = HashSet::new(); @@ -73,6 +73,7 @@ fn clean_inodes( size: x.size + new_children.iter().map(|c| c.size).sum::(), children: new_children, inode_device: x.inode_device, + depth: x.depth, }); } @@ -108,7 +109,12 @@ fn ignore_file(entry: &DirEntry, walk_data: &WalkData) -> bool { (is_dot_file && walk_data.ignore_hidden) || is_ignored_path } -fn walk(dir: PathBuf, permissions_flag: &AtomicBool, walk_data: &WalkData) -> Option { +fn walk( + dir: PathBuf, + permissions_flag: &AtomicBool, + walk_data: &WalkData, + depth: usize, +) -> Option { let mut children = vec![]; if let Ok(entries) = fs::read_dir(dir.clone()) { @@ -126,7 +132,7 @@ fn walk(dir: PathBuf, permissions_flag: &AtomicBool, walk_data: &WalkData) -> Op if !ignore_file(entry, walk_data) { if let Ok(data) = entry.file_type() { if data.is_dir() && !data.is_symlink() { - return walk(entry.path(), permissions_flag, walk_data); + return walk(entry.path(), permissions_flag, walk_data, depth + 1); } return build_node( entry.path(), @@ -137,6 +143,7 @@ fn walk(dir: PathBuf, permissions_flag: &AtomicBool, walk_data: &WalkData) -> Op data.is_symlink(), data.is_file(), walk_data.by_filecount, + depth, ); } } @@ -158,6 +165,7 @@ fn walk(dir: PathBuf, permissions_flag: &AtomicBool, walk_data: &WalkData) -> Op false, false, walk_data.by_filecount, + depth, ) } @@ -172,6 +180,7 @@ mod tests { size: 10, children: vec![], inode_device: Some((5, 6)), + depth: 0, } } diff --git a/src/filter.rs b/src/filter.rs index 70fc299..7c76447 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -5,18 +5,10 @@ use std::collections::HashMap; use std::collections::HashSet; use std::path::PathBuf; -pub fn get_by_depth(top_level_nodes: Vec, n: usize) -> Option { - if top_level_nodes.is_empty() { - // perhaps change this, bring back Error object? - return None; - } - let root = get_new_root(top_level_nodes); - Some(build_by_depth(&root, n - 1)) -} - pub fn get_biggest( top_level_nodes: Vec, n: usize, + depth: usize, using_a_filter: bool, ) -> Option { if top_level_nodes.is_empty() { @@ -30,14 +22,14 @@ pub fn get_biggest( let mut allowed_nodes = HashSet::new(); allowed_nodes.insert(&root.name); - heap = add_children(using_a_filter, &root, heap); + heap = add_children(using_a_filter, &root, depth, heap); for _ in number_top_level_nodes..n { let line = heap.pop(); match line { Some(line) => { allowed_nodes.insert(&line.name); - heap = add_children(using_a_filter, line, heap); + heap = add_children(using_a_filter, line, depth, heap); } None => break, } @@ -78,16 +70,19 @@ pub fn get_all_file_types(top_level_nodes: Vec, n: usize) -> Option( using_a_filter: bool, line: &'a Node, + depth: usize, mut heap: BinaryHeap<&'a Node>, ) -> BinaryHeap<&'a Node> { - if using_a_filter { - line.children.iter().for_each(|c| { - if c.name.is_file() || c.size > 0 { - heap.push(c) - } - }); - } else { - line.children.iter().for_each(|c| heap.push(c)); + if depth > line.depth { + if using_a_filter { + line.children.iter().for_each(|c| { + if c.name.is_file() || c.size > 0 { + heap.push(c) + } + }); + } else { + line.children.iter().for_each(|c| heap.push(c)); + } } heap } @@ -111,29 +106,6 @@ fn build_by_all_file_types(top_level_nodes: Vec, counter: &mut HashMap DisplayNode { - let new_children = { - if depth == 0 { - vec![] - } else { - let mut new_children: Vec<_> = node - .children - .iter() - .map(|c| build_by_depth(c, depth - 1)) - .collect(); - new_children.sort(); - new_children.reverse(); - new_children - } - }; - - DisplayNode { - name: node.name.clone(), - size: node.size, - children: new_children, - } -} - fn get_new_root(top_level_nodes: Vec) -> Node { if top_level_nodes.len() > 1 { let total_size = top_level_nodes.iter().map(|node| node.size).sum(); @@ -142,6 +114,7 @@ fn get_new_root(top_level_nodes: Vec) -> Node { size: total_size, children: top_level_nodes, inode_device: None, + depth: 0, } } else { top_level_nodes.into_iter().next().unwrap() diff --git a/src/main.rs b/src/main.rs index f88fb77..5be505c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,7 +10,7 @@ use self::display::draw_it; use clap::{crate_version, Arg}; use clap::{Command, Values}; use dir_walker::{walk_it, WalkData}; -use filter::{get_all_file_types, get_biggest, get_by_depth}; +use filter::{get_all_file_types, get_biggest}; use regex::Regex; use std::cmp::max; use std::path::PathBuf; @@ -99,9 +99,6 @@ fn get_regex_value(maybe_value: Option) -> Vec { } fn main() { - let default_height = get_height_of_terminal(); - let def_num_str = default_height.to_string(); - let options = Command::new("Dust") .about("Like du but more intuitive") .version(crate_version!()) @@ -112,7 +109,7 @@ fn main() { .long("depth") .help("Depth to show") .takes_value(true) - .conflicts_with("number_of_lines"), + .default_value(usize::MAX.to_string().as_ref()) ) .arg( Arg::new("number_of_lines") @@ -120,7 +117,6 @@ fn main() { .long("number-of-lines") .help("Number of lines of output to show. (Default is terminal_height - 10)") .takes_value(true) - .default_value(def_num_str.as_ref()), ) .arg( Arg::new("display_full_paths") @@ -188,7 +184,6 @@ fn main() { .multiple_occurrences(true) .conflicts_with("filter") .conflicts_with("types") - .conflicts_with("depth") .help("Exclude filepaths matching this regex. To ignore png files type: -v \"\\.png$\" "), ) .arg( @@ -199,7 +194,6 @@ fn main() { .number_of_values(1) .multiple_occurrences(true) .conflicts_with("types") - .conflicts_with("depth") .help("Only include filepaths matching this regex. For png files type: -e \"\\.png$\" "), ) .arg( @@ -236,26 +230,36 @@ fn main() { let filter_regexs = get_regex_value(options.values_of("filter")); let invert_filter_regexs = get_regex_value(options.values_of("invert_filter")); - let number_of_lines = match options.value_of_t("number_of_lines") { - Ok(v) => v, - Err(_) => { - eprintln!("Ignoring bad value for number_of_lines"); - default_height - } - }; - let terminal_width = match options.value_of_t("width") { Ok(v) => v, Err(_) => get_width_of_terminal(), }; - let depth = options.value_of("depth").and_then(|depth| { - depth - .parse::() - .map(|v| v + 1) - .map_err(|_| eprintln!("Ignoring bad value for depth")) - .ok() - }); + let depth = match options.value_of_t("depth") { + Ok(v) => v, + Err(_) => { + eprintln!("Ignoring bad value for depth"); + usize::MAX + } + }; + // If depth is set we set the default number_of_lines to be max + // instead of screen height + let default_height = if depth != usize::MAX { + usize::MAX + } else { + get_height_of_terminal() + }; + + let number_of_lines = match options.value_of("number_of_lines") { + Some(v) => match v.parse::() { + Ok(num_lines) => num_lines, + Err(_) => { + eprintln!("Ignoring bad value for number_of_lines"); + default_height + } + }, + None => default_height, + }; let no_colors = init_color(options.is_present("no_colors")); let use_apparent_size = options.is_present("display_apparent_size"); @@ -297,10 +301,10 @@ fn main() { let tree = { match (depth, summarize_file_types) { (_, true) => get_all_file_types(top_level_nodes, number_of_lines), - (Some(depth), _) => get_by_depth(top_level_nodes, depth), - (_, _) => get_biggest( + (depth, _) => get_biggest( top_level_nodes, number_of_lines, + depth, options.values_of("filter").is_some() || options.value_of("invert_filter").is_some(), ), diff --git a/src/node.rs b/src/node.rs index ee84ab0..3e1b3fe 100644 --- a/src/node.rs +++ b/src/node.rs @@ -12,6 +12,7 @@ pub struct Node { pub size: u64, pub children: Vec, pub inode_device: Option<(u64, u64)>, + pub depth: usize, } #[allow(clippy::too_many_arguments)] @@ -24,6 +25,7 @@ pub fn build_node( is_symlink: bool, is_file: bool, by_filecount: bool, + depth: usize, ) -> Option { match get_metadata(&dir, use_apparent_size) { Some(data) => { @@ -50,6 +52,7 @@ pub fn build_node( size, children, inode_device, + depth, }) } None => None,