aboutsummaryrefslogtreecommitdiffstats
path: root/validator_derive/src/quoting.rs
diff options
context:
space:
mode:
Diffstat (limited to 'validator_derive/src/quoting.rs')
-rw-r--r--validator_derive/src/quoting.rs332
1 files changed, 332 insertions, 0 deletions
diff --git a/validator_derive/src/quoting.rs b/validator_derive/src/quoting.rs
new file mode 100644
index 0000000..69353e9
--- /dev/null
+++ b/validator_derive/src/quoting.rs
@@ -0,0 +1,332 @@
+use quote;
+use validator::Validator;
+use syn;
+
+use lit::option_u64_to_tokens;
+use validation::{FieldValidation, SchemaValidation};
+use asserts::NUMBER_TYPES;
+
+
+/// Pass around all the information needed for creating a validation
+#[derive(Debug)]
+pub struct FieldQuoter {
+ ident: syn::Ident,
+ /// The field name
+ name: String,
+ /// The field type
+ _type: String,
+}
+
+impl FieldQuoter {
+ pub fn new(ident: syn::Ident, name: String, _type: String) -> FieldQuoter {
+ FieldQuoter { ident, name, _type }
+ }
+
+ /// Don't put a & in front a pointer since we are going to pass
+ /// a reference to the validator
+ /// Also just use the ident without if it's optional and will go through
+ /// a if let first
+ pub fn quote_validator_param(&self) -> quote::Tokens {
+ let ident = &self.ident;
+
+ if self._type.starts_with("Option<") {
+ quote!(#ident)
+ } else if self._type.starts_with("&") || NUMBER_TYPES.contains(&self._type.as_ref()) {
+ quote!(self.#ident)
+ } else {
+ quote!(&self.#ident)
+ }
+ }
+
+ pub fn get_optional_validator_param(&self) -> quote::Tokens {
+ let ident = &self.ident;
+ if self._type.starts_with("Option<&") || NUMBER_TYPES.contains(&self._type.as_ref()) {
+ quote!(#ident)
+ } else {
+ quote!(ref #ident)
+ }
+ }
+
+ /// Wrap the quoted output of a validation with a if let Some if
+ /// the field type is an option
+ pub fn wrap_if_option(&self, tokens: quote::Tokens) -> quote::Tokens {
+ let field_ident = &self.ident;
+ let optional_pattern_matched = self.get_optional_validator_param();
+ if self._type.starts_with("Option<") {
+ return quote!(
+ if let Some(#optional_pattern_matched) = self.#field_ident {
+ #tokens
+ }
+ )
+ }
+
+ tokens
+ }
+}
+
+/// Quote an actual end-user error creation automatically
+fn quote_error(validation: &FieldValidation) -> quote::Tokens {
+ let code = &validation.code;
+ let add_message_quoted = if let Some(ref m) = validation.message {
+ quote!(err.message = Some(::std::borrow::Cow::from(#m));)
+ } else {
+ quote!()
+ };
+
+ quote!(
+ let mut err = ::validator::ValidationError::new(#code);
+ #add_message_quoted
+ )
+}
+
+
+pub fn quote_length_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ 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);
+
+ 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 equal_err_param_quoted = if let Some(v) = equal {
+ quote!(err.add_param(::std::borrow::Cow::from("equal"), &#v);)
+ } else {
+ quote!()
+ };
+
+ 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
+ ) {
+ #quoted_error
+ #min_err_param_quoted
+ #max_err_param_quoted
+ #equal_err_param_quoted
+ err.add_param(::std::borrow::Cow::from("value"), &#validator_param);
+ errors.add(#field_name, err);
+ }
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!()
+}
+
+pub fn quote_range_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ let field_name = &field_quoter.name;
+ let quoted_ident = field_quoter.quote_validator_param();
+
+ if let Validator::Range { min, max } = validation.validator {
+ 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},
+ #quoted_ident as f64
+ ) {
+ #quoted_error
+ #min_err_param_quoted
+ #max_err_param_quoted
+ err.add_param(::std::borrow::Cow::from("value"), &#quoted_ident);
+ errors.add(#field_name, err);
+ }
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!()
+}
+
+pub fn quote_url_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ 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_url(#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_email_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ 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_email(#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_must_match_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ let ident = &field_quoter.ident;
+ let field_name = &field_quoter.name;
+
+ if let Validator::MustMatch(ref other) = validation.validator {
+ let other_ident = syn::Ident::new(other.clone());
+ let quoted_error = quote_error(&validation);
+ let quoted = quote!(
+ if !::validator::validate_must_match(&self.#ident, &self.#other_ident) {
+ #quoted_error
+ err.add_param(::std::borrow::Cow::from("value"), &self.#ident);
+ err.add_param(::std::borrow::Cow::from("other"), &self.#other_ident);
+ errors.add(#field_name, err);
+ }
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!();
+}
+
+pub fn quote_custom_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ let field_name = &field_quoter.name;
+ let validator_param = field_quoter.quote_validator_param();
+
+ if let Validator::Custom(ref fun) = validation.validator {
+ let fn_ident = syn::Ident::new(fun.clone());
+ let add_message_quoted = if let Some(ref m) = validation.message {
+ quote!(err.message = Some(::std::borrow::Cow::from(#m));)
+ } else {
+ quote!()
+ };
+
+ let quoted = quote!(
+ match #fn_ident(#validator_param) {
+ ::std::result::Result::Ok(()) => (),
+ ::std::result::Result::Err(mut err) => {
+ #add_message_quoted
+ err.add_param(::std::borrow::Cow::from("value"), &#validator_param);
+ errors.add(#field_name, err);
+ },
+ };
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!();
+}
+
+pub fn quote_contains_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ let field_name = &field_quoter.name;
+ let validator_param = field_quoter.quote_validator_param();
+
+ if let Validator::Contains(ref needle) = validation.validator {
+ let quoted_error = quote_error(&validation);
+ let quoted = quote!(
+ if !::validator::validate_contains(#validator_param, &#needle) {
+ #quoted_error
+ err.add_param(::std::borrow::Cow::from("value"), &#validator_param);
+ err.add_param(::std::borrow::Cow::from("needle"), &#needle);
+ errors.add(#field_name, err);
+ }
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!();
+}
+
+pub fn quote_regex_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ let field_name = &field_quoter.name;
+ let validator_param = field_quoter.quote_validator_param();
+
+ if let Validator::Regex(ref re) = validation.validator {
+ let re_ident = syn::Ident::new(re.clone());
+ let quoted_error = quote_error(&validation);
+ let quoted = quote!(
+ if !#re_ident.is_match(#validator_param) {
+ #quoted_error
+ err.add_param(::std::borrow::Cow::from("value"), &#validator_param);
+ errors.add(#field_name, err);
+ }
+ );
+
+ return field_quoter.wrap_if_option(quoted);
+ }
+
+ unreachable!();
+}
+
+pub fn quote_field_validation(field_quoter: &FieldQuoter, validation: &FieldValidation) -> quote::Tokens {
+ match validation.validator {
+ Validator::Length {..} => quote_length_validation(&field_quoter, validation),
+ Validator::Range {..} => quote_range_validation(&field_quoter, validation),
+ Validator::Email => quote_email_validation(&field_quoter, validation),
+ Validator::Url => quote_url_validation(&field_quoter, validation),
+ Validator::MustMatch(_) => quote_must_match_validation(&field_quoter, validation),
+ Validator::Custom(_) => quote_custom_validation(&field_quoter, validation),
+ Validator::Contains(_) => quote_contains_validation(&field_quoter, validation),
+ Validator::Regex(_) => quote_regex_validation(&field_quoter, validation),
+ }
+}
+
+
+pub fn quote_schema_validation(validation: Option<SchemaValidation>) -> quote::Tokens {
+ if let Some(v) = validation {
+ let fn_ident = syn::Ident::new(v.function);
+
+ let add_message_quoted = if let Some(ref m) = v.message {
+ quote!(err.message = Some(::std::borrow::Cow::from(#m));)
+ } else {
+ quote!()
+ };
+ let mut_err_token = if v.message.is_some() { quote!(mut) } else { quote!() };
+ let quoted = quote!(
+ match #fn_ident(self) {
+ ::std::result::Result::Ok(()) => (),
+ ::std::result::Result::Err(#mut_err_token err) => {
+ #add_message_quoted
+ errors.add("__all__", err);
+ },
+ };
+ );
+
+ if !v.skip_on_field_errors {
+ return quoted;
+ }
+
+ quote!(
+ if errors.is_empty() {
+ #quoted
+ }
+ )
+ } else {
+ quote!()
+ }
+}