serwus 0.2.3

Helpers for building actix-web/diesel based services
Documentation
//! Structure for handling validation errors

#[cfg(feature = "actix_validation")]
use actix_web::error;
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::HashMap};
use validator::ValidationErrors;
#[cfg(feature = "actix_validation")]
use validator::ValidationErrorsKind;

use super::string_utils::to_camel_case;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum CodeError {
    ValidationCodeError = 0,
}

impl CodeError {
    fn as_num(&self) -> i16 {
        match *self {
            CodeError::ValidationCodeError => 0,
        }
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Error {
    code: String,
    message: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ValidationError {
    code: i16,
    errors: HashMap<String, Vec<Error>>,
}

pub fn return_new_error(code: &'static str, message: &'static str) -> validator::ValidationError {
    return_new_dynamic_error(code, Cow::Borrowed(message))
}

pub fn return_new_dynamic_error(
    code: &'static str,
    message: Cow<'static, str>,
) -> validator::ValidationError {
    let mut error = validator::ValidationError::new(code);
    error.message = Some(message);
    error
}

impl ValidationError {
    pub fn from(val_errors: &ValidationErrors) -> Self {
        let mut validation_error = Self {
            code: CodeError::as_num(&CodeError::ValidationCodeError),
            errors: HashMap::new(),
        };

        for (field_key, field_val) in val_errors.field_errors().iter() {
            for val in field_val.iter() {
                if validation_error.errors.contains_key(field_key.as_ref()) {
                    if let Some(x) = validation_error.errors.get_mut(field_key.as_ref()) {
                        match &val.message {
                            Some(v) => x.push(Error {
                                code: val.code.clone().to_string(),
                                message: v.clone().to_string(),
                            }),
                            None => x.push(Error {
                                code: val.code.clone().to_string(),
                                message: "".to_string(),
                            }),
                        }
                    }
                } else {
                    let message = match &val.message {
                        Some(v) => v.clone().to_string(),
                        None => "".to_string(),
                    };

                    let errors: Vec<Error> = vec![Error {
                        code: val.code.clone().to_string(),
                        message,
                    }];

                    validation_error
                        .errors
                        .insert(to_camel_case(field_key), errors);
                }
            }
        }

        validation_error
    }
}

#[cfg(feature = "actix_validation")]
pub fn render_single_validation_error(err: &ValidationErrors) -> error::Error {
    match err.to_owned().errors().values().next() {
        Some(ValidationErrorsKind::Field(field_errors)) => {
            if let Some(field_error) = field_errors.iter().next() {
                let code = field_error.code.clone().into_owned();
                match field_error.message.clone() {
                    Some(message) => error::ErrorBadRequest(message),
                    None => error::ErrorInternalServerError(format!(
                        "No validation message for: {code}"
                    )),
                }
            } else {
                error::ErrorInternalServerError("Empty field errors".to_string())
            }
        }
        _ => error::ErrorInternalServerError("Malformed validation".to_string()),
    }
}