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

gengteng / axum-valid / 7259277903

19 Dec 2023 08:32AM UTC coverage: 90.967% (+0.06%) from 90.909%
7259277903

push

github

gengteng
fix: Lock validify dependency to version 1.0.12 in axum-valid

This commit addresses a compilation error caused by the validify crate update to version 1.1.1. By explicitly setting the validify dependency to version 1.0.12 in the Cargo.toml of the axum-valid project, we avoid the issue where the 'prost' attribute cannot be found in the newer version of validify. This serves as a temporary solution until the underlying issue with validify 1.1.1 is resolved.

1561 of 1716 relevant lines covered (90.97%)

13.81 hits per line

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

89.16
/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, Request};
14
use axum::http::request::Parts;
15
use axum::response::{IntoResponse, Response};
16
use std::fmt::{Display, Formatter};
17
use std::ops::{Deref, DerefMut};
18
use validify::{Modify, Validate, ValidationErrors, Validify};
19

20
/// # `Validated` data extractor
21
///
22
/// `Validated` provides simple data validation based on `validify`.
23
///
24
/// It only does validation, usage is similar to `Valid`.
25
///
26
#[derive(Debug, Clone, Copy, Default)]
27
#[cfg_attr(feature = "aide", derive(aide::OperationIo))]
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 {
3✔
34
        &self.0
×
35
    }
36
}
37

38
impl<E> DerefMut for Validated<E> {
39
    fn deref_mut(&mut self) -> &mut Self::Target {
3✔
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 {
3✔
46
        self.0.fmt(f)
3✔
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 {
3✔
56
        self.0
3✔
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
#[cfg_attr(feature = "aide", derive(aide::OperationIo))]
78
pub struct Modified<E>(pub E);
79

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

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

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

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

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

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

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

129
impl<E> Deref for Validified<E> {
130
    type Target = E;
131

132
    fn deref(&self) -> &Self::Target {
3✔
133
        &self.0
×
134
    }
135
}
136

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

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

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

159
/// # `ValidifiedByRef` data extractor
160
///
161
/// `ValidifiedByRef` is similar to `Validified`, but operates via reference.
162
///
163
/// Suitable for inner extractors not based on `serde`.
164
///
165
#[derive(Debug, Clone, Copy, Default)]
166
#[cfg_attr(feature = "aide", derive(aide::OperationIo))]
167
pub struct ValidifiedByRef<E>(pub E);
168

169
impl<E> Deref for ValidifiedByRef<E> {
170
    type Target = E;
171

172
    fn deref(&self) -> &Self::Target {
3✔
173
        &self.0
×
174
    }
175
}
176

177
impl<E> DerefMut for ValidifiedByRef<E> {
178
    fn deref_mut(&mut self) -> &mut Self::Target {
3✔
179
        &mut self.0
×
180
    }
181
}
182

183
impl<T: Display> Display for ValidifiedByRef<T> {
184
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
3✔
185
        self.0.fmt(f)
3✔
186
    }
187
}
188

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

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

203
impl<E> From<ValidationErrors> for ValidifyRejection<E> {
204
    fn from(value: ValidationErrors) -> Self {
45✔
205
        Self::Valid(value)
45✔
206
    }
207
}
208

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

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

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

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

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

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

255
    async fn from_request(req: Request, state: &State) -> Result<Self, Self::Rejection> {
198✔
256
        let inner = Extractor::from_request(req, state)
165✔
257
            .await
66✔
258
            .map_err(ValidifyRejection::Inner)?;
33✔
259
        inner.get_validate().validate()?;
99✔
260
        Ok(Validated(inner))
33✔
261
    }
262
}
263

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

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

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

290
    async fn from_request(req: Request, state: &State) -> Result<Self, Self::Rejection> {
198✔
291
        let mut inner = Extractor::from_request(req, state).await?;
99✔
292
        inner.get_modify().modify();
66✔
293
        Ok(Modified(inner))
33✔
294
    }
295
}
296

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

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

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

323
    async fn from_request(req: Request, state: &State) -> Result<Self, Self::Rejection> {
144✔
324
        let payload = Extractor::PayloadExtractor::from_request(req, state)
72✔
325
            .await
48✔
326
            .map_err(ValidifyRejection::Inner)?;
×
327
        Ok(Validified(Extractor::from_validified(
48✔
328
            Extractor::Validify::validify(payload.get_payload())?,
96✔
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> {
45✔
345
        let payload = Extractor::PayloadExtractor::from_request_parts(parts, state)
33✔
346
            .await
18✔
347
            .map_err(ValidifyRejection::Inner)?;
3✔
348
        Ok(Validified(Extractor::from_validified(
18✔
349
            Extractor::Validify::validify(payload.get_payload())?,
36✔
350
        )))
351
    }
352
}
353

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

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

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

382
    async fn from_request_parts(parts: &mut Parts, state: &State) -> Result<Self, Self::Rejection> {
120✔
383
        let mut inner = Extractor::from_request_parts(parts, state)
114✔
384
            .await
48✔
385
            .map_err(ValidifyRejection::Inner)?;
21✔
386
        inner.get_modify().modify();
48✔
387
        inner.get_validate().validate()?;
48✔
388
        Ok(ValidifiedByRef(inner))
24✔
389
    }
390
}
391

392
#[cfg(test)]
393
mod tests {
394
    use super::*;
395
    use axum::Json;
396
    use serde::Serialize;
397
    use std::error::Error;
398
    use std::io;
399

400
    const VALIDIFY: &str = "validify";
401

402
    #[test]
403
    fn validify_deref_deref_mut_into_inner() {
404
        let mut inner = String::from(VALIDIFY);
405
        let mut v = Validated(inner.clone());
406
        assert_eq!(&inner, v.deref());
407
        inner.push_str(VALIDIFY);
408
        v.deref_mut().push_str(VALIDIFY);
409
        assert_eq!(&inner, v.deref());
410
        println!("{}", v);
411
        assert_eq!(inner, v.into_inner());
412

413
        let mut inner = String::from(VALIDIFY);
414
        let mut v = Modified(inner.clone());
415
        assert_eq!(&inner, v.deref());
416
        inner.push_str(VALIDIFY);
417
        v.deref_mut().push_str(VALIDIFY);
418
        assert_eq!(&inner, v.deref());
419
        println!("{}", v);
420
        assert_eq!(inner, v.into_inner());
421

422
        let mut inner = String::from(VALIDIFY);
423
        let mut v = Validified(inner.clone());
424
        assert_eq!(&inner, v.deref());
425
        inner.push_str(VALIDIFY);
426
        v.deref_mut().push_str(VALIDIFY);
427
        assert_eq!(&inner, v.deref());
428
        println!("{}", v);
429
        assert_eq!(inner, v.into_inner());
430

431
        let mut inner = String::from(VALIDIFY);
432
        let mut v = ValidifiedByRef(inner.clone());
433
        assert_eq!(&inner, v.deref());
434
        inner.push_str(VALIDIFY);
435
        v.deref_mut().push_str(VALIDIFY);
436
        assert_eq!(&inner, v.deref());
437
        println!("{}", v);
438
        assert_eq!(inner, v.into_inner());
439
    }
440

441
    #[test]
442
    fn display_error() {
443
        // ValidifyRejection::Valid Display
444
        let mut report = ValidationErrors::new();
445
        report.add(validify::ValidationError::new_schema(VALIDIFY));
446
        let s = report.to_string();
447
        let vr = ValidifyRejection::<String>::Valid(report);
448
        assert_eq!(vr.to_string(), s);
449

450
        // ValidifyRejection::Inner Display
451
        let inner = String::from(VALIDIFY);
452
        let vr = ValidifyRejection::<String>::Inner(inner.clone());
453
        assert_eq!(inner.to_string(), vr.to_string());
454

455
        // ValidifyRejection::Valid Error
456
        let mut report = ValidationErrors::new();
457
        report.add(validify::ValidationError::new_schema(VALIDIFY));
458
        let vr = ValidifyRejection::<io::Error>::Valid(report);
459
        assert!(
460
            matches!(vr.source(), Some(source) if source.downcast_ref::<ValidationErrors>().is_some())
461
        );
462

463
        // ValidifyRejection::Valid Error
464
        let vr =
465
            ValidifyRejection::<io::Error>::Inner(io::Error::new(io::ErrorKind::Other, VALIDIFY));
466
        assert!(
467
            matches!(vr.source(), Some(source) if source.downcast_ref::<io::Error>().is_some())
468
        );
469
    }
470

471
    #[test]
472
    fn modified_into_response() {
473
        #[derive(Validify, Serialize)]
474
        struct Data {
475
            #[modify(trim)]
476
            v: String,
477
        }
478
        println!(
479
            "{:?}",
480
            Modified(Json(Data { v: "a  ".into() })).into_response()
481
        );
482
    }
483
}
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