diff options
| author | Vincent Prouillet | 2018-09-13 18:42:32 +0200 |
|---|---|---|
| committer | Vincent Prouillet | 2018-09-13 18:42:32 +0200 |
| commit | ab93a250013ad01620992581923583cbf93ed3db (patch) | |
| tree | 742920f0e0929aaf3709e3ffe93bbd9e857e7a1d | |
| parent | dfdc289626c448522c43c13f8d72033fe0d1cae8 (diff) | |
| download | validator-ab93a250013ad01620992581923583cbf93ed3db.tar.bz2 | |
Rustfmt
31 files changed, 508 insertions, 493 deletions
diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..d45dc0e --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +use_small_heuristics = "max" diff --git a/validator/src/lib.rs b/validator/src/lib.rs index ef9685f..f6aa8f0 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -1,5 +1,5 @@ -extern crate url; extern crate regex; +extern crate url; #[macro_use] extern crate lazy_static; extern crate idna; @@ -12,22 +12,22 @@ extern crate card_validate; #[cfg(feature = "phone")] pub extern crate phonenumber; +mod traits; mod types; mod validation; -mod traits; -pub use validation::ip::{validate_ip, validate_ip_v4, validate_ip_v6}; -pub use validation::email::{validate_email}; -pub use validation::length::{validate_length}; -pub use validation::range::{validate_range}; -pub use validation::urls::{validate_url}; -pub use validation::must_match::{validate_must_match}; -pub use validation::contains::{validate_contains}; #[cfg(feature = "card")] pub use validation::cards::validate_credit_card; +pub use validation::contains::validate_contains; +pub use validation::email::validate_email; +pub use validation::ip::{validate_ip, validate_ip_v4, validate_ip_v6}; +pub use validation::length::validate_length; +pub use validation::must_match::validate_must_match; #[cfg(feature = "phone")] pub use validation::phone::validate_phone; +pub use validation::range::validate_range; +pub use validation::urls::validate_url; pub use validation::Validator; +pub use traits::{Contains, HasLen, Validate}; pub use types::{ValidationError, ValidationErrors, ValidationErrorsKind}; -pub use traits::{Validate, HasLen, Contains}; diff --git a/validator/src/traits.rs b/validator/src/traits.rs index c34819c..f59113c 100644 --- a/validator/src/traits.rs +++ b/validator/src/traits.rs @@ -3,7 +3,6 @@ use std::collections::HashMap; use types::ValidationErrors; - /// Trait to implement if one wants to make the `length` validator /// work for more types /// diff --git a/validator/src/types.rs b/validator/src/types.rs index 3981989..d26079b 100644 --- a/validator/src/types.rs +++ b/validator/src/types.rs @@ -1,10 +1,9 @@ -use std::{self, fmt}; use std::borrow::Cow; -use std::collections::{BTreeMap, HashMap, hash_map::Entry::Vacant}; +use std::collections::{hash_map::Entry::Vacant, BTreeMap, HashMap}; +use std::{self, fmt}; -use serde_json::{Value, to_value}; use serde::ser::Serialize; - +use serde_json::{to_value, Value}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] pub struct ValidationError { @@ -15,11 +14,7 @@ pub struct ValidationError { impl ValidationError { pub fn new(code: &'static str) -> ValidationError { - ValidationError { - code: Cow::from(code), - message: None, - params: HashMap::new(), - } + ValidationError { code: Cow::from(code), message: None, params: HashMap::new() } } pub fn add_param<T: Serialize>(&mut self, name: Cow<'static, str>, val: &T) { @@ -34,8 +29,12 @@ impl fmt::Display for ValidationError { } impl std::error::Error for ValidationError { - fn description(&self) -> &str { &self.code } - fn cause(&self) -> Option<&std::error::Error> { None } + fn description(&self) -> &str { + &self.code + } + fn cause(&self) -> Option<&std::error::Error> { + None + } } #[derive(Debug, Serialize, Clone, PartialEq)] @@ -66,26 +65,37 @@ impl ValidationErrors { /// Returns the combined outcome of a struct's validation result along with the nested /// validation result for one of its fields. - pub fn merge(parent: Result<(), ValidationErrors>, field: &'static str, child: Result<(), ValidationErrors>) -> Result<(), ValidationErrors> { + pub fn merge( + parent: Result<(), ValidationErrors>, + field: &'static str, + child: Result<(), ValidationErrors>, + ) -> Result<(), ValidationErrors> { match child { Ok(()) => parent, - Err(errors) => parent.and_then(|_| Err(ValidationErrors::new())).map_err(|mut parent_errors| { - parent_errors.add_nested(field, ValidationErrorsKind::Struct(Box::new(errors))); - parent_errors - }) + Err(errors) => { + parent.and_then(|_| Err(ValidationErrors::new())).map_err(|mut parent_errors| { + parent_errors.add_nested(field, ValidationErrorsKind::Struct(Box::new(errors))); + parent_errors + }) + } } } /// Returns the combined outcome of a struct's validation result along with the nested /// validation result for one of its fields where that field is a vector of validating structs. - pub fn merge_all(parent: Result<(), ValidationErrors>, field: &'static str, children: Vec<Result<(), ValidationErrors>>) -> Result<(), ValidationErrors> { - let errors = children.into_iter().enumerate() + pub fn merge_all( + parent: Result<(), ValidationErrors>, + field: &'static str, + children: Vec<Result<(), ValidationErrors>>, + ) -> Result<(), ValidationErrors> { + let errors = children + .into_iter() + .enumerate() .filter_map(|(i, res)| res.err().map(|mut err| (i, err.remove(field)))) .filter_map(|(i, entry)| match entry { Some(ValidationErrorsKind::Struct(errors)) => Some((i, errors)), _ => None, - }) - .collect::<BTreeMap<_, _>>(); + }).collect::<BTreeMap<_, _>>(); if errors.is_empty() { parent @@ -105,18 +115,29 @@ impl ValidationErrors { /// 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>> { - self.0.into_iter() - .filter_map(|(k, v)| if let ValidationErrorsKind::Field(errors) = v { Some((k, errors)) } else { None }) - .collect() - } - - #[deprecated(since="0.7.3", note="Use `field_errors` instead, or `errors` to also access any errors from nested structs")] + self.0 + .into_iter() + .filter_map(|(k, v)| { + if let ValidationErrorsKind::Field(errors) = v { + Some((k, errors)) + } else { + None + } + }).collect() + } + + #[deprecated( + since = "0.7.3", + note = "Use `field_errors` instead, or `errors` to also access any errors from nested structs" + )] pub fn inner(self) -> HashMap<&'static str, Vec<ValidationError>> { self.field_errors() } pub fn add(&mut self, field: &'static str, error: ValidationError) { - if let ValidationErrorsKind::Field(ref mut vec) = self.0.entry(field).or_insert(ValidationErrorsKind::Field(vec![])) { + if let ValidationErrorsKind::Field(ref mut vec) = + self.0.entry(field).or_insert(ValidationErrorsKind::Field(vec![])) + { vec.push(error); } else { panic!("Attempt to add field validation to a non-Field ValidationErrorsKind instance"); @@ -145,8 +166,12 @@ impl ValidationErrors { } impl std::error::Error for ValidationErrors { - fn description(&self) -> &str { "Validation failed" } - fn cause(&self) -> Option<&std::error::Error> { None } + fn description(&self) -> &str { + "Validation failed" + } + fn cause(&self) -> Option<&std::error::Error> { + None + } } impl fmt::Display for ValidationErrors { diff --git a/validator/src/validation/cards.rs b/validator/src/validation/cards.rs index b565ed1..19d91ab 100644 --- a/validator/src/validation/cards.rs +++ b/validator/src/validation/cards.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; -use card_validate::{Validate as CardValidate}; - +use card_validate::Validate as CardValidate; pub fn validate_credit_card<'a, T>(card: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { CardValidate::from(&card.into()).is_ok() } diff --git a/validator/src/validation/email.rs b/validator/src/validation/email.rs index 699ab77..b0b49a3 100644 --- a/validator/src/validation/email.rs +++ b/validator/src/validation/email.rs @@ -1,10 +1,9 @@ use idna::domain_to_ascii; -use std::borrow::Cow; use regex::Regex; +use std::borrow::Cow; use validation::ip::validate_ip; - lazy_static! { // Regex from the specs // https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address @@ -19,7 +18,8 @@ lazy_static! { /// Validates whether the given string is an email based on Django `EmailValidator` and HTML5 specs pub fn validate_email<'a, T>(val: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { let val = val.into(); if val.is_empty() || !val.contains('@') { @@ -52,17 +52,14 @@ fn validate_domain_part(domain_part: &str) -> bool { // maybe we have an ip as a domain? match EMAIL_LITERAL_RE.captures(domain_part) { - Some(caps) => { - match caps.get(1) { - Some(c) => validate_ip(c.as_str()), - None => false, - } - } - None => false + Some(caps) => match caps.get(1) { + Some(c) => validate_ip(c.as_str()), + None => false, + }, + None => false, } } - #[cfg(test)] mod tests { use std::borrow::Cow; @@ -88,7 +85,10 @@ mod tests { // max length for domain name labels is 63 characters per RFC 1034 ("a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true), ("a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.atm", true), - ("a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbb.atm", true), + ( + "a@aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.bbbbbbbbbb.atm", + true, + ), ("a@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true), ("", false), ("abc", false), diff --git a/validator/src/validation/ip.rs b/validator/src/validation/ip.rs index 955cf3c..dea4260 100644 --- a/validator/src/validation/ip.rs +++ b/validator/src/validation/ip.rs @@ -2,10 +2,10 @@ use std::borrow::Cow; use std::net::IpAddr; use std::str::FromStr; - /// Validates whether the given string is an IP V4 pub fn validate_ip_v4<'a, T>(val: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { match IpAddr::from_str(val.into().as_ref()) { Ok(i) => match i { @@ -18,7 +18,8 @@ pub fn validate_ip_v4<'a, T>(val: T) -> bool /// Validates whether the given string is an IP V6 pub fn validate_ip_v6<'a, T>(val: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { match IpAddr::from_str(val.into().as_ref()) { Ok(i) => match i { @@ -31,7 +32,8 @@ pub fn validate_ip_v6<'a, T>(val: T) -> bool /// Validates whether the given string is an IP pub fn validate_ip<'a, T>(val: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { match IpAddr::from_str(val.into().as_ref()) { Ok(_) => true, @@ -39,12 +41,11 @@ pub fn validate_ip<'a, T>(val: T) -> bool } } - #[cfg(test)] mod tests { use std::borrow::Cow; - use super::{validate_ip_v4, validate_ip_v6, validate_ip}; + use super::{validate_ip, validate_ip_v4, validate_ip_v6}; #[test] fn test_validate_ip() { diff --git a/validator/src/validation/length.rs b/validator/src/validation/length.rs index c59a6cb..8e6a5a4 100644 --- a/validator/src/validation/length.rs +++ b/validator/src/validation/length.rs @@ -1,5 +1,5 @@ -use validation::Validator; use traits::HasLen; +use validation::Validator; /// Validates the length of the value given. /// If the validator has `equal` set, it will ignore any `min` and `max` value. @@ -23,8 +23,8 @@ pub fn validate_length<T: HasLen>(length: Validator, val: T) -> bool { return false; } } - }, - _ => unreachable!() + } + _ => unreachable!(), } true diff --git a/validator/src/validation/mod.rs b/validator/src/validation/mod.rs index 34d1087..065b27e 100644 --- a/validator/src/validation/mod.rs +++ b/validator/src/validation/mod.rs @@ -1,14 +1,14 @@ -pub mod ip; +#[cfg(feature = "card")] +pub mod cards; +pub mod contains; pub mod email; +pub mod ip; pub mod length; -pub mod range; -pub mod urls; pub mod must_match; -pub mod contains; -#[cfg(feature = "card")] -pub mod cards; #[cfg(feature = "phone")] pub mod phone; +pub mod range; +pub mod urls; /// Contains all the validators that can be used /// @@ -52,8 +52,8 @@ impl Validator { Validator::Custom(_) => "custom", Validator::Contains(_) => "contains", Validator::Regex(_) => "regex", - Validator::Range {..} => "range", - Validator::Length {..} => "length", + Validator::Range { .. } => "range", + Validator::Length { .. } => "length", #[cfg(feature = "card")] Validator::CreditCard => "credit_card", #[cfg(feature = "phone")] diff --git a/validator/src/validation/must_match.rs b/validator/src/validation/must_match.rs index 5024e9f..f7ea788 100644 --- a/validator/src/validation/must_match.rs +++ b/validator/src/validation/must_match.rs @@ -8,7 +8,7 @@ pub fn validate_must_match<T: Eq>(a: T, b: T) -> bool { mod tests { use std::borrow::Cow; - use super::{validate_must_match}; + use super::validate_must_match; #[test] fn test_validate_must_match_strings_valid() { diff --git a/validator/src/validation/phone.rs b/validator/src/validation/phone.rs index 41d45a5..7e4e31f 100644 --- a/validator/src/validation/phone.rs +++ b/validator/src/validation/phone.rs @@ -1,9 +1,9 @@ use phonenumber; use std::borrow::Cow; - pub fn validate_phone<'a, T>(phone_number: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { if let Ok(parsed) = phonenumber::parse(None, phone_number.into()) { phonenumber::is_valid(&parsed) diff --git a/validator/src/validation/range.rs b/validator/src/validation/range.rs index 8cb9ec7..c643762 100644 --- a/validator/src/validation/range.rs +++ b/validator/src/validation/range.rs @@ -5,10 +5,8 @@ use validation::Validator; /// TODO: see if can be generic over the number type pub fn validate_range(range: Validator, val: f64) -> bool { match range { - Validator::Range { min, max } => { - val >= min && val <= max - }, - _ => unreachable!() + Validator::Range { min, max } => val >= min && val <= max, + _ => unreachable!(), } } diff --git a/validator/src/validation/urls.rs b/validator/src/validation/urls.rs index 189ce8f..56c3390 100644 --- a/validator/src/validation/urls.rs +++ b/validator/src/validation/urls.rs @@ -1,10 +1,10 @@ use std::borrow::Cow; use url::Url; - /// Validates whether the string given is a url pub fn validate_url<'a, T>(val: T) -> bool - where T: Into<Cow<'a, str>> +where + T: Into<Cow<'a, str>>, { match Url::parse(val.into().as_ref()) { Ok(_) => true, @@ -16,8 +16,7 @@ pub fn validate_url<'a, T>(val: T) -> bool mod tests { use std::borrow::Cow; - use super::{validate_url}; - + use super::validate_url; #[test] fn test_validate_url() { diff --git a/validator_derive/src/asserts.rs b/validator_derive/src/asserts.rs index cdd0028..1a8dfff 100644 --- a/validator_derive/src/asserts.rs +++ b/validator_derive/src/asserts.rs @@ -5,20 +5,44 @@ lazy_static! { } pub static NUMBER_TYPES: [&'static str; 36] = [ - "usize", "u8", "u16", "u32", "u64", - "isize", "i8", "i16", "i32", "i64", - "f32", "f64", - - "Option<usize>", "Option<u8>", "Option<u16>", "Option<u32>", "Option<u64>", - "Option<isize>", "Option<i8>", "Option<i16>", "Option<i32>", "Option<i64>", - "Option<f32>", "Option<f64>", - - "Option<Option<usize>>", "Option<Option<u8>>", "Option<Option<u16>>", "Option<Option<u32>>", "Option<Option<u64>>", - "Option<Option<isize>>", "Option<Option<i8>>", "Option<Option<i16>>", "Option<Option<i32>>", "Option<Option<i64>>", - "Option<Option<f32>>", "Option<Option<f64>>", + "usize", + "u8", + "u16", + "u32", + "u64", + "isize", + "i8", + "i16", + "i32", + "i64", + "f32", + "f64", + "Option<usize>", + "Option<u8>", + "Option<u16>", + "Option<u32>", + "Option<u64>", + "Option<isize>", + "Option<i8>", + "Option<i16>", + "Option<i32>", + "Option<i64>", + "Option<f32>", + "Option<f64>", + "Option<Option<usize>>", + "Option<Option<u8>>", + "Option<Option<u16>>", + "Option<Option<u32>>", + "Option<Option<u64>>", + "Option<Option<isize>>", + "Option<Option<i8>>", + "Option<Option<i16>>", + "Option<Option<i32>>", + "Option<Option<i64>>", + "Option<Option<f32>>", + "Option<Option<f64>>", ]; - pub fn assert_string_type(name: &str, field_type: &String) { if field_type != "String" && field_type != "&str" @@ -26,8 +50,12 @@ pub fn assert_string_type(name: &str, field_type: &String) { && field_type != "Option<String>" && field_type != "Option<Option<String>>" && !(field_type.starts_with("Option<") && field_type.ends_with("str>")) - && !(field_type.starts_with("Option<Option<") && field_type.ends_with("str>>")) { - panic!("`{}` validator can only be used on String, &str, Cow<'_,str> or an Option of those", name); + && !(field_type.starts_with("Option<Option<") && field_type.ends_with("str>>")) + { + panic!( + "`{}` validator can only be used on String, &str, Cow<'_,str> or an Option of those", + name + ); } } @@ -52,8 +80,9 @@ pub fn assert_has_len(field_name: String, field_type: &String) { && !(field_type.starts_with("Option<") && field_type.ends_with("str>")) && !(field_type.starts_with("Option<Option<") && field_type.ends_with("str>>")) && !COW_TYPE.is_match(field_type) - && field_type != "&str" { - panic!( + && field_type != "&str" + { + panic!( "Validator `length` can only be used on types `String`, `&str`, Cow<'_,str> or `Vec` but found `{}` for field `{}`", field_type, field_name ); diff --git a/validator_derive/src/lib.rs b/validator_derive/src/lib.rs index 2a23ba4..0647a7d 100644 --- a/validator_derive/src/lib.rs +++ b/validator_derive/src/lib.rs @@ -18,17 +18,15 @@ use quote::ToTokens; use std::collections::HashMap; use validator::Validator; - -mod lit; -mod validation; mod asserts; +mod lit; mod quoting; +mod validation; +use asserts::{assert_has_len, assert_has_range, assert_string_type, assert_type_matches}; use lit::*; +use quoting::{quote_field_validation, quote_schema_validation, FieldQuoter}; use validation::*; -use asserts::{assert_string_type, assert_type_matches, assert_has_len, assert_has_range}; -use quoting::{FieldQuoter, quote_field_validation, quote_schema_validation}; - #[proc_macro_derive(Validate, attributes(validate))] pub fn derive_validation(input: TokenStream) -> TokenStream { @@ -36,7 +34,6 @@ pub fn derive_validation(input: TokenStream) -> TokenStream { impl_validate(&ast).into() } - fn impl_validate(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { // Ensure the macro is on a struct with named fields let fields = match ast.data { @@ -45,7 +42,7 @@ fn impl_validate(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { panic!("struct has unnamed fields"); } fields.iter().cloned().collect() - }, + } _ => panic!("#[derive(Validate)] can only be used with structs"), }; @@ -61,7 +58,12 @@ fn impl_validate(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { let field_quoter = FieldQuoter::new(field_ident, name, field_type); for validation in &field_validations { - quote_field_validation(&field_quoter, validation, &mut validations, &mut nested_validations); + quote_field_validation( + &field_quoter, + validation, + &mut validations, + &mut nested_validations, + ); } } @@ -96,7 +98,6 @@ fn impl_validate(ast: &syn::DeriveInput) -> proc_macro2::TokenStream { impl_ast } - /// Find if a struct has some schema validation and returns the info if so fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaValidation> { let error = |msg: &str| -> ! { @@ -187,7 +188,6 @@ fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaVa None } - /// Find the types (as string) for each field of the struct /// Needed for the `must_match` filter fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> { @@ -200,8 +200,7 @@ fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> { let mut tokens = proc_macro2::TokenStream::new(); path.to_tokens(&mut tokens); tokens.to_string().replace(' ', "") - - }, + } syn::Type::Reference(syn::TypeReference { ref lifetime, ref elem, .. }) => { let mut tokens = proc_macro2::TokenStream::new(); elem.to_tokens(&mut tokens); @@ -210,8 +209,8 @@ fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> { name.insert(0, '&') } name - }, - _ => panic!("Type `{:?}` of field `{}` not supported", field.ty, field_ident) + } + _ => panic!("Type `{:?}` of field `{}` not supported", field.ty, field_ident), }; //println!("{:?}", field_type); @@ -223,12 +222,19 @@ fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> { /// Find everything we need to know about a field: its real name if it's changed from the serialization /// and the list of validators to run on it -fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, String>) -> (String, Vec<FieldValidation>) { +fn find_validators_for_field( + field: &syn::Field, + field_types: &HashMap<String, String>, +) -> (String, Vec<FieldValidation>) { let rust_ident = field.ident.clone().unwrap().to_string(); let mut field_ident = field.ident.clone().unwrap().to_string(); let error = |msg: &str| -> ! { - panic!("Invalid attribute #[validate] on field `{}`: {}", field.ident.clone().unwrap().to_string(), msg); + panic!( + "Invalid attribute #[validate] on field `{}`: {}", + field.ident.clone().unwrap().to_string(), + msg + ); }; let field_type = field_types.get(&field_ident).unwrap(); @@ -265,38 +271,40 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S "email" => { assert_string_type("email", field_type); validators.push(FieldValidation::new(Validator::Email)); - }, + } "url" => { assert_string_type("url", field_type); validators.push(FieldValidation::new(Validator::Url)); - }, + } #[cfg(feature = "phone")] "phone" => { assert_string_type("phone", field_type); validators.push(FieldValidation::new(Validator::Phone)); - }, + } #[cfg(feature = "card")] "credit_card" => { assert_string_type("credit_card", field_type); validators.push(FieldValidation::new(Validator::CreditCard)); - }, - _ => panic!("Unexpected validator: {}", name) + } + _ => panic!("Unexpected validator: {}", name), }, // custom, contains, must_match, regex - syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => { + syn::Meta::NameValue(syn::MetaNameValue { + ref ident, ref lit, .. + }) => { match ident.to_string().as_ref() { "custom" => { match lit_to_string(lit) { Some(s) => validators.push(FieldValidation::new(Validator::Custom(s))), None => error("invalid argument for `custom` validator: only strings are allowed"), }; - }, + } "contains" => { match lit_to_string(lit) { Some(s) => validators.push(FieldValidation::new(Validator::Contains(s))), None => error("invalid argument for `contains` validator: only strings are allowed"), }; - }, + } "regex" => { match lit_to_string(lit) { Some(s) => validators.push(FieldValidation::new(Validator::Regex(s))), @@ -311,53 +319,88 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S }, None => error("invalid argument for `must_match` validator: only strings are allowed"), }; - }, + } v => panic!("unexpected name value validator: {:?}", v), }; - }, + } // Validators with several args syn::Meta::List(syn::MetaList { ref ident, ref nested, .. }) => { let meta_items = nested.iter().cloned().collect(); match ident.to_string().as_ref() { "length" => { assert_has_len(rust_ident.clone(), field_type); - validators.push(extract_length_validation(rust_ident.clone(), &meta_items)); - }, + validators.push(extract_length_validation( + rust_ident.clone(), + &meta_items, + )); + } "range" => { assert_has_range(rust_ident.clone(), field_type); - validators.push(extract_range_validation(rust_ident.clone(), &meta_items)); - }, + validators.push(extract_range_validation( + rust_ident.clone(), + &meta_items, + )); + } "email" | "url" | "phone" | "credit_card" => { - validators.push(extract_argless_validation(ident.to_string(), rust_ident.clone(), &meta_items)); - }, + validators.push(extract_argless_validation( + ident.to_string(), + rust_ident.clone(), + &meta_items, + )); + } "custom" => { - validators.push(extract_one_arg_validation("function", ident.to_string(), rust_ident.clone(), &meta_items)); - }, + validators.push(extract_one_arg_validation( + "function", + ident.to_string(), + rust_ident.clone(), + &meta_items, + )); + } "contains" => { - validators.push(extract_one_arg_validation("pattern", ident.to_string(), rust_ident.clone(), &meta_items)); - }, + validators.push(extract_one_arg_validation( + "pattern", + ident.to_string(), + rust_ident.clone(), + &meta_items, + )); + } "regex" => { - validators.push(extract_one_arg_validation("path", ident.to_string(), rust_ident.clone(), &meta_items)); - }, + validators.push(extract_one_arg_validation( + "path", + ident.to_string(), + rust_ident.clone(), + &meta_items, + )); + } "must_match" => { - let validation = extract_one_arg_validation("other", ident.to_string(), rust_ident.clone(), &meta_items); + let validation = extract_one_arg_validation( + "other", + ident.to_string(), + rust_ident.clone(), + &meta_items, + ); if let Validator::MustMatch(ref t2) = validation.validator { - assert_type_matches(rust_ident.clone(), field_type, field_types.get(t2)); + assert_type_matches( + rust_ident.clone(), + field_type, + field_types.get(t2), + ); } validators.push(validation); - }, - v => panic!("unexpected list validator: {:?}", v) + } + v => panic!("unexpected list validator: {:?}", v), } - }, + } }, - _ => unreachable!("Found a non Meta while looking for validators") + _ => unreachable!("Found a non Meta while looking for validators"), }; } - }, - Some(syn::Meta::Word(_)) => { - validators.push(FieldValidation::new(Validator::Nested)) - }, - _ => unreachable!("Got something other than a list of attributes while checking field `{}`", field_ident), + } + Some(syn::Meta::Word(_)) => validators.push(FieldValidation::new(Validator::Nested)), + _ => unreachable!( + "Got something other than a list of attributes while checking field `{}`", + field_ident + ), } } @@ -384,12 +427,12 @@ fn find_original_field_name(meta_items: &Vec<&syn::NestedMeta>) -> Option<String if ident == "rename" { original_name = Some(lit_to_string(lit).unwrap()); } - }, + } syn::Meta::List(syn::MetaList { ref nested, .. }) => { return find_original_field_name(&nested.iter().collect()); } }, - _ => unreachable!() + _ => unreachable!(), }; if original_name.is_some() { @@ -399,4 +442,3 @@ fn find_original_field_name(meta_items: &Vec<&syn::NestedMeta>) -> Option<String original_name } - diff --git a/validator_derive/src/lit.rs b/validator_derive/src/lit.rs index 1864d8f..14e3f56 100644 --- a/validator_derive/src/lit.rs +++ b/validator_derive/src/lit.rs @@ -1,6 +1,5 @@ -use syn; use proc_macro2; - +use syn; pub fn lit_to_string(lit: &syn::Lit) -> Option<String> { match *lit { @@ -32,7 +31,11 @@ pub fn lit_to_bool(lit: &syn::Lit) -> Option<bool> { match *lit { syn::Lit::Bool(ref s) => Some(s.value), // TODO: remove when attr_literals is stable - syn::Lit::Str(ref s) => if s.value() == "true" { Some(true) } else { Some(false) }, + syn::Lit::Str(ref s) => if s.value() == "true" { + Some(true) + } else { + Some(false) + }, _ => None, } } @@ -40,6 +43,6 @@ pub fn lit_to_bool(lit: &syn::Lit) -> Option<bool> { pub fn option_u64_to_tokens(opt: Option<u64>) -> proc_macro2::TokenStream { match opt { Some(ref t) => quote!(::std::option::Option::Some(#t)), - None => quote!(::std::option::Option::None) + None => quote!(::std::option::Option::None), } } diff --git a/validator_derive/src/quoting.rs b/validator_derive/src/quoting.rs index b47db6a..f94d8fc 100644 --- a/validator_derive/src/quoting.rs +++ b/validator_derive/src/quoting.rs @@ -1,11 +1,10 @@ -use validator::Validator; -use syn; use proc_macro2::{self, Span}; +use syn; +use validator::Validator; +use asserts::{COW_TYPE, NUMBER_TYPES}; use lit::option_u64_to_tokens; use validation::{FieldValidation, SchemaValidation}; -use asserts::{COW_TYPE, NUMBER_TYPES}; - /// Pass around all the information needed for creating a validation #[derive(Debug)] @@ -54,8 +53,10 @@ impl FieldQuoter { pub fn get_optional_validator_param(&self) -> proc_macro2::TokenStream { let ident = &self.ident; - if self._type.starts_with("Option<&") || self._type.starts_with("Option<Option<&") - || NUMBER_TYPES.contains(&self._type.as_ref()) { + if self._type.starts_with("Option<&") + || self._type.starts_with("Option<Option<&") + || NUMBER_TYPES.contains(&self._type.as_ref()) + { quote!(#ident) } else { quote!(ref #ident) @@ -72,19 +73,18 @@ impl FieldQuoter { if let Some(Some(#optional_pattern_matched)) = self.#field_ident { #tokens } - ) + ); } else if self._type.starts_with("Option<") { return quote!( if let Some(#optional_pattern_matched) = self.#field_ident { #tokens } - ) + ); } tokens } - /// Wrap the quoted output of a validation with a for loop if /// the field type is a vector pub fn wrap_if_vector(&self, tokens: proc_macro2::TokenStream) -> proc_macro2::TokenStream { @@ -99,7 +99,7 @@ impl FieldQuoter { result }).collect(); result = ::validator::ValidationErrors::merge_all(result, #field_name, results); - }) + }); } tokens @@ -121,8 +121,10 @@ fn quote_error(validation: &FieldValidation) -> proc_macro2::TokenStream { ) } - -pub fn quote_length_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_length_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -173,7 +175,10 @@ pub fn quote_length_validation(field_quoter: &FieldQuoter, validation: &FieldVal unreachable!() } -pub fn quote_range_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_range_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let quoted_ident = field_quoter.quote_validator_param(); @@ -201,7 +206,10 @@ pub fn quote_range_validation(field_quoter: &FieldQuoter, validation: &FieldVali } #[cfg(feature = "card")] -pub fn quote_credit_card_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_credit_card_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -218,7 +226,10 @@ pub fn quote_credit_card_validation(field_quoter: &FieldQuoter, validation: &Fie } #[cfg(feature = "phone")] -pub fn quote_phone_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_phone_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -234,7 +245,10 @@ pub fn quote_phone_validation(field_quoter: &FieldQuoter, validation: &FieldVali field_quoter.wrap_if_option(quoted) } -pub fn quote_url_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_url_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -250,7 +264,10 @@ pub fn quote_url_validation(field_quoter: &FieldQuoter, validation: &FieldValida field_quoter.wrap_if_option(quoted) } -pub fn quote_email_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_email_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -266,7 +283,10 @@ pub fn quote_email_validation(field_quoter: &FieldQuoter, validation: &FieldVali field_quoter.wrap_if_option(quoted) } -pub fn quote_must_match_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_must_match_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let ident = &field_quoter.ident; let field_name = &field_quoter.name; @@ -288,7 +308,10 @@ pub fn quote_must_match_validation(field_quoter: &FieldQuoter, validation: &Fiel unreachable!(); } -pub fn quote_custom_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_custom_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -317,7 +340,10 @@ pub fn quote_custom_validation(field_quoter: &FieldQuoter, validation: &FieldVal unreachable!(); } -pub fn quote_contains_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_contains_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -338,7 +364,10 @@ pub fn quote_contains_validation(field_quoter: &FieldQuoter, validation: &FieldV unreachable!(); } -pub fn quote_regex_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> proc_macro2::TokenStream { +pub fn quote_regex_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, +) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_param = field_quoter.quote_validator_param(); @@ -359,27 +388,42 @@ pub fn quote_regex_validation(field_quoter: &FieldQuoter, validation: &FieldVali unreachable!(); } -pub fn quote_nested_validation(field_quoter: &FieldQuoter) -> proc_macro2::TokenStream { +pub fn quote_nested_validation(field_quoter: &FieldQuoter) -> proc_macro2::TokenStream { let field_name = &field_quoter.name; let validator_field = field_quoter.quote_validator_field(); let quoted = quote!(result = ::validator::ValidationErrors::merge(result, #field_name, #validator_field.validate());); field_quoter.wrap_if_option(field_quoter.wrap_if_vector(quoted)) } -pub fn quote_field_validation(field_quoter: &FieldQuoter, validation: &FieldValidation, - validations: &mut Vec<proc_macro2::TokenStream>, - nested_validations: &mut Vec<proc_macro2::TokenStream>) { +pub fn quote_field_validation( + field_quoter: &FieldQuoter, + validation: &FieldValidation, + validations: &mut Vec<proc_macro2::TokenStream>, + nested_validations: &mut Vec<proc_macro2::TokenStream>, +) { match validation.validator { - Validator::Length {..} => validations.push(quote_length_validation(&field_quoter, validation)), - Validator::Range {..} => validations.push(quote_range_validation(&field_quoter, validation)), + Validator::Length { .. } => { + validations.push(quote_length_validation(&field_quoter, validation)) + } + Validator::Range { .. } => { + validations.push(quote_range_validation(&field_quoter, validation)) + } Validator::Email => validations.push(quote_email_validation(&field_quoter, validation)), Validator::Url => validations.push(quote_url_validation(&field_quoter, validation)), - Validator::MustMatch(_) => validations.push(quote_must_match_validation(&field_quoter, validation)), - Validator::Custom(_) => validations.push(quote_custom_validation(&field_quoter, validation)), - Validator::Contains(_) => validations.push(quote_contains_validation(&field_quoter, validation)), + Validator::MustMatch(_) => { + validations.push(quote_must_match_validation(&field_quoter, validation)) + } + Validator::Custom(_) => { + validations.push(quote_custom_validation(&field_quoter, validation)) + } + Validator::Contains(_) => { + validations.push(quote_contains_validation(&field_quoter, validation)) + } Validator::Regex(_) => validations.push(quote_regex_validation(&field_quoter, validation)), #[cfg(feature = "card")] - Validator::CreditCard => validations.push(quote_credit_card_validation(&field_quoter, validation)), + Validator::CreditCard => { + validations.push(quote_credit_card_validation(&field_quoter, validation)) + } #[cfg(feature = "phone")] Validator::Phone => validations.push(quote_phone_validation(&field_quoter, validation)), Validator::Nested => nested_validations.push(quote_nested_validation(&field_quoter)), diff --git a/validator_derive/src/validation.rs b/validator_derive/src/validation.rs index 227b9d9..a9762db 100644 --- a/validator_derive/src/validation.rs +++ b/validator_derive/src/validation.rs @@ -4,7 +4,6 @@ use validator::Validator; use lit::*; - #[derive(Debug)] pub struct SchemaValidation { pub function: String, @@ -13,7 +12,6 @@ pub struct SchemaValidation { pub message: Option<String>, } - #[derive(Debug)] pub struct FieldValidation { pub code: String, @@ -23,15 +21,14 @@ pub struct FieldValidation { impl FieldValidation { pub fn new(validator: Validator) -> FieldValidation { - FieldValidation { - code: validator.code().to_string(), - validator, - message: None, - } + FieldValidation { code: validator.code().to_string(), validator, message: None } } } -pub fn extract_length_validation(field: String, meta_items: &Vec<syn::NestedMeta>) -> FieldValidation { +pub fn extract_length_validation( + field: String, + meta_items: &Vec<syn::NestedMeta>, +) -> FieldValidation { let mut min = None; let mut max = None; let mut equal = None; @@ -71,7 +68,10 @@ pub fn extract_length_validation(field: String, meta_items: &Vec<syn::NestedMeta )) } } else { - panic!("unexpected item {:?} while parsing `length` validator of field {}", item, field) + panic!( + "unexpected item {:?} while parsing `length` validator of field {}", + item, field + ) } } } @@ -91,12 +91,15 @@ pub fn extract_length_validation(field: String, meta_items: &Vec<syn::NestedMeta } } -pub fn extract_range_validation(field: String, meta_items: &Vec<syn::NestedMeta>) -> FieldValidation { +pub fn extract_range_validation( + field: String, + meta_items: &Vec<syn::NestedMeta>, +) -> FieldValidation { let mut min = 0.0; let mut max = 0.0; let (message, code) = extract_message_and_code("range", &field, meta_items); - + let error = |msg: &str| -> ! { panic!("Invalid attribute #[validate] on field `{}`: {}", field, msg); }; @@ -108,32 +111,33 @@ pub fn extract_range_validation(field: String, meta_items: &Vec<syn::NestedMeta> for meta_item in meta_items { match *meta_item { syn::NestedMeta::Meta(ref item) => match *item { - syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => { - match ident.to_string().as_ref() { - "message" | "code" => continue, - "min" => { - min = match lit_to_float(lit) { + syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => match ident + .to_string() + .as_ref() + { + "message" | "code" => continue, + "min" => { + min = match lit_to_float(lit) { Some(s) => s, None => error("invalid argument type for `min` of `range` validator: only integers are allowed") }; - has_min = true; - }, - "max" => { - max = match lit_to_float(lit) { + has_min = true; + } + "max" => { + max = match lit_to_float(lit) { Some(s) => s, None => error("invalid argument type for `max` of `range` validator: only integers are allowed") }; - has_max = true; - }, - v => error(&format!( - "unknown argument `{}` for validator `range` (it only has `min`, `max`)", - v - )) + has_max = true; } + v => error(&format!( + "unknown argument `{}` for validator `range` (it only has `min`, `max`)", + v + )), }, - _ => panic!("unexpected item {:?} while parsing `range` validator", item) + _ => panic!("unexpected item {:?} while parsing `range` validator", item), }, - _=> unreachable!() + _ => unreachable!(), } } @@ -150,7 +154,11 @@ pub fn extract_range_validation(field: String, meta_items: &Vec<syn::NestedMeta> } /// Extract url/email/phone field validation with a code or a message -pub fn extract_argless_validation(validator_name: String, field: String, meta_items: &Vec<syn::NestedMeta>) -> FieldValidation { +pub fn extract_argless_validation( + validator_name: String, + field: String, + meta_items: &Vec<syn::NestedMeta>, +) -> FieldValidation { let (message, code) = extract_message_and_code(&validator_name, &field, meta_items); for meta_item in meta_items { @@ -162,12 +170,12 @@ pub fn extract_argless_validation(validator_name: String, field: String, meta_it v => panic!( "Unknown argument `{}` for validator `{}` on field `{}`", v, validator_name, field - ) + ), } - }, - _ => panic!("unexpected item {:?} while parsing `range` validator", item) + } + _ => panic!("unexpected item {:?} while parsing `range` validator", item), }, - _=> unreachable!() + _ => unreachable!(), } } @@ -177,7 +185,7 @@ pub fn extract_argless_validation(validator_name: String, field: String, meta_it "credit_card" => Validator::CreditCard, #[cfg(feature = "phone")] "phone" => Validator::Phone, - _ => Validator::Url + _ => Validator::Url, }; FieldValidation { @@ -188,7 +196,12 @@ pub fn extract_argless_validation(validator_name: String, field: String, meta_it } /// For custom, contains, regex, must_match -pub fn extract_one_arg_validation(val_name: &str, validator_name: String, field: String, meta_items: &Vec<syn::NestedMeta>) -> FieldValidation { +pub fn extract_one_arg_validation( + val_name: &str, + validator_name: String, + field: String, + meta_items: &Vec<syn::NestedMeta>, +) -> FieldValidation { let mut value = None; let (message, code) = extract_message_and_code(&validator_name, &field, meta_items); @@ -206,21 +219,24 @@ pub fn extract_one_arg_validation(val_name: &str, validator_name: String, field: val_name, validator_name, field ), }; - }, + } v => panic!( "Unknown argument `{}` for validator `{}` on field `{}`", v, validator_name, field - ) + ), } - }, - _ => panic!("unexpected item {:?} while parsing `range` validator", item) + } + _ => panic!("unexpected item {:?} while parsing `range` validator", item), }, - _=> unreachable!() + _ => unreachable!(), } } if value.is_none() { - panic!("Missing argument `{}` for validator `{}` on field `{}`", val_name, validator_name, field); + panic!( + "Missing argument `{}` for validator `{}` on field `{}`", + val_name, validator_name, field + ); } let validator = match validator_name.as_ref() { @@ -238,12 +254,21 @@ pub fn extract_one_arg_validation(val_name: &str, validator_name: String, field: } } -fn extract_message_and_code(validator_name: &str, field: &str, meta_items: &Vec<syn::NestedMeta>) -> (Option<String>, Option<String>) { +fn extract_message_and_code( + validator_name: &str, + field: &str, + meta_items: &Vec<syn::NestedMeta>, +) -> (Option<String>, Option<String>) { let mut message = None; let mut code = None; for meta_item in meta_items { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ref ident , ref lit, .. })) = *meta_item { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { + ref ident, + ref lit, + .. + })) = *meta_item + { match ident.to_string().as_ref() { "code" => { code = match lit_to_string(lit) { @@ -253,7 +278,7 @@ fn extract_message_and_code(validator_name: &str, field: &str, meta_items: &Vec< validator_name, field ), }; - }, + } "message" => { message = match lit_to_string(lit) { Some(s) => Some(s), @@ -262,8 +287,8 @@ fn extract_message_and_code(validator_name: &str, field: &str, meta_items: &Vec< validator_name, field ), }; - }, - _ => continue + } + _ => continue, } } } diff --git a/validator_derive/tests/complex.rs b/validator_derive/tests/complex.rs index 0df26c0..7826a49 100644 --- a/validator_derive/tests/complex.rs +++ b/validator_derive/tests/complex.rs @@ -3,15 +3,14 @@ extern crate validator_derive; extern crate validator; #[macro_use] extern crate serde_derive; -extern crate serde_json; extern crate regex; +extern crate serde_json; #[macro_use] extern crate lazy_static; use regex::Regex; -use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind}; use std::collections::HashMap; - +use validator::{Validate, ValidationError, ValidationErrors, ValidationErrorsKind}; fn validate_unique_username(username: &str) -> Result<(), ValidationError> { if username == "xXxShad0wxXx" { @@ -46,13 +45,13 @@ struct SignupData { #[validate] card: Option<Card>, #[validate] - preferences: Vec<Preference> + preferences: Vec<Preference>, } #[derive(Debug, Validate, Deserialize)] struct Phone { #[validate(phone)] - number: String + number: String, } #[derive(Debug, Validate, Deserialize)] @@ -77,19 +76,9 @@ fn is_fine_with_many_valid_validations() { site: "http://hello.com".to_string(), first_name: "Bob".to_string(), age: 18, - phone: Phone { - number: "+14152370800".to_string() - }, - card: Some(Card { - number: "5236313877109142".to_string(), - cvv: 123 - }), - preferences: vec![ - Preference { - name: "marketing".to_string(), - value: false - }, - ] + phone: Phone { number: "+14152370800".to_string() }, + card: Some(Card { number: "5236313877109142".to_string(), cvv: 123 }), + preferences: vec![Preference { name: "marketing".to_string(), value: false }], }; assert!(signup.validate().is_ok()); @@ -102,19 +91,9 @@ fn failed_validation_points_to_original_field_name() { site: "http://hello.com".to_string(), first_name: "".to_string(), age: 18, - phone: Phone { - number: "123 invalid".to_string(), - }, - card: Some(Card { - number: "1234567890123456".to_string(), - cvv: 1 - }), - preferences: vec![ - Preference { - name: "abc".to_string(), - value: true - }, - ] + phone: Phone { number: "123 invalid".to_string() }, + card: Some(Card { number: "1234567890123456".to_string(), cvv: 1 }), + preferences: vec![Preference { name: "abc".to_string(), value: true }], }; let res = signup.validate(); // println!("{}", serde_json::to_string(&res).unwrap()); @@ -286,9 +265,7 @@ fn test_works_with_question_mark_operator() { site: "http://hello.com".to_string(), first_name: "Bob".to_string(), age: 18, - phone: Phone { - number: "+14152370800".to_string() - }, + phone: Phone { number: "+14152370800".to_string() }, card: None, preferences: Vec::new(), }; @@ -314,27 +291,18 @@ fn test_works_with_none_values() { range: Option<usize>, } - let p = PutStruct { - name: None, - address: None, - age: None, - range: None, - }; + let p = PutStruct { name: None, address: None, age: None, range: None }; - let q = PutStruct { - name: None, - address: Some(None), - age: Some(None), - range: None, - }; + let q = PutStruct { name: None, address: Some(None), age: Some(None), range: None }; assert!(p.validate().is_ok()); assert!(q.validate().is_ok()); } fn unwrap_map<F>(errors: &Box<ValidationErrors>, f: F) - where F: FnOnce(HashMap<&'static str, ValidationErrorsKind>) +where + F: FnOnce(HashMap<&'static str, ValidationErrorsKind>), { let errors = *errors.clone(); f(errors.errors()); -}
\ No newline at end of file +} diff --git a/validator_derive/tests/contains.rs b/validator_derive/tests/contains.rs index 5e9060e..1fd568d 100644 --- a/validator_derive/tests/contains.rs +++ b/validator_derive/tests/contains.rs @@ -12,9 +12,7 @@ fn can_validate_contains_ok() { val: String, } - let s = TestStruct { - val: "hello".to_string(), - }; + let s = TestStruct { val: "hello".to_string() }; assert!(s.validate().is_ok()); } @@ -27,9 +25,7 @@ fn value_not_containing_needle_fails_validation() { val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -47,9 +43,7 @@ fn can_specify_code_for_contains() { #[validate(contains(pattern = "he", code = "oops"))] val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -65,9 +59,7 @@ fn can_specify_message_for_contains() { #[validate(contains(pattern = "he", message = "oops"))] val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/credit_card.rs b/validator_derive/tests/credit_card.rs index b1a9da3..d212c48 100644 --- a/validator_derive/tests/credit_card.rs +++ b/validator_derive/tests/credit_card.rs @@ -13,9 +13,7 @@ fn can_validate_valid_card_number() { val: String, } - let s = TestStruct { - val: "5236313877109142".to_string(), - }; + let s = TestStruct { val: "5236313877109142".to_string() }; assert!(s.validate().is_ok()); } @@ -29,9 +27,7 @@ fn bad_credit_card_fails_validation() { val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -49,9 +45,7 @@ fn can_specify_code_for_credit_card() { #[validate(credit_card(code = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -68,9 +62,7 @@ fn can_specify_message_for_credit_card() { #[validate(credit_card(message = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/custom.rs b/validator_derive/tests/custom.rs index cd1b354..c13853d 100644 --- a/validator_derive/tests/custom.rs +++ b/validator_derive/tests/custom.rs @@ -20,9 +20,7 @@ fn can_validate_custom_fn_ok() { val: String, } - let s = TestStruct { - val: "hello".to_string(), - }; + let s = TestStruct { val: "hello".to_string() }; assert!(s.validate().is_ok()); } @@ -35,9 +33,7 @@ fn can_fail_custom_fn_validation() { val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -54,9 +50,7 @@ fn can_specify_message_for_custom_fn() { #[validate(custom(function = "invalid_custom_fn", message = "oops"))] val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/email.rs b/validator_derive/tests/email.rs index 4fbc123..665f0da 100644 --- a/validator_derive/tests/email.rs +++ b/validator_derive/tests/email.rs @@ -4,7 +4,6 @@ extern crate validator; use validator::Validate; - #[test] fn can_validate_valid_email() { #[derive(Debug, Validate)] @@ -13,9 +12,7 @@ fn can_validate_valid_email() { val: String, } - let s = TestStruct { - val: "bob@bob.com".to_string(), - }; + let s = TestStruct { val: "bob@bob.com".to_string() }; assert!(s.validate().is_ok()); } @@ -28,9 +25,7 @@ fn bad_email_fails_validation() { val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -47,9 +42,7 @@ fn can_specify_code_for_email() { #[validate(email(code = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -65,9 +58,7 @@ fn can_specify_message_for_email() { #[validate(email(message = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/length.rs b/validator_derive/tests/length.rs index 12bffc6..6ad6272 100644 --- a/validator_derive/tests/length.rs +++ b/validator_derive/tests/length.rs @@ -12,9 +12,7 @@ fn can_validate_length_ok() { val: String, } - let s = TestStruct { - val: "hello".to_string(), - }; + let s = TestStruct { val: "hello".to_string() }; assert!(s.validate().is_ok()); } @@ -27,9 +25,7 @@ fn value_out_of_length_fails_validation() { val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -48,9 +44,7 @@ fn can_specify_code_for_length() { #[validate(length(min = "5", max = "10", code = "oops"))] val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -66,9 +60,7 @@ fn can_specify_message_for_length() { #[validate(length(min = "5", max = "10", message = "oops"))] val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/must_match.rs b/validator_derive/tests/must_match.rs index a8c95f9..32b3016 100644 --- a/validator_derive/tests/must_match.rs +++ b/validator_derive/tests/must_match.rs @@ -4,7 +4,6 @@ extern crate validator; use validator::Validate; - #[test] fn can_validate_valid_must_match() { #[derive(Debug, Validate)] @@ -14,10 +13,7 @@ fn can_validate_valid_must_match() { val2: String, } - let s = TestStruct { - val: "bob".to_string(), - val2: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string(), val2: "bob".to_string() }; assert!(s.validate().is_ok()); } @@ -31,10 +27,7 @@ fn not_matching_fails_validation() { val2: String, } - let s = TestStruct { - val: "bob".to_string(), - val2: "bobby".to_string(), - }; + let s = TestStruct { val: "bob".to_string(), val2: "bobby".to_string() }; let res = s.validate(); assert!(res.is_err()); @@ -54,10 +47,7 @@ fn can_specify_code_for_must_match() { val: String, val2: String, } - let s = TestStruct { - val: "bob".to_string(), - val2: "bobb".to_string(), - }; + let s = TestStruct { val: "bob".to_string(), val2: "bobb".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -74,10 +64,7 @@ fn can_specify_message_for_must_match() { val: String, val2: String, } - let s = TestStruct { - val: "bob".to_string(), - val2: "bobb".to_string(), - }; + let s = TestStruct { val: "bob".to_string(), val2: "bobb".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/nested.rs b/validator_derive/tests/nested.rs index 4f03714..a95b0ef 100644 --- a/validator_derive/tests/nested.rs +++ b/validator_derive/tests/nested.rs @@ -4,8 +4,10 @@ extern crate validator; #[macro_use] extern crate serde_derive; -use validator::{validate_length, Validate, ValidationError, ValidationErrors, ValidationErrorsKind, Validator}; use std::{borrow::Cow, collections::HashMap}; +use validator::{ + validate_length, Validate, ValidationError, ValidationErrors, ValidationErrorsKind, Validator, +}; #[derive(Debug, Validate)] struct Root<'a> { @@ -54,12 +56,7 @@ struct Child { fn is_fine_with_nested_validations() { let root = Root { value: "valid".to_string(), - a: &A { - value: "valid".to_string(), - b: B { - value: "valid".to_string(), - } - } + a: &A { value: "valid".to_string(), b: B { value: "valid".to_string() } }, }; assert!(root.validate().is_ok()); @@ -69,12 +66,7 @@ fn is_fine_with_nested_validations() { fn failed_validation_points_to_original_field_names() { let root = Root { value: String::new(), - a: &A { - value: String::new(), - b: B { - value: String::new(), - } - } + a: &A { value: String::new(), b: B { value: String::new() } }, }; let res = root.validate(); @@ -122,11 +114,7 @@ fn failed_validation_points_to_original_field_names() { #[test] fn test_can_validate_option_fields_without_lifetime() { - let instance = ParentWithOptionalChild { - child: Some(Child { - value: String::new(), - }) - }; + let instance = ParentWithOptionalChild { child: Some(Child { value: String::new() }) }; let res = instance.validate(); assert!(res.is_err()); @@ -157,13 +145,9 @@ fn test_can_validate_option_fields_with_lifetime() { child: Option<&'a Child>, } - let child = Child { - value: String::new(), - }; + let child = Child { value: String::new() }; - let instance = ParentWithLifetimeAndOptionalChild { - child: Some(&child) - }; + let instance = ParentWithLifetimeAndOptionalChild { child: Some(&child) }; let res = instance.validate(); assert!(res.is_err()); @@ -188,9 +172,7 @@ fn test_can_validate_option_fields_with_lifetime() { #[test] fn test_works_with_none_values() { - let instance = ParentWithOptionalChild { - child: None, - }; + let instance = ParentWithOptionalChild { child: None }; let res = instance.validate(); assert!(res.is_ok()); @@ -200,18 +182,10 @@ fn test_works_with_none_values() { fn test_can_validate_vector_fields() { let instance = ParentWithVectorOfChildren { child: vec![ - Child { - value: "valid".to_string(), - }, - Child { - value: String::new(), - }, - Child { - value: "valid".to_string(), - }, - Child { - value: String::new(), - } + Child { value: "valid".to_string() }, + Child { value: String::new() }, + Child { value: "valid".to_string() }, + Child { value: String::new() }, ], }; @@ -250,9 +224,7 @@ fn test_can_validate_vector_fields() { #[test] fn test_field_validations_take_priority_over_nested_validations() { - let instance = ParentWithVectorOfChildren { - child: Vec::new(), - }; + let instance = ParentWithVectorOfChildren { child: Vec::new() }; let res = instance.validate(); assert!(res.is_err()); @@ -271,7 +243,6 @@ fn test_field_validations_take_priority_over_nested_validations() { #[should_panic(expected = "Attempt to replace non-empty ValidationErrors entry")] #[allow(unused)] fn test_field_validation_errors_replaced_with_nested_validations_fails() { - #[derive(Debug)] struct ParentWithOverridingStructValidations { child: Vec<Child>, @@ -284,7 +255,10 @@ fn test_field_validation_errors_replaced_with_nested_validations_fails() { fn validate(&self) -> Result<(), ValidationErrors> { // First validate the length of the vector: let mut errors = ValidationErrors::new(); - if !validate_length(Validator::Length { min: Some(2u64), max: None, equal: None }, &self.child) { + if !validate_length( + Validator::Length { min: Some(2u64), max: None, equal: None }, + &self.child, + ) { let mut err = ValidationError::new("length"); err.add_param(Cow::from("min"), &2u64); err.add_param(Cow::from("value"), &&self.child); @@ -294,28 +268,29 @@ fn test_field_validation_errors_replaced_with_nested_validations_fails() { // Then validate the nested vector of structs without checking for existing field errors: let mut result = if errors.is_empty() { Ok(()) } else { Err(errors) }; { - let results: Vec<_> = self.child.iter().map(|child| { - let mut result = Ok(()); - result = ValidationErrors::merge(result, "child", child.validate()); - result - }).collect(); + let results: Vec<_> = self + .child + .iter() + .map(|child| { + let mut result = Ok(()); + result = ValidationErrors::merge(result, "child", child.validate()); + result + }).collect(); result = ValidationErrors::merge_all(result, "child", results); } result } } - let instance = ParentWithOverridingStructValidations { - child: vec![ - Child { - value: String::new() - }] - }; + let instance = + ParentWithOverridingStructValidations { child: vec![Child { value: String::new() }] }; instance.validate(); } #[test] -#[should_panic(expected = "Attempt to add field validation to a non-Field ValidationErrorsKind instance")] +#[should_panic( + expected = "Attempt to add field validation to a non-Field ValidationErrorsKind instance" +)] #[allow(unused)] fn test_field_validations_evaluated_after_nested_validations_fails() { #[derive(Debug)] @@ -331,16 +306,22 @@ fn test_field_validations_evaluated_after_nested_validations_fails() { // First validate the nested vector of structs: let mut result = Ok(()); if !ValidationErrors::has_error(&result, "child") { - let results: Vec<_> = self.child.iter().map(|child| { - let mut result = Ok(()); - result = ValidationErrors::merge(result, "child", child.validate()); - result - }).collect(); + let results: Vec<_> = self + .child + .iter() + .map(|child| { + let mut result = Ok(()); + result = ValidationErrors::merge(result, "child", child.validate()); + result + }).collect(); result = ValidationErrors::merge_all(result, "child", results); } // Then validate the length of the vector itself: - if !validate_length(Validator::Length { min: Some(2u64), max: None, equal: None }, &self.child) { + if !validate_length( + Validator::Length { min: Some(2u64), max: None, equal: None }, + &self.child, + ) { let mut err = ValidationError::new("length"); err.add_param(Cow::from("min"), &2u64); err.add_param(Cow::from("value"), &&self.child); @@ -353,18 +334,14 @@ fn test_field_validations_evaluated_after_nested_validations_fails() { } } - let instance = ParentWithStructValidationsFirst { - child: vec![ - Child { - value: String::new() - }] - }; + let instance = ParentWithStructValidationsFirst { child: vec![Child { value: String::new() }] }; let res = instance.validate(); } fn unwrap_map<F>(errors: &Box<ValidationErrors>, f: F) - where F: FnOnce(HashMap<&'static str, ValidationErrorsKind>) +where + F: FnOnce(HashMap<&'static str, ValidationErrorsKind>), { let errors = *errors.clone(); f(errors.errors()); -}
\ No newline at end of file +} diff --git a/validator_derive/tests/phone.rs b/validator_derive/tests/phone.rs index 3131bad..a87d037 100644 --- a/validator_derive/tests/phone.rs +++ b/validator_derive/tests/phone.rs @@ -4,7 +4,6 @@ extern crate validator; use validator::Validate; - #[cfg(feature = "phone")] #[test] fn can_validate_phone_ok() { @@ -14,9 +13,7 @@ fn can_validate_phone_ok() { val: String, } - let s = TestStruct { - val: "+14152370800".to_string(), - }; + let s = TestStruct { val: "+14152370800".to_string() }; assert!(s.validate().is_ok()); } @@ -30,9 +27,7 @@ fn bad_phone_fails_validation() { val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -49,9 +44,7 @@ fn can_specify_code_for_phone() { #[validate(phone(code = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -69,9 +62,7 @@ fn can_specify_message_for_phone() { #[validate(phone(message = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/range.rs b/validator_derive/tests/range.rs index 084e530..4f38f18 100644 --- a/validator_derive/tests/range.rs +++ b/validator_derive/tests/range.rs @@ -12,9 +12,7 @@ fn can_validate_range_ok() { val: usize, } - let s = TestStruct { - val: 6, - }; + let s = TestStruct { val: 6 }; assert!(s.validate().is_ok()); } @@ -27,9 +25,7 @@ fn value_out_of_range_fails_validation() { val: usize, } - let s = TestStruct { - val: 11, - }; + let s = TestStruct { val: 11 }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -45,9 +41,7 @@ fn can_specify_code_for_range() { #[validate(range(min = "5", max = "10", code = "oops"))] val: usize, } - let s = TestStruct { - val: 11, - }; + let s = TestStruct { val: 11 }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -66,9 +60,7 @@ fn can_specify_message_for_range() { #[validate(range(min = "5", max = "10", message = "oops"))] val: usize, } - let s = TestStruct { - val: 1, - }; + let s = TestStruct { val: 1 }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/regex.rs b/validator_derive/tests/regex.rs index b54df03..a6b9e68 100644 --- a/validator_derive/tests/regex.rs +++ b/validator_derive/tests/regex.rs @@ -5,8 +5,8 @@ extern crate lazy_static; extern crate validator_derive; extern crate validator; -use validator::Validate; use regex::Regex; +use validator::Validate; lazy_static! { static ref RE2: Regex = Regex::new(r"^[a-z]{2}$").unwrap(); @@ -20,9 +20,7 @@ fn can_validate_valid_regex() { val: String, } - let s = TestStruct { - val: "aa".to_string(), - }; + let s = TestStruct { val: "aa".to_string() }; assert!(s.validate().is_ok()); } @@ -35,9 +33,7 @@ fn bad_value_for_regex_fails_validation() { val: String, } - let s = TestStruct { - val: "2".to_string(), - }; + let s = TestStruct { val: "2".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -54,9 +50,7 @@ fn can_specify_code_for_regex() { #[validate(regex(path = "RE2", code = "oops"))] val: String, } - let s = TestStruct { - val: "2".to_string(), - }; + let s = TestStruct { val: "2".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -72,9 +66,7 @@ fn can_specify_message_for_regex() { #[validate(regex(path = "RE2", message = "oops"))] val: String, } - let s = TestStruct { - val: "2".to_string(), - }; + let s = TestStruct { val: "2".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); diff --git a/validator_derive/tests/schema.rs b/validator_derive/tests/schema.rs index 32684fa..b45f3a1 100644 --- a/validator_derive/tests/schema.rs +++ b/validator_derive/tests/schema.rs @@ -4,12 +4,11 @@ extern crate validator; use validator::{Validate, ValidationError}; - #[test] fn can_validate_schema_fn_ok() { fn valid_schema_fn(_: &TestStruct) -> Result<(), ValidationError> { - Ok(()) -} + Ok(()) + } #[derive(Debug, Validate)] #[validate(schema(function = "valid_schema_fn"))] @@ -17,9 +16,7 @@ fn can_validate_schema_fn_ok() { val: String, } - let s = TestStruct { - val: "hello".to_string(), - }; + let s = TestStruct { val: "hello".to_string() }; assert!(s.validate().is_ok()); } @@ -36,9 +33,7 @@ fn can_fail_schema_fn_validation() { val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -58,9 +53,7 @@ fn can_specify_message_for_schema_fn() { struct TestStruct { val: String, } - let s = TestStruct { - val: String::new(), - }; + let s = TestStruct { val: String::new() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -82,10 +75,7 @@ fn can_choose_to_run_schema_validation_even_after_field_errors() { num: usize, } - let s = TestStruct { - val: "hello".to_string(), - num: 0, - }; + let s = TestStruct { val: "hello".to_string(), num: 0 }; let res = s.validate(); assert!(res.is_err()); diff --git a/validator_derive/tests/url.rs b/validator_derive/tests/url.rs index 5a3b791..0ac3cb1 100644 --- a/validator_derive/tests/url.rs +++ b/validator_derive/tests/url.rs @@ -4,7 +4,6 @@ extern crate validator; use validator::Validate; - #[test] fn can_validate_url_ok() { #[derive(Debug, Validate)] @@ -13,9 +12,7 @@ fn can_validate_url_ok() { val: String, } - let s = TestStruct { - val: "https://google.com".to_string(), - }; + let s = TestStruct { val: "https://google.com".to_string() }; assert!(s.validate().is_ok()); } @@ -28,9 +25,7 @@ fn bad_url_fails_validation() { val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -46,9 +41,7 @@ fn can_specify_code_for_url() { #[validate(url(code = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); @@ -65,9 +58,7 @@ fn can_specify_message_for_url() { #[validate(url(message = "oops"))] val: String, } - let s = TestStruct { - val: "bob".to_string(), - }; + let s = TestStruct { val: "bob".to_string() }; let res = s.validate(); assert!(res.is_err()); let errs = res.unwrap_err().field_errors(); |
