axum_valid/
msgpack.rs

1//! # Support for `MsgPack<T>` and `MsgPackRaw<T>` from `axum-serde`
2//!
3//! ## Feature
4//!
5//! Enable the `msgpack` feature to use `Valid<MsgPack<T>>` and `Valid<MsgPackRaw<T>>`.
6//!
7//! ## Usage
8//!
9//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
10//! 2. In your handler function, use `Valid<MsgPack<T>>` or `Valid<MsgPackRaw<T>>` as some parameter's type.
11//!
12//! ## Example
13//!
14//! ```no_run
15//! #[cfg(feature = "validator")]
16//! mod validator_example {
17//!     use axum::routing::post;
18//!     use axum::Json;
19//!     use axum::Router;
20//!     use axum_serde::{MsgPack, MsgPackRaw};
21//!     use axum_valid::Valid;
22//!     use serde::Deserialize;
23//!     use validator::Validate;
24//!
25//!     pub fn router() -> Router {
26//!         Router::new()
27//!             .route("/msgpack", post(handler))
28//!             .route("/msgpackraw", post(raw_handler))
29//!     }
30//!     async fn handler(Valid(MsgPack(parameter)): Valid<MsgPack<Parameter>>) {
31//!         assert!(parameter.validate().is_ok());
32//!     }
33//!
34//!     async fn raw_handler(Valid(MsgPackRaw(parameter)): Valid<MsgPackRaw<Parameter>>) {
35//!         assert!(parameter.validate().is_ok());
36//!     }
37//!     #[derive(Validate, Deserialize)]
38//!     pub struct Parameter {
39//!         #[validate(range(min = 5, max = 10))]
40//!         pub v0: i32,
41//!         #[validate(length(min = 1, max = 10))]
42//!         pub v1: String,
43//!     }
44//! }
45//!
46//! #[cfg(feature = "garde")]
47//! mod garde_example {
48//!     use axum::routing::post;
49//!     use axum::Router;
50//!     use axum_serde::{MsgPack, MsgPackRaw};
51//!     use axum_valid::Garde;
52//!     use serde::Deserialize;
53//!     use garde::Validate;
54//!     
55//!     pub fn router() -> Router {
56//!         Router::new()
57//!             .route("/msgpack", post(handler))
58//!             .route("/msgpackraw", post(raw_handler))
59//!     }
60//!
61//!     async fn handler(Garde(MsgPack(parameter)): Garde<MsgPack<Parameter>>) {
62//!         assert!(parameter.validate_with(&()).is_ok());
63//!     }
64//!
65//!     async fn raw_handler(Garde(MsgPackRaw(parameter)): Garde<MsgPackRaw<Parameter>>) {
66//!         assert!(parameter.validate_with(&()).is_ok());
67//!     }
68//!     #[derive(Validate, Deserialize)]
69//!     pub struct Parameter {
70//!         #[garde(range(min = 5, max = 10))]
71//!         pub v0: i32,
72//!         #[garde(length(min = 1, max = 10))]
73//!         pub v1: String,
74//!     }
75//! }
76//!
77//! # #[tokio::main]
78//! # async fn main() -> anyhow::Result<()> {
79//! #     use std::net::SocketAddr;
80//! #     use axum::Router;
81//! #     use tokio::net::TcpListener;
82//! #     let router = Router::new();
83//! #     #[cfg(feature = "validator")]
84//! #     let router = router.nest("/validator", validator_example::router());
85//! #     #[cfg(feature = "garde")]
86//! #     let router = router.nest("/garde", garde_example::router());
87//! #     let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
88//! #     axum::serve(listener, router.into_make_service())
89//! #         .await?;
90//! #     Ok(())
91//! # }
92//! ```
93//!
94
95use crate::HasValidate;
96#[cfg(feature = "validator")]
97use crate::HasValidateArgs;
98use axum_serde::{MsgPack, MsgPackRaw};
99#[cfg(feature = "validator")]
100use validator::ValidateArgs;
101
102impl<T> HasValidate for MsgPack<T> {
103    type Validate = T;
104    fn get_validate(&self) -> &T {
105        &self.0
106    }
107}
108
109#[cfg(feature = "validator")]
110impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack<T> {
111    type ValidateArgs = T;
112    fn get_validate_args(&self) -> &Self::ValidateArgs {
113        &self.0
114    }
115}
116
117#[cfg(feature = "validify")]
118impl<T: validify::Modify> crate::HasModify for MsgPack<T> {
119    type Modify = T;
120
121    fn get_modify(&mut self) -> &mut Self::Modify {
122        &mut self.0
123    }
124}
125
126#[cfg(feature = "validify")]
127impl<T> crate::PayloadExtractor for MsgPack<T> {
128    type Payload = T;
129
130    fn get_payload(self) -> Self::Payload {
131        self.0
132    }
133}
134
135#[cfg(feature = "validify")]
136impl<T: validify::Validify + validify::ValidifyPayload> crate::HasValidify for MsgPack<T> {
137    type Validify = T;
138    type PayloadExtractor = MsgPack<T::Payload>;
139    fn from_validify(v: Self::Validify) -> Self {
140        MsgPack(v)
141    }
142}
143
144impl<T> HasValidate for MsgPackRaw<T> {
145    type Validate = T;
146    fn get_validate(&self) -> &T {
147        &self.0
148    }
149}
150
151#[cfg(feature = "validator")]
152impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPackRaw<T> {
153    type ValidateArgs = T;
154    fn get_validate_args(&self) -> &Self::ValidateArgs {
155        &self.0
156    }
157}
158
159#[cfg(feature = "validify")]
160impl<T: validify::Modify> crate::HasModify for MsgPackRaw<T> {
161    type Modify = T;
162
163    fn get_modify(&mut self) -> &mut Self::Modify {
164        &mut self.0
165    }
166}
167
168#[cfg(feature = "validify")]
169impl<T> crate::PayloadExtractor for MsgPackRaw<T> {
170    type Payload = T;
171
172    fn get_payload(self) -> Self::Payload {
173        self.0
174    }
175}
176
177#[cfg(feature = "validify")]
178impl<T: validify::Validify + validify::ValidifyPayload> crate::HasValidify for MsgPackRaw<T> {
179    type Validify = T;
180    type PayloadExtractor = MsgPackRaw<T::Payload>;
181    fn from_validify(v: Self::Validify) -> Self {
182        MsgPackRaw(v)
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use crate::tests::{ValidTest, ValidTestParameter};
189    use axum::http::StatusCode;
190    use axum_serde::{MsgPack, MsgPackRaw};
191    use reqwest::RequestBuilder;
192    use serde::Serialize;
193
194    impl<T: ValidTestParameter + Serialize> ValidTest for MsgPack<T> {
195        const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY;
196
197        fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
198            builder
199                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
200                .body(
201                    rmp_serde::to_vec_named(T::valid())
202                        .expect("Failed to serialize parameters to msgpack"),
203                )
204        }
205
206        fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
207            #[derive(Serialize, Default)]
208            struct ErrorData {
209                error_field0: i32,
210                error_field1: Option<String>,
211            }
212            builder
213                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
214                .body(
215                    rmp_serde::to_vec(&ErrorData::default())
216                        .expect("Failed to serialize parameters to msgpack"),
217                )
218        }
219
220        fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
221            builder
222                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
223                .body(
224                    rmp_serde::to_vec_named(T::invalid())
225                        .expect("Failed to serialize parameters to msgpack"),
226                )
227        }
228    }
229
230    impl<T: ValidTestParameter + Serialize> ValidTest for MsgPackRaw<T> {
231        const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY;
232
233        fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
234            builder
235                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
236                .body(
237                    rmp_serde::to_vec(T::valid())
238                        .expect("Failed to serialize parameters to msgpack"),
239                )
240        }
241
242        fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
243            #[derive(Serialize, Default)]
244            struct ErrorData {
245                error_field0: i32,
246                error_field1: Option<String>,
247            }
248            builder
249                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
250                .body(
251                    rmp_serde::to_vec(&ErrorData::default())
252                        .expect("Failed to serialize parameters to msgpack"),
253                )
254        }
255
256        fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
257            builder
258                .header(reqwest::header::CONTENT_TYPE, "application/msgpack")
259                .body(
260                    rmp_serde::to_vec(T::invalid())
261                        .expect("Failed to serialize parameters to msgpack"),
262                )
263        }
264    }
265}