diff --git a/src/main.rs b/src/main.rs index f860e61..90c027b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -119,6 +119,7 @@ fn is_executable(metadata: &Metadata) -> bool { struct Config { use_color: bool, show_hidden: bool, + show_only_dirs: bool, max_level: usize, include_glob: Option, } @@ -138,6 +139,7 @@ impl TryFrom<&Args> for Config { Ok(Config { use_color: value.color_on || !value.color_off, show_hidden: value.show_all, + show_only_dirs: value.only_dirs, max_level: value.max_level, include_glob, }) @@ -178,6 +180,7 @@ impl<'a> TreePrinter<'a> { fn get_iterator(&self, path: &Path) -> filter::FilteredIterator { let config = pathiterator::FileIteratorConfig { include_glob: self.config.include_glob.clone(), + show_only_dirs: self.config.show_only_dirs, max_level: self.config.max_level, show_hidden: self.config.show_hidden, }; @@ -229,6 +232,7 @@ impl<'a> TreePrinter<'a> { } } +#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Parser)] struct Args { /// Show hidden files @@ -249,6 +253,9 @@ struct Args { /// Descend only directories deep #[clap(short = 'L', long = "level", default_value_t = usize::max_value())] max_level: usize, + /// List directories only + #[clap(short = 'd', default_value = "false")] + only_dirs: bool, } fn main() -> Result<(), Box> { @@ -264,11 +271,15 @@ fn main() -> Result<(), Box> { .map_err(|e| format!("Program failed with error: {e}"))? }; - writeln!( - &mut term, - "\n{} directories, {} files", - summary.num_folders, summary.num_files - ) + if args.only_dirs { + writeln!(&mut term, "\n{} directories", summary.num_folders) + } else { + writeln!( + &mut term, + "\n{} directories, {} files", + summary.num_folders, summary.num_files + ) + } .map_err(|e| format!("Failed to print summary: {e}"))?; Ok(()) diff --git a/src/pathiterator.rs b/src/pathiterator.rs index 3cc894f..40f1059 100644 --- a/src/pathiterator.rs +++ b/src/pathiterator.rs @@ -43,6 +43,7 @@ impl IteratorItem { #[derive(Debug)] pub struct FileIteratorConfig { pub show_hidden: bool, + pub show_only_dirs: bool, pub max_level: usize, pub include_glob: Option, } @@ -57,9 +58,18 @@ fn order_dir_entry(a: &DirEntry, b: &DirEntry) -> Ordering { b.file_name().cmp(&a.file_name()) } -fn get_sorted_dir_entries(path: &Path) -> io::Result> { +fn get_sorted_dir_entries(path: &Path, only_dirs: bool) -> io::Result> { let entries = fs::read_dir(path)?; - let mut dir_entries: Vec = entries.into_iter().collect::>>()?; + let mut dir_entries: Vec = entries + .into_iter() + .filter(|entry| { + entry.as_ref().is_ok_and(|entry| { + entry + .metadata() + .is_ok_and(|meta| !only_dirs || meta.is_dir()) + }) + }) + .collect::>>()?; dir_entries.sort_by(order_dir_entry); Ok(dir_entries) } @@ -79,29 +89,28 @@ impl FileIterator { } fn is_included(&self, name: &str, is_dir: bool) -> bool { - (!name.starts_with('.') && self.config.show_hidden) || is_dir || self.is_glob_included(name) + (self.config.show_hidden || !name.starts_with('.')) + && (is_dir || self.is_glob_included(name)) } fn push_dir(&mut self, item: &IteratorItem) { - let entries = get_sorted_dir_entries(&item.path).unwrap_or_else(|_| { - panic!( - "Couldn't retrieve files in directory: {}", - item.path.display() - ) - }); - - let mut entries: Vec = entries - .iter() - .map(|e| IteratorItem::new(&e.path(), item.level + 1, false)) - .filter(|item| self.is_included(&item.file_name, item.is_dir())) - .collect(); - - if let Some(item) = entries.first_mut() { - item.is_last = true; + let entries = get_sorted_dir_entries(&item.path, self.config.show_only_dirs) + .unwrap_or_else(|_| { + panic!( + "Couldn't retrieve files in directory: {}", + item.path.display() + ) + }); + + for entry in entries { + let item = IteratorItem::new(&entry.path(), item.level + 1, false); + if self.is_included(&item.file_name, item.is_dir()) { + self.queue.push_back(item); + } } - for item in entries { - self.queue.push_back(item); + if let Some(item) = self.queue.back_mut() { + item.is_last = true; } } }