• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

gengteng / axum-valid / 6586130410

20 Oct 2023 09:46AM UTC coverage: 82.548% (-6.7%) from 89.277%
6586130410

push

github

gengteng
add support for validify

214 of 214 new or added lines in 7 files covered. (100.0%)

1173 of 1421 relevant lines covered (82.55%)

12.12 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

50.6
/src/validify.rs
1
//! # Validify support
2
//!
3
//! ## Feature
4
//!
5
//! Enable the `validify` feature to use `Validated<E>`, `Modified<E>`, `Validified<E>` and `ValidifiedByRef<E>`.
6
//!
7

8
#[cfg(test)]
9
pub mod test;
10

11
use crate::{HasValidate, ValidationRejection};
12
use axum::async_trait;
13
use axum::extract::{FromRequest, FromRequestParts};
14
use axum::http::request::Parts;
15
use axum::http::Request;
16
use axum::response::{IntoResponse, Response};
17
use std::fmt::{Display, Formatter};
18
use std::ops::{Deref, DerefMut};
19
use validify::{Modify, Validate, ValidationErrors, Validify};
20

21
/// # `Validated` data extractor
22
///
23
/// `Validated` provides simple data validation based on `validify`.
24
///
25
/// It only does validation, usage is similar to `Valid`.
26
///
27
#[derive(Debug, Clone, Copy, Default)]
28
pub struct Validated<E>(pub E);
29

30
impl<E> Deref for Validated<E> {
31
    type Target = E;
32

33
    fn deref(&self) -> &Self::Target {
×
34
        &self.0
×
35
    }
36
}
37

38
impl<E> DerefMut for Validated<E> {
39
    fn deref_mut(&mut self) -> &mut Self::Target {
×
40
        &mut self.0
×
41
    }
42
}
43

44
impl<T: Display> Display for Validated<T> {
45
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
46
        self.0.fmt(f)
×
47
    }
48
}
49

50
impl<E> Validated<E> {
51
    /// Consumes the `Validated` and returns the validated data within.
52
    ///
53
    /// This returns the `E` type which represents the data that has been
54
    /// successfully validated.
55
    pub fn into_inner(self) -> E {
×
56
        self.0
×
57
    }
58
}
59

60
/// # `Modified` data extractor / response
61
///
62
/// ## Extractor
63
///
64
/// `Modified` uses `validify`'s modification capabilities to alter data, without validation.
65
///
66
/// Operations like trimming and case modification can be done based on `modify` attributes.
67
///
68
/// ## Response
69
///
70
/// `Modified` also implements the `IntoResponse` trait. When its inner `IntoResponse` type also implements the `HasModify` trait:
71
///
72
/// `Modified` will call `validify`'s modify method to alter the inner data.
73
/// Then call the inner type's own `into_response` method to convert it into a HTTP response.
74
///
75
/// This allows applying modifications during response conversion by leveraging validify.
76
#[derive(Debug, Clone, Copy, Default)]
77
pub struct Modified<E>(pub E);
78

79
impl<E> Deref for Modified<E> {
80
    type Target = E;
81

82
    fn deref(&self) -> &Self::Target {
×
83
        &self.0
×
84
    }
85
}
86

87
impl<E> DerefMut for Modified<E> {
88
    fn deref_mut(&mut self) -> &mut Self::Target {
×
89
        &mut self.0
×
90
    }
91
}
92

93
impl<T: Display> Display for Modified<T> {
94
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
95
        self.0.fmt(f)
×
96
    }
97
}
98

99
impl<E> Modified<E> {
100
    /// Consumes the `Modified` and returns the modified data within.
101
    ///
102
    /// This returns the `E` type which represents the data that has been
103
    /// modified.
104
    pub fn into_inner(self) -> E {
×
105
        self.0
×
106
    }
107
}
108

109
impl<E: IntoResponse + HasModify> IntoResponse for Modified<E> {
110
    fn into_response(mut self) -> Response {
×
111
        self.get_modify().modify();
×
112
        self.0.into_response()
×
113
    }
114
}
115

116
/// # `Validified` data extractor
117
///
118
/// `Validified` provides construction, modification and validation abilities based on `validify`.
119
///
120
/// It requires a serde-based inner extractor.
121
///
122
/// And can treat missing fields as validation errors.
123
///
124
#[derive(Debug, Clone, Copy, Default)]
125
pub struct Validified<E>(pub E);
126

127
impl<E> Deref for Validified<E> {
128
    type Target = E;
129

130
    fn deref(&self) -> &Self::Target {
×
131
        &self.0
×
132
    }
133
}
134

135
impl<E> DerefMut for Validified<E> {
136
    fn deref_mut(&mut self) -> &mut Self::Target {
×
137
        &mut self.0
×
138
    }
139
}
140

141
impl<T: Display> Display for Validified<T> {
142
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
143
        self.0.fmt(f)
×
144
    }
145
}
146

147
impl<E> Validified<E> {
148
    /// Consumes the `Validified` and returns the modified and validated data within.
149
    ///
150
    /// This returns the `E` type which represents the data that has been
151
    /// successfully validated.
152
    pub fn into_inner(self) -> E {
×
153
        self.0
×
154
    }
155
}
156

157
/// # `ValidifiedByRef` data extractor
158
///
159
/// `ValidifiedByRef` is similar to `Validified`, but operates via reference.
160
///
161
/// Suitable for inner extractors not based on `serde`.
162
///
163
#[derive(Debug, Clone, Copy, Default)]
164
pub struct ValidifiedByRef<E>(pub E);
165

166
impl<E> Deref for ValidifiedByRef<E> {
167
    type Target = E;
168

169
    fn deref(&self) -> &Self::Target {
×
170
        &self.0
×
171
    }
172
}
173

174
impl<E> DerefMut for ValidifiedByRef<E> {
175
    fn deref_mut(&mut self) -> &mut Self::Target {
×
176
        &mut self.0
×
177
    }
178
}
179

180
impl<T: Display> Display for ValidifiedByRef<T> {
181
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
×
182
        self.0.fmt(f)
×
183
    }
184
}
185

186
impl<E> ValidifiedByRef<E> {
187
    /// Consumes the `ValidifiedByRef` and returns the modified and validated data within.
188
    ///
189
    /// This returns the `E` type which represents the data that has been
190
    /// successfully validated.
191
    pub fn into_inner(self) -> E {
×
192
        self.0
×
193
    }
194
}
195

196
/// `ValidifyRejection` is returned when the `Validated` / `Modified` / `Validified` / `ValidifiedByRef` extractor fails.
197
///
198
pub type ValidifyRejection<E> = ValidationRejection<ValidationErrors, E>;
199

200
impl<E> From<ValidationErrors> for ValidifyRejection<E> {
201
    fn from(value: ValidationErrors) -> Self {
39✔
202
        Self::Valid(value)
39✔
203
    }
204
}
205

206
/// Trait for types that can supply a reference that can be modified.
207
///
208
/// Extractor types `T` that implement this trait can be used with `Modified`.
209
///
210
pub trait HasModify {
211
    /// Inner type that can be modified
212
    type Modify: Modify;
213
    /// Get the inner value
214
    fn get_modify(&mut self) -> &mut Self::Modify;
215
}
216

217
/// Extractor to extract payload for constructing data
218
pub trait PayloadExtractor {
219
    /// Type of payload for constructing data
220
    type Payload;
221
    /// Get payload from the extractor
222
    fn get_payload(self) -> Self::Payload;
223
}
224

225
/// Trait for extractors whose inner data type that can be constructed using some payload,  
226
/// then modified and validated using `validify`.
227
///
228
/// Extractor types `T` that implement this trait can be used with `Validified`.
229
///
230
pub trait HasValidify: Sized {
231
    /// Inner type that can be modified and validated using `validify`.
232
    type Validify: Validify;
233

234
    /// Extracts payload from the request,
235
    /// which will be used to construct the `Self::Validify` type  
236
    /// and perform modification and validation on it.
237
    type PayloadExtractor: PayloadExtractor<Payload = <Self::Validify as Validify>::Payload>;
238

239
    /// Re-packages the validified data back into the inner Extractor type.  
240
    fn from_validified(v: Self::Validify) -> Self;
241
}
242

243
#[async_trait]
244
impl<State, Body, Extractor> FromRequest<State, Body> for Validated<Extractor>
245
where
246
    State: Send + Sync,
247
    Body: Send + Sync + 'static,
248
    Extractor: HasValidate + FromRequest<State, Body>,
249
    Extractor::Validate: validify::Validate,
250
{
251
    type Rejection = ValidifyRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
252

253
    async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
162✔
254
        let inner = Extractor::from_request(req, state)
135✔
255
            .await
54✔
256
            .map_err(ValidifyRejection::Inner)?;
27✔
257
        inner.get_validate().validate()?;
81✔
258
        Ok(Validated(inner))
27✔
259
    }
260
}
261

262
#[async_trait]
263
impl<State, Extractor> FromRequestParts<State> for Validated<Extractor>
264
where
265
    State: Send + Sync,
266
    Extractor: HasValidate + FromRequestParts<State>,
267
    Extractor::Validate: Validate,
268
{
269
    type Rejection = ValidifyRejection<<Extractor as FromRequestParts<State>>::Rejection>;
270

271
    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
120✔
272
        let inner = Extractor::from_request_parts(parts, state)
114✔
273
            .await
48✔
274
            .map_err(ValidifyRejection::Inner)?;
21✔
275
        inner.get_validate().validate()?;
72✔
276
        Ok(Validated(inner))
24✔
277
    }
278
}
279

280
#[async_trait]
281
impl<State, Body, Extractor> FromRequest<State, Body> for Modified<Extractor>
282
where
283
    State: Send + Sync,
284
    Body: Send + Sync + 'static,
285
    Extractor: HasModify + FromRequest<State, Body>,
286
{
287
    type Rejection = <Extractor as FromRequest<State, Body>>::Rejection;
288

289
    async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
18✔
290
        let mut inner = Extractor::from_request(req, state).await?;
9✔
291
        inner.get_modify().modify();
6✔
292
        Ok(Modified(inner))
3✔
293
    }
294
}
295

296
#[async_trait]
297
impl<State, Extractor> FromRequestParts<State> for Modified<Extractor>
298
where
299
    State: Send + Sync,
300
    Extractor: HasModify + FromRequestParts<State>,
301
{
302
    type Rejection = <Extractor as FromRequestParts<State>>::Rejection;
303

304
    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
45✔
305
        let mut inner = Extractor::from_request_parts(parts, state).await?;
27✔
306
        inner.get_modify().modify();
18✔
307
        Ok(Modified(inner))
9✔
308
    }
309
}
310

311
#[async_trait]
312
impl<State, Body, Extractor> FromRequest<State, Body> for Validified<Extractor>
313
where
314
    State: Send + Sync,
315
    Body: Send + Sync + 'static,
316
    Extractor: HasValidify,
317
    Extractor::Validify: Validify,
318
    Extractor::PayloadExtractor: FromRequest<State, Body>,
319
{
320
    type Rejection =
321
        ValidifyRejection<<Extractor::PayloadExtractor as FromRequest<State, Body>>::Rejection>;
322

323
    async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
×
324
        let payload = Extractor::PayloadExtractor::from_request(req, state)
×
325
            .await
×
326
            .map_err(ValidifyRejection::Inner)?;
×
327
        Ok(Validified(Extractor::from_validified(
×
328
            Extractor::Validify::validify(payload.get_payload())?,
×
329
        )))
330
    }
331
}
332

333
#[async_trait]
334
impl<State, Extractor> FromRequestParts<State> for Validified<Extractor>
335
where
336
    State: Send + Sync,
337
    Extractor: HasValidify,
338
    Extractor::Validify: Validify,
339
    Extractor::PayloadExtractor: FromRequestParts<State>,
340
{
341
    type Rejection =
342
        ValidifyRejection<<Extractor::PayloadExtractor as FromRequestParts<State>>::Rejection>;
343

344
    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
30✔
345
        let payload = Extractor::PayloadExtractor::from_request_parts(parts, state)
24✔
346
            .await
12✔
347
            .map_err(ValidifyRejection::Inner)?;
3✔
348
        Ok(Validified(Extractor::from_validified(
12✔
349
            Extractor::Validify::validify(payload.get_payload())?,
24✔
350
        )))
351
    }
352
}
353

354
#[async_trait]
355
impl<State, Body, Extractor> FromRequest<State, Body> for ValidifiedByRef<Extractor>
356
where
357
    State: Send + Sync,
358
    Body: Send + Sync + 'static,
359
    Extractor: HasValidate + HasModify + FromRequest<State, Body>,
360
    Extractor::Validate: Validate,
361
{
362
    type Rejection = ValidifyRejection<<Extractor as FromRequest<State, Body>>::Rejection>;
363

364
    async fn from_request(req: Request<Body>, state: &State) -> Result<Self, Self::Rejection> {
18✔
365
        let mut inner = Extractor::from_request(req, state)
15✔
366
            .await
6✔
367
            .map_err(ValidifyRejection::Inner)?;
3✔
368
        inner.get_modify().modify();
6✔
369
        inner.get_validate().validate()?;
6✔
370
        Ok(ValidifiedByRef(inner))
3✔
371
    }
372
}
373

374
#[async_trait]
375
impl<State, Extractor> FromRequestParts<State> for ValidifiedByRef<Extractor>
376
where
377
    State: Send + Sync,
378
    Extractor: HasValidate + HasModify + FromRequestParts<State>,
379
    Extractor::Validate: Validate,
380
{
381
    type Rejection = ValidifyRejection<<Extractor as FromRequestParts<State>>::Rejection>;
382

383
    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
45✔
384
        let mut inner = Extractor::from_request_parts(parts, state)
45✔
385
            .await
18✔
386
            .map_err(ValidifyRejection::Inner)?;
9✔
387
        inner.get_modify().modify();
18✔
388
        inner.get_validate().validate()?;
18✔
389
        Ok(ValidifiedByRef(inner))
9✔
390
    }
391
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc