aboutsummaryrefslogtreecommitdiffstats
path: root/validator_derive/src/lib.rs
diff options
context:
space:
mode:
authorVincent Prouillet2018-03-17 20:48:29 +0100
committerGitHub2018-03-17 20:48:29 +0100
commit3885c487867179d50b2caae87c6c41811fa98f2d (patch)
tree33869b207da3aaedec6c50a88ccafc8d9f28de92 /validator_derive/src/lib.rs
parentc0dd8f2fc1ba45547102cecc12c3dc7bfe44f2d4 (diff)
parentf8a91c125f801e678ec88396a4409883213e17fd (diff)
downloadvalidator-3885c487867179d50b2caae87c6c41811fa98f2d.tar.bz2
Merge pull request #43 from Eijebong/syn
Update syn to 0.12, quote to 0.4 and bump version
Diffstat (limited to 'validator_derive/src/lib.rs')
-rw-r--r--validator_derive/src/lib.rs163
1 files changed, 83 insertions, 80 deletions
diff --git a/validator_derive/src/lib.rs b/validator_derive/src/lib.rs
index 6700c2a..4aa37d0 100644
--- a/validator_derive/src/lib.rs
+++ b/validator_derive/src/lib.rs
@@ -4,6 +4,7 @@
#[macro_use]
extern crate quote;
extern crate proc_macro;
+#[macro_use]
extern crate syn;
#[macro_use]
extern crate if_chain;
@@ -30,23 +31,21 @@ use quoting::{FieldQuoter, quote_field_validation, quote_schema_validation};
#[proc_macro_derive(Validate, attributes(validate))]
pub fn derive_validation(input: TokenStream) -> TokenStream {
- let source = input.to_string();
- // Parse the string representation to an AST
- let ast = syn::parse_macro_input(&source).unwrap();
+ let ast = syn::parse(input).unwrap();
let expanded = impl_validate(&ast);
- expanded.parse().unwrap()
+ expanded.into()
}
-fn impl_validate(ast: &syn::MacroInput) -> quote::Tokens {
+fn impl_validate(ast: &syn::DeriveInput) -> quote::Tokens {
// Ensure the macro is on a struct with named fields
- let fields = match ast.body {
- syn::Body::Struct(syn::VariantData::Struct(ref fields)) => {
+ let fields = match ast.data {
+ syn::Data::Struct(syn::DataStruct { ref fields, .. }) => {
if fields.iter().any(|field| field.ident.is_none()) {
panic!("struct has unnamed fields");
}
- fields
+ fields.iter().cloned().collect()
},
_ => panic!("#[derive(Validate)] can only be used with structs"),
};
@@ -55,7 +54,7 @@ fn impl_validate(ast: &syn::MacroInput) -> quote::Tokens {
let field_types = find_fields_type(&fields);
- for field in fields {
+ for field in &fields {
let field_ident = field.ident.clone().unwrap();
let (name, field_validations) = find_validators_for_field(field, &field_types);
let field_type = field_types.get(&field_ident.to_string()).cloned().unwrap();
@@ -102,17 +101,17 @@ fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaVa
};
for attr in struct_attrs {
- if attr.value.name() != "validate" {
+ if attr.path != parse_quote!(validate) {
continue;
}
if_chain! {
- if let syn::MetaItem::List(_, ref meta_items) = attr.value;
- if let syn::NestedMetaItem::MetaItem(ref item) = meta_items[0];
- if let &syn::MetaItem::List(ref ident2, ref args) = item;
+ if let Some(syn::Meta::List(syn::MetaList { ref nested, .. })) = attr.interpret_meta();
+ if let syn::NestedMeta::Meta(ref item) = nested[0];
+ if let &syn::Meta::List(syn::MetaList { ref ident, ref nested, .. }) = item;
then {
- if ident2 != "schema" {
+ if ident.as_ref() != "schema" {
error("Only `schema` is allowed as validator on a struct")
}
@@ -121,36 +120,36 @@ fn find_struct_validation(struct_attrs: &Vec<syn::Attribute>) -> Option<SchemaVa
let mut code = None;
let mut message = None;
- for arg in args {
+ for arg in nested {
if_chain! {
- if let syn::NestedMetaItem::MetaItem(ref item) = *arg;
- if let syn::MetaItem::NameValue(ref name, ref val) = *item;
+ if let syn::NestedMeta::Meta(ref item) = *arg;
+ if let syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) = *item;
then {
- match name.to_string().as_ref() {
+ match ident.as_ref() {
"function" => {
- function = match lit_to_string(val) {
+ function = match lit_to_string(lit) {
Some(s) => s,
None => error("invalid argument type for `function` \
: only a string is allowed"),
};
},
"skip_on_field_errors" => {
- skip_on_field_errors = match lit_to_bool(val) {
+ skip_on_field_errors = match lit_to_bool(lit) {
Some(s) => s,
None => error("invalid argument type for `skip_on_field_errors` \
: only a bool is allowed"),
};
},
"code" => {
- code = match lit_to_string(val) {
+ code = match lit_to_string(lit) {
Some(s) => Some(s),
None => error("invalid argument type for `code` \
: only a string is allowed"),
};
},
"message" => {
- message = match lit_to_string(val) {
+ message = match lit_to_string(lit) {
Some(s) => Some(s),
None => error("invalid argument type for `message` \
: only a string is allowed"),
@@ -194,17 +193,17 @@ fn find_fields_type(fields: &Vec<syn::Field>) -> HashMap<String, String> {
for field in fields {
let field_ident = field.ident.clone().unwrap().to_string();
let field_type = match field.ty {
- syn::Ty::Path(_, ref p) => {
+ syn::Type::Path(syn::TypePath { ref path, .. }) => {
let mut tokens = quote::Tokens::new();
- p.to_tokens(&mut tokens);
+ path.to_tokens(&mut tokens);
tokens.to_string().replace(' ', "")
},
- syn::Ty::Rptr(ref l, ref p) => {
+ syn::Type::Reference(syn::TypeReference { ref lifetime, ref elem, .. }) => {
let mut tokens = quote::Tokens::new();
- p.ty.to_tokens(&mut tokens);
+ elem.to_tokens(&mut tokens);
let mut name = tokens.to_string().replace(' ', "");
- if l.is_some() {
+ if lifetime.is_some() {
name.insert(0, '&')
}
name
@@ -235,19 +234,20 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S
let mut has_validate = false;
for attr in &field.attrs {
- if attr.name() != "validate" && attr.name() != "serde" {
+ if attr.path != parse_quote!(validate) && attr.path != parse_quote!(serde) {
continue;
}
- if attr.name() == "validate" {
+ if attr.path == parse_quote!(validate) {
has_validate = true;
}
- match attr.value {
- syn::MetaItem::List(_, ref meta_items) => {
+ match attr.interpret_meta() {
+ Some(syn::Meta::List(syn::MetaList { ref nested, .. })) => {
+ let meta_items = nested.iter().collect();
// original name before serde rename
- if attr.name() == "serde" {
- if let Some(s) = find_original_field_name(meta_items) {
+ if attr.path == parse_quote!(serde) {
+ if let Some(s) = find_original_field_name(&meta_items) {
field_ident = s;
}
continue;
@@ -256,9 +256,9 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S
// only validation from there on
for meta_item in meta_items {
match *meta_item {
- syn::NestedMetaItem::MetaItem(ref item) => match *item {
+ syn::NestedMeta::Meta(ref item) => match *item {
// email, url, phone
- syn::MetaItem::Word(ref name) => match name.to_string().as_ref() {
+ syn::Meta::Word(ref name) => match name.to_string().as_ref() {
"email" => {
assert_string_type("email", field_type);
validators.push(FieldValidation::new(Validator::Email));
@@ -279,28 +279,28 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S
_ => panic!("Unexpected validator: {}", name)
},
// custom, contains, must_match, regex
- syn::MetaItem::NameValue(ref name, ref val) => {
- match name.to_string().as_ref() {
+ syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => {
+ match ident.as_ref() {
"custom" => {
- match lit_to_string(val) {
+ match lit_to_string(lit) {
Some(s) => validators.push(FieldValidation::new(Validator::Custom(s))),
None => error("invalid argument for `custom` validator: only strings are allowed"),
};
},
"contains" => {
- match lit_to_string(val) {
+ match lit_to_string(lit) {
Some(s) => validators.push(FieldValidation::new(Validator::Contains(s))),
None => error("invalid argument for `contains` validator: only strings are allowed"),
};
},
"regex" => {
- match lit_to_string(val) {
+ match lit_to_string(lit) {
Some(s) => validators.push(FieldValidation::new(Validator::Regex(s))),
None => error("invalid argument for `regex` validator: only strings are allowed"),
};
}
"must_match" => {
- match lit_to_string(val) {
+ match lit_to_string(lit) {
Some(s) => {
assert_type_matches(rust_ident.clone(), field_type, field_types.get(&s));
validators.push(FieldValidation::new(Validator::MustMatch(s)));
@@ -308,42 +308,45 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S
None => error("invalid argument for `must_match` validator: only strings are allowed"),
};
},
- _ => panic!("unexpected name value validator: {:?}", name),
+ v => panic!("unexpected name value validator: {:?}", v),
};
},
// Validators with several args
- syn::MetaItem::List(ref name, ref meta_items) => match name.to_string().as_ref() {
- "length" => {
- assert_has_len(rust_ident.clone(), field_type);
- validators.push(extract_length_validation(rust_ident.clone(), meta_items));
- },
- "range" => {
- assert_has_range(rust_ident.clone(), field_type);
- validators.push(extract_range_validation(rust_ident.clone(), meta_items));
- },
- "email" | "url" | "phone" | "credit_card" => {
- validators.push(extract_argless_validation(name.to_string(), rust_ident.clone(), meta_items));
- },
- "custom" => {
- validators.push(extract_one_arg_validation("function", name.to_string(), rust_ident.clone(), meta_items));
- },
- "contains" => {
- validators.push(extract_one_arg_validation("pattern", name.to_string(), rust_ident.clone(), meta_items));
- },
- "regex" => {
- validators.push(extract_one_arg_validation("path", name.to_string(), rust_ident.clone(), meta_items));
- },
- "must_match" => {
- let validation = extract_one_arg_validation("other", name.to_string(), rust_ident.clone(), meta_items);
- if let Validator::MustMatch(ref t2) = validation.validator {
- assert_type_matches(rust_ident.clone(), field_type, field_types.get(t2));
- }
- validators.push(validation);
- },
- _ => panic!("unexpected list validator: {:?}", name.to_string())
+ syn::Meta::List(syn::MetaList { ref ident, ref nested, .. }) => {
+ let meta_items = nested.iter().cloned().collect();
+ match ident.as_ref() {
+ "length" => {
+ assert_has_len(rust_ident.clone(), field_type);
+ validators.push(extract_length_validation(rust_ident.clone(), &meta_items));
+ },
+ "range" => {
+ assert_has_range(rust_ident.clone(), field_type);
+ validators.push(extract_range_validation(rust_ident.clone(), &meta_items));
+ },
+ "email" | "url" | "phone" | "credit_card" => {
+ validators.push(extract_argless_validation(ident.to_string(), rust_ident.clone(), &meta_items));
+ },
+ "custom" => {
+ validators.push(extract_one_arg_validation("function", ident.to_string(), rust_ident.clone(), &meta_items));
+ },
+ "contains" => {
+ validators.push(extract_one_arg_validation("pattern", ident.to_string(), rust_ident.clone(), &meta_items));
+ },
+ "regex" => {
+ validators.push(extract_one_arg_validation("path", ident.to_string(), rust_ident.clone(), &meta_items));
+ },
+ "must_match" => {
+ let validation = extract_one_arg_validation("other", ident.to_string(), rust_ident.clone(), &meta_items);
+ if let Validator::MustMatch(ref t2) = validation.validator {
+ assert_type_matches(rust_ident.clone(), field_type, field_types.get(t2));
+ }
+ validators.push(validation);
+ },
+ v => panic!("unexpected list validator: {:?}", v)
+ }
},
},
- _ => unreachable!("Found a non MetaItem while looking for validators")
+ _ => unreachable!("Found a non Meta while looking for validators")
};
}
},
@@ -363,20 +366,20 @@ fn find_validators_for_field(field: &syn::Field, field_types: &HashMap<String, S
///
/// For example a JS frontend might send camelCase fields and Rust converts them to snake_case
/// but we want to send the errors back with the original name
-fn find_original_field_name(meta_items: &Vec<syn::NestedMetaItem>) -> Option<String> {
+fn find_original_field_name(meta_items: &Vec<&syn::NestedMeta>) -> Option<String> {
let mut original_name = None;
for meta_item in meta_items {
match *meta_item {
- syn::NestedMetaItem::MetaItem(ref item) => match *item {
- syn::MetaItem::Word(_) => continue,
- syn::MetaItem::NameValue(ref name, ref val) => {
- if name == "rename" {
- original_name = Some(lit_to_string(val).unwrap());
+ &syn::NestedMeta::Meta(ref item) => match *item {
+ syn::Meta::Word(_) => continue,
+ syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. }) => {
+ if ident.as_ref() == "rename" {
+ original_name = Some(lit_to_string(lit).unwrap());
}
},
- syn::MetaItem::List(_, ref meta_items) => {
- return find_original_field_name(meta_items);
+ syn::Meta::List(syn::MetaList { ref nested, .. }) => {
+ return find_original_field_name(&nested.iter().collect());
}
},
_ => unreachable!()