axum_valid/
toml.rs

1//! # Support for `Toml<T>` from `axum-serde`
2//!
3//! ## Feature
4//!
5//! Enable the `toml` feature to use `Valid<Toml<T>>`.
6//!
7//! ## Usage
8//!
9//! 1. Implement `Deserialize` and `Validate` for your data type `T`.
10//! 2. In your handler function, use `Valid<Toml<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::Toml;
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("/toml", post(handler))
26//!     }
27//!
28//!     async fn handler(Valid(Toml(parameter)): Valid<Toml<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::Toml;
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("/toml", post(handler))
54//!     }
55//!
56//!     async fn handler(Garde(Toml(parameter)): Garde<Toml<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::Toml;
92#[cfg(feature = "validator")]
93use validator::ValidateArgs;
94
95impl<T> HasValidate for Toml<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 Toml<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 Toml<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 Toml<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 Toml<T> {
130    type Validify = T;
131    type PayloadExtractor = Toml<T::Payload>;
132    fn from_validify(v: Self::Validify) -> Self {
133        Toml(v)
134    }
135}
136#[cfg(test)]
137mod tests {
138    use crate::tests::{ValidTest, ValidTestParameter};
139    use axum::http::StatusCode;
140    use axum_serde::Toml;
141    use reqwest::RequestBuilder;
142    use serde::Serialize;
143
144    impl<T: ValidTestParameter + Serialize> ValidTest for Toml<T> {
145        const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY;
146
147        fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
148            builder
149                .header(reqwest::header::CONTENT_TYPE, "application/toml")
150                .body(toml::to_string(&T::valid()).expect("Failed to serialize parameters to toml"))
151        }
152
153        fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
154            #[derive(Serialize, Default)]
155            struct ErrorData {
156                error_field: i32,
157            }
158            builder
159                .header(reqwest::header::CONTENT_TYPE, "application/toml")
160                .body(
161                    toml::to_string(&ErrorData::default())
162                        .expect("Failed to serialize parameters to toml"),
163                )
164        }
165
166        fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
167            builder
168                .header(reqwest::header::CONTENT_TYPE, "application/toml")
169                .body(
170                    toml::to_string(&T::invalid()).expect("Failed to serialize parameters to toml"),
171                )
172        }
173    }
174}