lib_shared/
request.rs

1use std::collections::HashMap;
2use serde_json::Value;
3
4#[derive(PartialEq, Eq, Hash, Debug, Clone)]
5pub enum ContentType {
6    ApplicationJson,
7    TextPlain,
8    TextHtml,
9}
10
11#[derive(PartialEq, Eq, Hash, Debug, Clone)]
12pub enum QueryArg {
13    Single(String),
14    Multiple(Vec<String>),    
15}
16
17#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
18pub enum Method {
19    GET,    
20    POST,
21    PUT,
22    DELETE,
23    OPTIONS,       
24    NONE, 
25}
26
27#[derive(PartialEq, Eq, Debug, Clone)]
28pub struct Request {
29    pub method: Method,
30    pub uri: String,
31    pub path: String,
32    pub query: Option<String>,
33    pub headers: HashMap<String, String>,
34    pub body: Vec<u8>,
35    pub params: HashMap<String, String>,
36}
37
38impl Request {
39
40    /// A way to create a new empty Request
41    /// 
42    ///     Some important fields:
43    /// 
44    ///  method: Represents the http method 
45    ///  path: Represents the path that the Request uses
46    ///  headers: Represents the headers of a Request
47    ///  body: The body/data of the Request. Containts the "data"
48    ///  params: The params of the Request. Example: if the path is "/<int:test>", then the params are the int variable and the naem of the variable is test.    
49
50    pub fn new() -> Request {
51        Request {
52            method: Method::NONE,
53            uri: String::new(),
54            path: String::new(),
55            query: None,
56            headers: HashMap::new(),
57            body: Vec::new(),    
58            params: HashMap::new(),
59        }
60    }        
61
62    /// returns Request with the params being inserted to the Request's params field 
63
64    pub fn with_params(mut self, params: HashMap<String, String>) -> Self {
65        self.params = params;
66        self
67    }
68
69    /// Pulls the variable from the route, if the route is defined with params. 
70    /// 
71    /// Example:  
72    /// 
73    ///     #[route(path="/<int:name_defined_here>", method="[POST, GET]")]
74    ///     fn double_handler(req: &Request) -> Response {
75    ///         let pulled_variable: i32 = req.get_var("not_the_same_name"); // This panics because:
76    ///         // the path defined in the route_macro is NOT the same as the one given to the get_var() function
77    ///
78    ///         let mut res = Response::new();              
79    ///
80    ///         res
81    ///     }    
82
83    pub fn get_var<T: FromUri>(&self, name: &str) -> T {
84        if !self.params.contains_key(name) {
85            panic!("Invalid route parameter {:?}", name);
86        }
87
88        FromUri::from_uri(&self.params[name])
89    }
90
91    /// returns Request in json.
92    /// 
93    /// Returns Option<Value>, where:
94    /// 
95    /// Some: Is the Request in json. 
96    /// 
97    /// None: The request is not json.
98
99    pub fn json(&self) -> Option<Value> { // todo checkkaa toimiiks muilki serde json jutuils, jotka ei oo Value
100
101        let binding = self.body.clone();
102        let data = std::str::from_utf8(&binding).ok();
103
104        match data {
105            Some(data) =>{  let v: Option<Value> = serde_json::from_str(data).ok(); return v; },
106            None => { let v: Option<Value> = serde_json::from_str("").ok(); return v; },
107        }            
108    }
109
110    /// returns the Request's body as string
111
112    pub fn get_string(&self) -> String {
113        return String::from_utf8(self.body.clone()).expect("Bytes should be valid utf8");
114    }    
115
116    pub fn parse(&mut self, rqstr: &str) -> std::result::Result<(), RequestError> {
117        let mut lines = rqstr.split("\r\n");
118                
119        if let Some(request_line) = lines.next() {
120            let parts: Vec<&str> = request_line.splitn(3, ' ').collect();
121            if parts.len() != 3 {
122                return Err(RequestError::InvalidRequestLine);
123            }
124            self.method = match_method(parts[0]);
125            self.uri = parts[1].to_string();
126            self.parse_uri(parts[1]);
127        } else {
128            return Err(RequestError::InvalidRequestLine);
129        }
130        
131        for line in lines.by_ref() {
132            if line.is_empty() {
133                break; 
134            }
135
136            if let Some((name, value)) = line.split_once(": ") {
137                self.headers.insert(name.to_lowercase(), value.to_string());
138            }
139        }
140        
141        self.body = lines.collect::<Vec<&str>>().join("\r\n").into_bytes();
142
143        Ok(())
144    }
145
146    fn parse_uri(&mut self, uri: &str) {
147        if let Some((path, query)) = uri.split_once('?') {
148            self.path = path.to_string();
149            self.query = Some(query.to_string());
150        } else {
151            self.path = uri.to_string();
152            self.query = None;
153        }
154    }
155}
156
157pub fn match_method(method: &str) -> Method {
158    match method {
159        "GET" => Method::GET, 
160        "POST" => Method::POST,
161        "PUT" => Method::PUT,
162        "DELETE" => Method::DELETE,
163        "OPTIONS" => Method::OPTIONS,        
164        _ => { Method::NONE },
165    }
166}
167
168#[derive(Debug)]
169pub enum RequestError {
170    JsonStrError(serde_json::Error),
171    StrCopyError(std::string::FromUtf8Error),
172    InvalidRequestLine,
173}
174
175impl std::str::FromStr for Request {
176    type Err = RequestError;
177    
178    fn from_str(rqstr: &str) -> std::result::Result<Request, RequestError> {
179        let mut req = Request::new();
180        req.parse(rqstr).unwrap();
181        Ok(req)
182    }
183}
184
185pub trait FromUri {
186    fn from_uri(data: &str) -> Self;
187}
188
189impl FromUri for String {
190    fn from_uri(data: &str) -> String {
191        String::from(data)
192    }
193}
194
195impl FromUri for i32 {
196    fn from_uri(data: &str) -> i32 {
197        data.parse::<i32>().expect("matched integer can't be parsed")
198    }
199}
200
201impl FromUri for u32 {
202    fn from_uri(data: &str) -> u32 {
203        data.parse::<u32>().expect("matched integer can't be parsed")
204    }
205}
206
207impl FromUri for f32 {
208    fn from_uri(data: &str) -> f32 {
209        data.parse::<f32>().expect("matched float can't be parsed")
210    }
211}