//! # Support for `MsgPack` and `MsgPackRaw` from `axum-serde` //! //! ## Feature //! //! Enable the `msgpack` feature to use `Valid>` and `Valid>`. //! //! ## Usage //! //! 1. Implement `Deserialize` and `Validate` for your data type `T`. //! 2. In your handler function, use `Valid>` or `Valid>` as some parameter's type. //! //! ## Example //! //! ```no_run //! #[cfg(feature = "validator")] //! mod validator_example { //! use axum::routing::post; //! use axum::Json; //! use axum::Router; //! use axum_serde::{MsgPack, MsgPackRaw}; //! use axum_valid::Valid; //! use serde::Deserialize; //! use validator::Validate; //! //! pub fn router() -> Router { //! Router::new() //! .route("/msgpack", post(handler)) //! .route("/msgpackraw", post(raw_handler)) //! } //! async fn handler(Valid(MsgPack(parameter)): Valid>) { //! assert!(parameter.validate().is_ok()); //! } //! //! async fn raw_handler(Valid(MsgPackRaw(parameter)): Valid>) { //! assert!(parameter.validate().is_ok()); //! } //! #[derive(Validate, Deserialize)] //! pub struct Parameter { //! #[validate(range(min = 5, max = 10))] //! pub v0: i32, //! #[validate(length(min = 1, max = 10))] //! pub v1: String, //! } //! } //! //! #[cfg(feature = "garde")] //! mod garde_example { //! use axum::routing::post; //! use axum::Router; //! use axum_serde::{MsgPack, MsgPackRaw}; //! use axum_valid::Garde; //! use serde::Deserialize; //! use garde::Validate; //! //! pub fn router() -> Router { //! Router::new() //! .route("/msgpack", post(handler)) //! .route("/msgpackraw", post(raw_handler)) //! } //! //! async fn handler(Garde(MsgPack(parameter)): Garde>) { //! assert!(parameter.validate(&()).is_ok()); //! } //! //! async fn raw_handler(Garde(MsgPackRaw(parameter)): Garde>) { //! assert!(parameter.validate(&()).is_ok()); //! } //! #[derive(Validate, Deserialize)] //! pub struct Parameter { //! #[garde(range(min = 5, max = 10))] //! pub v0: i32, //! #[garde(length(min = 1, max = 10))] //! pub v1: String, //! } //! } //! //! # #[tokio::main] //! # async fn main() -> anyhow::Result<()> { //! # use std::net::SocketAddr; //! # use axum::Router; //! # use tokio::net::TcpListener; //! # let router = Router::new(); //! # #[cfg(feature = "validator")] //! # let router = router.nest("/validator", validator_example::router()); //! # #[cfg(feature = "garde")] //! # let router = router.nest("/garde", garde_example::router()); //! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?; //! # axum::serve(listener, router.into_make_service()) //! # .await?; //! # Ok(()) //! # } //! ``` //! use crate::HasValidate; #[cfg(feature = "validator")] use crate::HasValidateArgs; use axum_serde::{MsgPack, MsgPackRaw}; #[cfg(feature = "validator")] use validator::ValidateArgs; impl HasValidate for MsgPack { type Validate = T; fn get_validate(&self) -> &T { &self.0 } } #[cfg(feature = "validator")] impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPack { type ValidateArgs = T; fn get_validate_args(&self) -> &Self::ValidateArgs { &self.0 } } #[cfg(feature = "validify")] impl crate::HasModify for MsgPack { type Modify = T; fn get_modify(&mut self) -> &mut Self::Modify { &mut self.0 } } #[cfg(feature = "validify")] impl crate::PayloadExtractor for MsgPack { type Payload = T; fn get_payload(self) -> Self::Payload { self.0 } } #[cfg(feature = "validify")] impl crate::HasValidify for MsgPack { type Validify = T; type PayloadExtractor = MsgPack; fn from_validify(v: Self::Validify) -> Self { MsgPack(v) } } impl HasValidate for MsgPackRaw { type Validate = T; fn get_validate(&self) -> &T { &self.0 } } #[cfg(feature = "validator")] impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for MsgPackRaw { type ValidateArgs = T; fn get_validate_args(&self) -> &Self::ValidateArgs { &self.0 } } #[cfg(feature = "validify")] impl crate::HasModify for MsgPackRaw { type Modify = T; fn get_modify(&mut self) -> &mut Self::Modify { &mut self.0 } } #[cfg(feature = "validify")] impl crate::PayloadExtractor for MsgPackRaw { type Payload = T; fn get_payload(self) -> Self::Payload { self.0 } } #[cfg(feature = "validify")] impl crate::HasValidify for MsgPackRaw { type Validify = T; type PayloadExtractor = MsgPackRaw; fn from_validify(v: Self::Validify) -> Self { MsgPackRaw(v) } } #[cfg(test)] mod tests { use crate::tests::{ValidTest, ValidTestParameter}; use axum::http::StatusCode; use axum_serde::{MsgPack, MsgPackRaw}; use reqwest::RequestBuilder; use serde::Serialize; impl ValidTest for MsgPack { const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec_named(T::valid()) .expect("Failed to serialize parameters to msgpack"), ) } fn set_error_request(builder: RequestBuilder) -> RequestBuilder { #[derive(Serialize, Default)] struct ErrorData { error_field0: i32, error_field1: Option, } builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec(&ErrorData::default()) .expect("Failed to serialize parameters to msgpack"), ) } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec_named(T::invalid()) .expect("Failed to serialize parameters to msgpack"), ) } } impl ValidTest for MsgPackRaw { const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY; fn set_valid_request(builder: RequestBuilder) -> RequestBuilder { builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec(T::valid()) .expect("Failed to serialize parameters to msgpack"), ) } fn set_error_request(builder: RequestBuilder) -> RequestBuilder { #[derive(Serialize, Default)] struct ErrorData { error_field0: i32, error_field1: Option, } builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec(&ErrorData::default()) .expect("Failed to serialize parameters to msgpack"), ) } fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder { builder .header(reqwest::header::CONTENT_TYPE, "application/msgpack") .body( rmp_serde::to_vec(T::invalid()) .expect("Failed to serialize parameters to msgpack"), ) } } }