diff options
| author | Vincent Prouillet | 2017-05-26 00:49:54 +0900 |
|---|---|---|
| committer | Vincent Prouillet | 2017-05-28 23:52:07 +0900 |
| commit | b31fd25cc5cec96ccee737ba1313c6e9f702f32a (patch) | |
| tree | e71b13aea39c61b19e8aec09468a8e5c4a291e16 /validator_derive/src/quoting.rs | |
| parent | 69e35d6cc905b9d7a894af7e486237e376fae939 (diff) | |
| download | validator-b31fd25cc5cec96ccee737ba1313c6e9f702f32a.tar.bz2 | |
Revamp
Diffstat (limited to 'validator_derive/src/quoting.rs')
| -rw-r--r-- | validator_derive/src/quoting.rs | 332 |
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!() + } +} |
