1pub fn matches(pattern: &str, topic: &str) -> bool {
22 let pattern_parts: Vec<&str> = pattern.split('.').collect();
23 let topic_parts: Vec<&str> = topic.split('.').collect();
24 match_glob(&pattern_parts, &topic_parts)
25}
26
27fn match_glob(pattern: &[&str], topic: &[&str]) -> bool {
28 match (pattern.first(), topic.first()) {
29 (None, None) => true,
30 (None, Some(_)) => false,
31 (Some(&"**"), _) => {
32 if pattern.len() == 1 {
34 return true;
35 }
36 (0..=topic.len()).any(|i| match_glob(&pattern[1..], &topic[i..]))
38 }
39 (Some(&"*"), None) => false,
40 (Some(&"*"), Some(_)) => match_glob(&pattern[1..], &topic[1..]),
41 (Some(p), Some(t)) if *p == *t => match_glob(&pattern[1..], &topic[1..]),
42 _ => false,
43 }
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49
50 #[test]
51 fn test_exact_match() {
52 assert!(matches("user.created", "user.created"));
53 assert!(!matches("user.created", "user.deleted"));
54 }
55
56 #[test]
57 fn test_single_wildcard() {
58 assert!(matches("user.*", "user.created"));
59 assert!(matches("user.*", "user.deleted"));
60 assert!(!matches("user.*", "user.foo.bar"));
61 assert!(matches("*.created", "user.created"));
62 assert!(!matches("*.created", "user.deleted"));
63 }
64
65 #[test]
66 fn test_double_wildcard() {
67 assert!(matches("user.**", "user.created"));
68 assert!(matches("user.**", "user.foo.bar"));
69 assert!(matches("**", "anything.at.all"));
70 assert!(matches("*.**", "user.foo.bar"));
71 assert!(matches("user.**", "user"));
72 }
73
74 #[test]
75 fn test_empty_patterns() {
76 assert!(matches("", ""));
77 assert!(!matches("", "something"));
78 assert!(!matches("something", ""));
79 }
80
81 #[test]
82 fn test_multiple_wildcards() {
83 assert!(matches("*.created.*", "user.created.today"));
84 assert!(!matches("*.created.*", "user.created"));
85 assert!(matches("*.*.*", "a.b.c"));
86 assert!(!matches("*.*.*", "a.b"));
87 }
88}