speed_rs_core/
utils.rs

1use std::{collections::HashMap, fs::{self, File}, error::Error};
2
3use mime_guess::{MimeGuess};
4
5use crate::{HttpRequest, HttpServer, HttpStatusStruct, HttpResponse, RequestHandleFunc, RequestErrorHandleFunc};
6
7/// Stand alone function for breaking `HttpRequest` into path and params
8/// ```rust
9/// server.insert_handler(|req, res| {
10///     let (path, params) = break_request_uri(&req);
11///     Ok((req, res))
12/// });
13/// ```
14pub fn break_request_uri(req: &HttpRequest) -> (String, HashMap<String, String>) {
15    let uri = req.uri();
16    let parts: Vec<&str> = uri.split('?').collect();
17    let mut params = HashMap::<String, String>::new();
18    let path = String::from(if let Some(path) = parts.get(0) { path } else { "/" });
19    if parts.len() >= 2 {
20        let pairs: Vec<&str> = parts[1].split('&').collect();
21        for pair in pairs {
22            let key_val: Vec<&str> = pair.split('=').collect();
23            params.insert(String::from(key_val[0]), String::from(if let Some(val) = key_val.get(1) { val } else { "" }));
24        }
25    }
26    (path, params)
27}
28
29/// Provide more details for `HttpRequest`
30/// ```rust
31/// server.insert_handler(|req, res| {
32///     let path = req.path();
33///     let params = req.params();
34///     Ok((req, res))
35/// });
36/// ```
37pub trait MoreDetailsRequest {
38    /// Get request's path
39    fn path(&self) -> String;
40
41    /// Get request's parameters
42    fn params(&self) -> HashMap<String, String>;
43}
44
45impl MoreDetailsRequest for HttpRequest {
46    fn path(&self) -> String {
47        break_request_uri(&self).0
48    }
49
50    fn params(&self) -> HashMap<String, String> {
51        break_request_uri(&self).1
52    }
53}
54
55/// Provide `HttpServer` the ability to serve static files
56/// ```rust
57/// server.serve_static(None);      // Default folder is "public"
58/// server.serve_static(Some(String::from("your_dir")));
59/// ```
60pub trait ServeStatic {
61    /// Serve files in the `root_dir` folder. Default root dir is `public`.
62    fn serve_static(&mut self, root_dir: Option<String>);
63}
64
65impl ServeStatic for HttpServer {
66    fn serve_static(&mut self, root_dir: Option<String>) {
67        let roor_dir = root_dir.unwrap_or(String::from("public"));
68        self.insert_handler(move |req, mut res| {
69            let path = req.path();
70            let prefix = format!("/{}", &roor_dir);
71            if let Some(index) = path.find(&prefix) {
72                if index == 0 {
73                    let path = String::from(&path[prefix.len()..path.len()]);
74                    let file_path = format!("{}{}", &roor_dir, &path);
75                    if let Ok(file) = File::open(&file_path) {
76                        if let Ok(metadata) = file.metadata() {
77                            if metadata.is_file() {
78                                let data = fs::read(&file_path).unwrap_or(Vec::new());
79                                let guess = MimeGuess::from_path(&file_path);
80                                res.set_status(HttpStatusStruct(200, "OK"));
81                                res.insert_header(String::from("Content-Type"), guess.first_or(mime_guess::mime::TEXT_PLAIN).to_string());
82                                res.bytes(data);
83                            }
84                        }
85                    }
86                }
87            }
88            Ok((req, res))
89        });
90    }
91}
92
93/// Route definition
94pub struct Route(String, RequestHandleFunc);
95
96impl Route {
97    pub fn all<F>(path: &str, handler: F) -> Self
98            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
99        Self(String::from(path), Box::new(handler))
100    }
101
102    pub fn get<F>(path: &str, handler: F) -> Self
103            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
104        Self(String::from(path), Box::new(move |req, res| {
105            if req.method() == "GET" {
106                handler(req, res)
107            } else {
108                Ok((req, res))
109            }
110        }))
111    }
112
113    pub fn post<F>(path: &str, handler: F) -> Self
114            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
115        Self(String::from(path), Box::new(move |req, res| {
116            if req.method() == "POST" {
117                handler(req, res)
118            } else {
119                Ok((req, res))
120            }
121        }))
122    }
123    
124    pub fn put<F>(path: &str, handler: F) -> Self
125            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
126        Self(String::from(path), Box::new(move |req, res| {
127            if req.method() == "PUT" {
128                handler(req, res)
129            } else {
130                Ok((req, res))
131            }
132        }))
133    }
134
135    pub fn patch<F>(path: &str, handler: F) -> Self
136            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
137        Self(String::from(path), Box::new(move |req, res| {
138            if req.method() == "PATCH" {
139                handler(req, res)
140            } else {
141                Ok((req, res))
142            }
143        }))
144    }
145
146    pub fn delete<F>(path: &str, handler: F) -> Self
147            where F: Fn(HttpRequest, HttpResponse) -> Result<(HttpRequest, HttpResponse), (HttpRequest, HttpResponse, Box<dyn Error>)> + Send + Sync + 'static {
148        Self(String::from(path), Box::new(move |req, res| {
149            if req.method() == "DELETE" {
150                handler(req, res)
151            } else {
152                Ok((req, res))
153            }
154        }))
155    }
156}
157
158/// A standard router provides basic routing support.
159/// ```rust
160/// let mut router = Router::new();
161/// // define a route to handle request when client call GET /test/
162/// router.define_route(Route::get("/test/", |req, res| {
163///     res.insert_header("Content-Type".to_string(), "text/plain".to_string());
164///     res.set_status(HttpStatusStruct(200, "OK"));
165///     res.text(String::from("GET /test/"));
166///     Ok((req, res))
167/// }));
168/// ```
169/// Be mindful of the define order of the routes, for example:
170/// ```rust
171/// router.define_route(Route::all("/test/", |req, res| {...}));
172/// router.define_route(Route::get("/test/", |req, res| {...}));    // This will be called again if client request with a GET method
173/// ```
174/// Therefore, you should be careful when define routes.
175pub struct Router {
176    routes: Vec<Route>
177}
178
179impl Router {
180    pub fn new() -> Self {
181        Self { routes: Vec::new() }
182    }
183
184    pub fn define_route(&mut self, route: Route) {
185        self.routes.push(route);
186    }
187}
188
189/// Provide `HttpServer` the `insert_router()` function.
190/// ```rust
191/// let mut router = Router::new();
192/// 
193/// // Begin defining routes
194/// ...
195/// // End defining routes
196/// 
197/// server.insert_router(router);
198/// ```
199pub trait Routing {
200    fn insert_router(&mut self, router: Router);
201}
202
203impl Routing for HttpServer {
204    fn insert_router(&mut self, router: Router) {
205        self.insert_handler(move |req, res| {
206            let path = req.path();
207            let mut routes = router.routes.iter();
208            loop {
209                if let Some(route) = routes.next() {
210                    if route.0 == path {
211                        break route.1(req, res);
212                    }
213                } else {
214                    break Ok((req, res));
215                }
216            }
217        });
218    }
219}