From b31fd25cc5cec96ccee737ba1313c6e9f702f32a Mon Sep 17 00:00:00 2001 From: Vincent Prouillet Date: Fri, 26 May 2017 00:49:54 +0900 Subject: Revamp --- validator/Cargo.toml | 3 + validator/src/contains.rs | 76 --- validator/src/email.rs | 121 ---- validator/src/ip.rs | 107 ---- validator/src/length.rs | 108 ---- validator/src/lib.rs | 35 +- validator/src/must_match.rs | 26 - validator/src/range.rs | 30 - validator/src/traits.rs | 82 +++ validator/src/types.rs | 104 ++-- validator/src/urls.rs | 30 - validator/src/validation/contains.rs | 39 ++ validator/src/validation/email.rs | 121 ++++ validator/src/validation/ip.rs | 107 ++++ validator/src/validation/length.rs | 72 +++ validator/src/validation/mod.rs | 50 ++ validator/src/validation/must_match.rs | 26 + validator/src/validation/range.rs | 30 + validator/src/validation/urls.rs | 30 + validator_derive/Cargo.toml | 5 +- validator_derive/src/asserts.rs | 54 ++ validator_derive/src/lib.rs | 672 +++++---------------- validator_derive/src/lit.rs | 61 ++ validator_derive/src/quoting.rs | 332 ++++++++++ validator_derive/src/validation.rs | 290 +++++++++ .../tests/compile-fail/length/wrong_type.rs | 2 +- .../compile-fail/must_match/field_doesnt_exist.rs | 2 +- .../must_match/field_type_doesnt_match.rs | 2 +- .../tests/compile-fail/range/wrong_type.rs | 2 +- validator_derive/tests/complex.rs | 155 +++++ validator_derive/tests/contains.rs | 77 +++ validator_derive/tests/custom.rs | 66 ++ validator_derive/tests/email.rs | 77 +++ validator_derive/tests/length.rs | 78 +++ validator_derive/tests/must_match.rs | 87 +++ validator_derive/tests/range.rs | 78 +++ validator_derive/tests/regex.rs | 84 +++ validator_derive/tests/run-pass/custom.rs | 6 +- validator_derive/tests/run-pass/schema.rs | 11 +- validator_derive/tests/schema.rs | 99 +++ validator_derive/tests/test_derive.rs | 357 ----------- validator_derive/tests/url.rs | 77 +++ 42 files changed, 2404 insertions(+), 1467 deletions(-) delete mode 100644 validator/src/contains.rs delete mode 100644 validator/src/email.rs delete mode 100644 validator/src/ip.rs delete mode 100644 validator/src/length.rs delete mode 100644 validator/src/must_match.rs delete mode 100644 validator/src/range.rs create mode 100644 validator/src/traits.rs delete mode 100644 validator/src/urls.rs create mode 100644 validator/src/validation/contains.rs create mode 100644 validator/src/validation/email.rs create mode 100644 validator/src/validation/ip.rs create mode 100644 validator/src/validation/length.rs create mode 100644 validator/src/validation/mod.rs create mode 100644 validator/src/validation/must_match.rs create mode 100644 validator/src/validation/range.rs create mode 100644 validator/src/validation/urls.rs create mode 100644 validator_derive/src/asserts.rs create mode 100644 validator_derive/src/lit.rs create mode 100644 validator_derive/src/quoting.rs create mode 100644 validator_derive/src/validation.rs create mode 100644 validator_derive/tests/complex.rs create mode 100644 validator_derive/tests/contains.rs create mode 100644 validator_derive/tests/custom.rs create mode 100644 validator_derive/tests/email.rs create mode 100644 validator_derive/tests/length.rs create mode 100644 validator_derive/tests/must_match.rs create mode 100644 validator_derive/tests/range.rs create mode 100644 validator_derive/tests/regex.rs create mode 100644 validator_derive/tests/schema.rs delete mode 100644 validator_derive/tests/test_derive.rs create mode 100644 validator_derive/tests/url.rs diff --git a/validator/Cargo.toml b/validator/Cargo.toml index c20c1ac..3565fef 100644 --- a/validator/Cargo.toml +++ b/validator/Cargo.toml @@ -13,3 +13,6 @@ url = "1" regex = "0.2" lazy_static = "0.2" idna = "0.1" +serde = "1" +serde_derive = "1" +serde_json = "1" diff --git a/validator/src/contains.rs b/validator/src/contains.rs deleted file mode 100644 index 0c9d02e..0000000 --- a/validator/src/contains.rs +++ /dev/null @@ -1,76 +0,0 @@ -use std::collections::HashMap; - - -/// Trait to implement if one wants to make the `contains` validator -/// work for more types -pub trait Contains { - fn has_element(&self, needle: &str) -> bool; -} - -impl Contains for String { - fn has_element(&self, needle: &str) -> bool { - self.contains(needle) - } -} - -impl<'a> Contains for &'a String { - fn has_element(&self, needle: &str) -> bool { - self.contains(needle) - } -} - -impl<'a> Contains for &'a str { - fn has_element(&self, needle: &str) -> bool { - self.contains(needle) - } -} - -impl Contains for HashMap { - fn has_element(&self, needle: &str) -> bool { - self.contains_key(needle) - } -} - -impl<'a, S> Contains for &'a HashMap { - fn has_element(&self, needle: &str) -> bool { - self.contains_key(needle) - } -} - -/// Validates whether the value contains the needle -/// The value needs to implement the Contains trait, which is implement on String, str and Hashmap -/// by default. -pub fn validate_contains(val: T, needle: &str) -> bool { - val.has_element(needle) -} - -#[cfg(test)] -mod tests { - use std::collections::HashMap; - - use super::*; - - #[test] - fn test_validate_contains_string() { - assert!(validate_contains("hey", "e")); - } - - #[test] - fn test_validate_contains_string_can_fail() { - assert_eq!(validate_contains("hey", "o"), false); - } - - #[test] - fn test_validate_contains_hashmap_key() { - let mut map = HashMap::new(); - map.insert("hey".to_string(), 1); - assert!(validate_contains(map, "hey")); - } - - #[test] - fn test_validate_contains_hashmap_key_can_fail() { - let mut map = HashMap::new(); - map.insert("hey".to_string(), 1); - assert_eq!(validate_contains(map, "bob"), false); - } -} diff --git a/validator/src/email.rs b/validator/src/email.rs deleted file mode 100644 index 205d89f..0000000 --- a/validator/src/email.rs +++ /dev/null @@ -1,121 +0,0 @@ -use regex::Regex; - -use ip::{validate_ip}; -use idna::{domain_to_ascii}; - - -lazy_static! { - // Regex from the specs - // https://html.spec.whatwg.org/multipage/forms.html#valid-e-mail-address - // It will mark esoteric email addresses like quoted string as invalid - static ref EMAIL_USER_RE: Regex = Regex::new(r"^(?i)[a-z0-9.!#$%&'*+/=?^_`{|}~-]+\z").unwrap(); - static ref EMAIL_DOMAIN_RE: Regex = Regex::new( - r"(?i)^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$" - ).unwrap(); - // literal form, ipv4 or ipv6 address (SMTP 4.1.3) - static ref EMAIL_LITERAL_RE: Regex = Regex::new(r"(?i)\[([A-f0-9:\.]+)\]\z").unwrap(); -} - -/// Validates whether the given string is an email based on Django `EmailValidator` and HTML5 specs -pub fn validate_email(val: &str) -> bool { - if val.is_empty() || !val.contains('@') { - return false; - } - let parts: Vec<&str> = val.rsplitn(2, '@').collect(); - let user_part = parts[1]; - let domain_part = parts[0]; - - if !EMAIL_USER_RE.is_match(user_part) { - return false; - } - - if !validate_domain_part(domain_part) { - // Still the possibility of an [IDN](https://en.wikipedia.org/wiki/Internationalized_domain_name) - return match domain_to_ascii(domain_part) { - Ok(d) => validate_domain_part(&d), - Err(_) => false, - }; - } - - true -} - -/// Checks if the domain is a valid domain and if not, check whether it's an IP -fn validate_domain_part(domain_part: &str) -> bool { - if EMAIL_DOMAIN_RE.is_match(domain_part) { - return true; - } - - // 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 - } -} - - -#[cfg(test)] -mod tests { - use super::validate_email; - - #[test] - fn test_validate_email() { - // Test cases taken from Django - // https://github.com/django/django/blob/master/tests/validators/tests.py#L48 - let tests = vec![ - ("email@here.com", true), - ("weirder-email@here.and.there.com", true), - (r#"!def!xyz%abc@example.com"#, true), - ("email@[127.0.0.1]", true), - ("email@[2001:dB8::1]", true), - ("email@[2001:dB8:0:0:0:0:0:1]", true), - ("email@[::fffF:127.0.0.1]", true), - ("example@valid-----hyphens.com", true), - ("example@valid-with-hyphens.com", true), - ("test@domain.with.idn.tld.उदाहरण.परीक्षा", true), - (r#""test@test"@example.com"#, false), - // 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@atm.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", true), - ("", false), - ("abc", false), - ("abc@", false), - ("abc@bar", true), - ("a @x.cz", false), - // TODO: make that one below fail - // ("abc@.com", false), - ("something@@somewhere.com", false), - ("email@127.0.0.1", true), - ("email@[127.0.0.256]", false), - ("email@[2001:db8::12345]", false), - ("email@[2001:db8:0:0:0:0:1]", false), - ("email@[::ffff:127.0.0.256]", false), - ("example@invalid-.com", false), - ("example@-invalid.com", false), - ("example@invalid.com-", false), - ("example@inv-.alid-.com", false), - ("example@inv-.-alid.com", false), - (r#"test@example.com\n\n