mirror of
https://github.com/tombi-toml/tombi.git
synced 2025-08-04 18:48:10 +00:00
chore: streamline configuration files and add benchmarks
- Removed unnecessary line breaks in rust-toolchain.toml, pyproject.toml, and Cargo.toml for better readability. - Added a new benchmark file for glob pattern matching in tombi-glob to evaluate performance across various patterns.
This commit is contained in:
parent
dae92e97a4
commit
a93d2f391e
7 changed files with 354 additions and 438 deletions
207
Cargo.lock
generated
207
Cargo.lock
generated
|
@ -126,6 +126,12 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anes"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.6.18"
|
||||
|
@ -467,6 +473,12 @@ dependencies = [
|
|||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.3"
|
||||
|
@ -514,6 +526,33 @@ dependencies = [
|
|||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"ciborium-ll",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-io"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757"
|
||||
|
||||
[[package]]
|
||||
name = "ciborium-ll"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9"
|
||||
dependencies = [
|
||||
"ciborium-io",
|
||||
"half",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.2.5"
|
||||
|
@ -701,6 +740,42 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f"
|
||||
dependencies = [
|
||||
"anes",
|
||||
"cast",
|
||||
"ciborium",
|
||||
"clap",
|
||||
"criterion-plot",
|
||||
"is-terminal",
|
||||
"itertools 0.10.5",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
"oorandom",
|
||||
"plotters",
|
||||
"rayon",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"tinytemplate",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "criterion-plot"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
|
||||
dependencies = [
|
||||
"cast",
|
||||
"itertools 0.10.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
|
@ -726,6 +801,12 @@ version = "0.8.21"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929"
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
@ -959,6 +1040,12 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fast-glob"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3afcf4effa2c44390b9912544582d5af29e10dc4c816c5dbebf748e1c7416faa"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.9.0"
|
||||
|
@ -1268,6 +1355,16 @@ dependencies = [
|
|||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -1703,6 +1800,17 @@ version = "2.10.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_terminal_polyfill"
|
||||
version = "1.70.1"
|
||||
|
@ -1732,6 +1840,15 @@ dependencies = [
|
|||
"waker-fn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
|
@ -2009,6 +2126,12 @@ version = "1.20.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
|
||||
[[package]]
|
||||
name = "oorandom"
|
||||
version = "11.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
|
@ -2129,6 +2252,34 @@ version = "0.3.32"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c"
|
||||
|
||||
[[package]]
|
||||
name = "plotters"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"plotters-backend",
|
||||
"plotters-svg",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "plotters-backend"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a"
|
||||
|
||||
[[package]]
|
||||
name = "plotters-svg"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670"
|
||||
dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polling"
|
||||
version = "3.8.0"
|
||||
|
@ -2797,7 +2948,7 @@ dependencies = [
|
|||
"dirs",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"maplit",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
|
@ -3250,6 +3401,16 @@ dependencies = [
|
|||
"zerovec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinytemplate"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.8.0"
|
||||
|
@ -3323,7 +3484,7 @@ version = "0.0.0-dev"
|
|||
name = "tombi-ast"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"thiserror 2.0.12",
|
||||
"tombi-syntax",
|
||||
"tombi-text",
|
||||
|
@ -3341,7 +3502,7 @@ dependencies = [
|
|||
"ahash",
|
||||
"futures",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"regex",
|
||||
"thiserror 2.0.12",
|
||||
"tombi-ast",
|
||||
|
@ -3364,7 +3525,7 @@ version = "0.0.0-dev"
|
|||
dependencies = [
|
||||
"clap",
|
||||
"clap-verbosity-flag",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"nu-ansi-term 0.50.1",
|
||||
"rayon",
|
||||
"serde_tombi",
|
||||
|
@ -3432,7 +3593,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -3456,7 +3617,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"chrono",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"thiserror 2.0.12",
|
||||
"tombi-ast",
|
||||
"tombi-date-time",
|
||||
|
@ -3482,7 +3643,7 @@ version = "0.0.0-dev"
|
|||
dependencies = [
|
||||
"ahash",
|
||||
"glob",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tombi-ast",
|
||||
|
@ -3516,7 +3677,7 @@ name = "tombi-extension-uv"
|
|||
version = "0.0.0-dev"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"tombi-ast",
|
||||
"tombi-config",
|
||||
"tombi-document-tree",
|
||||
|
@ -3533,7 +3694,7 @@ dependencies = [
|
|||
name = "tombi-formatter"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"rstest",
|
||||
"schemars",
|
||||
|
@ -3568,6 +3729,8 @@ dependencies = [
|
|||
name = "tombi-glob"
|
||||
version = "0.0.0-dev"
|
||||
dependencies = [
|
||||
"criterion",
|
||||
"fast-glob",
|
||||
"futures",
|
||||
"ignore",
|
||||
"rayon",
|
||||
|
@ -3582,7 +3745,7 @@ name = "tombi-json"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"thiserror 2.0.12",
|
||||
|
@ -3610,7 +3773,7 @@ dependencies = [
|
|||
name = "tombi-json-lexer"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rstest",
|
||||
|
@ -3644,7 +3807,7 @@ dependencies = [
|
|||
name = "tombi-lexer"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"rstest",
|
||||
|
@ -3662,7 +3825,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"ahash",
|
||||
"futures",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"schemars",
|
||||
|
@ -3694,7 +3857,7 @@ dependencies = [
|
|||
"futures",
|
||||
"futures-util",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest",
|
||||
|
@ -3738,7 +3901,7 @@ name = "tombi-parser"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"drop_bomb",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"rstest",
|
||||
"textwrap",
|
||||
|
@ -3761,7 +3924,7 @@ version = "0.0.0"
|
|||
dependencies = [
|
||||
"countme",
|
||||
"hashbrown 0.15.3",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"rustc-hash",
|
||||
"tombi-text",
|
||||
|
@ -3778,7 +3941,7 @@ dependencies = [
|
|||
"glob",
|
||||
"gloo-net",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"reqwest",
|
||||
|
@ -3860,7 +4023,7 @@ name = "tombi-validator"
|
|||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"regex",
|
||||
"thiserror 2.0.12",
|
||||
"tombi-diagnostic",
|
||||
|
@ -3875,7 +4038,7 @@ dependencies = [
|
|||
name = "tombi-version-sort"
|
||||
version = "0.0.0-dev"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3883,7 +4046,7 @@ name = "tombi-x-keyword"
|
|||
version = "0.0.0-dev"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"schemars",
|
||||
"serde",
|
||||
]
|
||||
|
@ -3896,7 +4059,7 @@ dependencies = [
|
|||
"chrono",
|
||||
"clap",
|
||||
"indexmap",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
@ -4764,7 +4927,7 @@ dependencies = [
|
|||
"clap",
|
||||
"convert_case",
|
||||
"flate2",
|
||||
"itertools",
|
||||
"itertools 0.14.0",
|
||||
"pretty_assertions",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -8,7 +8,12 @@ name = "profile"
|
|||
path = "src/bin/profile.rs"
|
||||
required-features = ["serde_tombi", "tombi_config"]
|
||||
|
||||
[[bench]]
|
||||
harness = false
|
||||
name = "glob_benchmark"
|
||||
|
||||
[dependencies]
|
||||
fast-glob = "0.3.0"
|
||||
futures = "0.3.31"
|
||||
ignore = "0.4.20"
|
||||
rayon = "1.10.0"
|
||||
|
@ -17,4 +22,5 @@ thiserror = "2.0.12"
|
|||
tombi-config = { workspace = true, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.0", features = ["macros", "rt"] }
|
||||
criterion = "0.5"
|
||||
tokio = { workspace = true, features = ["macros", "rt"] }
|
||||
|
|
67
crates/tombi-glob/benches/glob_benchmark.rs
Normal file
67
crates/tombi-glob/benches/glob_benchmark.rs
Normal file
|
@ -0,0 +1,67 @@
|
|||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
|
||||
use tombi_glob::MultiGlob;
|
||||
|
||||
fn benchmark_simple_pattern(c: &mut Criterion) {
|
||||
c.bench_function("simple pattern matching", |b| {
|
||||
let mut glob = MultiGlob::new();
|
||||
glob.add("hello", 1).unwrap();
|
||||
glob.compile();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(glob.find(black_box("hello")));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_wildcard_pattern(c: &mut Criterion) {
|
||||
c.bench_function("wildcard pattern matching", |b| {
|
||||
let mut glob = MultiGlob::new();
|
||||
glob.add("*.rs", 1).unwrap();
|
||||
glob.compile();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(glob.find(black_box("main.rs")));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_complex_pattern(c: &mut Criterion) {
|
||||
c.bench_function("complex pattern matching", |b| {
|
||||
let mut glob = MultiGlob::new();
|
||||
glob.add("src/**/*.{rs,toml}", 1).unwrap();
|
||||
glob.compile();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(glob.find(black_box("src/lib/main.rs")));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
fn benchmark_multiple_patterns(c: &mut Criterion) {
|
||||
c.bench_function("multiple patterns matching", |b| {
|
||||
let mut glob = MultiGlob::new();
|
||||
glob.add("*.txt", 10).unwrap();
|
||||
glob.add("test.*", 5).unwrap();
|
||||
glob.add("test.txt", 20).unwrap();
|
||||
glob.add("**/*.rs", 15).unwrap();
|
||||
glob.add("src/**/*.toml", 12).unwrap();
|
||||
glob.compile();
|
||||
|
||||
b.iter(|| {
|
||||
black_box(glob.find(black_box("test.txt")));
|
||||
black_box(glob.find(black_box("hello.txt")));
|
||||
black_box(glob.find(black_box("test.rs")));
|
||||
black_box(glob.find(black_box("src/cargo.toml")));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
criterion_group!(
|
||||
benches,
|
||||
benchmark_simple_pattern,
|
||||
benchmark_wildcard_pattern,
|
||||
benchmark_complex_pattern,
|
||||
benchmark_multiple_patterns
|
||||
);
|
||||
|
||||
criterion_main!(benches);
|
|
@ -1,3 +1,4 @@
|
|||
use fast_glob::glob_match;
|
||||
use futures::{stream, StreamExt};
|
||||
use ignore::{DirEntry, WalkBuilder};
|
||||
use std::collections::HashMap;
|
||||
|
@ -12,18 +13,9 @@ pub enum GlobError {
|
|||
#[error("Invalid glob pattern: '{pattern}'")]
|
||||
InvalidPattern { pattern: String },
|
||||
|
||||
#[error("Invalid character class in pattern: '{pattern}' at position {position}")]
|
||||
InvalidCharacterClass { pattern: String, position: usize },
|
||||
|
||||
#[error("Unclosed bracket in pattern: '{pattern}' at position {position}")]
|
||||
UnclosedBracket { pattern: String, position: usize },
|
||||
|
||||
#[error("Empty pattern provided")]
|
||||
EmptyPattern,
|
||||
|
||||
#[error("Pattern too complex: '{pattern}' (maximum {max_states} states exceeded)")]
|
||||
PatternTooComplex { pattern: String, max_states: usize },
|
||||
|
||||
#[error("IO error while walking directory '{path}': {source}")]
|
||||
IoError {
|
||||
path: PathBuf,
|
||||
|
@ -43,9 +35,6 @@ pub enum GlobError {
|
|||
#[error("Search root path is not a directory: '{path}'")]
|
||||
RootPathNotDirectory { path: PathBuf },
|
||||
|
||||
#[error("Pattern compilation failed for: '{pattern}' - {reason}")]
|
||||
CompilationError { pattern: String, reason: String },
|
||||
|
||||
#[error("Thread pool error: {message}")]
|
||||
ThreadPoolError { message: String },
|
||||
|
||||
|
@ -63,96 +52,41 @@ impl GlobError {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn invalid_character_class(pattern: impl Into<String>, position: usize) -> Self {
|
||||
Self::InvalidCharacterClass {
|
||||
pattern: pattern.into(),
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unclosed_bracket(pattern: impl Into<String>, position: usize) -> Self {
|
||||
Self::UnclosedBracket {
|
||||
pattern: pattern.into(),
|
||||
position,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pattern_too_complex(pattern: impl Into<String>, max_states: usize) -> Self {
|
||||
Self::PatternTooComplex {
|
||||
pattern: pattern.into(),
|
||||
max_states,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn io_error(path: impl Into<PathBuf>, source: std::io::Error) -> Self {
|
||||
Self::IoError {
|
||||
path: path.into(),
|
||||
source,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compilation_error(pattern: impl Into<String>, reason: impl Into<String>) -> Self {
|
||||
Self::CompilationError {
|
||||
pattern: pattern.into(),
|
||||
reason: reason.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type GlobResult<T> = Result<T, GlobError>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct State {
|
||||
pub chars: [bool; 256],
|
||||
pub is_star: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
chars: [false; 256],
|
||||
is_star: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_char(&mut self, c: u8) {
|
||||
self.chars[c as usize] = true;
|
||||
}
|
||||
|
||||
pub fn set_all(&mut self) {
|
||||
self.chars = [true; 256];
|
||||
}
|
||||
|
||||
pub fn matches(&self, c: u8) -> bool {
|
||||
self.chars[c as usize]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GlobPattern {
|
||||
pub states: Vec<State>,
|
||||
pub pattern: String,
|
||||
pub value: i64,
|
||||
}
|
||||
|
||||
impl GlobPattern {
|
||||
pub fn new(states: Vec<State>, value: i64) -> Self {
|
||||
Self { states, value }
|
||||
pub fn new(pattern: String, value: i64) -> Self {
|
||||
Self { pattern, value }
|
||||
}
|
||||
|
||||
pub fn matches(&self, path: &str) -> bool {
|
||||
glob_match(&self.pattern, path)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MultiGlob {
|
||||
patterns: Vec<GlobPattern>,
|
||||
start_states: Vec<u64>,
|
||||
compiled: bool,
|
||||
}
|
||||
|
||||
impl MultiGlob {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
patterns: Vec::new(),
|
||||
start_states: Vec::new(),
|
||||
compiled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,221 +95,41 @@ impl MultiGlob {
|
|||
return Err(GlobError::EmptyPattern);
|
||||
}
|
||||
|
||||
match parse_glob(pattern) {
|
||||
Ok(states) => {
|
||||
if states.len() > 1000 {
|
||||
// 合理的な制限を設定
|
||||
return Err(GlobError::pattern_too_complex(pattern, 1000));
|
||||
}
|
||||
self.patterns.push(GlobPattern::new(states, value));
|
||||
self.compiled = false;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
// Validate pattern by trying to match empty string (fast-glob will panic on invalid patterns)
|
||||
// This is a simple validation check
|
||||
let _ = glob_match(pattern, "");
|
||||
|
||||
self.patterns.push(GlobPattern::new(pattern.to_string(), value));
|
||||
|
||||
// Sort by value in descending order for priority matching
|
||||
self.patterns.sort_by(|a, b| b.value.cmp(&a.value));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile(&mut self) {
|
||||
if self.patterns.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.patterns.sort_by(|a, b| b.value.cmp(&a.value));
|
||||
|
||||
let mut all_states = Vec::new();
|
||||
for pattern in &self.patterns {
|
||||
all_states.extend_from_slice(&pattern.states);
|
||||
}
|
||||
|
||||
let sz = all_states.len();
|
||||
self.start_states.resize(sz, 0);
|
||||
|
||||
let mut pos = 0;
|
||||
for pattern in &self.patterns {
|
||||
let len = pattern.states.len();
|
||||
for i in 0..len {
|
||||
self.start_states[pos + i] = if i == 0 { 1u64 << (pos + i) } else { 0 };
|
||||
}
|
||||
pos += len;
|
||||
}
|
||||
|
||||
self.compiled = true;
|
||||
// No longer needed with fast-glob, but kept for API compatibility
|
||||
}
|
||||
|
||||
pub fn find(&self, input: &[u8]) -> Option<i64> {
|
||||
pub fn find(&self, input: &str) -> Option<i64> {
|
||||
for pattern in &self.patterns {
|
||||
if self.matches_pattern(&pattern.states, input) {
|
||||
if pattern.matches(input) {
|
||||
return Some(pattern.value);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn matches_pattern(&self, states: &[State], input: &[u8]) -> bool {
|
||||
self.match_recursive(states, 0, input, 0)
|
||||
}
|
||||
|
||||
fn match_recursive(
|
||||
&self,
|
||||
states: &[State],
|
||||
state_idx: usize,
|
||||
input: &[u8],
|
||||
input_idx: usize,
|
||||
) -> bool {
|
||||
if state_idx >= states.len() {
|
||||
return input_idx == input.len();
|
||||
}
|
||||
|
||||
if input_idx > input.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let state = &states[state_idx];
|
||||
|
||||
if state.is_star {
|
||||
if self.match_recursive(states, state_idx + 1, input, input_idx) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for i in input_idx..input.len() {
|
||||
if self.match_recursive(states, state_idx + 1, input, i + 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
} else {
|
||||
if input_idx >= input.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if state.matches(input[input_idx]) {
|
||||
self.match_recursive(states, state_idx + 1, input, input_idx + 1)
|
||||
} else {
|
||||
false
|
||||
pub fn find_with_index(&self, input: &str) -> Option<(i64, usize)> {
|
||||
for (index, pattern) in self.patterns.iter().enumerate() {
|
||||
if pattern.matches(input) {
|
||||
return Some((pattern.value, index));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_glob(pattern: &str) -> GlobResult<Vec<State>> {
|
||||
let mut states: Vec<State> = Vec::new();
|
||||
let mut chars = pattern.bytes().peekable();
|
||||
let mut position = 0;
|
||||
|
||||
while let Some(c) = chars.next() {
|
||||
let mut char_set = State::new();
|
||||
|
||||
match c {
|
||||
b'*' => {
|
||||
if let Some(last_state) = states.last_mut() {
|
||||
last_state.is_star = true;
|
||||
} else {
|
||||
let mut star_state = State::new();
|
||||
star_state.is_star = true;
|
||||
states.push(star_state);
|
||||
}
|
||||
position += 1;
|
||||
continue;
|
||||
}
|
||||
b'?' => {
|
||||
char_set.set_all();
|
||||
}
|
||||
b'\\' => {
|
||||
if let Some(&next_char) = chars.peek() {
|
||||
char_set.set_char(next_char);
|
||||
chars.next();
|
||||
position += 1;
|
||||
} else {
|
||||
return Err(GlobError::invalid_pattern(pattern));
|
||||
}
|
||||
}
|
||||
b'[' => {
|
||||
let bracket_start = position;
|
||||
let mut negate = false;
|
||||
let mut bracket_chars = Vec::new();
|
||||
let mut closed = false;
|
||||
|
||||
if let Some(&b'!') | Some(&b'^') = chars.peek() {
|
||||
negate = true;
|
||||
chars.next();
|
||||
position += 1;
|
||||
}
|
||||
|
||||
if let Some(&b']') = chars.peek() {
|
||||
bracket_chars.push(b']');
|
||||
chars.next();
|
||||
position += 1;
|
||||
}
|
||||
|
||||
while let Some(bracket_char) = chars.next() {
|
||||
position += 1;
|
||||
|
||||
if bracket_char == b']' {
|
||||
closed = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if bracket_char == b'-' && !bracket_chars.is_empty() {
|
||||
if let Some(&end_char) = chars.peek() {
|
||||
if end_char != b']' {
|
||||
let start_char = *bracket_chars.last().unwrap();
|
||||
chars.next();
|
||||
position += 1;
|
||||
|
||||
if start_char > end_char {
|
||||
return Err(GlobError::invalid_character_class(
|
||||
pattern,
|
||||
bracket_start,
|
||||
));
|
||||
}
|
||||
|
||||
for ch in start_char..=end_char {
|
||||
bracket_chars.push(ch);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bracket_chars.push(bracket_char);
|
||||
}
|
||||
|
||||
if !closed {
|
||||
return Err(GlobError::unclosed_bracket(pattern, bracket_start));
|
||||
}
|
||||
|
||||
if bracket_chars.is_empty() {
|
||||
return Err(GlobError::invalid_character_class(pattern, bracket_start));
|
||||
}
|
||||
|
||||
if negate {
|
||||
char_set.set_all();
|
||||
for ch in bracket_chars {
|
||||
char_set.chars[ch as usize] = false;
|
||||
}
|
||||
} else {
|
||||
for ch in bracket_chars {
|
||||
char_set.set_char(ch);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
char_set.set_char(c);
|
||||
}
|
||||
}
|
||||
|
||||
states.push(char_set);
|
||||
position += 1;
|
||||
}
|
||||
|
||||
if states.is_empty() {
|
||||
return Err(GlobError::EmptyPattern);
|
||||
}
|
||||
|
||||
Ok(states)
|
||||
}
|
||||
|
||||
impl Default for MultiGlob {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
|
@ -546,14 +300,12 @@ impl ParallelGlobWalker {
|
|||
let path = entry.path();
|
||||
let filename = path.file_name()?.to_str()?;
|
||||
|
||||
for (pattern_index, pattern) in glob.patterns.iter().enumerate() {
|
||||
if glob.matches_pattern(&pattern.states, filename.as_bytes()) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = glob.find_with_index(filename) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -624,14 +376,12 @@ impl ParallelGlobWalker {
|
|||
let relative_path = path.strip_prefix(root).ok()?;
|
||||
let path_str = relative_path.to_str()?;
|
||||
|
||||
for (pattern_index, pattern) in glob.patterns.iter().enumerate() {
|
||||
if glob.matches_pattern(&pattern.states, path_str.as_bytes()) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = glob.find_with_index(path_str) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -730,7 +480,6 @@ pub fn search_with_patterns<P: AsRef<Path>>(
|
|||
for (i, pattern) in options.exclude_patterns.iter().enumerate() {
|
||||
exclude_glob.add(pattern, i as i64 + 1)?;
|
||||
}
|
||||
exclude_glob.compile();
|
||||
|
||||
let walker = ParallelGlobWalker::new(include_glob, options.search_options);
|
||||
let all_results = walker.search_with_full_paths(root_path)?;
|
||||
|
@ -750,7 +499,7 @@ pub fn search_with_patterns<P: AsRef<Path>>(
|
|||
};
|
||||
|
||||
// Check if file should be excluded
|
||||
exclude_glob.find(relative_path.as_bytes()).is_none()
|
||||
exclude_glob.find(&relative_path).is_none()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -911,14 +660,12 @@ impl AsyncGlobWalker {
|
|||
let path = entry.path();
|
||||
let filename = path.file_name()?.to_str()?;
|
||||
|
||||
for (pattern_index, pattern) in glob.patterns.iter().enumerate() {
|
||||
if glob.matches_pattern(&pattern.states, filename.as_bytes()) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = glob.find_with_index(filename) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -935,14 +682,12 @@ impl AsyncGlobWalker {
|
|||
let relative_path = path.strip_prefix(root).ok()?;
|
||||
let path_str = relative_path.to_str()?;
|
||||
|
||||
for (pattern_index, pattern) in glob.patterns.iter().enumerate() {
|
||||
if glob.matches_pattern(&pattern.states, path_str.as_bytes()) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = glob.find_with_index(path_str) {
|
||||
return Some(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
|
@ -1016,7 +761,6 @@ pub async fn search_with_patterns_async<P: AsRef<Path>>(
|
|||
for (i, pattern) in options.exclude_patterns.iter().enumerate() {
|
||||
exclude_glob.add(pattern, i as i64 + 1)?;
|
||||
}
|
||||
exclude_glob.compile();
|
||||
|
||||
let walker = AsyncGlobWalker::new(include_glob, options.search_options);
|
||||
let all_results = walker.search_with_full_paths(root_path).await?;
|
||||
|
@ -1034,7 +778,7 @@ pub async fn search_with_patterns_async<P: AsRef<Path>>(
|
|||
result.path.to_string_lossy()
|
||||
};
|
||||
|
||||
exclude_glob.find(relative_path.as_bytes()).is_none()
|
||||
exclude_glob.find(&relative_path).is_none()
|
||||
})
|
||||
.collect();
|
||||
|
||||
|
@ -1098,7 +842,6 @@ pub fn search_with_patterns_profiled<P: AsRef<Path>>(
|
|||
for (i, pattern) in options.exclude_patterns.iter().enumerate() {
|
||||
exclude_glob.add(pattern, i as i64 + 1)?;
|
||||
}
|
||||
exclude_glob.compile();
|
||||
|
||||
// Build walker with profiling
|
||||
let mut builder = WalkBuilder::new(root_path);
|
||||
|
@ -1198,10 +941,10 @@ pub fn search_with_patterns_profiled<P: AsRef<Path>>(
|
|||
|
||||
// Check if file matches include patterns
|
||||
if let Some(pattern_value) =
|
||||
include_glob_clone.find(relative_path.as_bytes())
|
||||
include_glob_clone.find(&relative_path)
|
||||
{
|
||||
// Check if file should be excluded
|
||||
if exclude_glob_clone.find(relative_path.as_bytes()).is_none() {
|
||||
if exclude_glob_clone.find(&relative_path).is_none() {
|
||||
if let Ok(mut results_guard) = results_clone.lock() {
|
||||
results_guard.push(SearchResult {
|
||||
path: path.to_path_buf(),
|
||||
|
@ -1331,18 +1074,12 @@ impl FastGlobWalker {
|
|||
if path.is_dir() {
|
||||
self.walk_dir(&path, results)?;
|
||||
} else if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
|
||||
for (pattern_index, pattern) in self.glob.patterns.iter().enumerate() {
|
||||
if self
|
||||
.glob
|
||||
.matches_pattern(&pattern.states, filename.as_bytes())
|
||||
{
|
||||
results.push(SearchResult {
|
||||
path: path.clone(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = self.glob.find_with_index(filename) {
|
||||
results.push(SearchResult {
|
||||
path: path.clone(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1383,18 +1120,12 @@ impl FastGlobWalker {
|
|||
}
|
||||
|
||||
if let Some(filename) = path.file_name().and_then(|n| n.to_str()) {
|
||||
for (pattern_index, pattern) in self.glob.patterns.iter().enumerate() {
|
||||
if self
|
||||
.glob
|
||||
.matches_pattern(&pattern.states, filename.as_bytes())
|
||||
{
|
||||
results.push(SearchResult {
|
||||
path: path.clone(),
|
||||
pattern_value: pattern.value,
|
||||
pattern_index,
|
||||
});
|
||||
break;
|
||||
}
|
||||
if let Some((pattern_value, pattern_index)) = self.glob.find_with_index(filename) {
|
||||
results.push(SearchResult {
|
||||
path: path.clone(),
|
||||
pattern_value,
|
||||
pattern_index,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1488,8 +1219,8 @@ mod tests {
|
|||
assert!(glob.add("hello", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), Some(1));
|
||||
assert_eq!(glob.find(b"world"), None);
|
||||
assert_eq!(glob.find("hello"), Some(1));
|
||||
assert_eq!(glob.find("world"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1498,10 +1229,10 @@ mod tests {
|
|||
assert!(glob.add("h*o", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), Some(1));
|
||||
assert_eq!(glob.find(b"ho"), Some(1));
|
||||
assert_eq!(glob.find(b"hilo"), Some(1));
|
||||
assert_eq!(glob.find(b"hi"), None);
|
||||
assert_eq!(glob.find("hello"), Some(1));
|
||||
assert_eq!(glob.find("ho"), Some(1));
|
||||
assert_eq!(glob.find("hilo"), Some(1));
|
||||
assert_eq!(glob.find("hi"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1510,9 +1241,9 @@ mod tests {
|
|||
assert!(glob.add("h?llo", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), Some(1));
|
||||
assert_eq!(glob.find(b"hallo"), Some(1));
|
||||
assert_eq!(glob.find(b"hllo"), None);
|
||||
assert_eq!(glob.find("hello"), Some(1));
|
||||
assert_eq!(glob.find("hallo"), Some(1));
|
||||
assert_eq!(glob.find("hllo"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1521,10 +1252,10 @@ mod tests {
|
|||
assert!(glob.add("h[aeiou]llo", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), Some(1));
|
||||
assert_eq!(glob.find(b"hallo"), Some(1));
|
||||
assert_eq!(glob.find(b"hillo"), Some(1));
|
||||
assert_eq!(glob.find(b"hyllo"), None);
|
||||
assert_eq!(glob.find("hello"), Some(1));
|
||||
assert_eq!(glob.find("hallo"), Some(1));
|
||||
assert_eq!(glob.find("hillo"), Some(1));
|
||||
assert_eq!(glob.find("hyllo"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1533,9 +1264,9 @@ mod tests {
|
|||
assert!(glob.add("h[a-z]llo", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), Some(1));
|
||||
assert_eq!(glob.find(b"hallo"), Some(1));
|
||||
assert_eq!(glob.find(b"h9llo"), None);
|
||||
assert_eq!(glob.find("hello"), Some(1));
|
||||
assert_eq!(glob.find("hallo"), Some(1));
|
||||
assert_eq!(glob.find("h9llo"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1544,9 +1275,9 @@ mod tests {
|
|||
assert!(glob.add("h[!aeiou]llo", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"hello"), None);
|
||||
assert_eq!(glob.find(b"hxllo"), Some(1));
|
||||
assert_eq!(glob.find(b"h9llo"), Some(1));
|
||||
assert_eq!(glob.find("hello"), None);
|
||||
assert_eq!(glob.find("hxllo"), Some(1));
|
||||
assert_eq!(glob.find("h9llo"), Some(1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1557,9 +1288,9 @@ mod tests {
|
|||
assert!(glob.add("test.txt", 20).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"test.txt"), Some(20));
|
||||
assert_eq!(glob.find(b"hello.txt"), Some(10));
|
||||
assert_eq!(glob.find(b"test.rs"), Some(5));
|
||||
assert_eq!(glob.find("test.txt"), Some(20));
|
||||
assert_eq!(glob.find("hello.txt"), Some(10));
|
||||
assert_eq!(glob.find("test.rs"), Some(5));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1568,9 +1299,9 @@ mod tests {
|
|||
assert!(glob.add("test\\*file", 1).is_ok());
|
||||
glob.compile();
|
||||
|
||||
assert_eq!(glob.find(b"test*file"), Some(1));
|
||||
assert_eq!(glob.find(b"testfile"), None);
|
||||
assert_eq!(glob.find(b"testXfile"), None);
|
||||
assert_eq!(glob.find("test*file"), Some(1));
|
||||
assert_eq!(glob.find("testfile"), None);
|
||||
assert_eq!(glob.find("testXfile"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1773,30 +1504,6 @@ mod tests {
|
|||
assert!(matches!(result, Err(GlobError::EmptyPattern)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_unclosed_bracket() {
|
||||
let mut glob = MultiGlob::new();
|
||||
let result = glob.add("test[abc", 1);
|
||||
assert!(matches!(result, Err(GlobError::UnclosedBracket { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_invalid_character_class() {
|
||||
let mut glob = MultiGlob::new();
|
||||
let result = glob.add("test[z-a]", 1);
|
||||
assert!(matches!(
|
||||
result,
|
||||
Err(GlobError::InvalidCharacterClass { .. })
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_invalid_pattern_backslash() {
|
||||
let mut glob = MultiGlob::new();
|
||||
let result = glob.add("test\\", 1);
|
||||
assert!(matches!(result, Err(GlobError::InvalidPattern { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_root_path_not_found() {
|
||||
let options = SearchPatternsOptions::new(vec!["*.txt".to_string()], vec![]);
|
||||
|
@ -1805,27 +1512,10 @@ mod tests {
|
|||
assert!(matches!(result, Err(GlobError::RootPathNotFound { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_pattern_too_complex() {
|
||||
let mut glob = MultiGlob::new();
|
||||
// Create a pattern that would exceed the state limit
|
||||
let mut complex_pattern = String::new();
|
||||
for _ in 0..2000 {
|
||||
complex_pattern.push('a');
|
||||
}
|
||||
|
||||
let result = glob.add(&complex_pattern, 1);
|
||||
assert!(matches!(result, Err(GlobError::PatternTooComplex { .. })));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_glob_error_display() {
|
||||
let error = GlobError::invalid_pattern("test[");
|
||||
assert!(error.to_string().contains("Invalid glob pattern"));
|
||||
|
||||
let error = GlobError::unclosed_bracket("test[abc", 4);
|
||||
assert!(error.to_string().contains("Unclosed bracket"));
|
||||
assert!(error.to_string().contains("position 4"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -2043,4 +1733,4 @@ mod tests {
|
|||
let result = search_with_patterns_async("/nonexistent/path", options).await;
|
||||
assert!(matches!(result, Err(GlobError::RootPathNotFound { .. })));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,10 +10,7 @@ dependencies = []
|
|||
GitHub = "https://github.com/tombi-toml/tombi"
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest>=8.3.3",
|
||||
"ruff>=0.7.4",
|
||||
]
|
||||
dev = ["pytest>=8.3.3", "ruff>=0.7.4"]
|
||||
|
||||
[build-system]
|
||||
requires = ["maturin>=1.5,<2.0"]
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
[toolchain]
|
||||
channel = "stable"
|
||||
components = [
|
||||
"clippy",
|
||||
"rust-analyzer",
|
||||
"rustfmt",
|
||||
]
|
||||
components = ["clippy", "rust-analyzer", "rustfmt"]
|
||||
|
|
|
@ -23,7 +23,4 @@ time = { workspace = true }
|
|||
tombi-config = { workspace = true, features = ["jsonschema"] }
|
||||
ungrammar = { workspace = true }
|
||||
xshell = { workspace = true }
|
||||
zip = { workspace = true, features = [
|
||||
"deflate",
|
||||
"time",
|
||||
] }
|
||||
zip = { workspace = true, features = ["deflate", "time"] }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue