xref: /aosp_15_r20/tools/netsim/rust/http-proxy/src/pattern_vec.rs (revision cf78ab8cffb8fc9207af348f23af247fb04370a6)
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