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