1#[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#[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 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
72pub 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 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 let inner = String::from(GARDE);
153 let vr = GardeRejection::<String>::Inner(inner.clone());
154 assert_eq!(inner.to_string(), vr.to_string());
155
156 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 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}