aws_lambda_router/
matcher.rs1use regex::Regex;
2use lazy_static::lazy_static;
3use std::collections::HashMap;
4
5lazy_static! {
6 static ref PARAM_REGEX: Regex = Regex::new(r":([a-zA-Z_][a-zA-Z0-9_]*)").unwrap();
7}
8
9#[derive(Debug, Clone)]
11pub struct PathMatcher {
12 pattern: String,
13 regex: Regex,
14 param_names: Vec<String>,
15}
16
17impl PathMatcher {
18 pub fn new(pattern: &str) -> Self {
21 let mut param_names = Vec::new();
22
23 for cap in PARAM_REGEX.captures_iter(pattern) {
25 param_names.push(cap[1].to_string());
26 }
27
28 let regex_pattern = PARAM_REGEX
30 .replace_all(pattern, r"([^/]+)")
31 .replace("/", r"/");
32
33 let regex_pattern = format!("^{}$", regex_pattern);
34 let regex = Regex::new(®ex_pattern).unwrap();
35
36 Self {
37 pattern: pattern.to_string(),
38 regex,
39 param_names,
40 }
41 }
42
43 pub fn matches(&self, path: &str) -> Option<HashMap<String, String>> {
45 self.regex.captures(path).map(|captures| {
46 self.param_names
47 .iter()
48 .enumerate()
49 .filter_map(|(i, name)| {
50 captures.get(i + 1).map(|m| (name.clone(), m.as_str().to_string()))
51 })
52 .collect()
53 })
54 }
55
56 pub fn pattern(&self) -> &str {
58 &self.pattern
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 #[test]
67 fn test_simple_path() {
68 let matcher = PathMatcher::new("/api/users");
69 assert!(matcher.matches("/api/users").is_some());
70 assert!(matcher.matches("/api/posts").is_none());
71 }
72
73 #[test]
74 fn test_single_param() {
75 let matcher = PathMatcher::new("/api/users/:userId");
76
77 let params = matcher.matches("/api/users/123").unwrap();
78 assert_eq!(params.get("userId"), Some(&"123".to_string()));
79
80 assert!(matcher.matches("/api/users").is_none());
81 assert!(matcher.matches("/api/users/123/extra").is_none());
82 }
83
84 #[test]
85 fn test_multiple_params() {
86 let matcher = PathMatcher::new("/api/users/:userId/posts/:postId");
87
88 let params = matcher.matches("/api/users/123/posts/456").unwrap();
89 assert_eq!(params.get("userId"), Some(&"123".to_string()));
90 assert_eq!(params.get("postId"), Some(&"456".to_string()));
91 }
92
93 #[test]
94 fn test_nested_paths() {
95 let matcher = PathMatcher::new("/api/chatbots/:chatbotId/conversations/:conversationId");
96
97 let params = matcher.matches("/api/chatbots/chatbot123/conversations/conv456").unwrap();
98 assert_eq!(params.get("chatbotId"), Some(&"chatbot123".to_string()));
99 assert_eq!(params.get("conversationId"), Some(&"conv456".to_string()));
100 }
101}