teo_runtime/handler/
map.rs1use indexmap::{IndexMap, indexmap};
2use regex::Regex;
3use hyper::Method;
4use crate::handler::r#match::HandlerMatch;
5
6#[derive(Debug, Clone)]
7pub struct Map {
8 records: IndexMap<(Method, String), (Vec<String>, String)>
9}
10
11impl Map {
12
13 pub fn new() -> Self {
14 Self {
15 records: indexmap!{}
16 }
17 }
18
19 pub fn add_record(&mut self, namespace_path: &Vec<&str>, group_name: Option<&str>, action_name: &str, method: Method, custom_url: Option<&str>, ignore_prefix: bool) {
20 let url = if ignore_prefix {
21 if custom_url.unwrap().starts_with("/") {
22 custom_url.unwrap().to_owned()
23 } else {
24 "/".to_owned() + custom_url.unwrap()
25 }
26 } else {
27 "/".to_owned() + &namespace_path.join(".") + &if let Some(group_name) = group_name {
28 "/".to_owned() + group_name
29 } else { "".to_owned() } + &if let Some(custom_url) = custom_url {
30 if custom_url.starts_with("/") {
31 custom_url.to_owned()
32 } else {
33 "/".to_owned() + custom_url
34 }
35 } else {
36 action_name.to_owned()
37 }
38 };
39 let url = url.replace("//", "/");
40 let mut result: Vec<String> = namespace_path.iter().map(|i| i.to_string()).collect();
41 if let Some(group_name) = group_name {
42 result.push(group_name.to_owned());
43 }
44 self.records.insert((method, url), (result, action_name.to_owned()));
45 }
46
47 pub fn match_all(&self, method: &Method, url: &str) -> Option<HandlerMatch> {
48 if let Some(result) = self.match_user_defined(method, url) {
49 return Some(result);
50 }
51 if let Some(result) = self.match_default(method.clone(), url) {
52 return Some(result);
53 }
54 None
55 }
56
57 pub fn match_user_defined(&self, method: &Method, url: &str) -> Option<HandlerMatch> {
58 for record in &self.records {
59 if let Some(result) = self.try_match(method, url, record) {
60 return Some(result);
61 }
62 }
63 None
64 }
65
66 fn try_match(&self, method: &Method, url: &str, record: (&(Method, String), &(Vec<String>, String))) -> Option<HandlerMatch> {
67 if method != Method::OPTIONS && record.0.0 != method {
68 return None;
69 }
70 let define = &record.0.1;
71 let arg_names = self.fetch_arg_names(define);
72 let regex_string = self.replace_arg_names(define);
73 let regex_string = format!("^{regex_string}$");
74 let regex = Regex::new(®ex_string).unwrap();
75 if regex.is_match(url) {
76 if let Some(captures) = regex.captures(url) {
77 let mut retval = indexmap!{};
78 for (index, r#match) in captures.iter().enumerate() {
79 if index == 0 {
80 continue
81 }
82 if let Some(r#match) = r#match {
83 retval.insert(arg_names.get(index - 1).unwrap().to_owned(), r#match.as_str().to_owned());
84 }
85 }
86 return Some(HandlerMatch::new(record.1.0.clone(), record.1.1.clone(), retval))
87 } else {
88 return Some(HandlerMatch::new(record.1.0.clone(), record.1.1.clone(), indexmap!{}))
89 }
90 }
91 None
92 }
93
94 fn fetch_arg_names(&self, define: &String) -> Vec<String> {
95 let regex = Regex::new("[:*]([^/]+)").unwrap();
96 let captures = regex.captures(define);
97 if let Some(captures) = captures {
98 captures.iter().map(|m| m.unwrap().as_str()[1..].to_string()).collect()
99 } else {
100 vec![]
101 }
102 }
103
104 fn replace_arg_names(&self, define: &String) -> String {
105 let normal_regex = Regex::new(":[^/]+").unwrap();
106 let replaced = normal_regex.replace(define, "([^/]+)");
107 let catch_regex = Regex::new("\\*[^/]+").unwrap();
108 let replaced = catch_regex.replace(replaced.as_ref(), "(.+)");
109 replaced.as_ref().to_string()
110 }
111
112 pub fn match_default(&self, method: Method, url: &str) -> Option<HandlerMatch> {
113 if method != Method::OPTIONS && method != Method::POST {
114 return None;
115 }
116 let mut url = url;
117 if url.starts_with("/") {
118 url = url.trim_start_matches("/");
119 }
120 if url.ends_with("/") {
121 url = url.trim_end_matches("/");
122 }
123 let parts = url.split("/");
124 if parts.clone().count() < 1 {
125 return None;
126 }
127 let mut result: Vec<String> = parts.map(|p| p.to_string()).collect();
128 let action = result.pop().unwrap().to_string();
129 Some(HandlerMatch::new(result, action, indexmap!{}))
130 }
131}