arcanum/
router.rs

1use std::{collections::HashMap, path::Path};
2
3use crate::{serve_static_file, webserver::RouteHandler, Request, Response, ReturnData};
4
5#[derive(Clone)]
6pub struct Router {
7    basepath: String,
8    routes: HashMap<String, RouteHandler>,
9}
10
11impl Router {
12    pub fn new(base_path: &str) -> Self {
13        Self {
14            basepath: base_path.to_string(),
15            routes: HashMap::new(),
16        }
17    }
18
19    pub fn add_simple_route(
20        &mut self,
21        route: &str,
22        function: fn(_: Request, _: &mut Response) -> ReturnData,
23    ) {
24        self.routes
25            .insert(route.to_owned(), RouteHandler::Simple(function));
26    }
27
28    pub fn add_route_with_params(
29        &mut self,
30        route: &str,
31        function: fn(Request, &mut Response, HashMap<String, String>) -> ReturnData,
32    ) {
33        self.routes
34            .insert(route.to_owned(), RouteHandler::WithRouteParams(function));
35    }
36
37    pub fn add_static_file_route(&mut self, route: &str, path: &str) {
38        let mut params: HashMap<String, String> = HashMap::new();
39        params.insert("basepath".to_string(), path.to_string());
40        self.routes.insert(
41            route.to_owned(),
42            RouteHandler::WithRouteAndOptionalParams(
43                |_req: Request, res: &mut Response, params: HashMap<String, String>| {
44                    if !params.contains_key("basepath") {
45                        res.set_status_code(500);
46                        return ReturnData::Text("Something went wrong!".to_string());
47                    }
48                    if !params.contains_key("path") {
49                        res.set_status_code(403);
50                        return ReturnData::Text("Cannot index folders".to_string());
51                    } else if params["path"].ends_with("/") {
52                        res.set_status_code(403);
53                        return ReturnData::Text("Cannot index folders".to_string());
54                    }
55                    let path = Path::new(&params["basepath"]).join(&params["path"]);
56                    serve_static_file(path.to_str().unwrap(), res)
57                },
58                params,
59            ),
60        );
61    }
62
63    
64
65    pub fn does_path_exists(
66        &self,
67        path: &str,
68    ) -> Option<String> {
69        let mut current_path = None;
70        let path_parts: Vec<&str> = path.split("/").filter(|i| !i.is_empty()).collect();
71        for i in self.routes.clone() {
72            let mut route_parts: Vec<&str> = i.0.split("/").filter(|i| !i.is_empty()).collect();
73            // println!("route_parts: {route_parts:?}\n path_parts: {path_parts:?}\n")
74            let basepath_parts: Vec<&str> = self.basepath.split("/").filter(|i| !i.is_empty()).collect();
75            route_parts.extend(basepath_parts);
76            // println!("route_parts: {route_parts:?}\n path_parts: {path_parts:?}\n");
77            if route_parts.len() != path_parts.len() {
78                let diff = path_parts.len() as i32 - route_parts.len() as i32;
79                if diff > 0 {
80                    for _ in 0..diff {
81                        route_parts.push("");
82                    }
83                }
84            }
85            if route_parts == path_parts {
86                current_path = Some(i.0.to_owned());
87                continue;
88            }
89
90            for (route_part, path_part) in route_parts.iter().zip(path_parts.iter()) {
91                // println!("route_part: {route_part}\n path_part: {path_part}\n");
92                if route_part.starts_with(":") || route_part == path_part || route_part == &"*" {
93                    current_path = Some(i.0.to_owned());
94                } else if route_part == &"**" {
95                    current_path = Some(i.0.to_owned());
96                    break;
97                } else if route_part != path_part {
98                    current_path = None;
99                    break;
100                }
101            }
102            if current_path.is_some() {
103                break;
104            }
105        }
106        // DEBUG: println!("{:?}", current_path);
107        current_path
108    }
109    pub fn route_handler_from_path(&self, path: String) -> Option<RouteHandler> {
110        if self.routes.keys().any(|i| i == &path) {
111            return Some(self.routes[&path].clone());
112        }
113        None
114    }
115}