diff options
| author | Vincent Prouillet | 2019-10-20 18:18:41 +0200 | 
|---|---|---|
| committer | GitHub | 2019-10-20 18:18:41 +0200 | 
| commit | 97a11c88f26b00e897d21709d9576ad3cea6f220 (patch) | |
| tree | 7469738d72627423c5b99df7dfae099951f4d322 | |
| parent | 87cf7cdac24dd0d4ea83a8a88640da95ae7ac93a (diff) | |
| parent | 52cd62998b8f45be56512ce1d89aef8bf1a9f670 (diff) | |
| download | validator-97a11c88f26b00e897d21709d9576ad3cea6f220.tar.bz2 | |
Merge pull request #87 from Keats/next
Use Github actions for CI
| -rw-r--r-- | .github/workflows/ci.yml | 45 | ||||
| -rw-r--r-- | .travis.yml | 11 | ||||
| -rw-r--r-- | README.md | 17 | ||||
| -rw-r--r-- | validator/Cargo.toml | 8 | ||||
| -rw-r--r-- | validator/src/lib.rs | 16 | ||||
| -rw-r--r-- | validator/src/types.rs | 5 | ||||
| -rw-r--r-- | validator/src/validation/cards.rs | 2 | ||||
| -rw-r--r-- | validator/src/validation/email.rs | 3 | ||||
| -rw-r--r-- | validator/src/validation/mod.rs | 6 | ||||
| -rw-r--r-- | validator/src/validation/non_control_character.rs | 46 | ||||
| -rw-r--r-- | validator_derive/Cargo.toml | 12 | ||||
| -rw-r--r-- | validator_derive/src/asserts.rs | 2 | ||||
| -rw-r--r-- | validator_derive/src/lib.rs | 110 | ||||
| -rw-r--r-- | validator_derive/src/lit.rs | 7 | ||||
| -rw-r--r-- | validator_derive/src/quoting.rs | 31 | ||||
| -rw-r--r-- | validator_derive/src/validation.rs | 99 | ||||
| -rw-r--r-- | validator_derive/tests/non_control.rs | 75 | ||||
| -rw-r--r-- | validator_derive/tests/regex.rs | 8 | ||||
| -rw-r--r-- | validator_derive/tests/run-pass/custom.rs | 4 | ||||
| -rw-r--r-- | validator_derive/tests/run-pass/regex.rs | 4 | 
20 files changed, 363 insertions, 148 deletions
| diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3212907 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,45 @@ +name: ci +on: [push, pull_request] + +jobs: +  test_validator: +    name: test validator +    runs-on: ${{ matrix.os }} +    strategy: +      matrix: +        build: [pinned, stable, nightly] +        include: +        - build: pinned +          os: ubuntu-18.04 +          rust: 1.33.0 +        - build: stable +          os: ubuntu-18.04 +          rust: stable +        - build: nightly +          os: ubuntu-18.04 +          rust: nightly +    steps: +    - uses: actions/checkout@v1 +    - name: Install Rust +      uses: hecrj/setup-rust-action@v1 +      with: +        rust-version: ${{ matrix.rust }} +    - name: Build System Info +      run: rustc --version + +    - name: tests validator with all features +      run: cd validator && cargo test --all-features +    - name: tests validator with no features +      run: cd validator && cargo test --no-default-features + +  test_validator_derive: +    name: test validator_derive +    runs-on: ubuntu-18.04 +    steps: +    - uses: actions/checkout@v1 +    - name: Install Rust +      uses: hecrj/setup-rust-action@v1 +      with: +        rust-version: nightly +    - name: tests validator_derive with all features +      run: cd validator_derive && cargo test --all-features diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e379ead..0000000 --- a/.travis.yml +++ /dev/null @@ -1,11 +0,0 @@ -language: rust - -rust: -  - nightly - -script: -  - (cd validator && cargo test --all-features) -  - (cd validator_derive && cargo clean && cargo test --all-features) - -notifications: -  email: false @@ -5,7 +5,7 @@  Macros 1.1 custom derive to simplify struct validation inspired by [marshmallow](http://marshmallow.readthedocs.io/en/latest/) and  [Django validators](https://docs.djangoproject.com/en/1.10/ref/validators/). -It requires Rust 1.30. +It requires Rust 1.33.  A short example: @@ -255,6 +255,11 @@ Examples:  #[validate]  ``` +### non_control_character +Tests whether the String has any utf-8 control caracters, fails validation if it does. +To use this validator, you must enable the `unic` feature for the `validator_derive` crate. +This validator doesn't take any arguments: `#[validate(non_control_character)]`; +  ## Struct level validation  Often, some error validation can only be applied when looking at the full struct, here's how it works here: @@ -297,6 +302,10 @@ For example, the following attributes all work:  ### validator +#### 0.10.0 (2019/10/18) + +- Add `non_control_characters` validation +  #### 0.9.0 (2019/05/xx)  - `ValidationErrors::errors` and `ValidationErrors::field_errors` now use `&self` instead of `self` @@ -328,6 +337,12 @@ For example, the following attributes all work:  ### validator_derive + +#### 0.10.0 (2019/10/18) + +- Update syn & quote +- Move to edition 2018 +  #### 0.9.0 (2019/05/xx)  - Use literals in macros now that it's stable -> bumping minimum Rust version to 1.30 diff --git a/validator/Cargo.toml b/validator/Cargo.toml index 96e8104..b7e43c0 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -1,6 +1,6 @@  [package]  name = "validator" -version = "0.9.0" +version = "0.10.0"  authors = ["Vincent Prouillet <prouillet.vincent@gmail.com"]  license = "MIT"  description = "Common validation functions (email, url, length, ...) and trait - to be used with `validator_derive`" @@ -10,16 +10,18 @@ keywords = ["validation", "api", "validator"]  edition = "2018"  [dependencies] -url = "1" +url = "2"  regex = "1"  lazy_static = "1" -idna = "0.1" +idna = "0.2"  serde = "1"  serde_derive = "1"  serde_json = "1"  card-validate = { version = "2.1", optional = true }  phonenumber = { version = "0.2.0+8.7.0", optional = true } +unic-ucd-common = { version = "0.9.0", optional = true }  [features]  phone = ["phonenumber"]  card = ["card-validate"] +unic = ["unic-ucd-common"]
\ No newline at end of file diff --git a/validator/src/lib.rs b/validator/src/lib.rs index f6aa8f0..5313c41 100644 --- a/validator/src/lib.rs +++ b/validator/src/lib.rs @@ -1,17 +1,3 @@ -extern crate regex; -extern crate url; -#[macro_use] -extern crate lazy_static; -extern crate idna; -extern crate serde; -extern crate serde_json; -#[macro_use] -extern crate serde_derive; -#[cfg(feature = "card")] -extern crate card_validate; -#[cfg(feature = "phone")] -pub extern crate phonenumber; -  mod traits;  mod types;  mod validation; @@ -23,6 +9,8 @@ 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 = "unic")] +pub use validation::non_control_character::validate_non_control_character;  #[cfg(feature = "phone")]  pub use validation::phone::validate_phone;  pub use validation::range::validate_range; diff --git a/validator/src/types.rs b/validator/src/types.rs index 09e4c7d..5d90d04 100644 --- a/validator/src/types.rs +++ b/validator/src/types.rs @@ -3,6 +3,7 @@ use std::collections::{hash_map::Entry::Vacant, BTreeMap, HashMap};  use std::{self, fmt};  use serde::ser::Serialize; +use serde_derive::{Deserialize, Serialize};  use serde_json::{to_value, Value};  #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -32,7 +33,7 @@ impl std::error::Error for ValidationError {      fn description(&self) -> &str {          &self.code      } -    fn cause(&self) -> Option<&std::error::Error> { +    fn cause(&self) -> Option<&dyn std::error::Error> {          None      }  } @@ -168,7 +169,7 @@ impl std::error::Error for ValidationErrors {      fn description(&self) -> &str {          "Validation failed"      } -    fn cause(&self) -> Option<&std::error::Error> { +    fn cause(&self) -> Option<&dyn std::error::Error> {          None      }  } diff --git a/validator/src/validation/cards.rs b/validator/src/validation/cards.rs index 8c97694..19d91ab 100644 --- a/validator/src/validation/cards.rs +++ b/validator/src/validation/cards.rs @@ -1,6 +1,6 @@  use std::borrow::Cow; -use crate::card_validate::Validate as CardValidate; +use card_validate::Validate as CardValidate;  pub fn validate_credit_card<'a, T>(card: T) -> bool  where diff --git a/validator/src/validation/email.rs b/validator/src/validation/email.rs index 6ea2fae..7a48f19 100644 --- a/validator/src/validation/email.rs +++ b/validator/src/validation/email.rs @@ -1,4 +1,5 @@  use idna::domain_to_ascii; +use lazy_static::lazy_static;  use regex::Regex;  use std::borrow::Cow; @@ -120,7 +121,7 @@ mod tests {          ];          for (input, expected) in tests { -            println!("{} - {}", input, expected); +            // println!("{} - {}", input, expected);              assert_eq!(validate_email(input), expected);          }      } diff --git a/validator/src/validation/mod.rs b/validator/src/validation/mod.rs index 95d9b79..1675730 100644 --- a/validator/src/validation/mod.rs +++ b/validator/src/validation/mod.rs @@ -5,6 +5,8 @@ pub mod email;  pub mod ip;  pub mod length;  pub mod must_match; +#[cfg(feature = "unic")] +pub mod non_control_character;  #[cfg(feature = "phone")]  pub mod phone;  pub mod range; @@ -41,6 +43,8 @@ pub enum Validator {      #[cfg(feature = "phone")]      Phone,      Nested, +    #[cfg(feature = "unic")] +    NonControlCharacter,  }  impl Validator { @@ -59,6 +63,8 @@ impl Validator {              #[cfg(feature = "phone")]              Validator::Phone => "phone",              Validator::Nested => "nested", +            #[cfg(feature = "unic")] +            Validator::NonControlCharacter => "non_control_character",          }      }  } diff --git a/validator/src/validation/non_control_character.rs b/validator/src/validation/non_control_character.rs new file mode 100644 index 0000000..ec4308c --- /dev/null +++ b/validator/src/validation/non_control_character.rs @@ -0,0 +1,46 @@ +use std::borrow::Cow; +use unic_ucd_common::control; + +pub fn validate_non_control_character<'a, T>(alphabetic: T) -> bool +where +    T: Into<Cow<'a, str>> + Clone, +{ +    alphabetic.into().chars().into_iter().all(|code| !control::is_control(code)) +} + +#[cfg(test)] +mod tests { +    use std::borrow::Cow; + +    use super::validate_non_control_character; + +    #[test] +    fn test_non_control_character() { +        let tests = vec![ +            ("Himmel", true), +            ("आकाश", true), +            ("வானத்தில்", true), +            ("하늘", true), +            ("небо", true), +            ("2H₂ + O₂ ⇌ 2H₂O", true), +            ("\u{000c}", false), +            ("\u{009F}", false), +        ]; + +        for (input, expected) in tests { +            assert_eq!(validate_non_control_character(input), expected); +        } +    } + +    #[test] +    fn test_non_control_character_cow() { +        let test: Cow<'static, str> = "आकाश".into(); +        assert_eq!(validate_non_control_character(test), true); +        let test: Cow<'static, str> = String::from("வானத்தில்").into(); +        assert_eq!(validate_non_control_character(test), true); +        let test: Cow<'static, str> = "\u{000c}".into(); +        assert_eq!(validate_non_control_character(test), false); +        let test: Cow<'static, str> = String::from("\u{009F}").into(); +        assert_eq!(validate_non_control_character(test), false); +    } +} diff --git a/validator_derive/Cargo.toml b/validator_derive/Cargo.toml index e8e50aa..4062170 100644 --- a/validator_derive/Cargo.toml +++ b/validator_derive/Cargo.toml @@ -1,12 +1,13 @@  [package]  name = "validator_derive" -version = "0.9.0" +version = "0.10.0"  authors = ["Vincent Prouillet <prouillet.vincent@gmail.com"]  license = "MIT"  description = "Macros 1.1 implementation of #[derive(Validate)]"  homepage = "https://github.com/Keats/validator"  repository = "https://github.com/Keats/validator"  keywords = ["validation", "api", "validator"] +edition = "2018"  [lib]  proc-macro = true @@ -14,13 +15,14 @@ proc-macro = true  [features]  phone = ["validator/phone"]  card = ["validator/card"] +unic = ["validator/unic"]  [dependencies] -syn = { version = "0.15", features = ["extra-traits"] } -quote = "0.6" -proc-macro2 = "0.4" +syn = { version = "1", features = ["extra-traits"] } +quote = "1" +proc-macro2 = "1"  if_chain = "1" -validator = { version = "0.9", path = "../validator"} +validator = { version = "0.10", path = "../validator"}  regex = "1"  lazy_static = "1" diff --git a/validator_derive/src/asserts.rs b/validator_derive/src/asserts.rs index a5892a8..023dd0d 100644 --- a/validator_derive/src/asserts.rs +++ b/validator_derive/src/asserts.rs @@ -1,5 +1,7 @@  use regex::Regex; +use lazy_static::lazy_static; +  lazy_static! {      pub static ref COW_TYPE: Regex = Regex::new(r"Cow<'[a-z]+,str>").unwrap();  } diff --git a/validator_derive/src/lib.rs b/validator_derive/src/lib.rs index f3796b4..6f74202 100644 --- a/validator_derive/src/lib.rs +++ b/validator_derive/src/lib.rs @@ -1,21 +1,11 @@  #![recursion_limit = "128"] - -#[macro_use] -extern crate if_chain; -#[macro_use] -extern crate lazy_static;  extern crate proc_macro; -extern crate proc_macro2; -#[macro_use] -extern crate quote; -extern crate regex; -#[macro_use] -extern crate syn; -extern crate validator; - -use proc_macro::TokenStream; + +use if_chain::if_chain; +use quote::quote;  use quote::ToTokens;  use std::collections::HashMap; +use syn::parse_quote;  use validator::Validator;  mod asserts; @@ -29,7 +19,7 @@ use quoting::{quote_field_validation, quote_schema_validation, FieldQuoter};  use validation::*;  #[proc_macro_derive(Validate, attributes(validate))] -pub fn derive_validation(input: TokenStream) -> TokenStream { +pub fn derive_validation(input: proc_macro::TokenStream) -> proc_macro::TokenStream {      let ast = syn::parse(input).unwrap();      impl_validate(&ast).into()  } @@ -110,11 +100,12 @@ fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaVa          }          if_chain! { -            if let Some(syn::Meta::List(syn::MetaList { ref nested, .. })) = attr.interpret_meta(); +            if let Ok(syn::Meta::List(syn::MetaList { ref nested, .. })) = attr.parse_meta();              if let syn::NestedMeta::Meta(ref item) = nested[0]; -            if let &syn::Meta::List(syn::MetaList { ref ident, ref nested, .. }) = item; +            if let &syn::Meta::List(syn::MetaList { ref path, ref nested, .. }) = item;              then { +                let ident = path.get_ident().unwrap();                  if ident != "schema" {                      error("Only `schema` is allowed as validator on a struct")                  } @@ -127,9 +118,10 @@ fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaVa                  for arg in nested {                      if_chain! {                          if let syn::NestedMeta::Meta(ref item) = *arg; -                        if let syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) = *item; +                        if let syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) = *item;                          then { +                            let ident = path.get_ident().unwrap();                              match ident.to_string().as_ref() {                                  "function" => {                                      function = match lit_to_string(lit) { @@ -251,8 +243,8 @@ fn find_validators_for_field(              has_validate = true;          } -        match attr.interpret_meta() { -            Some(syn::Meta::List(syn::MetaList { ref nested, .. })) => { +        match attr.parse_meta() { +            Ok(syn::Meta::List(syn::MetaList { ref nested, .. })) => {                  let meta_items = nested.iter().collect();                  // original name before serde rename                  if attr.path == parse_quote!(serde) { @@ -266,32 +258,43 @@ fn find_validators_for_field(                  for meta_item in meta_items {                      match *meta_item {                          syn::NestedMeta::Meta(ref item) => match *item { -                            // email, url, phone -                            syn::Meta::Word(ref name) => match name.to_string().as_ref() { -                                "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)); +                            // email, url, phone, credit_card, non_control_character +                            syn::Meta::Path(ref name) => { +                                match name.get_ident().unwrap().to_string().as_ref() { +                                    "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)); +                                    } +                                    #[cfg(feature = "unic")] +                                    "non_control_character" => { +                                        assert_string_type("non_control_character", field_type); +                                        validators.push(FieldValidation::new( +                                            Validator::NonControlCharacter, +                                        )); +                                    } +                                    _ => panic!("Unexpected validator: {:?}", name.get_ident()),                                  } -                                _ => panic!("Unexpected validator: {}", name), -                            }, +                            }                              // custom, contains, must_match, regex                              syn::Meta::NameValue(syn::MetaNameValue { -                                ref ident, ref lit, .. +                                ref path, ref lit, ..                              }) => { +                                let ident = path.get_ident().unwrap();                                  match ident.to_string().as_ref() {                                      "custom" => {                                          match lit_to_string(lit) { @@ -324,8 +327,9 @@ fn find_validators_for_field(                                  };                              }                              // Validators with several args -                            syn::Meta::List(syn::MetaList { ref ident, ref nested, .. }) => { +                            syn::Meta::List(syn::MetaList { ref path, ref nested, .. }) => {                                  let meta_items = nested.iter().cloned().collect(); +                                let ident = path.get_ident().unwrap();                                  match ident.to_string().as_ref() {                                      "length" => {                                          assert_has_len(rust_ident.clone(), field_type); @@ -341,7 +345,11 @@ fn find_validators_for_field(                                              &meta_items,                                          ));                                      } -                                    "email" | "url" | "phone" | "credit_card" => { +                                    "email" +                                    | "url" +                                    | "phone" +                                    | "credit_card" +                                    | "non_control_character" => {                                          validators.push(extract_argless_validation(                                              ident.to_string(),                                              rust_ident.clone(), @@ -396,10 +404,11 @@ fn find_validators_for_field(                      };                  }              } -            Some(syn::Meta::Word(_)) => validators.push(FieldValidation::new(Validator::Nested)), -            _ => unreachable!( -                "Got something other than a list of attributes while checking field `{}`", -                field_ident +            Ok(syn::Meta::Path(_)) => validators.push(FieldValidation::new(Validator::Nested)), +            Ok(syn::Meta::NameValue(_)) => panic!("Unexpected name=value argument"), +            Err(e) => unreachable!( +                "Got something other than a list of attributes while checking field `{}`: {:?}", +                field_ident, e              ),          }      } @@ -422,8 +431,9 @@ fn find_original_field_name(meta_items: &Vec<&syn::NestedMeta>) -> Option<String      for meta_item in meta_items {          match **meta_item {              syn::NestedMeta::Meta(ref item) => match *item { -                syn::Meta::Word(_) => continue, -                syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => { +                syn::Meta::Path(_) => continue, +                syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) => { +                    let ident = path.get_ident().unwrap();                      if ident == "rename" {                          original_name = Some(lit_to_string(lit).unwrap());                      } diff --git a/validator_derive/src/lit.rs b/validator_derive/src/lit.rs index 41deffc..f11cdd6 100644 --- a/validator_derive/src/lit.rs +++ b/validator_derive/src/lit.rs @@ -1,4 +1,5 @@  use proc_macro2; +use quote::quote;  use syn;  pub fn lit_to_string(lit: &syn::Lit) -> Option<String> { @@ -10,15 +11,15 @@ pub fn lit_to_string(lit: &syn::Lit) -> Option<String> {  pub fn lit_to_int(lit: &syn::Lit) -> Option<u64> {      match *lit { -        syn::Lit::Int(ref s) => Some(s.value()), +        syn::Lit::Int(ref s) => Some(s.base10_parse().unwrap()),          _ => None,      }  }  pub fn lit_to_float(lit: &syn::Lit) -> Option<f64> {      match *lit { -        syn::Lit::Float(ref s) => Some(s.value()), -        syn::Lit::Int(ref s) => Some(s.value() as f64), +        syn::Lit::Float(ref s) => Some(s.base10_parse::<f64>().unwrap()), +        syn::Lit::Int(ref s) => Some(s.base10_parse::<f64>().unwrap()),          _ => None,      }  } diff --git a/validator_derive/src/quoting.rs b/validator_derive/src/quoting.rs index b94962c..dc466ac 100644 --- a/validator_derive/src/quoting.rs +++ b/validator_derive/src/quoting.rs @@ -1,10 +1,11 @@  use proc_macro2::{self, Span}; +use quote::quote;  use syn;  use validator::Validator; -use asserts::{COW_TYPE, NUMBER_TYPES}; -use lit::{option_f64_to_tokens, option_u64_to_tokens}; -use validation::{FieldValidation, SchemaValidation}; +use crate::asserts::{COW_TYPE, NUMBER_TYPES}; +use crate::lit::{option_f64_to_tokens, option_u64_to_tokens}; +use crate::validation::{FieldValidation, SchemaValidation};  /// Pass around all the information needed for creating a validation  #[derive(Debug)] @@ -258,6 +259,26 @@ pub fn quote_phone_validation(      field_quoter.wrap_if_option(quoted)  } +#[cfg(feature = "unic")] +pub fn quote_non_control_character_validation( +    field_quoter: &FieldQuoter, +    validation: &FieldValidation, +) -> proc_macro2::TokenStream { +    let field_name = &field_quoter.name; +    let validator_param = field_quoter.quote_validator_param(); + +    let quoted_error = quote_error(&validation); +    let quoted = quote!( +        if !::validator::validate_non_control_character(#validator_param) { +            #quoted_error +            err.add_param(::std::borrow::Cow::from("value"), &#validator_param); +            errors.add(#field_name, err); +        } +    ); + +    field_quoter.wrap_if_option(quoted) +} +  pub fn quote_url_validation(      field_quoter: &FieldQuoter,      validation: &FieldValidation, @@ -440,6 +461,10 @@ pub fn quote_field_validation(          #[cfg(feature = "phone")]          Validator::Phone => validations.push(quote_phone_validation(&field_quoter, validation)),          Validator::Nested => nested_validations.push(quote_nested_validation(&field_quoter)), +        #[cfg(feature = "unic")] +        Validator::NonControlCharacter => { +            validations.push(quote_non_control_character_validation(&field_quoter, validation)) +        }      }  } diff --git a/validator_derive/src/validation.rs b/validator_derive/src/validation.rs index 5f42d7b..f0657ef 100644 --- a/validator_derive/src/validation.rs +++ b/validator_derive/src/validation.rs @@ -2,7 +2,7 @@ use syn;  use validator::Validator; -use lit::*; +use crate::lit::*;  #[derive(Debug)]  pub struct SchemaValidation { @@ -41,32 +41,33 @@ pub fn extract_length_validation(      for meta_item in meta_items {          if let syn::NestedMeta::Meta(ref item) = *meta_item { -            if let syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) = *item { +            if let syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) = *item { +                let ident = path.get_ident().unwrap();                  match ident.to_string().as_ref() { -                    "message" | "code" => continue, -                    "min" => { -                        min = match lit_to_int(lit) { -                            Some(s) => Some(s), -                            None => error("invalid argument type for `min` of `length` validator: only integers are allowed"), -                        }; -                    }, -                    "max" => { -                        max = match lit_to_int(lit) { -                            Some(s) => Some(s), -                            None => error("invalid argument type for `max` of `length` validator: only integers are allowed"), -                        }; -                    }, -                    "equal" => { -                        equal = match lit_to_int(lit) { -                            Some(s) => Some(s), -                            None => error("invalid argument type for `equal` of `length` validator: only integers are allowed"), -                        }; -                    }, -                    v => error(&format!( -                        "unknown argument `{}` for validator `length` (it only has `min`, `max`, `equal`)", -                        v -                    )) -                } +                        "message" | "code" => continue, +                        "min" => { +                            min = match lit_to_int(lit) { +                                Some(s) => Some(s), +                                None => error("invalid argument type for `min` of `length` validator: only integers are allowed"), +                            }; +                        }, +                        "max" => { +                            max = match lit_to_int(lit) { +                                Some(s) => Some(s), +                                None => error("invalid argument type for `max` of `length` validator: only integers are allowed"), +                            }; +                        }, +                        "equal" => { +                            equal = match lit_to_int(lit) { +                                Some(s) => Some(s), +                                None => error("invalid argument type for `equal` of `length` validator: only integers are allowed"), +                            }; +                        }, +                        v => error(&format!( +                            "unknown argument `{}` for validator `length` (it only has `min`, `max`, `equal`)", +                            v +                        )) +                    }              } else {                  panic!(                      "unexpected item {:?} while parsing `length` validator of field {}", @@ -107,7 +108,8 @@ pub fn extract_range_validation(      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, .. }) => { +                syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) => { +                    let ident = path.get_ident().unwrap();                      match ident.to_string().as_ref() {                          "message" | "code" => continue,                          "min" => { @@ -123,9 +125,9 @@ pub fn extract_range_validation(                              };                          }                          v => error(&format!( -                        "unknown argument `{}` for validator `range` (it only has `min`, `max`)", -                        v -                    )), +                            "unknown argument `{}` for validator `range` (it only has `min`, `max`)", +                            v +                        )),                      }                  }                  _ => panic!("unexpected item {:?} while parsing `range` validator", item), @@ -146,7 +148,7 @@ pub fn extract_range_validation(      }  } -/// Extract url/email/phone field validation with a code or a message +/// Extract url/email/phone/non_control_character field validation with a code or a message  pub fn extract_argless_validation(      validator_name: String,      field: String, @@ -157,7 +159,8 @@ pub fn extract_argless_validation(      for meta_item in meta_items {          match *meta_item {              syn::NestedMeta::Meta(ref item) => match *item { -                syn::Meta::NameValue(syn::MetaNameValue { ref ident, .. }) => { +                syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) => { +                    let ident = path.get_ident().unwrap();                      match ident.to_string().as_ref() {                          "message" | "code" => continue,                          v => panic!( @@ -178,6 +181,8 @@ pub fn extract_argless_validation(          "credit_card" => Validator::CreditCard,          #[cfg(feature = "phone")]          "phone" => Validator::Phone, +        #[cfg(feature = "unic")] +        "non_control_character" => Validator::NonControlCharacter,          _ => Validator::Url,      }; @@ -201,7 +206,8 @@ pub fn extract_one_arg_validation(      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, .. }) => { +                syn::Meta::NameValue(syn::MetaNameValue { ref path, ref lit, .. }) => { +                    let ident = path.get_ident().unwrap();                      match ident.to_string().as_ref() {                          "message" | "code" => continue,                          v if v == val_name => { @@ -257,29 +263,30 @@ fn extract_message_and_code(      for meta_item in meta_items {          if let syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { -            ref ident, +            ref path,              ref lit,              ..          })) = *meta_item          { +            let ident = path.get_ident().unwrap();              match ident.to_string().as_ref() {                  "code" => {                      code = match lit_to_string(lit) { -                        Some(s) => Some(s), -                        None => panic!( -                            "Invalid argument type for `code` for validator `{}` on field `{}`: only a string is allowed", -                            validator_name, field -                        ), -                    }; +                                Some(s) => Some(s), +                                None => panic!( +                                    "Invalid argument type for `code` for validator `{}` on field `{}`: only a string is allowed", +                                    validator_name, field +                                ), +                            };                  }                  "message" => {                      message = match lit_to_string(lit) { -                        Some(s) => Some(s), -                        None => panic!( -                            "Invalid argument type for `message` for validator `{}` on field `{}`: only a string is allowed", -                            validator_name, field -                        ), -                    }; +                                Some(s) => Some(s), +                                None => panic!( +                                    "Invalid argument type for `message` for validator `{}` on field `{}`: only a string is allowed", +                                    validator_name, field +                                ), +                            };                  }                  _ => continue,              } diff --git a/validator_derive/tests/non_control.rs b/validator_derive/tests/non_control.rs new file mode 100644 index 0000000..37fb153 --- /dev/null +++ b/validator_derive/tests/non_control.rs @@ -0,0 +1,75 @@ +#[macro_use] +extern crate validator_derive; +extern crate validator; + +use validator::Validate; + +#[cfg(feature = "unic")] +#[test] +fn can_validate_utf8_ok() { +    #[derive(Debug, Validate)] +    struct TestStruct { +        #[validate(non_control_character)] +        val: String, +    } + +    let s = TestStruct { val: "하늘".to_string() }; + +    assert!(s.validate().is_ok()); +} + +#[cfg(feature = "unic")] +#[test] +fn utf8_with_control_fails_validation() { +    #[derive(Debug, Validate)] +    struct TestStruct { +        #[validate(non_control_character)] +        val: String, +    } + +    let s = TestStruct { val: "\u{009F}하늘".to_string() }; +    let res = s.validate(); +    assert!(res.is_err()); +    let err = res.unwrap_err(); +    let errs = err.field_errors(); +    assert!(errs.contains_key("val")); +    assert_eq!(errs["val"].len(), 1); +    assert_eq!(errs["val"][0].code, "non_control_character"); +} + +#[cfg(feature = "unic")] +#[test] +fn can_specify_code_for_non_control_character() { +    #[derive(Debug, Validate)] +    struct TestStruct { +        #[validate(non_control_character(code = "oops"))] +        val: String, +    } +    let s = TestStruct { val: "\u{009F}하늘".to_string() }; +    let res = s.validate(); +    assert!(res.is_err()); +    let err = res.unwrap_err(); +    let errs = err.field_errors(); +    assert!(errs.contains_key("val")); +    assert_eq!(errs["val"].len(), 1); +    assert_eq!(errs["val"][0].code, "oops"); +    assert_eq!(errs["val"][0].params["value"], "\u{9F}하늘"); +} + +#[cfg(feature = "unic")] +#[test] +fn can_specify_message_for_non_control_character() { +    #[derive(Debug, Validate)] +    struct TestStruct { +        #[validate(non_control_character(message = "oops"))] +        val: String, +    } +    let s = TestStruct { val: "\u{009F}하늘".to_string() }; +    let res = s.validate(); +    assert!(res.is_err()); +    let err = res.unwrap_err(); +    let errs = err.field_errors(); +    assert!(errs.contains_key("val")); +    assert_eq!(errs["val"].len(), 1); +    assert_eq!(errs["val"][0].clone().message.unwrap(), "oops"); +} diff --git a/validator_derive/tests/regex.rs b/validator_derive/tests/regex.rs index 7e3dfc5..d3e3c1e 100644 --- a/validator_derive/tests/regex.rs +++ b/validator_derive/tests/regex.rs @@ -16,7 +16,7 @@ lazy_static! {  fn can_validate_valid_regex() {      #[derive(Debug, Validate)]      struct TestStruct { -        #[validate(regex = "RE2")] +        #[validate(regex = "crate::RE2")]          val: String,      } @@ -29,7 +29,7 @@ fn can_validate_valid_regex() {  fn bad_value_for_regex_fails_validation() {      #[derive(Debug, Validate)]      struct TestStruct { -        #[validate(regex = "RE2")] +        #[validate(regex = "crate::RE2")]          val: String,      } @@ -48,7 +48,7 @@ fn bad_value_for_regex_fails_validation() {  fn can_specify_code_for_regex() {      #[derive(Debug, Validate)]      struct TestStruct { -        #[validate(regex(path = "RE2", code = "oops"))] +        #[validate(regex(path = "crate::RE2", code = "oops"))]          val: String,      }      let s = TestStruct { val: "2".to_string() }; @@ -65,7 +65,7 @@ fn can_specify_code_for_regex() {  fn can_specify_message_for_regex() {      #[derive(Debug, Validate)]      struct TestStruct { -        #[validate(regex(path = "RE2", message = "oops"))] +        #[validate(regex(path = "crate::RE2", message = "oops"))]          val: String,      }      let s = TestStruct { val: "2".to_string() }; diff --git a/validator_derive/tests/run-pass/custom.rs b/validator_derive/tests/run-pass/custom.rs index dff6375..f745de6 100644 --- a/validator_derive/tests/run-pass/custom.rs +++ b/validator_derive/tests/run-pass/custom.rs @@ -6,13 +6,13 @@ use validator::{Validate, ValidationError};  #[derive(Validate)]  struct Test { -    #[validate(custom = "validate_something")] +    #[validate(custom = "crate::validate_something")]      s: String,  }  #[derive(Validate)]  struct TestPath { -    #[validate(custom = "::validate_something")] +    #[validate(custom = "crate::validate_something")]      s: String,  } diff --git a/validator_derive/tests/run-pass/regex.rs b/validator_derive/tests/run-pass/regex.rs index dcac3c0..e15b3a6 100644 --- a/validator_derive/tests/run-pass/regex.rs +++ b/validator_derive/tests/run-pass/regex.rs @@ -14,13 +14,13 @@ lazy_static! {  #[derive(Validate)]  struct Test { -    #[validate(regex = "RE2")] +    #[validate(regex = "crate::RE2")]      s: String,  }  #[derive(Validate)]  struct TestPath { -    #[validate(regex = "::RE2")] +    #[validate(regex = "crate::RE2")]      s: String,  } | 
