1use super::body::{collect_body, parse_form, parse_json};
2use super::ParamError;
3use crate::error::FrameworkError;
4use bytes::Bytes;
5use serde::de::DeserializeOwned;
6use std::collections::HashMap;
7
8pub struct Request {
10 inner: hyper::Request<hyper::body::Incoming>,
11 params: HashMap<String, String>,
12}
13
14impl Request {
15 pub fn new(inner: hyper::Request<hyper::body::Incoming>) -> Self {
16 Self {
17 inner,
18 params: HashMap::new(),
19 }
20 }
21
22 pub fn with_params(mut self, params: HashMap<String, String>) -> Self {
23 self.params = params;
24 self
25 }
26
27 pub fn method(&self) -> &hyper::Method {
29 self.inner.method()
30 }
31
32 pub fn path(&self) -> &str {
34 self.inner.uri().path()
35 }
36
37 pub fn param(&self, name: &str) -> Result<&str, ParamError> {
40 self.params
41 .get(name)
42 .map(|s| s.as_str())
43 .ok_or_else(|| ParamError {
44 param_name: name.to_string(),
45 })
46 }
47
48 pub fn params(&self) -> &HashMap<String, String> {
50 &self.params
51 }
52
53 pub fn inner(&self) -> &hyper::Request<hyper::body::Incoming> {
55 &self.inner
56 }
57
58 pub fn header(&self, name: &str) -> Option<&str> {
60 self.inner
61 .headers()
62 .get(name)
63 .and_then(|v| v.to_str().ok())
64 }
65
66 pub fn content_type(&self) -> Option<&str> {
68 self.header("content-type")
69 }
70
71 pub fn is_inertia(&self) -> bool {
73 self.header("X-Inertia")
74 .map(|v| v == "true")
75 .unwrap_or(false)
76 }
77
78 pub fn inertia_version(&self) -> Option<&str> {
80 self.header("X-Inertia-Version")
81 }
82
83 pub fn inertia_partial_component(&self) -> Option<&str> {
85 self.header("X-Inertia-Partial-Component")
86 }
87
88 pub fn inertia_partial_data(&self) -> Option<Vec<&str>> {
90 self.header("X-Inertia-Partial-Data")
91 .map(|v| v.split(',').collect())
92 }
93
94 pub async fn body_bytes(self) -> Result<(RequestParts, Bytes), FrameworkError> {
96 let content_type = self
97 .inner
98 .headers()
99 .get("content-type")
100 .and_then(|v| v.to_str().ok())
101 .map(|s| s.to_string());
102
103 let params = self.params;
104 let bytes = collect_body(self.inner.into_body()).await?;
105
106 Ok((RequestParts { params, content_type }, bytes))
107 }
108
109 pub async fn json<T: DeserializeOwned>(self) -> Result<T, FrameworkError> {
125 let (_, bytes) = self.body_bytes().await?;
126 parse_json(&bytes)
127 }
128
129 pub async fn form<T: DeserializeOwned>(self) -> Result<T, FrameworkError> {
145 let (_, bytes) = self.body_bytes().await?;
146 parse_form(&bytes)
147 }
148
149 pub async fn input<T: DeserializeOwned>(self) -> Result<T, FrameworkError> {
157 let (parts, bytes) = self.body_bytes().await?;
158
159 match parts.content_type.as_deref() {
160 Some(ct) if ct.starts_with("application/x-www-form-urlencoded") => parse_form(&bytes),
161 _ => parse_json(&bytes),
162 }
163 }
164
165 pub fn into_parts(self) -> (RequestParts, hyper::body::Incoming) {
169 let content_type = self
170 .inner
171 .headers()
172 .get("content-type")
173 .and_then(|v| v.to_str().ok())
174 .map(|s| s.to_string());
175
176 let params = self.params;
177 let body = self.inner.into_body();
178
179 (RequestParts { params, content_type }, body)
180 }
181}
182
183#[derive(Clone)]
187pub struct RequestParts {
188 pub params: HashMap<String, String>,
189 pub content_type: Option<String>,
190}