Repository: Keats/validator (rust)

Issue:
Allow range validator to refer to a variable
The range validator requires the raw numerical value in quotes. It should let it be a variable name in scope as well (or instead).

Pull request:
The `range` validator can now take a value reference as a parameter #66
This implementation required some changes when it comes to the `Validator::Range` value. I therefor suggest that we add a depreciation notice on that value and add a new value `Validator::RangeRef` for compatibility reasons. This will only effect users that used the validation directly, indirect usage with `#[derive(Validation)]` will be unaffected.  The `validate_range` function was also extended to take generic parameters. The old function still exists and forwards the call to `validate_range_generic`.

These changes are enough to raise the version number, especially with the suggestion that we depreciate something. I therefor noted `0.12.0` as the depreciation version. (I can remove the depreciation notice, that is just a suggestion)

---

This implementation supports constant and struct values like this:

```rust
// Global constant values
#[validate(range(min = "MAX_CONSTANT"))]
#[validate(range(min = "crate::MAX_CONSTANT"))]

// Struct value
#[validate(range(min = "self.max"))] 
```

This separation in struct and global values is readable but brings some inconstancy when it comes to other validations. The `must_match` validator only takes struct values. Changing it to also use the `self.` prefix would be very simple but would break some current usages.

I can easily take this out again but I think that it would be cool if this would be consistent in the entire crate.

---

The validation message resolves the references automatically the error message contains the number it self and not the referenced value: 
```
ValidationErrors({"val": Field([ValidationError { code: "range", message: None, params: {"value": Number(6), "max": Number(5), "min": Number(4)} }])})
```

---

This should also close #66

Patch:
diff --git a/README.md b/README.md
index a153c46..727914c 100644
--- a/README.md
+++ b/README.md
@@ -52,9 +52,10 @@ match signup_data.validate() {
 };
 ```
 
-The `validate()` method returns a `Result<(), ValidationErrors>`. In the case of an invalid result, the
-`ValidationErrors` instance includes a map of errors keyed against the struct's field names. Errors may be represented
-in three ways, as described by the `ValidationErrorsKind` enum:
+A validation on an `Option<_>` field will be executed on the contained type if the option is `Some`. The `validate()`
+ method returns a `Result<(), ValidationErrors>`. In the case of an invalid result, the `ValidationErrors` instance includes
+a map of errors keyed against the struct's field names. Errors may be represented in three ways, as described by the 
+`ValidationErrorsKind` enum:
 
 ```rust
 #[derive(Debug, Serialize, Clone, PartialEq)]
@@ -83,10 +84,6 @@ Note that `validator` works in conjunction with serde: in the example we can see
 field is renamed from/to `firstName`. Any error on that field will be in the `firstName` key of the hashmap,
 not `first_name`.
 
-If you are adding a validation on a `Option<..>` field, it will only be ran if there is a value. The exception
-being `must_match` that doesn't currently work with `Option` due to me not finding a use case for it. If you have one,
-please comment on https://github.com/Keats/validator/issues/7.
-
 The other two `ValidationErrorsKind` types represent errors discovered in nested (vectors of) structs, as described in
 this example:
 
@@ -170,23 +167,32 @@ At least one argument is required with a maximum of 2 (having `min` and `max` at
 Examples:
 
 ```rust
+const MIN_CONST: u64 = 1;
+const MAX_CONST: u64 = 10;
+
 #[validate(length(min = 1, max = 10))]
 #[validate(length(min = 1))]
 #[validate(length(max = 10))]
 #[validate(length(equal = 10))]
+#[validate(length(min = "MIN_CONST", max = "MAX_CONST"))]
 ```
 
 ### range
-Tests whether a number is in the given range. `range` takes between 1 and 2 number arguments: `min` and `max`.
+Tests whether a number is in the given range. `range` takes 1 or 2 arguments `min` and `max` that can be a number or a value path.
 
 Examples:
 
 ```rust
-#[validate(range(min = 1, max = 10))]
+const MAX_CONSTANT: i32 = 10;
+const MIN_CONSTANT: i32 = 0;
+
 #[validate(range(min = 1))]
-#[validate(range(min = 1, max = 10.8))]
+#[validate(range(min = "MIN_CONSTANT"))]
+#[validate(range(min = 1, max = 10))]
 #[validate(range(min = 1.1, max = 10.8))]
 #[validate(range(max = 10.8))]
+#[validate(range(min = "MAX_CONSTANT"))]
+#[validate(range(min = "crate::MAX_CONSTANT"))]
 ```
 
 ### must_match
diff --git a/validator/src/lib.rs b/validator/src/lib.rs
index 0c40712..12df506 100644
--- a/validator/src/lib.rs
+++ b/validator/src/lib.rs
@@ -14,6 +14,7 @@ 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;
+
 pub use validation::required::validate_required;
 pub use validation::urls::validate_url;
 pub use validation::Validator;
diff --git a/validator/src/validation/length.rs b/validator/src/validation/length.rs
index 4c18b6b..e4a9d83 100644
--- a/validator/src/validation/length.rs
+++ b/validator/src/validation/length.rs
@@ -1,5 +1,4 @@
 use crate::traits::HasLen;
-use crate::validation::Validator;
 
 /// Validates the length of the value given.
 /// If the validator has `equal` set, it will ignore any `min` and `max` value.
@@ -7,25 +6,27 @@ use crate::validation::Validator;
 /// If you apply it on String, don't forget that the length can be different
 /// from the number of visual characters for Unicode
 #[must_use]
-pub fn validate_length<T: HasLen>(length: Validator, val: T) -> bool {
-    match length {
-        Validator::Length { min, max, equal } => {
-            let val_length = val.length();
-            if let Some(eq) = equal {
-                return val_length == eq;
-            }
-            if let Some(m) = min {
-                if val_length < m {
-                    return false;
-                }
+pub fn validate_length<T: HasLen>(
+    value: T,
+    min: Option<u64>,
+    max: Option<u64>,
+    equal: Option<u64>,
+) -> bool {
+    let val_length = value.length();
+
+    if let Some(eq) = equal {
+        return val_length == eq;
+    } else {
+        if let Some(m) = min {
+            if val_length < m {
+                return false;
             }
-            if let Some(m) = max {
-                if val_length > m {
-                    return false;
-                }
+        }
+        if let Some(m) = max {
+            if val_length > m {
+                return false;
             }
         }
-        _ => unreachable!(),
     }
 
     true
@@ -35,51 +36,44 @@ pub fn validate_length<T: HasLen>(length: Validator, val: T) -> bool {
 mod tests {
     use std::borrow::Cow;
 
-    use super::{validate_length, Validator};
+    use super::validate_length;
 
     #[test]
     fn test_validate_length_equal_overrides_min_max() {
-        let validator = Validator::Length { min: Some(1), max: Some(2), equal: Some(5) };
-        assert_eq!(validate_length(validator, "hello"), true);
+        assert_eq!(validate_length("hello", Some(1), Some(2), Some(5)), true);
     }
 
     #[test]
     fn test_validate_length_string_min_max() {
-        let validator = Validator::Length { min: Some(1), max: Some(10), equal: None };
-        assert_eq!(validate_length(validator, "hello"), true);
+        assert_eq!(validate_length("hello", Some(1), Some(10), None), true);
     }
 
     #[test]
     fn test_validate_length_string_min_only() {
-        let validator = Validator::Length { min: Some(10), max: None, equal: None };
-        assert_eq!(validate_length(validator, "hello"), false);
+        assert_eq!(validate_length("hello", Some(10), None, None), false);
     }
 
     #[test]
     fn test_validate_length_string_max_only() {
-        let validator = Validator::Length { min: None, max: Some(1), equal: None };
-        assert_eq!(validate_length(validator, "hello"), false);
+        assert_eq!(validate_length("hello", None, Some(1), None), false);
     }
 
     #[test]
     fn test_validate_length_cow() {
-        let validator = Validator::Length { min: Some(1), max: Some(2), equal: Some(5) };
         let test: Cow<'static, str> = "hello".into();
-        assert_eq!(validate_length(validator, test), true);
-        let validator = Validator::Length { min: Some(1), max: Some(2), equal: Some(5) };
+        assert_eq!(validate_length(test, None, None, Some(5)), true);
+
         let test: Cow<'static, str> = String::from("hello").into();
-        assert_eq!(validate_length(validator, test), true);
+        assert_eq!(validate_length(test, None, None, Some(5)), true);
     }
 
     #[test]
     fn test_validate_length_vec() {
-        let validator = Validator::Length { min: None, max: None, equal: Some(3) };
-        assert_eq!(validate_length(validator, vec![1, 2, 3]), true);
+        assert_eq!(validate_length(vec![1, 2, 3], None, None, Some(3)), true);
     }
 
     #[test]
     fn test_validate_length_unicode_chars() {
-        let validator = Validator::Length { min: None, max: None, equal: Some(2) };
-        assert_eq!(validate_length(validator, "日本"), true);
+        assert_eq!(validate_length("日本", None, None, Some(2)), true);
     }
 }
diff --git a/validator/src/validation/must_match.rs b/validator/src/validation/must_match.rs
index 2a2e7b8..5c93de1 100644
--- a/validator/src/validation/must_match.rs
+++ b/validator/src/validation/must_match.rs
@@ -37,12 +37,12 @@ mod tests {
     fn test_validate_must_match_numbers_option_false() {
         assert_eq!(false, validate_must_match(Some(2), Some(3)));
     }
-    
+
     #[test]
     fn test_validate_must_match_numbers_option_true() {
         assert!(validate_must_match(Some(6), Some(6)));
     }
-    
+
     #[test]
     fn test_validate_must_match_none_some_false() {
         assert_eq!(false, validate_must_match(None, Some(3)));
diff --git a/validator/src/validation/range.rs b/validator/src/validation/range.rs
index 4e53686..72e78fd 100644
--- a/validator/src/validation/range.rs
+++ b/validator/src/validation/range.rs
@@ -1,67 +1,59 @@
-use crate::validation::Validator;
-
-/// Validates that a number is in the given range
+/// Validates that the given `value` is inside the defined range. The `max` and `min` parameters are
+/// optional and will only be validated if they are not `None`
 ///
-/// TODO: see if can be generic over the number type
 #[must_use]
-pub fn validate_range(range: Validator, val: f64) -> bool {
-    match range {
-        Validator::Range { min, max } => {
-            if let Some(m) = min {
-                if val < m {
-                    return false;
-                }
-            }
-
-            if let Some(m) = max {
-                if val > m {
-                    return false;
-                }
-            }
+pub fn validate_range<T>(value: T, min: Option<T>, max: Option<T>) -> bool
+where
+    T: PartialOrd + PartialEq,
+{
+    if let Some(max) = max {
+        if value > max {
+            return false;
+        }
+    }
 
-            true
+    if let Some(min) = min {
+        if value < min {
+            return false;
         }
-        _ => unreachable!(),
     }
+
+    true
 }
 
 #[cfg(test)]
 mod tests {
-    use super::{validate_range, Validator};
-
-    #[test]
-    fn test_validate_range_ok() {
-        let validator = Validator::Range { min: Some(0.0), max: Some(10.0) };
-        assert_eq!(validate_range(validator, 1_f64), true);
-    }
-
-    #[test]
-    fn test_validate_range_fail() {
-        let validator = Validator::Range { min: Some(0.0), max: Some(10.0) };
-        assert_eq!(validate_range(validator, 20_f64), false);
-    }
+    // This is needed to suppress the `validate_range` depreciation notice
+    // We want to keep the tests to make sure that this function will remain valid
+    use super::validate_range;
 
     #[test]
-    fn test_validate_range_min_only_valid() {
-        let validator = Validator::Range { min: Some(10.0), max: None };
-        assert_eq!(validate_range(validator, 10.0), true);
+    fn test_validate_range_generic_ok() {
+        // Unspecified generic type:
+        assert_eq!(true, validate_range(10, Some(-10), Some(10)));
+        assert_eq!(true, validate_range(0.0, Some(0.0), Some(10.0)));
+
+        // Specified type:
+        assert_eq!(true, validate_range(5u8, Some(0), Some(255)));
+        assert_eq!(true, validate_range(4u16, Some(0), Some(16)));
+        assert_eq!(true, validate_range(6u32, Some(0), Some(23)));
     }
 
     #[test]
-    fn test_validate_range_min_only_invalid() {
-        let validator = Validator::Range { min: Some(10.0), max: None };
-        assert_eq!(validate_range(validator, 9.0), false);
+    fn test_validate_range_generic_fail() {
+        assert_eq!(false, validate_range(5, Some(17), Some(19)));
+        assert_eq!(false, validate_range(-1.0, Some(0.0), Some(10.0)));
     }
 
     #[test]
-    fn test_validate_range_max_only_valid() {
-        let validator = Validator::Range { min: None, max: Some(10.0) };
-        assert_eq!(validate_range(validator, 10.0), true);
+    fn test_validate_range_generic_min_only() {
+        assert_eq!(false, validate_range(5, Some(10), None));
+        assert_eq!(true, validate_range(15, Some(10), None));
     }
 
     #[test]
-    fn test_validate_range_max_only_invalid() {
-        let validator = Validator::Range { min: None, max: Some(10.0) };
-        assert_eq!(validate_range(validator, 11.0), false);
+    fn test_validate_range_generic_max_only() {
+        assert_eq!(true, validate_range(5, None, Some(10)));
+        assert_eq!(false, validate_range(15, None, Some(10)));
     }
 }
diff --git a/validator_derive/src/lit.rs b/validator_derive/src/lit.rs
index 661168c..6743972 100644
--- a/validator_derive/src/lit.rs
+++ b/validator_derive/src/lit.rs
@@ -1,4 +1,5 @@
 use quote::quote;
+use validator_types::ValueOrPath;
 
 pub fn lit_to_string(lit: &syn::Lit) -> Option<String> {
     match *lit {
@@ -22,6 +23,34 @@ pub fn lit_to_float(lit: &syn::Lit) -> Option<f64> {
     }
 }
 
+pub fn lit_to_u64_or_path(lit: &syn::Lit) -> Option<ValueOrPath<u64>> {
+    let number = lit_to_int(lit);
+    if let Some(number) = number {
+        return Some(ValueOrPath::Value(number));
+    }
+
+    let path = lit_to_string(lit);
+    if let Some(path) = path {
+        return Some(ValueOrPath::Path(path));
+    }
+
+    None
+}
+
+pub fn lit_to_float_or_path(lit: &syn::Lit) -> Option<ValueOrPath<f64>> {
+    let number = lit_to_float(lit);
+    if let Some(number) = number {
+        return Some(ValueOrPath::Value(number));
+    }
+
+    let path = lit_to_string(lit);
+    if let Some(path) = path {
+        return Some(ValueOrPath::Path(path));
+    }
+
+    None
+}
+
 pub fn lit_to_bool(lit: &syn::Lit) -> Option<bool> {
     match *lit {
         syn::Lit::Bool(ref s) => Some(s.value),
@@ -29,16 +58,23 @@ pub fn lit_to_bool(lit: &syn::Lit) -> Option<bool> {
     }
 }
 
-pub fn option_u64_to_tokens(opt: Option<u64>) -> proc_macro2::TokenStream {
+pub fn option_to_tokens<T: quote::ToTokens>(opt: &Option<T>) -> proc_macro2::TokenStream {
     match opt {
         Some(ref t) => quote!(::std::option::Option::Some(#t)),
         None => quote!(::std::option::Option::None),
     }
 }
 
-pub fn option_f64_to_tokens(opt: Option<f64>) -> proc_macro2::TokenStream {
-    match opt {
-        Some(ref t) => quote!(::std::option::Option::Some(#t)),
-        None => quote!(::std::option::Option::None),
+pub fn value_or_path_to_tokens<T>(value: &ValueOrPath<T>) -> proc_macro2::TokenStream
+where
+    T: quote::ToTokens + std::clone::Clone + std::cmp::PartialEq + std::fmt::Debug,
+{
+    match value {
+        ValueOrPath::Value(ref t) => quote!(#t),
+        ValueOrPath::Path(ref path) => {
+            // Global space
+            let ident: syn::Path = syn::parse_str(&path.to_string()).unwrap();
+            quote!(#ident)
+        }
     }
 }
diff --git a/validator_derive/src/quoting.rs b/validator_derive/src/quoting.rs
index c85a800..8c4954c 100644
--- a/validator_derive/src/quoting.rs
+++ b/validator_derive/src/quoting.rs
@@ -3,7 +3,7 @@ use quote::quote;
 use validator_types::Validator;
 
 use crate::asserts::{COW_TYPE, NUMBER_TYPES};
-use crate::lit::{option_f64_to_tokens, option_u64_to_tokens};
+use crate::lit::{option_to_tokens, value_or_path_to_tokens};
 use crate::validation::{FieldValidation, SchemaValidation};
 
 /// Pass around all the information needed for creating a validation
@@ -128,37 +128,43 @@ pub fn quote_length_validation(
     let field_name = &field_quoter.name;
     let validator_param = field_quoter.quote_validator_param();
 
-    if let Validator::Length { min, max, equal } = validation.validator {
-        // Can't interpolate None
-        let min_tokens = option_u64_to_tokens(min);
-        let max_tokens = option_u64_to_tokens(max);
-        let equal_tokens = option_u64_to_tokens(equal);
-
+    if let Validator::Length { min, max, equal } = &validation.validator {
         let min_err_param_quoted = if let Some(v) = min {
+            let v = value_or_path_to_tokens(v);
             quote!(err.add_param(::std::borrow::Cow::from("min"), &#v);)
         } else {
             quote!()
         };
         let max_err_param_quoted = if let Some(v) = max {
+            let v = value_or_path_to_tokens(v);
             quote!(err.add_param(::std::borrow::Cow::from("max"), &#v);)
         } else {
             quote!()
         };
         let equal_err_param_quoted = if let Some(v) = equal {
+            let v = value_or_path_to_tokens(v);
             quote!(err.add_param(::std::borrow::Cow::from("equal"), &#v);)
         } else {
             quote!()
         };
 
+        let min_tokens = option_to_tokens(
+            &min.clone().map(|ref x| value_or_path_to_tokens(x)).map(|x| quote!(#x as u64)),
+        );
+        let max_tokens = option_to_tokens(
+            &max.clone().map(|ref x| value_or_path_to_tokens(x)).map(|x| quote!(#x as u64)),
+        );
+        let equal_tokens = option_to_tokens(
+            &equal.clone().map(|ref x| value_or_path_to_tokens(x)).map(|x| quote!(#x as u64)),
+        );
+
         let quoted_error = quote_error(&validation);
         let quoted = quote!(
             if !::validator::validate_length(
-                ::validator::Validator::Length {
-                    min: #min_tokens,
-                    max: #max_tokens,
-                    equal: #equal_tokens
-                },
-                #validator_param
+                #validator_param,
+                #min_tokens,
+                #max_tokens,
+                #equal_tokens
             ) {
                 #quoted_error
                 #min_err_param_quoted
@@ -182,27 +188,35 @@ pub fn quote_range_validation(
     let field_name = &field_quoter.name;
     let quoted_ident = field_quoter.quote_validator_param();
 
-    if let Validator::Range { min, max } = validation.validator {
-        // Can't interpolate None
-        let min_tokens = option_f64_to_tokens(min);
-        let max_tokens = option_f64_to_tokens(max);
-
+    if let Validator::Range { ref min, ref max } = validation.validator {
         let min_err_param_quoted = if let Some(v) = min {
+            let v = value_or_path_to_tokens(v);
             quote!(err.add_param(::std::borrow::Cow::from("min"), &#v);)
         } else {
             quote!()
         };
         let max_err_param_quoted = if let Some(v) = max {
+            let v = value_or_path_to_tokens(v);
             quote!(err.add_param(::std::borrow::Cow::from("max"), &#v);)
         } else {
             quote!()
         };
 
+        // Can't interpolate None
+        let min_tokens =
+            min.clone().map(|x| value_or_path_to_tokens(&x)).map(|x| quote!(#x as f64));
+        let min_tokens = option_to_tokens(&min_tokens);
+
+        let max_tokens =
+            max.clone().map(|x| value_or_path_to_tokens(&x)).map(|x| quote!(#x as f64));
+        let max_tokens = option_to_tokens(&max_tokens);
+
         let quoted_error = quote_error(&validation);
         let quoted = quote!(
             if !::validator::validate_range(
-                ::validator::Validator::Range {min: #min_tokens, max: #max_tokens},
-                #quoted_ident as f64
+                #quoted_ident as f64,
+                #min_tokens,
+                #max_tokens
             ) {
                 #quoted_error
                 #min_err_param_quoted
diff --git a/validator_derive/src/validation.rs b/validator_derive/src/validation.rs
index 9b50a0b..ff856d2 100644
--- a/validator_derive/src/validation.rs
+++ b/validator_derive/src/validation.rs
@@ -48,21 +48,21 @@ pub fn extract_length_validation(
                 match ident.to_string().as_ref() {
                         "message" | "code" => continue,
                         "min" => {
-                            min = match lit_to_int(lit) {
+                            min = match lit_to_u64_or_path(lit) {
                                 Some(s) => Some(s),
-                                None => error(lit.span(), "invalid argument type for `min` of `length` validator: only integers are allowed"),
+                                None => error(lit.span(), "invalid argument type for `min` of `length` validator: only number literals or value paths are allowed"),
                             };
                         },
                         "max" => {
-                            max = match lit_to_int(lit) {
+                            max = match lit_to_u64_or_path(lit) {
                                 Some(s) => Some(s),
-                                None => error(lit.span(), "invalid argument type for `max` of `length` validator: only integers are allowed"),
+                                None => error(lit.span(), "invalid argument type for `max` of `length` validator: only number literals or value paths are allowed"),
                             };
                         },
                         "equal" => {
-                            equal = match lit_to_int(lit) {
+                            equal = match lit_to_u64_or_path(lit) {
                                 Some(s) => Some(s),
-                                None => error(lit.span(), "invalid argument type for `equal` of `length` validator: only integers are allowed"),
+                                None => error(lit.span(), "invalid argument type for `equal` of `length` validator: only number literals or value paths are allowed"),
                             };
                         },
                         v => error(path.span(), &format!(
@@ -123,15 +123,15 @@ pub fn extract_range_validation(
                     match ident.to_string().as_ref() {
                         "message" | "code" => continue,
                         "min" => {
-                            min = match lit_to_float(lit) {
+                            min = match lit_to_float_or_path(lit) {
                                 Some(s) => Some(s),
-                                None => error(lit.span(), "invalid argument type for `min` of `range` validator: only integers are allowed")
+                                None => error(lit.span(), "invalid argument type for `min` of `range` validator: only number literals or value paths are allowed")
                             };
                         }
                         "max" => {
-                            max = match lit_to_float(lit) {
+                            max = match lit_to_float_or_path(lit) {
                                 Some(s) => Some(s),
-                                None => error(lit.span(), "invalid argument type for `max` of `range` validator: only integers are allowed")
+                                None => error(lit.span(), "invalid argument type for `max` of `range` validator: only number literals or value paths are allowed")
                             };
                         }
                         v => error(path.span(), &format!(
diff --git a/validator_types/src/lib.rs b/validator_types/src/lib.rs
index 43e4f62..2e04c20 100644
--- a/validator_types/src/lib.rs
+++ b/validator_types/src/lib.rs
@@ -15,14 +15,14 @@ pub enum Validator {
     // No implementation in this crate, it's all in validator_derive
     Regex(String),
     Range {
-        min: Option<f64>,
-        max: Option<f64>,
+        min: Option<ValueOrPath<f64>>,
+        max: Option<ValueOrPath<f64>>,
     },
     // Any value that impl HasLen can be validated with Length
     Length {
-        min: Option<u64>,
-        max: Option<u64>,
-        equal: Option<u64>,
+        min: Option<ValueOrPath<u64>>,
+        max: Option<ValueOrPath<u64>>,
+        equal: Option<ValueOrPath<u64>>,
     },
     #[cfg(feature = "card")]
     CreditCard,
@@ -35,6 +35,12 @@ pub enum Validator {
     RequiredNested,
 }
 
+#[derive(Debug, Clone, PartialEq)]
+pub enum ValueOrPath<T: std::fmt::Debug + Clone + PartialEq> {
+    Value(T),
+    Path(String),
+}
+
 impl Validator {
     pub fn code(&self) -> &'static str {
         match *self {


Test patch:
diff --git a/validator_derive/tests/length.rs b/validator_derive/tests/length.rs
index 0c36555..c4998de 100644
--- a/validator_derive/tests/length.rs
+++ b/validator_derive/tests/length.rs
@@ -1,5 +1,11 @@
 use validator::Validate;
 
+const MIN_CONST: u64 = 1;
+const MAX_CONST: u64 = 10;
+
+const MAX_CONST_I32: i32 = 2;
+const NEGATIVE_CONST_I32: i32 = -10;
+
 #[test]
 fn can_validate_length_ok() {
     #[derive(Debug, Validate)]
@@ -13,6 +19,58 @@ fn can_validate_length_ok() {
     assert!(s.validate().is_ok());
 }
 
+#[test]
+fn validate_length_with_ref_ok() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(length(min = "MIN_CONST", max = "MAX_CONST"))]
+        val: String,
+    }
+
+    let s = TestStruct { val: "hello".to_string() };
+
+    assert!(s.validate().is_ok());
+}
+
+#[test]
+fn validate_length_with_ref_fails() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(length(min = "MIN_CONST", max = "MAX_CONST"))]
+        val: String,
+    }
+
+    let s = TestStruct { val: "".to_string() };
+
+    assert_eq!(s.validate().is_ok(), false);
+}
+
+#[test]
+fn validate_length_with_ref_i32_fails() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(length(max = "MAX_CONST_I32"))]
+        val: String,
+    }
+
+    let s = TestStruct { val: "TO_LONG_YAY".to_string() };
+
+    assert_eq!(s.validate().is_ok(), false);
+}
+
+#[test]
+fn validate_length_with_ref_negative_i32_fails() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(length(max = "NEGATIVE_CONST_I32"))]
+        val: String,
+    }
+
+    let s = TestStruct { val: "TO_LONG_YAY".to_string() };
+
+    assert_eq!(s.validate().is_ok(), true);
+}
+
 #[test]
 fn value_out_of_length_fails_validation() {
     #[derive(Debug, Validate)]
diff --git a/validator_derive/tests/nested.rs b/validator_derive/tests/nested.rs
index c88640f..5779675 100644
--- a/validator_derive/tests/nested.rs
+++ b/validator_derive/tests/nested.rs
@@ -1,7 +1,7 @@
 use serde::Serialize;
 use std::{borrow::Cow, collections::HashMap};
 use validator::{
-    validate_length, Validate, ValidationError, ValidationErrors, ValidationErrorsKind, Validator,
+    validate_length, Validate, ValidationError, ValidationErrors, ValidationErrorsKind,
 };
 
 #[derive(Debug, Validate)]
@@ -255,10 +255,7 @@ 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(&self.child, Some(2u64), None, None) {
                 let mut err = ValidationError::new("length");
                 err.add_param(Cow::from("min"), &2u64);
                 err.add_param(Cow::from("value"), &&self.child);
@@ -320,10 +317,7 @@ fn test_field_validations_evaluated_after_nested_validations_fails() {
             }
 
             // 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(&self.child, Some(2u64), None, None) {
                 let mut err = ValidationError::new("length");
                 err.add_param(Cow::from("min"), &2u64);
                 err.add_param(Cow::from("value"), &&self.child);
diff --git a/validator_derive/tests/range.rs b/validator_derive/tests/range.rs
index 55cb74c..c517ac1 100644
--- a/validator_derive/tests/range.rs
+++ b/validator_derive/tests/range.rs
@@ -1,5 +1,8 @@
 use validator::{Validate, ValidationErrors};
 
+const MAX_CONST: usize = 10;
+const MIN_CONST: usize = 0;
+
 // Loose floating point comparison using EPSILON error bound
 macro_rules! assert_float {
     ($e1:expr, $e2:expr) => {
@@ -20,6 +23,45 @@ fn can_validate_range_ok() {
     assert!(s.validate().is_ok());
 }
 
+#[test]
+fn can_validate_only_min_ok() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(range(min = 5))]
+        val: usize,
+    }
+
+    let s = TestStruct { val: 6 };
+
+    assert!(s.validate().is_ok());
+}
+
+#[test]
+fn can_validate_only_max_ok() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(range(max = 50))]
+        val: usize,
+    }
+
+    let s = TestStruct { val: 6 };
+
+    assert!(s.validate().is_ok());
+}
+
+#[test]
+fn can_validate_range_value_crate_path_ok() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(range(min = "MIN_CONST", max = "MAX_CONST"))]
+        val: usize,
+    }
+
+    let s = TestStruct { val: 6 };
+
+    assert!(s.validate().is_ok());
+}
+
 #[test]
 fn value_out_of_range_fails_validation() {
     #[derive(Debug, Validate)]
@@ -38,6 +80,26 @@ fn value_out_of_range_fails_validation() {
     assert_eq!(errs["val"][0].code, "range");
 }
 
+#[test]
+fn value_out_of_range_fails_validation_with_crate_path() {
+    #[derive(Debug, Validate)]
+    struct TestStruct {
+        #[validate(range(min = "MIN_CONST", max = "MAX_CONST"))]
+        val: usize,
+    }
+
+    let s = TestStruct { val: 16 };
+
+    let res = s.validate();
+    assert!(res.is_err());
+    let err = res.unwrap_err();
+    println!("{}", err);
+    let errs = err.field_errors();
+    assert!(errs.contains_key("val"));
+    assert_eq!(errs["val"].len(), 1);
+    assert_eq!(errs["val"][0].code, "range");
+}
+
 #[test]
 fn can_specify_code_for_range() {
     #[derive(Debug, Validate)]