Skip to main content

ullun/
request.rs

1//! Request types and extractors
2
3use crate::cookies::Cookies;
4use crate::error::{Error, Result};
5use serde::de::DeserializeOwned;
6use std::cell::OnceCell;
7use std::collections::HashMap;
8
9/// Path parameters extracted from route
10#[derive(Debug, Clone)]
11pub struct Params {
12    inner: HashMap<String, String>,
13}
14
15impl Params {
16    pub fn new() -> Self {
17        Self {
18            inner: HashMap::new(),
19        }
20    }
21
22    pub fn insert(&mut self, key: String, value: String) {
23        self.inner.insert(key, value);
24    }
25
26    pub fn get(&self, key: &str) -> Result<&str> {
27        self.inner
28            .get(key)
29            .map(|s| s.as_str())
30            .ok_or_else(|| Error::bad_request(format!("Missing param: {}", key)))
31    }
32
33    pub fn get_opt(&self, key: &str) -> Option<&str> {
34        self.inner.get(key).map(|s| s.as_str())
35    }
36}
37
38impl Default for Params {
39    fn default() -> Self {
40        Self::new()
41    }
42}
43
44/// Query parameters from URL
45#[derive(Debug, Clone)]
46pub struct Query {
47    inner: HashMap<String, String>,
48}
49
50impl Query {
51    pub fn new() -> Self {
52        Self {
53            inner: HashMap::new(),
54        }
55    }
56
57    pub fn parse_query_string(query: &str) -> Self {
58        let mut q = Query::new();
59        for pair in query.split('&') {
60            if let Some((key, value)) = pair.split_once('=') {
61                q.inner.insert(
62                    key.to_string(),
63                    urlencoding::decode(value).unwrap_or_default().to_string(),
64                );
65            }
66        }
67        q
68    }
69
70    pub fn get(&self, key: &str) -> Option<&str> {
71        self.inner.get(key).map(|s| s.as_str())
72    }
73
74    pub fn parse<T: DeserializeOwned>(&self) -> Result<T> {
75        let value = serde_json::to_value(&self.inner)
76            .map_err(|e| Error::internal(format!("Failed to serialize query params: {}", e)))?;
77        serde_json::from_value(value)
78            .map_err(|e| Error::bad_request(format!("Invalid query params: {}", e)))
79    }
80}
81
82impl Default for Query {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88/// HTTP Request wrapper
89pub struct Request {
90    pub method: http::Method,
91    pub uri: http::Uri,
92    pub headers: http::HeaderMap,
93    pub body: bytes::Bytes,
94    pub params: Params,
95    pub query: Query,
96    cookies: OnceCell<Cookies>,
97}
98
99impl Request {
100    pub fn new(
101        method: http::Method,
102        uri: http::Uri,
103        headers: http::HeaderMap,
104        body: bytes::Bytes,
105    ) -> Self {
106        let query = uri
107            .query()
108            .map(Query::parse_query_string)
109            .unwrap_or_default();
110
111        Self {
112            method,
113            uri,
114            headers,
115            body,
116            params: Params::new(),
117            query,
118            cookies: OnceCell::new(),
119        }
120    }
121
122    /// Get cookies from the request
123    pub fn cookies(&self) -> &Cookies {
124        self.cookies.get_or_init(|| {
125            if let Some(cookie_header) = self.headers.get("cookie") {
126                if let Ok(cookie_str) = cookie_header.to_str() {
127                    Cookies::parse(cookie_str)
128                } else {
129                    Cookies::new()
130                }
131            } else {
132                Cookies::new()
133            }
134        })
135    }
136
137    /// Parse JSON body
138    pub fn json<T: DeserializeOwned>(&self) -> Result<T> {
139        serde_json::from_slice(&self.body)
140            .map_err(|e| Error::bad_request(format!("Invalid JSON: {}", e)))
141    }
142}