From 5182865cbe439518b10856626cec07ab4271c220 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 30 Jun 2026 14:31:48 +0100 Subject: [PATCH 1/4] Use `ignore` crate --- Cargo.lock | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/main.rs | 66 +++++++++++++++++++----------------------- 3 files changed, 114 insertions(+), 36 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 157a381..fb2d8d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,31 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "difflib" version = "0.4.0" @@ -107,6 +132,7 @@ version = "0.5.0" dependencies = [ "assert_cmd", "futures", + "ignore", "predicates", "prettyplease", "schemars", @@ -233,6 +259,19 @@ dependencies = [ "r-efi", ] +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -256,6 +295,22 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "ignore" +version = "0.4.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b915661dd01db3f05050265b2477bcc6527b3792388e2749b41623cc592be67d" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -445,6 +500,15 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schemars" version = "0.8.22" @@ -738,6 +802,25 @@ dependencies = [ "libc", ] +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + [[package]] name = "windows-link" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 35ee2b7..e8ca2af 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ serde_args = { version = "0.2.0" } serde_yaml_ng = { workspace = true } tokio = { version = "1.52.3", features = ["default", "rt", "rt-multi-thread", "macros"] } futures = { version = "0.3.32" } +ignore = { version = "0.4.26" } [build-dependencies] typify = { version = "0.7.0" } diff --git a/src/main.rs b/src/main.rs index 0a558ed..64b156b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,16 +6,14 @@ mod generated { mod rule; -use crate::generated::RulesConfigRules; +use crate::generated::{RulesConfigExcludeDirsItem, RulesConfigRules}; use crate::rule::{BUFFER_SIZE, Context, apply_rule, apply_rules}; use futures::stream::{self, StreamExt}; +use ignore::overrides::OverrideBuilder; use serde::Deserialize; -use std::collections::VecDeque; use std::env::current_dir; use std::fs::File; -use std::ops::Not; use std::path::PathBuf; -use std::str::FromStr; use std::sync::Arc; #[serde_args::generate(version)] @@ -26,6 +24,7 @@ struct Args { config_file: PathBuf, #[serde(alias = "d")] directory: Option, + use_gitignore: Option, } fn validate_directory(path: PathBuf) -> Result { @@ -38,41 +37,34 @@ fn validate_directory(path: PathBuf) -> Result { } } +fn get_files( + in_directory: &PathBuf, + ignoring_patterns: Vec, + use_gitignore: bool, +) -> Vec { + let mut override_builder = OverrideBuilder::new(in_directory); + ignoring_patterns.iter().for_each(|pattern| { + // TODO: Nicer error messages here + override_builder.add(&pattern.to_string()).unwrap(); + }); + + ignore::WalkBuilder::new(in_directory) + .standard_filters(false) + .git_ignore(use_gitignore) + .overrides(override_builder.build().unwrap()) + .build() + // TODO: Maybe report errors to user + .filter(|entry| entry.is_ok()) + .map(|entry| entry.unwrap().path().to_path_buf()) + .collect::>() +} + async fn find_files_in_directory_for_config( directory: &PathBuf, config: generated::RulesConfig, + use_gitignore: bool, ) -> Vec { - let mut all_files: Vec = Vec::new(); - let mut to_explore: VecDeque = directory - .read_dir() - .unwrap() - .map(|e| e.unwrap().path()) - .filter(|path| { - path.is_file() - || config - .exclude_dirs - .contains( - &generated::RulesConfigExcludeDirsItem::from_str( - path.strip_prefix(directory).unwrap().to_str().unwrap(), - ) - .unwrap(), - ) - .not() - }) - .collect(); - - while !to_explore.is_empty() { - let current_path = to_explore.pop_front().unwrap(); - if current_path.is_file() { - all_files.push(current_path); - } else { - let read_dir = current_path.read_dir().unwrap(); - read_dir.for_each(|entry| { - let path = entry.unwrap().path(); - to_explore.push_back(path); - }); - } - } + let all_files = get_files(directory, config.exclude_dirs.clone(), use_gitignore); let rules = Arc::new(&config.rules); @@ -120,7 +112,9 @@ async fn main() { .map(Result::unwrap) .unwrap_or(current_dir().unwrap()); - let matched_files = find_files_in_directory_for_config(&directory, config).await; + let matched_files = + find_files_in_directory_for_config(&directory, config, args.use_gitignore.unwrap_or(false)) + .await; let mut matched_files_relative = matched_files .iter() From f780f5255f5cc016b41912c83fff9a6c2fc0a8af Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 30 Jun 2026 14:33:05 +0100 Subject: [PATCH 2/4] Rename to ignore --- schema.yaml | 2 +- src/main.rs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/schema.yaml b/schema.yaml index c183c08..e449536 100644 --- a/schema.yaml +++ b/schema.yaml @@ -119,7 +119,7 @@ $defs: oneOf: - $ref: "#/$defs/RuleCombinator" - $ref: "#/$defs/Rule" - exclude_dirs: + ignore: type: array items: type: string diff --git a/src/main.rs b/src/main.rs index 64b156b..c457fbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,7 @@ mod generated { mod rule; -use crate::generated::{RulesConfigExcludeDirsItem, RulesConfigRules}; +use crate::generated::{RulesConfigIgnoreItem, RulesConfigRules}; use crate::rule::{BUFFER_SIZE, Context, apply_rule, apply_rules}; use futures::stream::{self, StreamExt}; use ignore::overrides::OverrideBuilder; @@ -39,7 +39,7 @@ fn validate_directory(path: PathBuf) -> Result { fn get_files( in_directory: &PathBuf, - ignoring_patterns: Vec, + ignoring_patterns: Vec, use_gitignore: bool, ) -> Vec { let mut override_builder = OverrideBuilder::new(in_directory); @@ -64,7 +64,7 @@ async fn find_files_in_directory_for_config( config: generated::RulesConfig, use_gitignore: bool, ) -> Vec { - let all_files = get_files(directory, config.exclude_dirs.clone(), use_gitignore); + let all_files = get_files(directory, config.ignore.clone(), use_gitignore); let rules = Arc::new(&config.rules); From 34ab7c80734a1d5b56726717fbf93a7d0803bf92 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 30 Jun 2026 14:34:43 +0100 Subject: [PATCH 3/4] Add changeset --- .changeset/changesets/neglectfully-rousing-rabbitfish.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/changesets/neglectfully-rousing-rabbitfish.md diff --git a/.changeset/changesets/neglectfully-rousing-rabbitfish.md b/.changeset/changesets/neglectfully-rousing-rabbitfish.md new file mode 100644 index 0000000..dfe676f --- /dev/null +++ b/.changeset/changesets/neglectfully-rousing-rabbitfish.md @@ -0,0 +1,5 @@ +--- +file-finder: major +--- + +Support proper glob matching in `ignore` field. Removed `excluded_dirs` field from config From 7407c9e1d6fe8084f2cdeb0e32d3fda22c06d2b8 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 30 Jun 2026 14:40:10 +0100 Subject: [PATCH 4/4] Fix --- src/main.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index c457fbf..40f37b6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -54,8 +54,17 @@ fn get_files( .overrides(override_builder.build().unwrap()) .build() // TODO: Maybe report errors to user - .filter(|entry| entry.is_ok()) - .map(|entry| entry.unwrap().path().to_path_buf()) + .filter_map(|entry| match entry { + Ok(entry) => { + let path = entry.path(); + if path.is_file() { + Some(path.to_path_buf()) + } else { + None + } + } + Err(_) => None, + }) .collect::>() }