aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCaroline Glassberg-Powell2019-04-28 18:45:40 +0100
committerCaroline Glassberg-Powell2019-04-28 19:01:08 +0100
commit6e8effdaa2b47f4056a00d301a9e7a5a1a2fd8dc (patch)
tree781b99ec57192adb777681243b8161fd37a10bae
parent4b9fe3939b106c151bff11e490a41212559f9a4a (diff)
downloadvalidator-6e8effdaa2b47f4056a00d301a9e7a5a1a2fd8dc.tar.bz2
Issue #69: Change `range` to require only one arg
Currently range is hard-coded to take both max and min. This is unhelpful in cases where we may only want to check one of the bounds. Update so that it behaves more like the `length` validator and can take either max or min, as well as both. Also ensure that at least one of them is supplied.
-rw-r--r--README.md4
-rw-r--r--validator/src/validation/mod.rs4
-rw-r--r--validator/src/validation/range.rs44
-rw-r--r--validator_derive/src/lit.rs7
-rw-r--r--validator_derive/src/quoting.rs21
-rw-r--r--validator_derive/src/validation.rs43
-rw-r--r--validator_derive/tests/compile-fail/range/missing_arg.rs15
-rw-r--r--validator_derive/tests/compile-fail/range/no_args.rs2
-rw-r--r--validator_derive/tests/run-pass/range.rs4
9 files changed, 93 insertions, 51 deletions
diff --git a/README.md b/README.md
index c9bda8d..ce86ead 100644
--- a/README.md
+++ b/README.md
@@ -179,14 +179,16 @@ Examples:
```
### range
-Tests whether a number is in the given range. `range` takes 2 number arguments: `min` and `max`.
+Tests whether a number is in the given range. `range` takes between 1 and 2 number arguments: `min` and `max`.
Examples:
```rust
#[validate(range(min = "1", max = "10"))]
+#[validate(range(min = "1"))]
#[validate(range(min = "1", max = "10.8"))]
#[validate(range(min = "1.1", max = "10.8"))]
+#[validate(range(max = "10.8"))]
```
### must_match
diff --git a/validator/src/validation/mod.rs b/validator/src/validation/mod.rs
index 065b27e..95d9b79 100644
--- a/validator/src/validation/mod.rs
+++ b/validator/src/validation/mod.rs
@@ -27,8 +27,8 @@ pub enum Validator {
// No implementation in this crate, it's all in validator_derive
Regex(String),
Range {
- min: f64,
- max: f64,
+ min: Option<f64>,
+ max: Option<f64>,
},
// Any value that impl HasLen can be validated with Length
Length {
diff --git a/validator/src/validation/range.rs b/validator/src/validation/range.rs
index c643762..a89f707 100644
--- a/validator/src/validation/range.rs
+++ b/validator/src/validation/range.rs
@@ -5,7 +5,21 @@ 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,
+ Validator::Range { min, max } => {
+ if let Some(m) = min {
+ if val < m {
+ return false;
+ }
+ }
+
+ if let Some(m) = max {
+ if val > m {
+ return false;
+ }
+ }
+
+ true
+ }
_ => unreachable!(),
}
}
@@ -16,13 +30,37 @@ mod tests {
#[test]
fn test_validate_range_ok() {
- let validator = Validator::Range { min: 0.0, max: 10.0 };
+ let validator = Validator::Range { min: Some(0.0), max: Some(10.0) };
assert_eq!(validate_range(validator, 1 as f64), true);
}
#[test]
fn test_validate_range_fail() {
- let validator = Validator::Range { min: 0.0, max: 10.0 };
+ let validator = Validator::Range { min: Some(0.0), max: Some(10.0) };
assert_eq!(validate_range(validator, 20 as f64), false);
}
+
+ #[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);
+ }
+
+ #[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);
+ }
+
+ #[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);
+ }
+
+ #[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);
+ }
}
diff --git a/validator_derive/src/lit.rs b/validator_derive/src/lit.rs
index 14e3f56..de0e21e 100644
--- a/validator_derive/src/lit.rs
+++ b/validator_derive/src/lit.rs
@@ -46,3 +46,10 @@ pub fn option_u64_to_tokens(opt: Option<u64>) -> proc_macro2::TokenStream {
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),
+ }
+}
diff --git a/validator_derive/src/quoting.rs b/validator_derive/src/quoting.rs
index f94d8fc..fa97623 100644
--- a/validator_derive/src/quoting.rs
+++ b/validator_derive/src/quoting.rs
@@ -3,7 +3,7 @@ use syn;
use validator::Validator;
use asserts::{COW_TYPE, NUMBER_TYPES};
-use lit::option_u64_to_tokens;
+use lit::{option_f64_to_tokens, option_u64_to_tokens};
use validation::{FieldValidation, SchemaValidation};
/// Pass around all the information needed for creating a validation
@@ -183,12 +183,25 @@ pub fn quote_range_validation(
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);
+
+ let min_err_param_quoted = if let Some(v) = min {
+ quote!(err.add_param(::std::borrow::Cow::from("min"), &#v);)
+ } else {
+ quote!()
+ };
+ let max_err_param_quoted = if let Some(v) = max {
+ quote!(err.add_param(::std::borrow::Cow::from("max"), &#v);)
+ } else {
+ quote!()
+ };
+
let quoted_error = quote_error(&validation);
- let min_err_param_quoted = quote!(err.add_param(::std::borrow::Cow::from("min"), &#min););
- let max_err_param_quoted = quote!(err.add_param(::std::borrow::Cow::from("max"), &#max););
let quoted = quote!(
if !::validator::validate_range(
- ::validator::Validator::Range {min: #min, max: #max},
+ ::validator::Validator::Range {min: #min_tokens, max: #max_tokens},
#quoted_ident as f64
) {
#quoted_error
diff --git a/validator_derive/src/validation.rs b/validator_derive/src/validation.rs
index a9762db..d9ad5a5 100644
--- a/validator_derive/src/validation.rs
+++ b/validator_derive/src/validation.rs
@@ -95,8 +95,8 @@ pub fn extract_range_validation(
field: String,
meta_items: &Vec<syn::NestedMeta>,
) -> FieldValidation {
- let mut min = 0.0;
- let mut max = 0.0;
+ let mut min = None;
+ let mut max = None;
let (message, code) = extract_message_and_code("range", &field, meta_items);
@@ -104,45 +104,38 @@ pub fn extract_range_validation(
panic!("Invalid attribute #[validate] on field `{}`: {}", field, msg);
};
- // whether it has both `min` and `max`
- let mut has_min = false;
- let mut has_max = false;
-
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) {
- Some(s) => s,
+ 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) => Some(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) {
- Some(s) => s,
+ }
+ "max" => {
+ max = match lit_to_float(lit) {
+ Some(s) => Some(s),
None => error("invalid argument type for `max` of `range` validator: only integers are allowed")
};
- has_max = true;
- }
- v => error(&format!(
+ }
+ v => error(&format!(
"unknown argument `{}` for validator `range` (it only has `min`, `max`)",
v
)),
- },
+ }
+ }
_ => panic!("unexpected item {:?} while parsing `range` validator", item),
},
_ => unreachable!(),
}
}
- if !has_min || !has_max {
- error("Validator `range` requires 2 arguments: `min` and `max`");
+ if !min.is_some() && !max.is_some() {
+ error("Validator `range` requires at least 1 argument out of `min` and `max`");
}
let validator = Validator::Range { min, max };
diff --git a/validator_derive/tests/compile-fail/range/missing_arg.rs b/validator_derive/tests/compile-fail/range/missing_arg.rs
deleted file mode 100644
index 3aed1f0..0000000
--- a/validator_derive/tests/compile-fail/range/missing_arg.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-#![feature(attr_literals)]
-
-#[macro_use] extern crate validator_derive;
-extern crate validator;
-use validator::Validate;
-
-#[derive(Validate)]
-//~^ ERROR: proc-macro derive panicked
-//~^^ HELP: Invalid attribute #[validate] on field `s`: Validator `range` requires 2 arguments: `min` and `max`
-struct Test {
- #[validate(range(min = 2.0))]
- s: i32,
-}
-
-fn main() {}
diff --git a/validator_derive/tests/compile-fail/range/no_args.rs b/validator_derive/tests/compile-fail/range/no_args.rs
index c2bdd03..b097b0a 100644
--- a/validator_derive/tests/compile-fail/range/no_args.rs
+++ b/validator_derive/tests/compile-fail/range/no_args.rs
@@ -4,7 +4,7 @@ use validator::Validate;
#[derive(Validate)]
//~^ ERROR: proc-macro derive panicked
-//~^^ HELP: Invalid attribute #[validate] on field `s`: Validator `range` requires 2 arguments: `min` and `max`
+//~^^ HELP: Invalid attribute #[validate] on field `s`: Validator `range` requires at least 1 argument out of `min` and `max`
struct Test {
#[validate(range())]
s: i32,
diff --git a/validator_derive/tests/run-pass/range.rs b/validator_derive/tests/run-pass/range.rs
index e35f3df..520dd64 100644
--- a/validator_derive/tests/run-pass/range.rs
+++ b/validator_derive/tests/run-pass/range.rs
@@ -24,6 +24,10 @@ struct Test {
s8: u8,
#[validate(range(min = 18.0, max = 22))]
s9: Option<u8>,
+ #[validate(range(min = 18.0))]
+ s10: Option<u8>,
+ #[validate(range(max = 18.0))]
+ s11: Option<u8>,
}
fn main() {}