use glob::{MatchOptions, Pattern};
const FS_PREFIX: &str = "fs://workspace/";
pub fn matches_uri(pattern: &str, uri: &str) -> bool {
if pattern.contains("://") {
let pattern_scheme = scheme_of(pattern);
let uri_scheme = scheme_of(uri);
if pattern_scheme != uri_scheme {
return false;
}
glob_match(pattern, uri)
} else {
if !uri.starts_with(FS_PREFIX) {
return false;
}
let full_pattern = format!("{}{}", FS_PREFIX, pattern);
glob_match(&full_pattern, uri)
}
}
fn scheme_of(uri: &str) -> &str {
uri.split("://").next().unwrap_or("")
}
fn glob_match(pattern: &str, target: &str) -> bool {
let opts = MatchOptions {
require_literal_separator: true,
..Default::default()
};
match Pattern::new(pattern) {
Ok(p) => p.matches_with(target, opts),
Err(_) => false,
}
}
pub fn resolve_pattern(pattern: &str) -> String {
if pattern.contains("://") {
pattern.to_string()
} else {
format!("{}{}", FS_PREFIX, pattern)
}
}
pub fn filter_uris<'a>(patterns: &[&str], uris: &[&'a str]) -> Vec<&'a str> {
uris.iter()
.filter(|uri| patterns.iter().any(|pat| matches_uri(pat, uri)))
.copied()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bare_pattern_matches_fs_uri() {
assert!(matches_uri("src/**", "fs://workspace/src/main.rs"));
assert!(matches_uri("src/*.rs", "fs://workspace/src/lib.rs"));
assert!(matches_uri("README.md", "fs://workspace/README.md"));
}
#[test]
fn bare_pattern_does_not_match_other_schemes() {
assert!(!matches_uri("src/**", "gmail://inbox/src/draft"));
assert!(!matches_uri("src/**", "drive://docs/src/readme"));
assert!(!matches_uri("*.rs", "db://tables/schema.rs"));
}
#[test]
fn bare_pattern_no_match_outside_scope() {
assert!(!matches_uri("src/**", "fs://workspace/tests/test.rs"));
assert!(!matches_uri("src/*.rs", "fs://workspace/src/sub/deep.rs"));
}
#[test]
fn explicit_fs_pattern_matches() {
assert!(matches_uri(
"fs://workspace/src/**",
"fs://workspace/src/main.rs"
));
assert!(matches_uri(
"fs://workspace/**",
"fs://workspace/Cargo.toml"
));
}
#[test]
fn explicit_gmail_pattern_matches() {
assert!(matches_uri("gmail://inbox/*", "gmail://inbox/msg-456"));
assert!(matches_uri("gmail://**", "gmail://inbox/msg-123"));
assert!(!matches_uri("gmail://*", "gmail://inbox/msg-123"));
}
#[test]
fn scheme_mismatch_never_matches() {
assert!(!matches_uri("fs://workspace/**", "gmail://inbox/msg-123"));
assert!(!matches_uri("gmail://*", "fs://workspace/src/main.rs"));
}
#[test]
fn exact_path_match() {
assert!(matches_uri("src/main.rs", "fs://workspace/src/main.rs"));
assert!(!matches_uri("src/main.rs", "fs://workspace/src/lib.rs"));
}
#[test]
fn double_star_matches_deep_paths() {
assert!(matches_uri(
"src/**",
"fs://workspace/src/deeply/nested/file.rs"
));
}
#[test]
fn invalid_glob_pattern_never_matches() {
assert!(!matches_uri("[invalid", "fs://workspace/src/main.rs"));
}
#[test]
fn empty_pattern_does_not_match() {
assert!(!matches_uri("", "fs://workspace/src/main.rs"));
}
#[test]
fn resolve_bare_pattern() {
assert_eq!(resolve_pattern("src/**"), "fs://workspace/src/**");
}
#[test]
fn resolve_explicit_pattern_unchanged() {
assert_eq!(resolve_pattern("gmail://*"), "gmail://*");
}
#[test]
fn filter_uris_selects_matching() {
let uris = vec![
"fs://workspace/src/main.rs",
"fs://workspace/src/lib.rs",
"fs://workspace/tests/test.rs",
"gmail://inbox/msg-1",
];
let matched = filter_uris(&["src/**"], &uris);
assert_eq!(matched.len(), 2);
assert!(matched.contains(&"fs://workspace/src/main.rs"));
assert!(matched.contains(&"fs://workspace/src/lib.rs"));
}
#[test]
fn filter_uris_multiple_patterns() {
let uris = vec![
"fs://workspace/src/main.rs",
"fs://workspace/tests/test.rs",
"gmail://inbox/msg-1",
];
let matched = filter_uris(&["src/**", "gmail://**"], &uris);
assert_eq!(matched.len(), 2);
assert!(matched.contains(&"fs://workspace/src/main.rs"));
assert!(matched.contains(&"gmail://inbox/msg-1"));
}
}