diff --git a/validator/src/traits.rs b/validator/src/traits.rs index 790ed26a..123eaf76 100644 --- a/validator/src/traits.rs +++ b/validator/src/traits.rs @@ -1,4 +1,5 @@ use crate::types::{ValidationErrors, ValidationErrorsKind}; +use std::borrow::Cow; use std::collections::btree_map::BTreeMap; use std::collections::HashMap; @@ -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) @@ -64,8 +65,10 @@ impl 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) } } @@ -85,7 +88,8 @@ impl Validate for &HashMap { 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) } } @@ -105,7 +109,8 @@ impl Validate for &BTreeMap { 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) } } diff --git a/validator/src/types.rs b/validator/src/types.rs index 861f2d28..9bf49da6 100644 --- a/validator/src/types.rs +++ b/validator/src/types.rs @@ -38,7 +38,7 @@ impl std::error::Error for ValidationError { } } -#[derive(Debug, Serialize, Clone, PartialEq)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] #[serde(untagged)] pub enum ValidationErrorsKind { Struct(Box), @@ -46,8 +46,8 @@ pub enum ValidationErrorsKind { Field(Vec), } -#[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, ValidationErrorsKind>); impl ValidationErrors { pub fn new() -> ValidationErrors { @@ -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, 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, 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, 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> { + pub fn field_errors(&self) -> HashMap, &Vec> { self.0 .iter() .filter_map(|(k, v)| { if let ValidationErrorsKind::Field(errors) = v { - Some((*k, errors)) + Some((k.clone(), errors)) } else { None } @@ -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 { @@ -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"); diff --git a/validator_derive/src/tokens/nested.rs b/validator_derive/src/tokens/nested.rs index b74aab4d..f4840e4c 100644 --- a/validator_derive/src/tokens/nested.rs +++ b/validator_derive/src/tokens/nested.rs @@ -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()); } } diff --git a/validator_derive_tests/tests/complex.rs b/validator_derive_tests/tests/complex.rs index 342eea1b..33e9e8f9 100644 --- a/validator_derive_tests/tests/complex.rs +++ b/validator_derive_tests/tests/complex.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; use once_cell::sync::Lazy; use regex::Regex; @@ -213,7 +213,7 @@ fn test_works_with_none_values() { #[allow(dead_code)] fn unwrap_map(errors: &ValidationErrors, f: F) where - F: FnOnce(HashMap<&'static str, ValidationErrorsKind>), + F: FnOnce(HashMap, ValidationErrorsKind>), { let errors = errors.clone(); f(errors.errors().clone()); diff --git a/validator_derive_tests/tests/custom.rs b/validator_derive_tests/tests/custom.rs index b48cd25b..1b2e440e 100644 --- a/validator_derive_tests/tests/custom.rs +++ b/validator_derive_tests/tests/custom.rs @@ -1,4 +1,4 @@ -use std::collections::HashMap; +use std::{borrow::Cow, collections::HashMap}; use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind}; @@ -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), ]))) ); } diff --git a/validator_derive_tests/tests/nested.rs b/validator_derive_tests/tests/nested.rs index ee1abd4c..96ddd2c0 100644 --- a/validator_derive_tests/tests/nested.rs +++ b/validator_derive_tests/tests/nested.rs @@ -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), ])))) )]))) ); @@ -905,7 +905,7 @@ fn test_field_validations_evaluated_after_nested_validations_fails() { #[allow(dead_code)] fn unwrap_map(errors: &ValidationErrors, f: F) where - F: FnOnce(HashMap<&'static str, ValidationErrorsKind>), + F: FnOnce(HashMap, ValidationErrorsKind>), { let errors = errors.clone(); f(errors.errors().clone());