use std::fmt::Display;
use std::path::Path;
use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind};
use crate::ConfigError;
pub fn validate_ascii(name: &impl ToString) -> Result<(), ValidationError> {
if !name.to_string().is_ascii() {
return Err(ValidationError::new("The value is not ASCII"));
}
Ok(())
}
pub fn validate_path_exists(file_path: &Path) -> Result<(), ValidationError> {
if !file_path.exists() {
let mut error = ValidationError::new("file or directory not found");
error.message = Some(
"Please create the file/directory or change the path in the configuration.".into(),
);
return Err(error);
}
Ok(())
}
#[derive(Debug)]
pub struct ParsedValidationError {
pub param_path: String,
pub code: String,
pub message: Option<String>,
pub params: String,
}
#[derive(thiserror::Error, Debug)]
pub struct ParsedValidationErrors(pub Vec<ParsedValidationError>);
impl From<ValidationErrors> for ParsedValidationErrors {
fn from(errors: ValidationErrors) -> Self {
let mut parsed_errors: ParsedValidationErrors = ParsedValidationErrors(vec![]);
parse_validation_error(&errors, "".to_string(), &mut parsed_errors);
parsed_errors
}
}
impl Display for ParsedValidationErrors {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut error_string = String::new();
for error in &self.0 {
error_string.push_str(&format!(
"Configuration error: The field: {} has an error: {} with value: {}, {} \n",
&error.param_path,
&error.code,
&error.params,
&error.message.clone().unwrap_or("".to_string()),
));
}
error_string = error_string.replace('\"', "");
write!(f, "{}", error_string)
}
}
pub fn config_validate<T: Validate>(config: &T) -> Result<(), ConfigError> {
config
.validate()
.map_err(|errors| ConfigError::ConfigValidationError(ParsedValidationErrors::from(errors)))
}
fn parse_validation_error(
errors: &ValidationErrors,
current_path: String,
parsed_errors: &mut ParsedValidationErrors,
) {
for (field, error) in errors.errors().iter() {
let new_path = if current_path.is_empty() {
field.to_string()
} else {
format!("{}.{}", current_path, field)
};
match error {
ValidationErrorsKind::Struct(errors) => {
parse_validation_error(errors, new_path, parsed_errors);
}
ValidationErrorsKind::List(errors) => {
for (index, error) in errors.iter().enumerate() {
parse_validation_error(
error.1,
format!("{}[{}]", new_path, index),
parsed_errors,
);
}
}
ValidationErrorsKind::Field(errors) => {
for error in errors {
let parsed_error = ParsedValidationError {
param_path: new_path.to_owned(),
code: error.code.to_string(),
message: error.message.as_ref().map(|cow_string| cow_string.to_string()),
params: {
let params = &error.params;
params
.iter()
.map(|(_k, v)| v.to_string().replace('\"', ""))
.collect::<Vec<String>>()
.join(", ")
}
.to_owned(),
};
parsed_errors.0.push(parsed_error);
}
}
}
}
}