teo_runtime/handler/
map.rs

1use 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(&regex_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}