axum_valid/cbor.rs
1//! # Support for `Cbor<T>`
2//!
3//! ## Feature
4//!
5//! Enable the `cbor` feature to use `Valid<Cbor<T>>`.
6//!
7//! ## Usage
8//!
9//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
10//! 2. In your handler function, use `Valid<Cbor<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_serde::Cbor;
19//! use axum::Router;
20//! use axum_valid::Valid;
21//! use serde::Deserialize;
22//! use validator::Validate;
23//!
24//! pub fn router() -> Router {
25//! Router::new().route("/cbor", post(handler))
26//! }
27//!
28//! async fn handler(Valid(Cbor(parameter)): Valid<Cbor<Parameter>>) {
29//! assert!(parameter.validate().is_ok());
30//! // Support automatic dereferencing
31//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1);
32//! }
33//!
34//! #[derive(Validate, Deserialize)]
35//! pub struct Parameter {
36//! #[validate(range(min = 5, max = 10))]
37//! pub v0: i32,
38//! #[validate(length(min = 1, max = 10))]
39//! pub v1: String,
40//! }
41//! }
42//!
43//! #[cfg(feature = "garde")]
44//! mod garde_example {
45//! use axum::routing::post;
46//! use axum_serde::Cbor;
47//! use axum::Router;
48//! use axum_valid::Garde;
49//! use serde::Deserialize;
50//! use garde::Validate;
51//!
52//! pub fn router() -> Router {
53//! Router::new().route("/cbor", post(handler))
54//! }
55//!
56//! async fn handler(Garde(Cbor(parameter)): Garde<Cbor<Parameter>>) {
57//! assert!(parameter.validate_with(&()).is_ok());
58//! // Support automatic dereferencing
59//! println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1);
60//! }
61//!
62//! #[derive(Validate, Deserialize)]
63//! pub struct Parameter {
64//! #[garde(range(min = 5, max = 10))]
65//! pub v0: i32,
66//! #[garde(length(min = 1, max = 10))]
67//! pub v1: String,
68//! }
69//! }
70//!
71//! # #[tokio::main]
72//! # async fn main() -> anyhow::Result<()> {
73//! # use std::net::SocketAddr;
74//! # use axum::Router;
75//! # use tokio::net::TcpListener;
76//! # let router = Router::new();
77//! # #[cfg(feature = "validator")]
78//! # let router = router.nest("/validator", validator_example::router());
79//! # #[cfg(feature = "garde")]
80//! # let router = router.nest("/garde", garde_example::router());
81//! # let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
82//! # axum::serve(listener, router.into_make_service())
83//! # .await?;
84//! # Ok(())
85//! # }
86//! ```
87
88use crate::HasValidate;
89#[cfg(feature = "validator")]
90use crate::HasValidateArgs;
91use axum_serde::Cbor;
92#[cfg(feature = "validator")]
93use validator::ValidateArgs;
94
95impl<T> HasValidate for Cbor<T> {
96 type Validate = T;
97 fn get_validate(&self) -> &T {
98 &self.0
99 }
100}
101
102#[cfg(feature = "validator")]
103impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Cbor<T> {
104 type ValidateArgs = T;
105 fn get_validate_args(&self) -> &Self::ValidateArgs {
106 &self.0
107 }
108}
109
110#[cfg(feature = "validify")]
111impl<T: validify::Modify> crate::HasModify for Cbor<T> {
112 type Modify = T;
113
114 fn get_modify(&mut self) -> &mut Self::Modify {
115 &mut self.0
116 }
117}
118
119#[cfg(feature = "validify")]
120impl<T> crate::PayloadExtractor for Cbor<T> {
121 type Payload = T;
122
123 fn get_payload(self) -> Self::Payload {
124 self.0
125 }
126}
127
128#[cfg(feature = "validify")]
129impl<T: validify::Validify + validify::ValidifyPayload> crate::HasValidify for Cbor<T> {
130 type Validify = T;
131 type PayloadExtractor = Cbor<T::Payload>;
132 fn from_validify(v: Self::Validify) -> Self {
133 Cbor(v)
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use crate::tests::{ValidTest, ValidTestParameter};
140 use axum::http::StatusCode;
141 use axum_serde::Cbor;
142 use reqwest::RequestBuilder;
143 use serde::Serialize;
144
145 impl<T: ValidTestParameter + Serialize> ValidTest for Cbor<T> {
146 const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY;
147
148 fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
149 let mut vec = Vec::new();
150 ciborium::ser::into_writer(&T::valid(), &mut vec)
151 .expect("Failed to serialize parameters to cbor");
152 builder
153 .header(reqwest::header::CONTENT_TYPE, "application/cbor")
154 .body(vec)
155 }
156
157 fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
158 #[derive(Serialize, Default)]
159 struct ErrorData {
160 error_field: i32,
161 }
162 let mut vec = Vec::new();
163 ciborium::ser::into_writer(&ErrorData::default(), &mut vec)
164 .expect("Failed to serialize parameters to cbor");
165 builder
166 .header(reqwest::header::CONTENT_TYPE, "application/cbor")
167 .body(vec)
168 }
169
170 fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
171 let mut vec = Vec::new();
172 ciborium::ser::into_writer(&T::invalid(), &mut vec)
173 .expect("Failed to serialize parameters to cbor");
174 builder
175 .header(reqwest::header::CONTENT_TYPE, "application/cbor")
176 .body(vec)
177 }
178 }
179}