tuono_lib/
request.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4use axum::http::{HeaderMap, Uri};
5
6/// Location must match client side interface
7#[derive(Serialize, Debug)]
8pub struct Location {
9    href: String,
10    pathname: String,
11    #[serde(rename(serialize = "searchStr"))]
12    search_str: String,
13    search: HashMap<String, String>,
14}
15
16impl Location {
17    pub fn pathname(&self) -> &String {
18        &self.pathname
19    }
20}
21
22impl From<Uri> for Location {
23    fn from(uri: Uri) -> Self {
24        let query = uri.query().unwrap_or("");
25        Location {
26            // TODO: build correct href
27            href: uri.to_string(),
28            pathname: uri.path().to_string(),
29            search_str: query.to_string(),
30            search: serde_urlencoded::from_str(query).unwrap_or(HashMap::new()),
31        }
32    }
33}
34
35#[derive(Debug, Clone)]
36pub struct Request {
37    pub uri: Uri,
38    pub headers: HeaderMap,
39    pub params: HashMap<String, String>,
40}
41
42impl Request {
43    pub fn new(uri: Uri, headers: HeaderMap, params: HashMap<String, String>) -> Request {
44        Request {
45            uri,
46            headers,
47            params,
48        }
49    }
50
51    pub fn location(&self) -> Location {
52        Location::from(self.uri.to_owned())
53    }
54
55    pub fn body<'de, T: Deserialize<'de>>(&'de self) -> Result<T, BodyParseError> {
56        if let Some(body) = self.headers.get("body") {
57            if let Ok(body) = body.to_str() {
58                let body = serde_json::from_str::<T>(body)?;
59                return Ok(body);
60            }
61            return Err(BodyParseError::Io(std::io::Error::new(
62                std::io::ErrorKind::InvalidData,
63                "Failed to read body",
64            )));
65        }
66        Err(BodyParseError::Io(std::io::Error::new(
67            std::io::ErrorKind::InvalidData,
68            "No body found",
69        )))
70    }
71}
72
73#[derive(Debug)]
74pub enum BodyParseError {
75    Io(std::io::Error),
76    Serde(serde_json::Error),
77}
78
79impl From<serde_json::Error> for BodyParseError {
80    fn from(err: serde_json::Error) -> BodyParseError {
81        BodyParseError::Serde(err)
82    }
83}
84
85#[cfg(test)]
86mod tests {
87
88    use super::*;
89
90    #[derive(Debug, Deserialize)]
91    struct FakeBody {
92        field1: bool,
93        field2: String,
94    }
95
96    #[test]
97    fn it_correctly_parse_the_body() {
98        let mut request = Request::new(
99            Uri::from_static("http://localhost:3000"),
100            HeaderMap::new(),
101            HashMap::new(),
102        );
103
104        request.headers.insert(
105            "body",
106            r#"{"field1": true, "field2": "hello"}"#.parse().unwrap(),
107        );
108
109        let body: FakeBody = request.body().expect("Failed to parse body");
110
111        assert!(body.field1);
112        assert_eq!(body.field2, "hello".to_string());
113    }
114
115    #[test]
116    fn it_should_trigger_an_error_when_no_body_is_found() {
117        let request = Request::new(
118            Uri::from_static("http://localhost:3000"),
119            HeaderMap::new(),
120            HashMap::new(),
121        );
122
123        let body: Result<FakeBody, BodyParseError> = request.body();
124
125        assert!(body.is_err());
126    }
127
128    #[test]
129    fn it_should_trigger_an_error_when_body_is_invalid() {
130        let mut request = Request::new(
131            Uri::from_static("http://localhost:3000"),
132            HeaderMap::new(),
133            HashMap::new(),
134        );
135
136        request
137            .headers
138            .insert("body", r#"{"field1": true"#.parse().unwrap());
139
140        let body: Result<FakeBody, BodyParseError> = request.body();
141
142        assert!(body.is_err());
143    }
144}