Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions validator/src/traits.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::types::{ValidationErrors, ValidationErrorsKind};
use std::borrow::Cow;
use std::collections::btree_map::BTreeMap;
use std::collections::HashMap;

Expand Down Expand Up @@ -32,7 +33,7 @@ macro_rules! impl_validate_list {
} else {
let err_kind = ValidationErrorsKind::List(vec_err);
let errors = ValidationErrors(std::collections::HashMap::from([(
"_tmp_validator",
Cow::Borrowed("_tmp_validator"),
err_kind,
)]));
Err(errors)
Expand Down Expand Up @@ -64,8 +65,10 @@ impl<T: Validate, const N: usize> Validate for [T; N] {
Ok(())
} else {
let err_kind = ValidationErrorsKind::List(vec_err);
let errors =
ValidationErrors(std::collections::HashMap::from([("_tmp_validator", err_kind)]));
let errors = ValidationErrors(std::collections::HashMap::from([(
Cow::Borrowed("_tmp_validator"),
err_kind,
)]));
Err(errors)
}
}
Expand All @@ -85,7 +88,8 @@ impl<K, V: Validate, S> Validate for &HashMap<K, V, S> {
Ok(())
} else {
let err_kind = ValidationErrorsKind::List(vec_err);
let errors = ValidationErrors(HashMap::from([("_tmp_validator", err_kind)]));
let errors =
ValidationErrors(HashMap::from([(Cow::Borrowed("_tmp_validator"), err_kind)]));
Err(errors)
}
}
Expand All @@ -105,7 +109,8 @@ impl<K, V: Validate> Validate for &BTreeMap<K, V> {
Ok(())
} else {
let err_kind = ValidationErrorsKind::List(vec_err);
let errors = ValidationErrors(HashMap::from([("_tmp_validator", err_kind)]));
let errors =
ValidationErrors(HashMap::from([(Cow::Borrowed("_tmp_validator"), err_kind)]));
Err(errors)
}
}
Expand Down
24 changes: 13 additions & 11 deletions validator/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,16 @@ impl std::error::Error for ValidationError {
}
}

#[derive(Debug, Serialize, Clone, PartialEq)]
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(untagged)]
pub enum ValidationErrorsKind {
Struct(Box<ValidationErrors>),
List(BTreeMap<usize, Box<ValidationErrors>>),
Field(Vec<ValidationError>),
}

#[derive(Default, Debug, Serialize, Clone, PartialEq)]
pub struct ValidationErrors(pub HashMap<&'static str, ValidationErrorsKind>);
#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
pub struct ValidationErrors(pub HashMap<Cow<'static, str>, ValidationErrorsKind>);

impl ValidationErrors {
pub fn new() -> ValidationErrors {
Expand Down Expand Up @@ -134,28 +134,28 @@ impl ValidationErrors {

/// Returns a map of field-level validation errors found for the struct that was validated and
/// any of it's nested structs that are tagged for validation.
pub fn errors(&self) -> &HashMap<&'static str, ValidationErrorsKind> {
pub fn errors(&self) -> &HashMap<Cow<'static, str>, ValidationErrorsKind> {
&self.0
}

/// Returns a mutable map of field-level validation errors found for the struct that was validated and
/// any of it's nested structs that are tagged for validation.
pub fn errors_mut(&mut self) -> &mut HashMap<&'static str, ValidationErrorsKind> {
pub fn errors_mut(&mut self) -> &mut HashMap<Cow<'static, str>, ValidationErrorsKind> {
&mut self.0
}

/// Consume the struct, returning the validation errors found
pub fn into_errors(self) -> HashMap<&'static str, ValidationErrorsKind> {
pub fn into_errors(self) -> HashMap<Cow<'static, str>, ValidationErrorsKind> {
self.0
}

/// Returns a map of only field-level validation errors found for the struct that was validated.
pub fn field_errors(&self) -> HashMap<&'static str, &Vec<ValidationError>> {
pub fn field_errors(&self) -> HashMap<Cow<'static, str>, &Vec<ValidationError>> {
self.0
.iter()
.filter_map(|(k, v)| {
if let ValidationErrorsKind::Field(errors) = v {
Some((*k, errors))
Some((k.clone(), errors))
} else {
None
}
Expand All @@ -164,8 +164,10 @@ impl ValidationErrors {
}

pub fn add(&mut self, field: &'static str, error: ValidationError) {
if let ValidationErrorsKind::Field(ref mut vec) =
self.0.entry(field).or_insert_with(|| ValidationErrorsKind::Field(vec![]))
if let ValidationErrorsKind::Field(ref mut vec) = self
.0
.entry(Cow::Borrowed(field))
.or_insert_with(|| ValidationErrorsKind::Field(vec![]))
{
vec.push(error);
} else {
Expand All @@ -179,7 +181,7 @@ impl ValidationErrors {
}

fn add_nested(&mut self, field: &'static str, errors: ValidationErrorsKind) {
if let Vacant(entry) = self.0.entry(field) {
if let Vacant(entry) = self.0.entry(Cow::Borrowed(field)) {
entry.insert(errors);
} else {
panic!("Attempt to replace non-empty ValidationErrors entry");
Expand Down
2 changes: 1 addition & 1 deletion validator_derive/src/tokens/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ pub fn nested_tokens(
field_name_str: &str,
) -> proc_macro2::TokenStream {
quote! {
if let std::collections::hash_map::Entry::Vacant(entry) = errors.0.entry(#field_name_str) {
if let std::collections::hash_map::Entry::Vacant(entry) = errors.0.entry(::std::borrow::Cow::Borrowed(#field_name_str)) {
errors.merge_self(#field_name_str, (&#field_name).validate());
}
}
Expand Down
4 changes: 2 additions & 2 deletions validator_derive_tests/tests/complex.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

use once_cell::sync::Lazy;
use regex::Regex;
Expand Down Expand Up @@ -213,7 +213,7 @@ fn test_works_with_none_values() {
#[allow(dead_code)]
fn unwrap_map<F>(errors: &ValidationErrors, f: F)
where
F: FnOnce(HashMap<&'static str, ValidationErrorsKind>),
F: FnOnce(HashMap<Cow<'static, str>, ValidationErrorsKind>),
{
let errors = errors.clone();
f(errors.errors().clone());
Expand Down
8 changes: 4 additions & 4 deletions validator_derive_tests/tests/custom.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::{borrow::Cow, collections::HashMap};

use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind};

Expand Down Expand Up @@ -171,9 +171,9 @@ fn custom_fn_on_optional_types_work() {
assert_eq!(
t.validate(),
Err(ValidationErrors(HashMap::from_iter([
("plain", error_kind.clone()),
("option", error_kind.clone()),
("option_option", error_kind),
(Cow::Borrowed("plain"), error_kind.clone()),
(Cow::Borrowed("option"), error_kind.clone()),
(Cow::Borrowed("option_option"), error_kind),
])))
);
}
8 changes: 4 additions & 4 deletions validator_derive_tests/tests/nested.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ fn fails_nested_validation_multiple_members() {
assert_eq!(
root.validate(),
Err(ValidationErrors(HashMap::from_iter([(
"a",
Cow::Borrowed("a"),
ValidationErrorsKind::Struct(Box::new(ValidationErrors(HashMap::from_iter([
("value1", error_kind.clone()),
("value2", error_kind),
(Cow::Borrowed("value1"), error_kind.clone()),
(Cow::Borrowed("value2"), error_kind),
]))))
)])))
);
Expand Down Expand Up @@ -905,7 +905,7 @@ fn test_field_validations_evaluated_after_nested_validations_fails() {
#[allow(dead_code)]
fn unwrap_map<F>(errors: &ValidationErrors, f: F)
where
F: FnOnce(HashMap<&'static str, ValidationErrorsKind>),
F: FnOnce(HashMap<Cow<'static, str>, ValidationErrorsKind>),
{
let errors = errors.clone();
f(errors.errors().clone());
Expand Down
Loading