axum_valid/
garde.rs

1//! # Garde support
2//!
3//! ## Feature
4//!
5//! Enable the `garde` feature to use `Garde<E>`.
6//!
7
8#[cfg(test)]
9pub mod test;
10
11use crate::{HasValidate, ValidationRejection};
12use axum::extract::{FromRef, FromRequest, FromRequestParts, Request};
13use axum::http::request::Parts;
14use garde::{Report, Validate};
15use std::fmt::{Display, Formatter};
16use std::ops::{Deref, DerefMut};
17
18/// # `Garde` data extractor
19///
20/// Garde uses garde to validate data, supporting validation with or without arguments.
21///
22/// If not using arguments, its usage is similar to `Valid`. However, if your axum router uses a state, you need to implement `FromRef<StateType>` for `()`.
23///
24/// If using arguments, you must pass the arguments to Garde extractor via state, meaning implementing `FromRef<StateType>` for your validation arguments type.
25///
26#[derive(Debug, Clone, Copy, Default)]
27pub struct Garde<E>(pub E);
28
29impl<E> Deref for Garde<E> {
30    type Target = E;
31
32    fn deref(&self) -> &Self::Target {
33        &self.0
34    }
35}
36
37impl<E> DerefMut for Garde<E> {
38    fn deref_mut(&mut self) -> &mut Self::Target {
39        &mut self.0
40    }
41}
42
43impl<T: Display> Display for Garde<T> {
44    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45        self.0.fmt(f)
46    }
47}
48
49impl<E> Garde<E> {
50    /// Consumes the `Garde` and returns the validated data within.
51    ///
52    /// This returns the `E` type which represents the data that has been
53    /// successfully validated.
54    pub fn into_inner(self) -> E {
55        self.0
56    }
57}
58
59#[cfg(feature = "aide")]
60impl<T> aide::OperationInput for Garde<T>
61where
62    T: aide::OperationInput,
63{
64    fn operation_input(
65        ctx: &mut aide::generate::GenContext,
66        operation: &mut aide::openapi::Operation,
67    ) {
68        T::operation_input(ctx, operation);
69    }
70}
71
72/// `GardeRejection` is returned when the `Garde` extractor fails.
73///
74pub type GardeRejection<E> = ValidationRejection<Report, E>;
75
76impl<E> From<Report> for GardeRejection<E> {
77    fn from(value: Report) -> Self {
78        Self::Valid(value)
79    }
80}
81
82impl<State, Extractor, Context> FromRequest<State> for Garde<Extractor>
83where
84    State: Send + Sync,
85    Context: Send + Sync + FromRef<State>,
86    Extractor: HasValidate + FromRequest<State>,
87    <Extractor as HasValidate>::Validate: Validate<Context = Context>,
88{
89    type Rejection = GardeRejection<<Extractor as FromRequest<State>>::Rejection>;
90
91    async fn from_request(req: Request, state: &State) -> Result<Self, Self::Rejection> {
92        let context: Context = FromRef::from_ref(state);
93        let inner = Extractor::from_request(req, state)
94            .await
95            .map_err(GardeRejection::Inner)?;
96
97        inner.get_validate().validate_with(&context)?;
98        Ok(Garde(inner))
99    }
100}
101
102impl<State, Extractor, Context> FromRequestParts<State> for Garde<Extractor>
103where
104    State: Send + Sync,
105    Context: Send + Sync + FromRef<State>,
106    Extractor: HasValidate + FromRequestParts<State>,
107    <Extractor as HasValidate>::Validate: garde::Validate<Context = Context>,
108{
109    type Rejection = GardeRejection<<Extractor as FromRequestParts<State>>::Rejection>;
110
111    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
112        let context: Context = FromRef::from_ref(state);
113        let inner = Extractor::from_request_parts(parts, state)
114            .await
115            .map_err(GardeRejection::Inner)?;
116        inner.get_validate().validate_with(&context)?;
117        Ok(Garde(inner))
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use garde::{Path, Report};
125    use std::error::Error;
126    use std::io;
127
128    const GARDE: &str = "garde";
129
130    #[test]
131    fn garde_deref_deref_mut_into_inner() {
132        let mut inner = String::from(GARDE);
133        let mut v = Garde(inner.clone());
134        assert_eq!(&inner, v.deref());
135        inner.push_str(GARDE);
136        v.deref_mut().push_str(GARDE);
137        assert_eq!(&inner, v.deref());
138        println!("{}", v);
139        assert_eq!(inner, v.into_inner());
140    }
141
142    #[test]
143    fn display_error() {
144        // GardeRejection::Valid Display
145        let mut report = Report::new();
146        report.append(Path::empty(), garde::Error::new(GARDE));
147        let s = report.to_string();
148        let vr = GardeRejection::<String>::Valid(report);
149        assert_eq!(vr.to_string(), s);
150
151        // GardeRejection::Inner Display
152        let inner = String::from(GARDE);
153        let vr = GardeRejection::<String>::Inner(inner.clone());
154        assert_eq!(inner.to_string(), vr.to_string());
155
156        // GardeRejection::Valid Error
157        let mut report = Report::new();
158        report.append(Path::empty(), garde::Error::new(GARDE));
159        let vr = GardeRejection::<io::Error>::Valid(report);
160        assert!(matches!(vr.source(), Some(source) if source.downcast_ref::<Report>().is_some()));
161
162        // GardeRejection::Valid Error
163        let vr = GardeRejection::<io::Error>::Inner(io::Error::other(GARDE));
164        assert!(
165            matches!(vr.source(), Some(source) if source.downcast_ref::<io::Error>().is_some())
166        );
167    }
168}