pub fn glob_match(pattern: &str, text: &str) -> bool {
let pattern = pattern.as_bytes();
let text = text.as_bytes();
let (mut pattern_index, mut text_index) = (0usize, 0usize);
let mut star: Option<(usize, usize)> = None;
while text_index < text.len() {
if pattern_index < pattern.len() && pattern[pattern_index] == b'*' {
star = Some((pattern_index, text_index));
pattern_index += 1;
} else if pattern_index < pattern.len() && pattern[pattern_index] == text[text_index] {
pattern_index += 1;
text_index += 1;
} else if let Some((star_pattern_index, star_text_index)) = star {
let next_text_index = star_text_index + 1;
star = Some((star_pattern_index, next_text_index));
pattern_index = star_pattern_index + 1;
text_index = next_text_index;
} else {
return false;
}
}
while pattern_index < pattern.len() && pattern[pattern_index] == b'*' {
pattern_index += 1;
}
pattern_index == pattern.len()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exact_prefix_suffix_and_middle_forms() {
assert!(glob_match("bash", "bash"));
assert!(glob_match("bash*", "bash_tool"));
assert!(glob_match("*_write", "edit_write"));
assert!(glob_match("mcp__*__search", "mcp__github__search"));
assert!(!glob_match("bash", "bash_tool"));
}
#[test]
fn pathological_star_pattern_is_non_recursive() {
let pattern = "*a*a*a*a*a*a*a*a*a*a*b";
let text = "a".repeat(100_000);
assert!(!glob_match(pattern, &text));
}
}