actix_web_validator/
error.rs

1//! Error declaration.
2use actix_web::http::StatusCode;
3use actix_web::{HttpResponse, ResponseError};
4use thiserror::Error;
5use validator::{ValidationError, ValidationErrors, ValidationErrorsKind};
6
7#[derive(Error, Debug)]
8pub enum Error {
9    #[error("Validation error: {0}")]
10    Validate(#[from] validator::ValidationErrors),
11    #[error(transparent)]
12    Deserialize(#[from] DeserializeErrors),
13    #[error("Payload error: {0}")]
14    JsonPayloadError(#[from] actix_web::error::JsonPayloadError),
15    #[error("Url encoded error: {0}")]
16    UrlEncodedError(#[from] actix_web::error::UrlencodedError),
17    #[error("Query error: {0}")]
18    QsError(#[from] serde_qs::Error),
19}
20
21#[derive(Error, Debug)]
22pub enum DeserializeErrors {
23    #[error("Query deserialize error: {0}")]
24    DeserializeQuery(serde_urlencoded::de::Error),
25    #[error("Json deserialize error: {0}")]
26    DeserializeJson(serde_json::error::Error),
27    #[error("Path deserialize error: {0}")]
28    DeserializePath(serde::de::value::Error),
29}
30
31impl From<serde_json::error::Error> for Error {
32    fn from(error: serde_json::error::Error) -> Self {
33        Error::Deserialize(DeserializeErrors::DeserializeJson(error))
34    }
35}
36
37impl From<serde_urlencoded::de::Error> for Error {
38    fn from(error: serde_urlencoded::de::Error) -> Self {
39        Error::Deserialize(DeserializeErrors::DeserializeQuery(error))
40    }
41}
42
43impl ResponseError for Error {
44    fn error_response(&self) -> HttpResponse {
45        HttpResponse::build(StatusCode::BAD_REQUEST).body(match self {
46            Self::Validate(e) => {
47                format!(
48                    "Validation errors in fields:\n{}",
49                    flatten_errors(e)
50                        .iter()
51                        .map(|(_, field, err)| { format!("\t{field}: {err}") })
52                        .collect::<Vec<_>>()
53                        .join("\n")
54                )
55            }
56            _ => format!("{}", *self),
57        })
58    }
59}
60
61/// Helper function for error extraction and formatting.
62/// Return Vec of tuples where first element is full field path (separated by dot)
63/// and second is error.
64#[inline]
65pub fn flatten_errors(errors: &ValidationErrors) -> Vec<(u16, String, &ValidationError)> {
66    _flatten_errors(errors, None, None)
67}
68
69#[inline]
70fn _flatten_errors(
71    errors: &ValidationErrors,
72    path: Option<String>,
73    indent: Option<u16>,
74) -> Vec<(u16, String, &ValidationError)> {
75    errors
76        .errors()
77        .iter()
78        .flat_map(|(field, err)| {
79            let indent = indent.unwrap_or(0);
80            let actual_path = path
81                .as_ref()
82                .map(|path| [path.as_str(), field].join("."))
83                .unwrap_or_else(|| field.to_string());
84            match err {
85                ValidationErrorsKind::Field(field_errors) => field_errors
86                    .iter()
87                    .map(|error| (indent, actual_path.clone(), error))
88                    .collect::<Vec<_>>(),
89                ValidationErrorsKind::List(list_error) => list_error
90                    .iter()
91                    .flat_map(|(index, errors)| {
92                        let actual_path = format!("{}[{}]", actual_path.as_str(), index);
93                        _flatten_errors(errors, Some(actual_path), Some(indent + 1))
94                    })
95                    .collect::<Vec<_>>(),
96                ValidationErrorsKind::Struct(struct_errors) => {
97                    _flatten_errors(struct_errors, Some(actual_path), Some(indent + 1))
98                }
99            }
100        })
101        .collect::<Vec<_>>()
102}