1*cf78ab8cSAndroid Build Coastguard Worker // Copyright 2024 Google LLC 2*cf78ab8cSAndroid Build Coastguard Worker // 3*cf78ab8cSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); 4*cf78ab8cSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License. 5*cf78ab8cSAndroid Build Coastguard Worker // You may obtain a copy of the License at 6*cf78ab8cSAndroid Build Coastguard Worker // 7*cf78ab8cSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0 8*cf78ab8cSAndroid Build Coastguard Worker // 9*cf78ab8cSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software 10*cf78ab8cSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, 11*cf78ab8cSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*cf78ab8cSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and 13*cf78ab8cSAndroid Build Coastguard Worker // limitations under the License. 14*cf78ab8cSAndroid Build Coastguard Worker 15*cf78ab8cSAndroid Build Coastguard Worker /// A vector of tuples representing wildcard patterns. 16*cf78ab8cSAndroid Build Coastguard Worker /// Each tuple contains a prefix string and a suffix string. 17*cf78ab8cSAndroid Build Coastguard Worker pub struct PatternVec { 18*cf78ab8cSAndroid Build Coastguard Worker patterns: Vec<(String, String)>, 19*cf78ab8cSAndroid Build Coastguard Worker } 20*cf78ab8cSAndroid Build Coastguard Worker 21*cf78ab8cSAndroid Build Coastguard Worker impl PatternVec { 22*cf78ab8cSAndroid Build Coastguard Worker /// Creates a new PatternVec from a string containing semicolon (;) or comma (,) 23*cf78ab8cSAndroid Build Coastguard Worker /// separated wildcard patterns. new(pattern_list: impl Into<String>) -> PatternVec24*cf78ab8cSAndroid Build Coastguard Worker pub fn new(pattern_list: impl Into<String>) -> PatternVec { 25*cf78ab8cSAndroid Build Coastguard Worker let pattern_list = pattern_list.into(); 26*cf78ab8cSAndroid Build Coastguard Worker let patterns = if pattern_list.trim().is_empty() { 27*cf78ab8cSAndroid Build Coastguard Worker Vec::new() 28*cf78ab8cSAndroid Build Coastguard Worker } else { 29*cf78ab8cSAndroid Build Coastguard Worker pattern_list 30*cf78ab8cSAndroid Build Coastguard Worker .split([';', ',']) 31*cf78ab8cSAndroid Build Coastguard Worker // Splits a string at the first occurrence of '*', returning a tuple 32*cf78ab8cSAndroid Build Coastguard Worker // containing the prefix (before *) and suffix (after *). 33*cf78ab8cSAndroid Build Coastguard Worker // If no '*' is found, returns the entire string as prefix. 34*cf78ab8cSAndroid Build Coastguard Worker .map(|s| match s.find('*') { 35*cf78ab8cSAndroid Build Coastguard Worker Some(i) => (String::from(&s[..i]), String::from(&s[i + 1..])), 36*cf78ab8cSAndroid Build Coastguard Worker None => (String::from(s), String::new()), 37*cf78ab8cSAndroid Build Coastguard Worker }) 38*cf78ab8cSAndroid Build Coastguard Worker .collect() 39*cf78ab8cSAndroid Build Coastguard Worker }; 40*cf78ab8cSAndroid Build Coastguard Worker PatternVec { patterns } 41*cf78ab8cSAndroid Build Coastguard Worker } 42*cf78ab8cSAndroid Build Coastguard Worker 43*cf78ab8cSAndroid Build Coastguard Worker /// Checks if a given string matches any of the patterns in the PatternVec. 44*cf78ab8cSAndroid Build Coastguard Worker /// A match occurs if the string starts with a pattern's prefix and ends with its suffix. matches(&self, s: &str) -> bool45*cf78ab8cSAndroid Build Coastguard Worker pub fn matches(&self, s: &str) -> bool { 46*cf78ab8cSAndroid Build Coastguard Worker self.patterns.iter().any(|(prefix, suffix)| s.starts_with(prefix) && s.ends_with(suffix)) 47*cf78ab8cSAndroid Build Coastguard Worker } 48*cf78ab8cSAndroid Build Coastguard Worker } 49*cf78ab8cSAndroid Build Coastguard Worker 50*cf78ab8cSAndroid Build Coastguard Worker #[cfg(test)] 51*cf78ab8cSAndroid Build Coastguard Worker mod tests { 52*cf78ab8cSAndroid Build Coastguard Worker use super::*; 53*cf78ab8cSAndroid Build Coastguard Worker 54*cf78ab8cSAndroid Build Coastguard Worker macro_rules! tuple_str { 55*cf78ab8cSAndroid Build Coastguard Worker ($a:expr, $b:expr) => { 56*cf78ab8cSAndroid Build Coastguard Worker (String::from($a), String::from($b)) 57*cf78ab8cSAndroid Build Coastguard Worker }; 58*cf78ab8cSAndroid Build Coastguard Worker } 59*cf78ab8cSAndroid Build Coastguard Worker 60*cf78ab8cSAndroid Build Coastguard Worker #[test] test_new_empty_string()61*cf78ab8cSAndroid Build Coastguard Worker fn test_new_empty_string() { 62*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new(""); 63*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns.len(), 0); 64*cf78ab8cSAndroid Build Coastguard Worker } 65*cf78ab8cSAndroid Build Coastguard Worker 66*cf78ab8cSAndroid Build Coastguard Worker #[test] test_new_single_pattern()67*cf78ab8cSAndroid Build Coastguard Worker fn test_new_single_pattern() { 68*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.example.com"); 69*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns.len(), 1); 70*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns[0], tuple_str!("", ".example.com")); 71*cf78ab8cSAndroid Build Coastguard Worker } 72*cf78ab8cSAndroid Build Coastguard Worker 73*cf78ab8cSAndroid Build Coastguard Worker #[test] test_new_multiple_patterns()74*cf78ab8cSAndroid Build Coastguard Worker fn test_new_multiple_patterns() { 75*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.example.com;*.org"); 76*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns.len(), 2); 77*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns[0], tuple_str!("", ".example.com")); 78*cf78ab8cSAndroid Build Coastguard Worker assert_eq!(pattern_vec.patterns[1], tuple_str!("", ".org")); 79*cf78ab8cSAndroid Build Coastguard Worker } 80*cf78ab8cSAndroid Build Coastguard Worker 81*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_exact_match()82*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_exact_match() { 83*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("example.com"); 84*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("example.com")); 85*cf78ab8cSAndroid Build Coastguard Worker } 86*cf78ab8cSAndroid Build Coastguard Worker 87*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_prefix_match()88*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_prefix_match() { 89*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.google.com"); 90*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("foo.google.com")); 91*cf78ab8cSAndroid Build Coastguard Worker } 92*cf78ab8cSAndroid Build Coastguard Worker 93*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_suffix_match()94*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_suffix_match() { 95*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.com"); 96*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("example.com")); 97*cf78ab8cSAndroid Build Coastguard Worker } 98*cf78ab8cSAndroid Build Coastguard Worker 99*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_no_match()100*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_no_match() { 101*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.google.com"); 102*cf78ab8cSAndroid Build Coastguard Worker assert!(!pattern_vec.matches("example.org")); 103*cf78ab8cSAndroid Build Coastguard Worker } 104*cf78ab8cSAndroid Build Coastguard Worker 105*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_multiple_patterns()106*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_multiple_patterns() { 107*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("*.example.com;*.org"); 108*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("some.example.com")); 109*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("another.org")); 110*cf78ab8cSAndroid Build Coastguard Worker } 111*cf78ab8cSAndroid Build Coastguard Worker 112*cf78ab8cSAndroid Build Coastguard Worker #[test] test_matches_middle_wildcard()113*cf78ab8cSAndroid Build Coastguard Worker fn test_matches_middle_wildcard() { 114*cf78ab8cSAndroid Build Coastguard Worker let pattern_vec = PatternVec::new("some*.com"); 115*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("somemiddle.com")); 116*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("some.middle.com")); 117*cf78ab8cSAndroid Build Coastguard Worker assert!(pattern_vec.matches("some.middle.example.com")); 118*cf78ab8cSAndroid Build Coastguard Worker } 119*cf78ab8cSAndroid Build Coastguard Worker } 120