pub(super) fn matches_host_pattern(hostname: &str, patterns: &[String]) -> bool {
for pattern in patterns {
if matches_pattern(hostname, pattern) {
return true;
}
}
false
}
pub(super) fn matches_pattern(hostname: &str, pattern: &str) -> bool {
if let Some(neg_pattern) = pattern.strip_prefix('!') {
return !matches_pattern(hostname, neg_pattern);
}
if pattern.contains('*') || pattern.contains('?') {
wildcard_match(hostname, pattern)
} else {
hostname.eq_ignore_ascii_case(pattern)
}
}
pub(super) fn wildcard_match(text: &str, pattern: &str) -> bool {
wildcard_match_impl(text, pattern)
}
fn wildcard_match_impl(text: &str, pattern: &str) -> bool {
let text_chars: Vec<char> = text.chars().collect();
let pattern_chars: Vec<char> = pattern.chars().collect();
match_recursive(&text_chars, &pattern_chars, 0, 0)
}
pub(super) fn match_recursive(
text_chars: &[char],
pattern_chars: &[char],
text_idx: usize,
pattern_idx: usize,
) -> bool {
if pattern_idx >= pattern_chars.len() {
return text_idx >= text_chars.len();
}
if text_idx >= text_chars.len() {
return pattern_chars[pattern_idx..].iter().all(|&c| c == '*');
}
let pattern_char = pattern_chars[pattern_idx];
let text_char = text_chars[text_idx];
match pattern_char {
'*' => {
if match_recursive(text_chars, pattern_chars, text_idx, pattern_idx + 1) {
return true;
}
if match_recursive(text_chars, pattern_chars, text_idx + 1, pattern_idx) {
return true;
}
false
}
'?' => {
match_recursive(text_chars, pattern_chars, text_idx + 1, pattern_idx + 1)
}
_ => {
if text_char.eq_ignore_ascii_case(&pattern_char) {
match_recursive(text_chars, pattern_chars, text_idx + 1, pattern_idx + 1)
} else {
false
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_wildcard_matching() {
assert!(wildcard_match("web1.example.com", "web*.example.com"));
assert!(wildcard_match("web123.example.com", "web*.example.com"));
assert!(!wildcard_match("db1.example.com", "web*.example.com"));
assert!(wildcard_match("test", "?est"));
assert!(!wildcard_match("testing", "?est"));
assert!(wildcard_match("anything", "*"));
}
#[test]
fn test_pattern_matching_with_negation() {
assert!(matches_pattern("web1.example.com", "web*.example.com"));
assert!(!matches_pattern("web1.example.com", "!web*.example.com"));
assert!(matches_pattern("db1.example.com", "!web*.example.com"));
}
#[test]
fn test_host_pattern_matching() {
let patterns = vec!["web*.example.com".to_string(), "*.test.com".to_string()];
assert!(matches_host_pattern("web1.example.com", &patterns));
assert!(matches_host_pattern("api.test.com", &patterns));
assert!(!matches_host_pattern("db1.example.com", &patterns));
}
}