Skip to main content

har/
glob.rs

1/// Minimal glob: `*` matches any run of characters. Case-insensitive.
2pub fn glob_match(pattern: &str, text: &str) -> bool {
3    let p = pattern.to_ascii_lowercase();
4    let t = text.to_ascii_lowercase();
5    if !p.contains('*') {
6        return p == t;
7    }
8    let parts: Vec<&str> = p.split('*').collect();
9    let mut pos = 0usize;
10    for (i, part) in parts.iter().enumerate() {
11        if part.is_empty() {
12            continue;
13        }
14        if i == 0 {
15            if !t[pos..].starts_with(part) {
16                return false;
17            }
18            pos += part.len();
19        } else if i == parts.len() - 1 {
20            return t[pos..].ends_with(part);
21        } else if let Some(found) = t[pos..].find(part) {
22            pos += found + part.len();
23        } else {
24            return false;
25        }
26    }
27    true
28}
29
30#[cfg(test)]
31mod tests {
32    use super::glob_match;
33
34    #[test]
35    fn exact_match_no_star() {
36        assert!(glob_match("api.foo.com", "API.FOO.COM"));
37        assert!(!glob_match("api.foo.com", "api.bar.com"));
38    }
39
40    #[test]
41    fn star_matches_substring() {
42        assert!(glob_match("*login*", "/v1/login/start"));
43        assert!(glob_match("api.*.com", "api.foo.com"));
44        assert!(!glob_match("api.*.com", "cdn.foo.net"));
45    }
46
47    #[test]
48    fn leading_and_trailing_star() {
49        assert!(glob_match("*.nexioapp.org", "torii.nexioapp.org"));
50        assert!(glob_match("torii.*", "torii.nexioapp.org"));
51    }
52}