1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
use lazy_static::lazy_static; use regex::Regex; lazy_static! { static ref PATH_PARAMS_RE: Regex = Regex::new(r"(?s)(?::([^/]+))|(?:\*)").unwrap(); } fn generate_common_regex_str(path: &str) -> (String, Vec<String>) { let mut regex_str = String::with_capacity(path.len()); let mut param_names = Vec::new(); let mut pos: usize = 0; for caps in PATH_PARAMS_RE.captures_iter(path) { let whole = caps.get(0).unwrap(); let path_s = &path[pos..whole.start()]; regex_str += ®ex::escape(path_s); if whole.as_str() == "*" { regex_str += r"(.*)"; param_names.push("*".to_owned()); } else { regex_str += r"([^/]+)"; param_names.push(caps.get(1).unwrap().as_str().to_owned()); } pos = whole.end(); } let left_over_path_s = &path[pos..]; regex_str += ®ex::escape(left_over_path_s); (regex_str, param_names) } pub(crate) fn generate_exact_match_regex(path: &str) -> crate::Result<(Regex, Vec<String>)> { let (common_regex_str, params) = generate_common_regex_str(path); let re_str = format!("{}{}{}", r"(?s)^", common_regex_str, "$"); let re = Regex::new(re_str.as_str())?; Ok((re, params)) } #[allow(dead_code)] pub(crate) fn generate_prefix_match_regex(path: &str) -> crate::Result<(Regex, Vec<String>)> { let (common_regex_str, params) = generate_common_regex_str(path); let re_str = format!("{}{}", r"(?s)^", common_regex_str); let re = Regex::new(re_str.as_str())?; Ok((re, params)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_generate_common_regex_str_normal() { let path = "/"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/".to_owned(), Vec::<String>::new())); let path = "/api/v1/services/get_ip"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/api/v1/services/get_ip".to_owned(), Vec::<String>::new())) } #[test] fn test_generate_common_regex_str_special_character() { let path = "/users/user-data/view"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/user\-data/view".to_owned(), Vec::<String>::new())) } #[test] fn test_generate_common_regex_str_params() { let path = "/users/:username/data"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/([^/]+)/data".to_owned(), vec!["username".to_owned()])); let path = "/users/:username/data/:attr/view"; let r = generate_common_regex_str(path); assert_eq!( r, ( r"/users/([^/]+)/data/([^/]+)/view".to_owned(), vec!["username".to_owned(), "attr".to_owned()] ) ); let path = "/users/:username"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/([^/]+)".to_owned(), vec!["username".to_owned()])); let path = ":username"; let r = generate_common_regex_str(path); assert_eq!(r, (r"([^/]+)".to_owned(), vec!["username".to_owned()])); } #[test] fn test_generate_common_regex_str_star_globe() { let path = "*"; let r = generate_common_regex_str(path); assert_eq!(r, (r"(.*)".to_owned(), vec!["*".to_owned()])); let path = "/users/*"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/(.*)".to_owned(), vec!["*".to_owned()])); let path = "/users/*/data"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/(.*)/data".to_owned(), vec!["*".to_owned()])); let path = "/users/*/data/*"; let r = generate_common_regex_str(path); assert_eq!( r, ( r"/users/(.*)/data/(.*)".to_owned(), vec!["*".to_owned(), "*".to_owned()] ) ); let path = "/users/**"; let r = generate_common_regex_str(path); assert_eq!(r, (r"/users/(.*)(.*)".to_owned(), vec!["*".to_owned(), "*".to_owned()])); } }