Skip to main content

dce_router/
router.rs

1use std::{any::type_name, collections::{HashMap, HashSet}, sync::RwLock};
2
3use dce_util::{arena_tree::{ArenaTree, KeyFactory}, result::{DceError, DceResult}};
4use log::{debug, warn};
5
6use crate::{api::{Api, Handler, Hook, Suffix, MARK_PATH_PART_SEPARATOR, MARK_SUFFIX_BOUNDARY, MARK_VARIABLE_CLOSING, MARK_VARIABLE_OPENER, MARK_VAR_TYPE_EMPTABLE_VECTOR, MARK_VAR_TYPE_OPTIONAL, MARK_VAR_TYPE_VECTOR}, context::Param, protocol::RoutableProtocol};
7
8const CODE_NOT_FOUND: isize = 404;
9const HOOK_PATH_SUFFIXES: [&str; 2] = ["+", "*"];
10
11pub struct Router<Rp: RoutableProtocol + 'static> {
12    path_separator: &'static str,
13    suffix_boundary: &'static str,
14    api_buffer: RwLock<Vec<&'static Api<Rp>>>,
15    raw_omitted_paths: Vec<&'static str>,
16    id_api_mapping: HashMap<&'static str, &'static Api<Rp>>,
17    apis_mapping: HashMap<&'static str, Vec<&'static Api<Rp>>>,
18    apis_tree: ArenaTree<ApiBranch<Rp>, &'static str>,
19    before_mapping: HashMap<&'static str, Hook<Rp>>,
20    after_mapping: HashMap<&'static str, Hook<Rp>>,
21    path_before_mapping: HashMap<&'static str, &'static str>,
22    path_after_mapping: HashMap<&'static str, &'static str>,
23}
24
25impl <Rp: RoutableProtocol> Router<Rp> {
26    pub fn new() -> Self {
27        Router {
28            path_separator: MARK_PATH_PART_SEPARATOR,
29            suffix_boundary: MARK_SUFFIX_BOUNDARY,
30            api_buffer: Default::default(),
31            raw_omitted_paths: Default::default(),
32            id_api_mapping: Default::default(),
33            apis_mapping: Default::default(),
34            apis_tree: ArenaTree::new(ApiBranch::new("")),
35            before_mapping: Default::default(),
36            after_mapping: Default::default(),
37            path_before_mapping: Default::default(),
38            path_after_mapping: Default::default(),
39        }
40    }
41
42    pub fn bind(&mut self, path: &'static str, handler: Handler<Rp>) -> &mut Self {
43        let api = Api::new(path);
44        let api = api.bind_handler(handler);
45        self.add(api)
46    }
47
48    pub fn bind_api(&mut self, api: Api<Rp>, handler: Handler<Rp>) -> &mut Self {
49        let api = api.bind_handler(handler);
50        self.add(api)
51    }
52
53    pub fn register(&mut self, api_supplier: fn() -> Api<Rp>) -> &mut Self {
54        let api = api_supplier();
55        self.add(api)
56    }
57
58    pub fn add(&mut self, api: Api<Rp>) -> &mut Self {
59        let api = Box::leak(Box::new(api.upgrade()));
60        let mut api_buffer = self.api_buffer.write().unwrap();
61        api_buffer.push(api);
62        if api.omission {
63            self.raw_omitted_paths.push(api.path);
64        }
65        if let Some(id) = &api.id {
66            self.id_api_mapping.insert(id, api);
67        }
68        drop(api_buffer);
69        self
70    }
71
72    pub fn ready(&mut self) {
73        self.build_tree();
74        let mut api_buffer = self.api_buffer.write().unwrap();
75        while api_buffer.len() > 0 {
76            let api = api_buffer.remove(0);
77            let path = self.omitted_path(api.path);
78            let mut apis = vec![api];
79            let mut suffixes = api.suffixes.iter().map(|s| s).collect::<HashSet<_>>();
80            let mut i = 0;
81            while i < api_buffer.len() {
82			    // collect the omitted same path into an array
83                if path == self.omitted_path(api_buffer.get(i).unwrap().path) {
84                    let omitted_api = api_buffer.remove(i);
85                    apis.push(omitted_api);
86                    suffixes.extend(omitted_api.suffixes.iter().collect::<Vec<_>>());                    
87                } else {
88                    i += 1;
89                }
90            }
91            // append suffix to path as api mapping key to grouping the apis
92            for suffix in suffixes {
93                // insert suffix matched apis into the mapping
94                self.apis_mapping.insert(Box::leak(Self::suffixed_path(path.as_str(), Some(suffix)).into_boxed_str()),
95                    apis.iter().filter(|a| a.suffixes.contains(suffix)).map(|a| *a).collect::<Vec<_>>());
96            }
97        }
98        // self.api_buffer.read().unwrap().iter().for_each(|a| println!("{}", a.path()));
99        self.path_before_mapping = self.map_middleware(&self.before_mapping, true);
100        self.path_after_mapping = self.map_middleware(&self.after_mapping, false);
101    }
102
103    fn build_tree(&mut self) {
104    	// 1. make apis to ApiBranches
105        let api_buffer = self.api_buffer.write().unwrap();
106        let mut path_apis = api_buffer.iter().map(|a| a.path).collect::<HashSet<_>>()
107            .into_iter().map(|p| (p, ApiBranch::new(p)))
108            .collect::<HashMap<_, _>>();
109        for api in api_buffer.iter() {
110            path_apis.get_mut(api.path).map(|v| v.push(*api));
111        }
112	    // 2. init the apis_tree
113        self.apis_tree.fill(path_apis.into_values().collect::<Vec<_>>());
114	    // 3. fill the ApiBranche properties
115        for i in 1..self.apis_tree.len() {
116            let mut is_omitted_child = false;
117            let node = (*self.apis_tree).get(i).unwrap();
118            let node_var_type = node.element().var_type.clone();
119            let mut parent_index = *node.parent_index();
120            while let Some(Some(parent)) = parent_index.map(|pi| (*self.apis_tree).get_mut(pi)) {
121                parent_index = *parent.parent_index();
122                if parent.element().is_omission {
123                    is_omitted_child = true;
124                    continue;
125                }
126                match parent.element().var_type {
127                    VarType::Required(_) => parent.element_mut().is_mid_var = true,
128                    VarType::NotVar => break,
129                    _ => panic!(r#"Ambiguous type var "{}" cannot in middle."#, parent.element().id()),
130                };
131                if !matches!(node_var_type, VarType::NotVar) {
132                    parent.element_mut().var_children.push(i);
133                } else if is_omitted_child {
134                    parent.element_mut().omitted_children.push(i);
135                }
136            }
137        }
138    }
139
140    fn omitted_path(&self, path: &str) -> String {
141        // Path in api field should always be `MARK_PATH_PART_SEPARATOR`
142        let parts = path.split(MARK_PATH_PART_SEPARATOR).collect::<Vec<_>>();
143        parts.iter().enumerate()
144            .filter(|&(i, _)| !self.raw_omitted_paths.contains(&parts[..=i].join(MARK_PATH_PART_SEPARATOR).as_str()))
145            .map(|(_, p)| *p).collect::<Vec<_>>()
146            .join(MARK_PATH_PART_SEPARATOR)
147    }
148
149    fn suffixed_path(path: &str, suffix: Option<&Suffix>) -> String {
150        suffix.filter(|s| !s.as_ref().is_empty())
151            .map_or_else(|| path.to_owned(), |s| format!("{}{}{}", path, MARK_SUFFIX_BOUNDARY, s.as_ref()))
152    }
153
154    fn map_middleware(&self, handler_mapping: &HashMap<&'static str, Hook<Rp>>, pre: bool) -> HashMap<&'static str, &'static str> {
155        let mut apis_paths = self.apis_mapping.keys().collect::<Vec<_>>();
156        let mut path_mapping: HashMap<&'static str, &'static str> = Default::default();
157
158        for (key, _) in handler_mapping {
159            let mut path = key.to_string();
160            let mut suffix = String::new();
161            let wildcard = HOOK_PATH_SUFFIXES.iter().find(|s| key.ends_with(*s));
162
163            if let Some(w) = wildcard {
164                suffix = key[key.len() - w.len()..].to_string();
165                path = key[..key.len() - w.len()].to_string();
166            }
167            apis_paths = apis_paths
168                .into_iter()
169                .filter(|api_path| {
170                    if {
171                        if suffix != HOOK_PATH_SUFFIXES[0] && path.eq(*api_path) {
172                            true
173                        } else if matches!(wildcard, Some(_)) {
174                            path.is_empty() || api_path.starts_with(&(path.clone() + MARK_PATH_PART_SEPARATOR))
175                        } else {
176                            false
177                        }
178                    } {
179                        Self::hook_override_warn(api_path, &path_mapping, pre);
180                        path_mapping.insert(api_path, key);
181                        false
182                    } else {
183                        true
184                    }
185                })
186                .collect();
187        }
188        path_mapping
189    }
190
191    fn hook_override_warn(path: &str, mapping: &HashMap<&'static str, &'static str>, pre: bool) {
192        if mapping.contains_key(path) {            
193            let hook = if pre { "pre-handler" } else { "post-handler" };
194            warn!(r#"Path "{}" already has a {}; reassigning it will overwrite the current one."#, path, hook);
195        }
196    }
197
198    pub fn lookup(&self, rp: &Rp) -> DceResult<RouteMatch<'_, Rp>> {
199        self.lookup_api(rp)
200            .map(|(api, params, suffix)| {
201                let pre_hook = self.path_before_mapping.get(api.path)
202                    .map(|p| self.before_mapping.get(p)).flatten();
203                let post_hook = self.path_after_mapping.get(api.path)
204                    .map(|p| self.after_mapping.get(p)).flatten();
205                RouteMatch{api, params, suffix, pre_hook, post_hook}
206            })
207    }
208
209    fn lookup_api(&self, rp: &Rp) -> DceResult<(&'static Api<Rp>, HashMap<String, Param>, Option<Suffix>)> {
210        let req_path = rp.path();
211        let mut api = None;
212        let mut suffix = None;
213        let mut params = HashMap::new();
214        let mut path = req_path;
215        while api.is_none() {
216            let mut apis = self.apis_mapping.get(path);
217            if apis.is_none() {
218                if let Some((tmp_path, tmp_params, tmp_suffix)) = self.lookup_var(path) {
219                    apis = self.apis_mapping.get(Self::suffixed_path(tmp_path, tmp_suffix.as_ref()).as_str());
220                    params = tmp_params;
221                    suffix = tmp_suffix;
222                }
223            }
224            if let Some(apis) = apis {
225                if let Some(matched) = rp.match_api(apis) {
226                    if let Some(redirect) = matched.redirect {
227                        path = redirect;
228                        continue;
229                    }
230                    api = Some(matched);
231                    break;
232                }
233            }
234            if self.apis_mapping.is_empty() {
235                if self.api_buffer.read().unwrap().is_empty() {
236				    panic!(r#"Lookup failed, "Router.apiBuffer" is empty, you may need to call the "Router.Push()" to bind apis"#);
237                } else {
238				    panic!(r#"Router is not ready, please call "Router.ready()" first before lookup"#);
239                }
240            } else {
241                break;
242            }
243        }
244        api.map(|a| {
245            debug!(r#"{}: path "{}" matched api "{}""#, type_name::<Rp>(), req_path, a.path);
246            (a, params, suffix)
247        }).ok_or_else(|| DceError::pub_msg(CODE_NOT_FOUND, format!(r#"path "{}" route failed, could not matched by Router"#, path)))
248    }
249
250    fn lookup_var(&self, path: &str) -> Option<(&'static str, HashMap<String, Param>, Option<Suffix>)> {
251        let mut path_parts = path.split(self.path_separator).collect::<Vec<_>>();
252        let mut branch_and_part_indexes = vec![(0, 0)];
253        let mut params = HashMap::new();
254        let mut target_api_branch = None;
255        let mut suffix = None;
256        'outer: while let Some((branch_index, part_index)) = branch_and_part_indexes.pop() {
257            let api_branch = (*self.apis_tree).get(branch_index).unwrap();
258            let is_last_part = part_index == path_parts.len() - 1;
259            let is_overflowed = part_index >= path_parts.len();
260            if is_overflowed && ! api_branch.element().apis.is_empty() {
261                // should be finished at last request path part if not a bare tree
262                target_api_branch = Some(api_branch);
263			    break;
264            }
265            if ! is_overflowed {
266                // if not overflow and request path matched, then it must be a normal path
267                if let Some((sub_api_index, matched_suffix)) = self.find_consider_suffix(path_parts[part_index], is_last_part, api_branch.child_indexes(), &api_branch.element().omitted_children) {
268                    branch_and_part_indexes.push((sub_api_index, part_index + 1));
269                    suffix = matched_suffix;
270                    continue;
271                }
272            }
273
274            let insert_pos = branch_and_part_indexes.len();
275            for (var_branch_index, var_api_branch) in api_branch.element().var_children.iter().filter_map(
276                |i| self.apis_tree.by_index(*i).map(|n| (i, n))).collect::<Vec<_>>() {
277                if ! var_api_branch.element().is_mid_var {
278                    // if not a middle var, then should finish var path match and collect vars and end the outer loop
279                    match &var_api_branch.element().var_type {
280                        VarType::Optional(_) if is_overflowed => {},
281                        VarType::Optional(var_name) | VarType::Required(var_name) if is_last_part =>
282                            suffix = self.suffix_trimmer(&mut path_parts, var_api_branch.element(),
283                                &mut |ps|{ params.insert(var_name.clone(), Param::Scalar(ps.get(0).map(|p| p.to_string()).unwrap())); }),
284                        VarType::EmptableVector(_) if is_overflowed => {},
285                        VarType::EmptableVector(var_name) | VarType::Vector(var_name) if ! is_overflowed =>
286                            suffix = self.suffix_trimmer(&mut path_parts, var_api_branch.element(),
287                                &mut |ps|{ params.insert(var_name.clone(), Param::Vector(ps.iter().map(|p| p.to_string()).collect::<Vec<_>>())); }),
288                        _ => continue,
289                    };
290                    target_api_branch = Some(var_api_branch);
291                    break 'outer
292                } else if let VarType::Required(var_name) = &var_api_branch.element().var_type {
293				    // if it's middle var then insert to loop queue to handle it next cycle
294                    params.insert(var_name.clone(), Param::Scalar(path_parts[part_index].to_owned()));
295                    branch_and_part_indexes.insert(insert_pos, (*var_branch_index, part_index + 1));
296                }
297            }
298        }
299        target_api_branch.map(|b| (b.element().path, params, suffix))
300    }
301
302    fn suffix_trimmer(&self, parts: &mut Vec<&str>, branch: &ApiBranch<Rp>, consumer: &mut dyn FnMut(&Vec<&str>)) -> Option<Suffix> {
303        // just need to check is_last_part because should already handle suffix if overflowed
304        // pop out the last part to clean (cut off the suffix)
305        let mut suffix = None;
306        if let Some(mut last_part) = parts.pop() {
307            if let Some(ts) = branch.apis.iter().flat_map(|a| &a.suffixes).find(|s| last_part.ends_with(format!("{}{}", self.suffix_boundary, s.as_ref()).as_str())) {
308                last_part = &last_part[..last_part.len() - self.suffix_boundary.len() - ts.as_ref().len()];
309                suffix = Some(ts.clone());
310            }
311            // Push last part (basename without suffix) back into path parts
312            parts.push(last_part);
313        }
314        consumer(parts);
315        suffix
316    }
317
318    fn find_consider_suffix(&self, path_part: &str, is_last_part: bool, child_indexes: &Vec<usize>, omitted_indexes: &Vec<usize>) -> Option<(usize, Option<Suffix>)> {
319        let mut matches = self.find_from_indexes_by_part(child_indexes, path_part);
320        if matches.is_none() {
321            matches = self.find_from_indexes_by_part(omitted_indexes, path_part);
322        }
323        let mut suffix = None;
324        if matches.is_none() && is_last_part {
325            let mut boundary = Some(path_part.len());
326            loop {
327                if let Some((base_part, index)) = boundary.map(|i| (&path_part[..i], i)) {
328                    matches = self.find_from_indexes_by_part(child_indexes, base_part);
329                    if matches.is_none() {
330                        matches = self.find_from_indexes_by_part(omitted_indexes, base_part);
331                    }
332                    // 如果从剩余的路径找到了后缀边界符,且为找到匹配的API分支,则尝试以切割剩余的尾部与 Apis 里的 suffixes 匹配,两者都匹配才是真正找到
333                    suffix = matches.map(|(_, ab)| ab.apis.iter().flat_map(|a| &a.suffixes)
334                        .find(|s| path_part[index + 1..].eq(s.as_ref())))
335                        .flatten().map(|s| s.clone());
336                    if suffix.is_none() {
337                        // 如果未找到API分支或后缀不匹配,则跳过此轮循环以尝试前移边界匹配API分支
338                        boundary = path_part[..index].rfind(self.suffix_boundary);
339                        continue;
340                    }
341                }
342                break;
343            }
344        }
345        matches.map(|(i, _)| (i, suffix))
346    }
347
348    fn find_from_indexes_by_part(&self, indexes: &Vec<usize>, part: &str) -> Option<(usize, &ApiBranch<Rp>)> {
349        indexes.iter()
350            .filter_map(|i| self.apis_tree.by_index(*i).map(|n| (*i, n.element())))
351            .find(|(_, ab)| ab.path_part().eq(part))
352    }
353}
354
355pub struct RouteMatch<'a, Rp: RoutableProtocol + 'static> {
356    pub api: &'static Api<Rp>,
357    pub params: HashMap<String, Param>,
358    pub suffix: Option<Suffix>,
359    pub pre_hook: Option<&'a Hook<Rp>>,
360    pub post_hook: Option<&'a Hook<Rp>>,
361}
362
363#[derive(Clone)]
364pub enum VarType {
365    NotVar,
366    Required(String),
367    Optional(String),
368    Vector(String),
369    EmptableVector(String),
370}
371
372struct ApiBranch<Rp: RoutableProtocol + 'static> {
373    path: &'static str,
374    var_type: VarType,
375    is_mid_var: bool,
376    is_omission: bool,
377    apis: Vec<&'static Api<Rp>>,
378    var_children: Vec<usize>,
379    omitted_children: Vec<usize>,
380}
381
382impl <Rp: RoutableProtocol + 'static> ApiBranch<Rp> {
383    fn push(&mut self, api: &'static Api<Rp>) {
384        self.apis.push(api);
385        if api.omission {
386            self.is_omission = true;
387        }
388    }
389
390    fn path_part(&self) -> &str {
391        self.path.rfind(MARK_PATH_PART_SEPARATOR).map_or(self.path, |i| &self.path[i+1..])
392    }
393
394    fn new(path: &'static str) -> Self {
395        let mut var_type = VarType::NotVar;
396        if path.starts_with(MARK_VARIABLE_OPENER) && path.ends_with(MARK_VARIABLE_CLOSING) {
397            let mut var_name = path[MARK_VARIABLE_OPENER.len() .. path.len() - MARK_VARIABLE_CLOSING.len()].to_string();
398            if var_name.ends_with(MARK_VAR_TYPE_OPTIONAL) {
399                var_name = var_name[..var_name.len() - MARK_VAR_TYPE_OPTIONAL.len()].to_string();
400                var_type = VarType::Optional(var_name);
401            } else if var_name.ends_with(MARK_VAR_TYPE_EMPTABLE_VECTOR) {
402                var_name = var_name[..var_name.len() - MARK_VAR_TYPE_EMPTABLE_VECTOR.len()].to_string();
403                var_type = VarType::EmptableVector(var_name);
404            } else if var_name.ends_with(MARK_VAR_TYPE_VECTOR) {
405                var_name = var_name[..var_name.len() - MARK_VAR_TYPE_VECTOR.len()].to_string();
406                var_type = VarType::Vector(var_name);
407            } else {
408                var_type = VarType::Required(var_name);
409            }
410        }
411        Self {
412            path,
413            var_type,
414            is_mid_var: Default::default(),
415            is_omission: Default::default(),
416            apis: Default::default(),
417            var_children: Default::default(),
418            omitted_children: Default::default(),
419        }
420    }
421}
422
423impl <Rp: RoutableProtocol + 'static> KeyFactory<&'static str> for ApiBranch<Rp> {
424    fn id(&self) -> &'static str {
425        self.path
426    }
427
428    fn child_of(&self, parent: &Self) -> bool {
429        self.path.rfind(MARK_PATH_PART_SEPARATOR).map_or_else(|| parent.path.is_empty(), |i| self.path[..i].eq(parent.path))
430    }
431    
432    fn new_parent(&self) -> Self {
433        Self::new(self.path.rfind(MARK_PATH_PART_SEPARATOR).map_or("", |i| &self.path[..i]))
434    }
435}