1pub 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}