nestforge_core/
request.rs1use std::ops::Deref;
2
3use axum::{
4 extract::{FromRequest, FromRequestParts, Path},
5 http::request::Parts,
6};
7use serde::de::DeserializeOwned;
8
9use crate::{HttpException, Validate};
10
11#[derive(Debug, Clone, Copy)]
21pub struct Param<T>(pub T);
22
23impl<T> Deref for Param<T> {
24 type Target = T;
25
26 fn deref(&self) -> &Self::Target {
27 &self.0
28 }
29}
30
31impl<T> Param<T> {
32 pub fn into_inner(self) -> T {
33 self.0
34 }
35
36 pub fn value(self) -> T {
37 self.0
38 }
39}
40
41impl<S, T> FromRequestParts<S> for Param<T>
45where
46 S: Send + Sync,
47 T: DeserializeOwned + Send + 'static,
48{
49 type Rejection = HttpException;
50
51 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
52 let Path(value) = Path::<T>::from_request_parts(parts, state)
53 .await
54 .map_err(|_| HttpException::bad_request("Invalid route parameter"))?;
55
56 Ok(Self(value))
57 }
58}
59
60pub struct Body<T>(pub T);
70
71impl<T> Deref for Body<T> {
72 type Target = T;
73
74 fn deref(&self) -> &Self::Target {
75 &self.0
76 }
77}
78
79impl<T> Body<T> {
80 pub fn into_inner(self) -> T {
81 self.0
82 }
83
84 pub fn value(self) -> T {
85 self.0
86 }
87}
88
89impl<S, T> FromRequest<S> for Body<T>
93where
94 S: Send + Sync,
95 T: DeserializeOwned + Send + 'static,
96{
97 type Rejection = HttpException;
98
99 async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> {
100 let axum::Json(value) = axum::Json::<T>::from_request(req, state)
101 .await
102 .map_err(|_| HttpException::bad_request("Invalid JSON body"))?;
103
104 Ok(Self(value))
105 }
106}
107
108pub struct ValidatedBody<T>(pub T);
109
110impl<T> Deref for ValidatedBody<T> {
111 type Target = T;
112
113 fn deref(&self) -> &Self::Target {
114 &self.0
115 }
116}
117
118impl<T> ValidatedBody<T> {
119 pub fn into_inner(self) -> T {
120 self.0
121 }
122
123 pub fn value(self) -> T {
124 self.0
125 }
126}
127
128impl<S, T> FromRequest<S> for ValidatedBody<T>
129where
130 S: Send + Sync,
131 T: DeserializeOwned + Validate + Send + 'static,
132{
133 type Rejection = HttpException;
134
135 async fn from_request(req: axum::extract::Request, state: &S) -> Result<Self, Self::Rejection> {
136 let axum::Json(value) = axum::Json::<T>::from_request(req, state)
137 .await
138 .map_err(|_| HttpException::bad_request("Invalid JSON body"))?;
139
140 value
141 .validate()
142 .map_err(HttpException::bad_request_validation)?;
143
144 Ok(Self(value))
145 }
146}