From 32a57ab5fbc14e726ec2b9a8969c5958ba6834ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20=C5=A0tancl?= Date: Sat, 13 Sep 2025 22:21:48 +0200 Subject: [PATCH] perf: store glob exclude patterns instead of traversing glob(...) Previously add_excludes_from_gitignore() would use glob() and recursively traverse the generated paths to add them to excludes. Now we store excludes as an enum - Path or Glob - and use the glob crate's `Pattern.matches_path()` as needed, instead of the preemptive traversal. --- src/main.rs | 10 +++++----- src/scan.rs | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main.rs b/src/main.rs index 8fea53d..5344371 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,7 @@ use std::path::PathBuf; use clap::{Parser, ArgAction}; use crate::entries::Entry; use crate::render::render_entries; -use crate::scan::{Stats, scan_dir, scan_todo_file, scan_readme_file}; +use crate::scan::{Stats, scan_dir, scan_todo_file, scan_readme_file, Exclude}; pub mod scan; pub mod render; @@ -43,7 +43,7 @@ fn main() { let root_dir: PathBuf = std::env::current_dir().unwrap(); let mut paths: Vec = vec![]; - let mut excludes: Vec = vec![]; + let mut excludes: Vec = vec![]; let mut entries: Vec = vec![]; let mut stats = Stats::new(args.verbose); @@ -68,7 +68,7 @@ fn main() { if path.exists() { if let Ok(realpath) = canonicalize(path) { - excludes.push(realpath); + excludes.push(Exclude::Path(realpath)); } } } @@ -80,13 +80,13 @@ fn main() { readme_path.push(&args.readme); if todos_path.exists() { - excludes.push(todos_path.clone()); + excludes.push(Exclude::Path(todos_path.clone())); scan_todo_file(&todos_path, &mut entries).unwrap(); } if readme_path.exists() { - excludes.push(readme_path.clone()); + excludes.push(Exclude::Path(readme_path.clone())); scan_readme_file(&readme_path, &mut entries).unwrap(); } diff --git a/src/scan.rs b/src/scan.rs index 6d6e8fb..0280771 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -1,12 +1,27 @@ use std::io; use std::fs::{self, canonicalize}; use std::path::{Path, PathBuf}; -use glob::glob; +use glob::{Pattern}; const PRIORITY_CHARS: [char; 10] = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; use crate::entries::{Entry, EntryData, Location}; +#[derive(Debug, PartialEq, Eq)] +pub enum Exclude { + Path(PathBuf), + Glob(Pattern), +} + +impl Exclude { + pub fn matches(&self, path: &Path) -> bool { + match self { + Exclude::Path(p) => p == path, + Exclude::Glob(g) => g.matches_path(path), + } + } +} + pub struct Stats { visited_folder_count: usize, visited_file_count: usize, @@ -100,7 +115,7 @@ fn clean_line<'a>(line: &'a str, delimiter_word: &str) -> &'a str { .trim() } -pub fn add_excludes_from_gitignore(base_dir: &PathBuf, excludes: &mut Vec) { +pub fn add_excludes_from_gitignore(base_dir: &PathBuf, excludes: &mut Vec) { let mut gitignore = base_dir.clone(); gitignore.push(".gitignore"); @@ -115,7 +130,7 @@ pub fn add_excludes_from_gitignore(base_dir: &PathBuf, excludes: &mut Vec) -> io::Result<()> { Ok(()) } -pub fn scan_dir(dir: &Path, entries: &mut Vec, excludes: &mut Vec, stats: &mut Stats) -> io::Result<()> { +pub fn scan_dir(dir: &Path, entries: &mut Vec, excludes: &mut Vec, stats: &mut Stats) -> io::Result<()> { let mut gitignore = dir.to_path_buf().clone(); gitignore.push(".gitignore"); @@ -234,11 +251,11 @@ pub fn scan_dir(dir: &Path, entries: &mut Vec, excludes: &mut Vec, excludes: &mut Vec