1use std::{collections::HashMap, future::Future, pin::Pin};
2
3use regex::Regex;
4use url::Url;
5
6use crate::enums::request_method::RequestMethod;
7
8type Action = Box<dyn Fn(HashMap<String, String>) -> Pin<Box<dyn Future<Output = ()>>>>;
9
10struct Node {
11 children: HashMap<String, Node>,
12 action: Option<Action>,
13}
14
15pub struct Router {
16 root: Node,
17}
18
19impl Router {
20 const HOST: &str = "https://www.example.com";
21
22 pub fn new() -> Self {
23 Router {
24 root: Node {
25 children: HashMap::new(),
26 action: None,
27 },
28 }
29 }
30
31 pub fn register(&mut self, method: RequestMethod, route: &str, action: Action) {
32 let mut ptr = &mut self.root;
33
34 if !ptr.children.contains_key(&method.to_string()) {
35 ptr.children.insert(
36 method.to_string(),
37 Node {
38 children: HashMap::new(),
39 action: None,
40 },
41 );
42 }
43
44 ptr = ptr.children.get_mut(&method.to_string()).unwrap();
45
46 for s in route.split("/") {
47 if !ptr.children.contains_key(s) {
48 ptr.children.insert(
49 s.to_string(),
50 Node {
51 children: HashMap::new(),
52 action: None,
53 },
54 );
55 }
56
57 ptr = ptr.children.get_mut(s).unwrap();
58 }
59
60 ptr.action = Some(action);
61 }
62
63 pub async fn resolve(
64 &self,
65 method: RequestMethod,
66 route: &str,
67 action_args: Option<HashMap<String, String>>,
68 ) -> bool {
69 let mut route = route.to_string();
70 let path_param_regex: Regex = Regex::new(r"^<(?<key>.+)>$").unwrap();
71
72 let mut action_args: HashMap<String, String> = action_args.unwrap_or(HashMap::new());
73
74 if let Ok(url) = Url::parse(&format!("{}{}", Self::HOST, route)) {
75 for (k, v) in url.query_pairs() {
76 action_args.insert(k.to_string(), v.to_string());
77 }
78 route = url.path().to_string();
79 }
80
81 let mut ptr = &self.root;
82
83 if !ptr.children.contains_key(&method.to_string()) {
84 return false;
85 }
86
87 ptr = ptr.children.get(&method.to_string()).unwrap();
88
89 for s in route.split("/") {
90 if !ptr.children.contains_key(s) {
91 if let Some(param) = ptr
92 .children
93 .keys()
94 .find(|key| path_param_regex.is_match(key))
95 {
96 let caps = path_param_regex.captures(¶m).unwrap();
97 action_args.insert(caps["key"].to_string(), s.to_string());
98 ptr = ptr.children.get(param).unwrap();
99 continue;
100 } else {
101 return false;
102 }
103 }
104
105 ptr = ptr.children.get(s).unwrap();
106 }
107
108 if let Some(action) = &ptr.action {
109 action(action_args).await;
110 true
111 } else {
112 false
113 }
114 }
115}