Loading pageā¦
Rust walkthroughs
Loading pageā¦
glob::Pattern::matches_path for path-based pattern matching with components?matches_path matches a glob pattern against a complete path including all directory components, while matches only checks against the final filename component. This distinction matters when patterns include directory separatorsāmatches_path understands that src/**/*.rs should match src/lib.rs by examining the full path, whereas matches would only see lib.rs. The method uses std::path::Path as input rather than strings, providing proper handling of platform-specific path separators and enabling patterns like **/src/**/*.rs to correctly traverse directory hierarchies.
use glob::Pattern;
fn main() {
let pattern = Pattern::new("src/**/*.rs").unwrap();
// matches: Only checks the filename component
let filename = "lib.rs";
println!("matches '{}': {}", filename, pattern.matches(filename));
// false - "lib.rs" doesn't match "src/**/*.rs" as a filename
// matches_path: Checks the complete path
let path = std::path::Path::new("src/lib.rs");
println!("matches_path '{:?}': {}", path, pattern.matches_path(path));
// true - the full path matches the pattern
let nested = std::path::Path::new("src/module/sub.rs");
println!("matches_path '{:?}': {}", nested, pattern.matches_path(nested));
// true - ** matches any directory depth
}matches only sees filenames; matches_path sees the entire directory structure.
use glob::Pattern;
use std::path::Path;
fn main() {
// Pattern with directory components
let pattern = Pattern::new("docs/**/*.md").unwrap();
// Path components are matched individually:
// "docs" -> literal match
// "**" -> matches zero or more directories
// "*.md" -> matches any .md filename
let paths = [
Path::new("docs/readme.md"), // matches
Path::new("docs/api/guide.md"), // matches (** matches "api")
Path::new("docs/api/v1/spec.md"), // matches (** matches "api/v1")
Path::new("other/readme.md"), // no match (wrong root)
Path::new("docs/README"), // no match (wrong extension)
];
for path in paths {
println!("{:?}: {}", path, pattern.matches_path(path));
}
// docs/readme.md: true
// docs/api/guide.md: true
// docs/api/v1/spec.md: true
// other/readme.md: false
// docs/README: false
}Path components are matched against pattern components, not as strings.
use glob::Pattern;
use std::path::Path;
fn main() {
let pattern = Pattern::new("**/test/**/*.rs").unwrap();
// ** matches zero or more directory components
let test_paths = [
Path::new("test/lib.rs"), // ** matches zero directories before "test"
Path::new("src/test/lib.rs"), // ** matches "src"
Path::new("src/module/test/lib.rs"), // ** matches "src/module"
Path::new("test/unit/lib.rs"), // ** after "test" matches "unit"
Path::new("test/unit/module/lib.rs"), // ** matches "unit/module"
];
for path in test_paths {
println!("{:?}: {}", path, pattern.matches_path(path));
}
// Single * only matches within a single component
let single_star = Pattern::new("*/test/*.rs").unwrap();
println!("\nSingle * pattern:");
println!("test/lib.rs: {}", single_star.matches_path(Path::new("test/lib.rs"))); // false (no dir before test)
println!("src/test/lib.rs: {}", single_star.matches_path(Path::new("src/test/lib.rs"))); // true
println!("src/mod/test/lib.rs: {}", single_star.matches_path(Path::new("src/mod/test/lib.rs"))); // false (* doesn't match "src/mod")
}** is the key differenceāit matches across directory boundaries in path matching.
use glob::Pattern;
use std::path::Path;
fn main() {
let pattern = Pattern::new("*.rs").unwrap();
// For simple patterns without path separators, both work similarly
println!("Simple pattern '*.rs':");
println!(" matches('lib.rs'): {}", pattern.matches("lib.rs")); // true
println!(" matches_path('lib.rs'): {}", pattern.matches_path(Path::new("lib.rs"))); // true
println!(" matches_path('src/lib.rs'): {}", pattern.matches_path(Path::new("src/lib.rs"))); // false
// But for patterns WITH path components:
let path_pattern = Pattern::new("src/*.rs").unwrap();
println!("\nPath pattern 'src/*.rs':");
println!(" matches('lib.rs'): {}", path_pattern.matches("lib.rs")); // false - no "src/" in filename
println!(" matches('src/lib.rs'): {}", path_pattern.matches("src/lib.rs")); // false - "src/" is a path component!
println!(" matches_path('src/lib.rs'): {}", path_pattern.matches_path(Path::new("src/lib.rs"))); // true
}matches treats the pattern as matching against a filename string; matches_path parses the path into components.
use glob::Pattern;
use std::path::Path;
fn main() {
// glob Pattern normalizes path separators
let pattern = Pattern::new("src/**/*.rs").unwrap();
// On Unix:
#[cfg(unix)]
{
let path = Path::new("src/module/lib.rs");
println!("Unix path: {} -> {}", path.display(), pattern.matches_path(path));
// true
}
// On Windows:
#[cfg(windows)]
{
let path = Path::new("src\\module\\lib.rs");
println!("Windows path: {} -> {}", path.display(), pattern.matches_path(path));
// true - Pattern handles backslashes
}
// Pattern uses / internally regardless of platform
// matches_path converts Path to the internal representation
// Mixed separators (not recommended but handled):
let mixed = Path::new("src/module/sub/lib.rs"); // Always uses /
println!("Mixed path: {}", pattern.matches_path(mixed));
}matches_path uses std::path::Path which handles platform-specific separators automatically.
use glob::Pattern;
use std::path::{Path, PathBuf};
fn main() {
let pattern = Pattern::new("project/src/**/*.rs").unwrap();
// matches_path accepts &Path
let path_buf = PathBuf::from("project/src/main.rs");
println!("PathBuf: {}", pattern.matches_path(&path_buf));
// Works with references
let path_ref: &Path = Path::new("project/src/lib.rs");
println!("&Path: {}", pattern.matches_path(path_ref));
// From canonical paths (resolved symlinks, etc.)
let canonical = std::fs::canonicalize(".").ok();
// Note: canonical paths are absolute, pattern must account for that
// Relative paths work naturally
let relative = Path::new("./src/lib.rs");
// Pattern would need to handle "./" if present
// Absolute paths need absolute patterns
let absolute_pattern = Pattern::new("/home/user/project/**/*.rs").unwrap();
let absolute_path = Path::new("/home/user/project/src/lib.rs");
println!("Absolute: {}", absolute_pattern.matches_path(absolute_path));
}matches_path integrates with Rust's path types for idiomatic usage.
use glob::Pattern;
use std::path::Path;
fn find_matching_files(root: &Path, pattern: &Pattern) -> Vec<PathBuf> {
let mut matches = Vec::new();
if let Ok(entries) = std::fs::read_dir(root) {
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
// Recursively search directories
matches.extend(find_matching_files(&path, pattern));
} else if pattern.matches_path(&path) {
// File matches the pattern
matches.push(path);
}
}
}
matches
}
fn main() {
let pattern = Pattern::new("**/test_*.rs").unwrap();
let root = Path::new(".");
let test_files = find_matching_files(root, &pattern);
println!("Test files found: {:?}", test_files);
// Finds:
// ./tests/test_basic.rs
// ./tests/integration/test_api.rs
// ./src/test_utils.rs
}Use matches_path when implementing custom file traversal with pattern filtering.
use glob::Pattern;
use std::path::Path;
fn main() {
// Special characters in patterns: *, ?, [, ], {, }
// To match literally, escape them
// Pattern to match files with literal brackets
let pattern = Pattern::new("data/[special].txt").unwrap();
// This won't match "data/[special].txt" because [] is a pattern
let literal_path = Path::new("data/[special].txt");
println!("Unescaped pattern: {}", pattern.matches_path(literal_path));
// Probably false - [] interpreted as character class
// Escape special characters:
let escaped_pattern = Pattern::new("data/\\[special\\].txt").unwrap();
println!("Escaped pattern: {}", escaped_pattern.matches_path(literal_path));
// true - brackets treated literally
// Pattern::escape for automatic escaping
let filename = "file[1].txt";
let escaped_filename = Pattern::escape(filename);
println!("Escaped '{}': '{}'", filename, escaped_filename);
// "file\\[1\\].txt"
let escaped_pattern = Pattern::new(&escaped_filename).unwrap();
println!("Matches escaped: {}", escaped_pattern.matches("file[1].txt"));
}Escape special characters when matching literal filenames that contain pattern syntax.
use glob::Pattern;
use std::path::Path;
fn main() {
// Match all Rust source files recursively
let all_rust = Pattern::new("**/*.rs").unwrap();
println!("src/lib.rs: {}", all_rust.matches_path(Path::new("src/lib.rs")));
println!("tests/integration/test.rs: {}", all_rust.matches_path(Path::new("tests/integration/test.rs")));
// Match files in specific directory only
let specific_dir = Pattern::new("src/*.rs").unwrap();
println!("src/main.rs: {}", specific_dir.matches_path(Path::new("src/main.rs"))); // true
println!("src/module/lib.rs: {}", specific_dir.matches_path(Path::new("src/module/lib.rs"))); // false
// Match files with specific naming pattern across directories
let test_files = Pattern::new("**/test_*.rs").unwrap();
println!("tests/test_basic.rs: {}", test_files.matches_path(Path::new("tests/test_basic.rs")));
println!("src/test_utils.rs: {}", test_files.matches_path(Path::new("src/test_utils.rs")));
// Exclude patterns using character classes
let not_backup = Pattern::new("**/[^.]*").unwrap(); // Files not starting with .
println!(".hidden: {}", not_backup.matches_path(Path::new(".hidden"))); // false
println!("visible: {}", not_backup.matches_path(Path::new("visible"))); // true
// Alternative extensions
let configs = Pattern::new("**/*.{toml,yaml,json}").unwrap();
println!("config.toml: {}", configs.matches_path(Path::new("config.toml"))); // true
println!("config.yaml: {}", configs.matches_path(Path::new("config.yaml"))); // true
println!("config.txt: {}", configs.matches_path(Path::new("config.txt"))); // false
}These patterns demonstrate practical use cases for file filtering.
use glob::{glob, Pattern};
fn main() {
// glob::glob returns actual files from filesystem
// Pattern::matches_path tests patterns against paths
// Use glob when you want to find files:
for entry in glob("src/**/*.rs").unwrap() {
if let Ok(path) = entry {
println!("Found: {:?}", path);
}
}
// Use Pattern::matches_path when you have paths and want to filter:
let pattern = Pattern::new("**/test_*.rs").unwrap();
let paths = [
std::path::PathBuf::from("tests/test_basic.rs"),
std::path::PathBuf::from("src/lib.rs"),
std::path::PathBuf::from("src/test_utils.rs"),
];
let matching_paths: Vec<_> = paths
.into_iter()
.filter(|p| pattern.matches_path(p))
.collect();
println!("Matching paths: {:?}", matching_paths);
// [tests/test_basic.rs, src/test_utils.rs]
// glob does filesystem I/O; Pattern::matches_path is pure computation
}glob does filesystem traversal; Pattern::matches_path is a pure predicate.
use glob::Pattern;
use std::path::Path;
// Useful when:
// 1. Paths come from a database or API
// 2. Testing pattern logic without filesystem
// 3. Processing path lists from external sources
// 4. Virtual or in-memory filesystems
fn filter_virtual_paths(paths: &[&str], pattern_str: &str) -> Vec<&str> {
let pattern = Pattern::new(pattern_str)
.expect("Invalid pattern");
paths
.iter()
.filter(|p| pattern.matches_path(Path::new(p)))
.copied()
.collect()
}
fn main() {
let virtual_paths = [
"project/src/main.rs",
"project/src/lib.rs",
"project/tests/test_main.rs",
"project/README.md",
"project/build/output.rs",
];
let src_files = filter_virtual_paths(&virtual_paths, "project/src/**/*.rs");
println!("Source files: {:?}", src_files);
// ["project/src/main.rs", "project/src/lib.rs"]
let all_rust = filter_virtual_paths(&virtual_paths, "**/*.rs");
println!("All Rust files: {:?}", all_rust);
// All .rs files
}matches_path works on any path without requiring actual files to exist.
use glob::Pattern;
use std::path::Path;
fn main() {
// Empty path
let any_pattern = Pattern::new("*").unwrap();
println!("Empty path: {}", any_pattern.matches_path(Path::new(""))); // false
// Path with . (current directory)
let pattern = Pattern::new("src/*.rs").unwrap();
println!("./src/lib.rs: {}", pattern.matches_path(Path::new("./src/lib.rs"))); // May not match!
// Pattern doesn't have ./, so it might not match
// Use canonical paths or strip ./
let clean_path = Path::new("./src/lib.rs")
.strip_prefix(".")
.unwrap_or(Path::new("./src/lib.rs"));
println!("Cleaned: {}", pattern.matches_path(clean_path));
// Symlinks: Pattern matches the path string, not resolved target
// A symlink "link" pointing to "target" matches "link" not "target"
// Unicode paths work naturally
let unicode_pattern = Pattern::new("**/ę攣/*.md").unwrap();
println!("Unicode: {}", unicode_pattern.matches_path(Path::new("docs/ę攣/guide.md")));
// Very deep paths
let deep = Path::new("a/b/c/d/e/f/g/h/i/j/file.txt");
let deep_pattern = Pattern::new("**/file.txt").unwrap();
println!("Deep path: {}", deep_pattern.matches_path(deep)); // true
}Handle path normalization and edge cases appropriately for your use case.
Key differences between matches and matches_path:
| Method | Input Type | Matching Scope | Use Case |
|--------|------------|----------------|---------|
| matches | &str | Filename only | Simple filename patterns |
| matches_path | &Path | Full path with components | Directory-aware patterns |
When to use matches_path:
/ or ** (directory components)When matches suffices:
*.rs)Path conversionPattern components:
| Syntax | Matches |
|--------|---------|
| * | Any characters within single component |
| ** | Zero or more directory components |
| ? | Single character |
| [abc] | Character class |
| {a,b} | Alternatives |
| **/src/**/*.rs | Complex directory matching |
Key insight: matches_path enables proper glob pattern matching against hierarchical paths by treating the path as components rather than a flat string. This is essential when patterns include ** or directory separatorsāthe pattern src/**/*.rs can only match src/lib.rs correctly when matches_path parses the path into ["src", "lib.rs"] and matches each component. Without component-aware matching, ** cannot span directories correctly. Use matches_path whenever your glob pattern references directory structure; use matches only for simple filename patterns without path separators.