restless_web/
request.rs

1use std::collections::HashMap;
2use std::fmt::{Display, Formatter};
3use std::str::FromStr;
4
5#[derive(Default, Debug)]
6pub struct Req {
7    pub body: Option<String>,
8    pub path: String,
9    pub method: ReqMethod,
10    pub queries: HashMap<String, String>,
11    protocol: String,
12    headers: HashMap<String, String>,
13}
14
15#[derive(Default, Debug)]
16pub enum ReqMethod {
17    #[default]
18    Get,
19    Post,
20    Patch,
21    Put,
22    Delete,
23}
24
25impl Display for ReqMethod {
26    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
27        match self {
28            ReqMethod::Get => write!(f, "GET"),
29            ReqMethod::Post => write!(f, "POST"),
30            ReqMethod::Patch => write!(f, "PATCH"),
31            ReqMethod::Put => write!(f, "PUT"),
32            ReqMethod::Delete => write!(f, "DELETE"),
33        }
34    }
35}
36
37impl FromStr for ReqMethod {
38    type Err = ();
39
40    fn from_str(s: &str) -> Result<Self, Self::Err> {
41        match s {
42            "GET" => Ok(ReqMethod::Get),
43            "POST" => Ok(ReqMethod::Post),
44            "PATCH" => Ok(ReqMethod::Patch),
45            "PUT" => Ok(ReqMethod::Put),
46            "DELETE" => Ok(ReqMethod::Delete),
47            _ => Err(()),
48        }
49    }
50}
51
52impl Req {
53    pub fn new(raw_req: &str) -> Option<Req> {
54        let mut lines = raw_req.lines();
55        let mut req = Req::default();
56
57        let main_info = match lines.next() {
58            Some(v) => v,
59            None => {
60                return None;
61            }
62        };
63
64        let (req_method, path, protocol) = Req::parse_first_line(main_info).expect("Wrong format");
65
66        req.method = req_method;
67        req.path = path.parse().unwrap();
68        req.protocol = protocol.parse().unwrap();
69
70        // Pulling headers
71        lines.clone().take_while(|l| !l.is_empty()).for_each(|l| {
72            let mut split = l.split(": ");
73            let header_name = split.next().unwrap();
74            let header_value = split.next().unwrap();
75
76            req.headers
77                .insert(header_name.parse().unwrap(), header_value.parse().unwrap());
78        });
79
80        req.derive_queries();
81
82        // Pulling body
83        let body = lines
84            .clone()
85            .skip_while(|l| !l.is_empty())
86            .fold(String::new(), |mut acc, l| {
87                acc += l;
88                acc
89            });
90
91        if !body.is_empty() {
92            req.body = Some(body);
93        };
94
95        Some(req)
96    }
97
98    pub fn get(&self, header_key: &str) -> Option<&str> {
99        let header_value = self.headers.get(header_key)?;
100
101        Some(header_value)
102    }
103
104    fn parse_first_line(line: &str) -> Result<(ReqMethod, &str, &str), ()> {
105        let mut splitted = line.split_whitespace();
106
107        let method = splitted.next().ok_or(())?;
108        let path = splitted.next().ok_or(())?;
109        let protocol = splitted.next().ok_or(())?;
110
111        let req_method = ReqMethod::from_str(method)?;
112
113        Ok((req_method, path, protocol))
114    }
115
116    fn derive_queries(&mut self) {
117        if !self.path.contains('?') {
118            return;
119        }
120
121        let splitted_path = self.path.split('?').collect::<Vec<_>>();
122        let query_string = splitted_path.get(1).unwrap();
123
124        self.queries = Req::parse_query_string(query_string);
125
126        let pure_path = splitted_path.first().unwrap();
127        self.path = pure_path.parse().unwrap();
128    }
129
130    fn parse_query_string(query_string: &str) -> HashMap<String, String> {
131        let mut res = HashMap::new();
132
133        let pairs: Vec<_> = query_string.split('&').collect();
134        pairs.into_iter().for_each(|p| {
135            let mut splitted = p.split('=');
136            let name = splitted.next().unwrap().to_owned();
137            let value = splitted.next().unwrap().to_owned();
138
139            res.insert(name, value);
140        });
141
142        res
143    }
144}