axum_valid/extra/
protobuf.rs

1//! # Support for `Protobuf<T>` from `axum-extra`
2//!
3//! ## Feature
4//!
5//! Enable the `extra_protobuf` feature to use `Valid<Protobuf<T>>`.
6//!
7//! ## Usage
8//!
9//! 1. Implement `prost::Message` and `Validate` for your data type `T`.
10//! 2. In your handler function, use `Valid<Protobuf<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_extra::protobuf::Protobuf;
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("/protobuf", post(handler))
26//!     }
27//!
28//!     async fn handler(Valid(Protobuf(parameter)): Valid<Protobuf<Parameter>>) {
29//!         assert!(parameter.validate().is_ok());
30//!         // Support automatic dereferencing
31//!         println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1);
32//!     }
33//!
34//!     #[derive(Validate, prost::Message)]
35//!     pub struct Parameter {
36//!         #[validate(range(min = 5, max = 10))]
37//!         #[prost(int32, tag = "1")]
38//!         pub v0: i32,
39//!         #[validate(length(min = 1, max = 10))]
40//!         #[prost(string, tag = "2")]
41//!         pub v1: String,
42//!     }
43//! }
44//!
45//! #[cfg(feature = "garde")]
46//! mod garde_example {
47//!     use axum::routing::post;
48//!     use axum::Router;
49//!     use axum_extra::protobuf::Protobuf;
50//!     use axum_valid::Garde;
51//!     use serde::Deserialize;
52//!     use garde::Validate;
53//!
54//!     pub fn router() -> Router {
55//!         Router::new().route("/protobuf", post(handler))
56//!     }
57//!
58//!     async fn handler(Garde(Protobuf(parameter)): Garde<Protobuf<Parameter>>) {
59//!         assert!(parameter.validate_with(&()).is_ok());
60//!         // Support automatic dereferencing
61//!         println!("v0 = {}, v1 = {}", parameter.v0, parameter.v1);
62//!     }
63//!
64//!     #[derive(Validate, prost::Message)]
65//!     pub struct Parameter {
66//!         #[garde(range(min = 5, max = 10))]
67//!         #[prost(int32, tag = "1")]
68//!         pub v0: i32,
69//!         #[garde(length(min = 1, max = 10))]
70//!         #[prost(string, tag = "2")]
71//!         pub v1: String,
72//!     }
73//! }
74//!
75//! # #[tokio::main]
76//! # async fn main() -> anyhow::Result<()> {
77//! #     use std::net::SocketAddr;
78//! #     use axum::Router;
79//! #     use tokio::net::TcpListener;
80//! #     let router = Router::new();
81//! #     #[cfg(feature = "validator")]
82//! #     let router = router.nest("/validator", validator_example::router());
83//! #     #[cfg(feature = "garde")]
84//! #     let router = router.nest("/garde", garde_example::router());
85//! #     let listener = TcpListener::bind(&SocketAddr::from(([0u8, 0, 0, 0], 0u16))).await?;
86//! #     axum::serve(listener, router.into_make_service())
87//! #         .await?;
88//! #     Ok(())
89//! # }
90//! ```
91
92use crate::HasValidate;
93#[cfg(feature = "validator")]
94use crate::HasValidateArgs;
95use axum_extra::protobuf::Protobuf;
96#[cfg(feature = "validator")]
97use validator::ValidateArgs;
98
99impl<T> HasValidate for Protobuf<T> {
100    type Validate = T;
101    fn get_validate(&self) -> &T {
102        &self.0
103    }
104}
105
106#[cfg(feature = "validator")]
107impl<'v, T: ValidateArgs<'v>> HasValidateArgs<'v> for Protobuf<T> {
108    type ValidateArgs = T;
109    fn get_validate_args(&self) -> &Self::ValidateArgs {
110        &self.0
111    }
112}
113
114#[cfg(feature = "validify")]
115impl<T: validify::Modify> crate::HasModify for Protobuf<T> {
116    type Modify = T;
117
118    fn get_modify(&mut self) -> &mut Self::Modify {
119        &mut self.0
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use crate::tests::{ValidTest, ValidTestParameter};
126    use axum::http::StatusCode;
127    use axum_extra::protobuf::Protobuf;
128    use reqwest::RequestBuilder;
129
130    impl<T: ValidTestParameter + prost::Message> ValidTest for Protobuf<T> {
131        const ERROR_STATUS_CODE: StatusCode = StatusCode::UNPROCESSABLE_ENTITY;
132
133        fn set_valid_request(builder: RequestBuilder) -> RequestBuilder {
134            builder.body(T::valid().encode_to_vec())
135        }
136
137        fn set_error_request(builder: RequestBuilder) -> RequestBuilder {
138            builder.body("invalid protobuf")
139        }
140
141        fn set_invalid_request(builder: RequestBuilder) -> RequestBuilder {
142            builder.body(T::invalid().encode_to_vec())
143        }
144    }
145}