The Globset Matching Engine
The globset crate turns glob patterns into compiled matchers and applies them to paths. Its purpose is to make repeated path filtering cheaper than interpreting each glob from scratch.
It supports both individual pattern matching and set matching. A GlobSet groups patterns by useful matching strategy, so one candidate path can be tested across all relevant strategies in one traversal.
Sources: crates/globset/src/glob.rs:76-81, crates/globset/src/glob.rs:133-138, crates/globset/src/lib.rs:309-312, crates/globset/src/lib.rs:350-360
Core concepts
A compiled glob
A compiled Glob retains the original pattern, generated regex text, options, and parsed tokens. Glob::new delegates construction to GlobBuilder::build, which parses the pattern and converts its tokens into regex text. The regex is anchored by to_regex_with, which adds ^ and $.
Sources: crates/globset/src/glob.rs:76-81, crates/globset/src/glob.rs:283-285, crates/globset/src/glob.rs:579-610, crates/globset/src/glob.rs:673-690
Tokens
Tokens are the parser’s structured representation of glob syntax. Token includes literals, single-character matches, ordinary repetition, recursive forms, character classes, and alternation groups.
Sources: crates/globset/src/glob.rs:270-279
A candidate path
A Candidate is a normalized path plus derived basename and extension views. These views let specialized strategies avoid running a full regex when a literal lookup is sufficient.
Sources: crates/globset/src/lib.rs:599-603, crates/globset/src/lib.rs:633-638
A match strategy
MatchStrategy classifies a pattern as a literal, basename literal, extension, prefix, suffix, required-extension, or regex match. The classifier chooses the first applicable specialized representation and falls back to Regex when no safe extraction is available.
| Strategy | What it checks | Useful input |
|---|---|---|
Literal | The complete path equals a literal | candidate.path |
BasenameLiteral | The basename equals a literal | candidate.basename |
Extension | The extension equals a literal | candidate.ext |
Prefix | A literal starts the path | A path prefix |
Suffix | A suffix literal is checked against the path; component can require a path-component boundary | A path suffix |
RequiredExtension | The extension is necessary, then a regex confirms the whole path | Extension plus path |
Regex | The compiled regex decides the result | The complete path |
These strategies are mutually exclusive classifications for a compiled pattern. A GlobSet stores strategy implementations in its strats vector and only adds strategies that contain patterns.
Sources: crates/globset/src/glob.rs:16-47, crates/globset/src/glob.rs:51-67, crates/globset/src/lib.rs:309-312, crates/globset/src/lib.rs:526-552
How one glob becomes a matcher
GlobBuilder stores the source pattern and GlobOptions; its options control case sensitivity, separator behavior, backslash escaping, empty alternates, and unclosed classes. GlobBuilder::build initializes a Parser, parses the pattern, and constructs a Glob from the resulting tokens.
The parser recognizes ?, *, [... ], {... }, commas, and backslashes. Character classes can be negated and can contain ranges; invalid or unclosed classes produce parsing errors unless the relevant option permits an unclosed class to be treated literally.
A double star is context-sensitive. parse_star emits ordinary ZeroOrMore tokens when the surrounding characters do not form a recursive path pattern, and emits RecursivePrefix, RecursiveSuffix, or RecursiveZeroOrMore when separators or path boundaries make recursion meaningful. The generated regex maps these recursive tokens to expressions that can cross path components, while ordinary * becomes either .* or [^/]* depending on literal_separator.
Brace expansion is represented as Token::Alternates. Opening a group records an alternation branch, and closing the group collects the active branches into one alternation token. A comma is dispatched to parse_comma; the parser treats it as a literal outside an alternation group. The regex converter recursively converts each branch and joins non-empty branches with |; empty_alternates controls whether empty branches are retained.
The token sequence becomes an anchored regex: the converter adds ^ and $, and treats a glob consisting only of recursive-prefix syntax as matching everything. Each token is then translated into regex syntax, including escaped literals, wildcards, recursive forms, classes, and alternates.
Evidence
- glob-inputcrates/globset/src/glob.rs:283
- glob-buildercrates/globset/src/glob.rs:211
- glob-buildercrates/globset/src/glob.rs:579
- glob-parsercrates/globset/src/glob.rs:579
- glob-parsercrates/globset/src/glob.rs:791
- glob-tokenscrates/globset/src/glob.rs:253
- glob-tokenscrates/globset/src/glob.rs:579
- glob-regexcrates/globset/src/glob.rs:579
- glob-regexcrates/globset/src/glob.rs:673
- compiled-globcrates/globset/src/glob.rs:76
- compiled-globcrates/globset/src/glob.rs:579
For direct matching, GlobMatcher stores both the original Glob and a compiled Regex; compile_matcher builds that regex. GlobStrategic stores the classified MatchStrategy and a compiled regex, while its is_match_candidate dispatches according to that strategy.
Sources: crates/globset/src/glob.rs:211-216, crates/globset/src/glob.rs:220-237, crates/globset/src/glob.rs:579-610, crates/globset/src/glob.rs:823-837, crates/globset/src/glob.rs:962-1052, crates/globset/src/glob.rs:901-960, crates/globset/src/glob.rs:692-763, crates/globset/src/glob.rs:839-843, crates/globset/src/glob.rs:845-853, crates/globset/src/glob.rs:876-885, crates/globset/src/glob.rs:673-690, crates/globset/src/glob.rs:133-138, crates/globset/src/glob.rs:288-292, crates/globset/src/glob.rs:160-165, crates/globset/src/glob.rs:175-201
How a glob set matches one path
A GlobSet contains a pattern count and a vector of populated GlobSetMatchStrategy values. GlobSet::new enumerates the input patterns, assigns each a global index, classifies it with MatchStrategy::new, and adds it to the corresponding specialized builder.
Literal, basename-literal, and extension patterns use separate hash-map strategies that retain global pattern indices. Prefixes and suffixes are collected into multi-pattern builders, then compiled into Aho–Corasick matchers with a map back to global indices.
Required-extension patterns first group regexes by extension and compile those regexes during set construction. General regex patterns are combined into one regex-set matcher, with a pattern pool used to reuse temporary match sets.
A path becomes a Candidate once it is normalized and split into path, basename, and extension components. Calling GlobSet::is_match creates that candidate and delegates to is_match_candidate. The latter returns immediately for an empty set; otherwise it asks each stored strategy whether it matches and returns true on the first success.
Evidence
- path-inputcrates/globset/src/lib.rs:342
- candidatecrates/globset/src/lib.rs:599
- candidatecrates/globset/src/lib.rs:617
- candidatecrates/globset/src/lib.rs:633
- glob-setcrates/globset/src/lib.rs:309
- glob-setcrates/globset/src/lib.rs:461
- strategy-listcrates/globset/src/lib.rs:654
- strategy-listcrates/globset/src/lib.rs:665
- literal-indexescrates/globset/src/lib.rs:710
- literal-indexescrates/globset/src/lib.rs:721
- multi-matcherscrates/globset/src/lib.rs:825
- multi-matcherscrates/globset/src/lib.rs:870
- regex-setcrates/globset/src/lib.rs:964
- regex-setcrates/globset/src/lib.rs:982
When callers need every matching pattern rather than a boolean, matches_candidate_into asks every strategy to append hits, then sorts and deduplicates the global indices. This preserves pattern identity even when multiple strategies report the same pattern, and it does not retain matches between calls.
matches_all_candidate has a different meaning: it requires every stored strategy group to report success. This is why a set containing multiple patterns in the same strategy group can require all of them, while is_match_candidate only needs one strategy to succeed.
Sources: crates/globset/src/lib.rs:309-312, crates/globset/src/lib.rs:461-553, crates/globset/src/lib.rs:710, crates/globset/src/lib.rs:742, crates/globset/src/lib.rs:780, crates/globset/src/lib.rs:1035-1041, crates/globset/src/lib.rs:1043-1049, crates/globset/src/lib.rs:1078-1083, crates/globset/src/lib.rs:1085-1095, crates/globset/src/lib.rs:964-976, crates/globset/src/lib.rs:986-995, crates/globset/src/lib.rs:1051-1061, crates/globset/src/lib.rs:633-638, crates/globset/src/lib.rs:342-344, crates/globset/src/lib.rs:350-360, crates/globset/src/lib.rs:442-456, crates/globset/src/lib.rs:390-397
How it connects
The ignore crate imports Candidate, GlobBuilder, GlobSet, and GlobSetBuilder for gitignore, file-type, and override matching. This makes globset the reusable pattern engine underneath Gitignore and Override Matching.
Glob syntax is also used by decompression associations; that builder documents its syntax as the globset crate’s syntax. The same compiled matching behavior therefore supports file filtering and other path-based associations without duplicating glob parsing.
The integration test exercises recursive, extension, and literal patterns together, including src/**/*.rs, *.c, and src/lib.rs. Another test demonstrates brace expansion and case-insensitive matching in one set.
Sources: crates/ignore/src/gitignore.rs:17-20, crates/ignore/src/types.rs:87-94, crates/cli/src/decompress.rs:81-89, crates/globset/src/lib.rs:1149-1166, crates/globset/src/lib.rs:1243-1255
Key takeaways
- A
Globis parsed into tokens and compiled into an anchored regex. **, character classes, and brace alternates are represented explicitly during parsing.GlobSetclassifies patterns and builds specialized indexes or multi-pattern matchers.- One candidate path is normalized once, then dispatched across populated strategies.
- Boolean matching stops at the first success; collecting matches visits all strategies and deduplicates indices.