http_pool/
route.rs

1use std::cmp::Ordering;
2use std::collections::HashMap;
3use std::fmt::Debug;
4
5/// 路由类型
6#[derive(Debug, Clone, Copy, PartialOrd, PartialEq, Ord, Eq)]
7pub enum RouteType {
8    /// 静态路径: /home/sub
9    Static,
10    /// 动态路径: /home/:page
11    Dynamic,
12    /// 通配符: /home/*
13    Wildcard,
14}
15
16/// 路由匹配结果
17pub enum RouteMatch<T: Clone> {
18    Matched {
19        handler: T,
20        params: HashMap<String, String>,
21    },
22    NotMatched,
23}
24
25/// 路由规则
26pub struct Route<T: Clone> {
27    path: String,
28    parts: Vec<String>,
29    handler: T,
30    route_type: RouteType,
31}
32
33/// 路由
34pub struct Router<T: Clone> {
35    routes: Vec<Route<T>>,
36}
37
38impl<T: Clone> Router<T> {
39    pub fn new() -> Self {
40        Router { routes: vec![] }
41    }
42
43    /// 添加路由规则
44    /// 支持以下路由规则
45    ///     1.静态路径匹配
46    ///     2.动态路径匹配,
47    ///     3.通配符匹配
48    pub fn add(&mut self, path: impl Into<String>, handler: T) {
49        let path = path.into();
50
51        let route_type = if path.contains("*") {
52            RouteType::Wildcard
53        } else if path.contains(":") {
54            RouteType::Dynamic
55        } else {
56            RouteType::Static
57        };
58
59        let parts = path
60            .trim_matches('/')
61            .split('/')
62            .map(|s| s.to_string())
63            .collect();
64
65        self.routes.push(Route {
66            path,
67            parts,
68            handler,
69            route_type,
70        });
71
72        // 按优先级排序(Static > Dynamic > Wildcard)
73        self.routes.sort_by(|a, b| a.route_type.cmp(&b.route_type))
74    }
75
76    /// 移除路由规则
77    pub fn remove(&mut self, path: &str) -> Option<Route<T>> {
78        for i in (0..self.routes.len()).rev() {
79            if path.cmp(&self.routes[i].path) == Ordering::Equal {
80                return Some(self.routes.remove(i));
81            }
82        }
83
84        None
85    }
86
87    /// 匹配路由规则
88    /// 支持以下路由规则
89    ///     1.静态路径匹配
90    ///     2.动态路径匹配,
91    ///     3.通配符匹配
92    pub fn go_match(&self, path: &str) -> RouteMatch<T> {
93        let parts: Vec<&str> = path.trim_matches('/').split('/').collect();
94
95        for route in &self.routes {
96            let route_parts: Vec<&str> = route.parts.iter().map(|s| s as &str).collect();
97            let mut params = HashMap::new();
98
99            if route_parts.len() != parts.len()
100                && !route_parts.iter().any(|part| part.starts_with('*'))
101            {
102                continue;
103            }
104
105            let mut matched = true;
106
107            for (i, route_part) in route_parts.iter().enumerate() {
108                if *route_part == "*" || route_part.starts_with('*') {
109                    // wildcard match
110                    let key = route_part.trim_start_matches('*');
111                    params.insert(key.to_string(), parts[i..].join("/"));
112                    break;
113                } else if route_part.starts_with(':') {
114                    // dynamic match
115                    let key = route_part.trim_start_matches(':');
116                    if let Some(val) = parts.get(i) {
117                        params.insert(key.to_string(), val.to_string());
118                    } else {
119                        matched = false;
120                        break;
121                    }
122                } else if let Some(req_part) = parts.get(i) {
123                    if route_part != req_part {
124                        matched = false;
125                        break;
126                    }
127                } else {
128                    matched = false;
129                    break;
130                }
131            }
132
133            if matched {
134                return RouteMatch::Matched {
135                    handler: route.handler.clone(),
136                    params,
137                };
138            }
139        }
140
141        RouteMatch::NotMatched
142    }
143}