From 614fb8970327ac4dad5b8fac7846515f022445f3 Mon Sep 17 00:00:00 2001 From: Coenen Benjamin Date: Fri, 5 Jun 2020 15:06:53 +0200 Subject: [PATCH] add feature attribute to field attribute inside Object (#153) * add feature support in field attribute --- async-graphql-derive/src/args.rs | 18 ++++++++++++++++++ async-graphql-derive/src/object.rs | 21 +++++++++++++++++++++ async-graphql-derive/src/subscription.rs | 22 ++++++++++++++++++++++ src/lib.rs | 2 ++ 4 files changed, 63 insertions(+) diff --git a/async-graphql-derive/src/args.rs b/async-graphql-derive/src/args.rs index 7cd3fd4f..fa15e3fb 100644 --- a/async-graphql-derive/src/args.rs +++ b/async-graphql-derive/src/args.rs @@ -200,6 +200,7 @@ pub struct Field { pub is_ref: bool, pub guard: Option, pub post_guard: Option, + pub features: Vec, } impl Field { @@ -211,6 +212,7 @@ impl Field { let mut external = false; let mut provides = None; let mut requires = None; + let mut features = Vec::new(); let mut is_ref = false; let mut guard = None; let mut post_guard = None; @@ -277,6 +279,21 @@ impl Field { "Attribute 'requires' should be a string.", )); } + } else if nv.path.is_ident("feature") { + if let syn::Lit::Str(lit) = &nv.lit { + features = lit + .value() + .to_string() + .split(',') + .into_iter() + .map(|s| s.trim().to_string()) + .collect(); + } else { + return Err(Error::new_spanned( + &nv.lit, + "Attribute 'feature' should be a string.", + )); + } } } NestedMeta::Meta(Meta::List(ls)) => { @@ -307,6 +324,7 @@ impl Field { is_ref, guard, post_guard, + features, })) } } diff --git a/async-graphql-derive/src/object.rs b/async-graphql-derive/src/object.rs index 9c668304..413c506b 100644 --- a/async-graphql-derive/src/object.rs +++ b/async-graphql-derive/src/object.rs @@ -200,6 +200,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< .map(|s| quote! {Some(#s)}) .unwrap_or_else(|| quote! {None}); let external = field.external; + let features = field.features; let requires = match &field.requires { Some(requires) => quote! { Some(#requires) }, None => quote! { None }, @@ -365,6 +366,26 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< ) .expect("invalid result type"); } + if !features.is_empty() { + let block = &method.block; + let error_message = format!( + "`{}` is only available if the features `{}` are enabled", + field_name, + features.join(",") + ); + let new_block = quote!({ + #[cfg(not(all(#(feature = #features),*)))] + { + return Err(#crate_name::FieldError::from(#error_message)).map_err(std::convert::Into::into); + } + #[cfg(all(#(feature = #features),*))] + { + #block + } + }); + + method.block = syn::parse2::(new_block).expect("invalid block"); + } let resolve_obj = quote! { { let res = self.#field_ident(ctx, #(#use_params),*).await; diff --git a/async-graphql-derive/src/subscription.rs b/async-graphql-derive/src/subscription.rs index 59a073cb..e6118cc3 100644 --- a/async-graphql-derive/src/subscription.rs +++ b/async-graphql-derive/src/subscription.rs @@ -59,6 +59,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< .as_ref() .map(|s| quote! {Some(#s)}) .unwrap_or_else(|| quote! {None}); + let features = field.features; if method.sig.asyncness.is_none() { return Err(Error::new_spanned( @@ -205,6 +206,27 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result< .expect("invalid result type"); } + if !features.is_empty() { + let block = &method.block; + let error_message = format!( + "`{}` is only available if the features `{}` are enabled", + field_name, + features.join(",") + ); + let new_block = quote!({ + #[cfg(not(all(#(feature = #features),*)))] + { + return Err(#crate_name::FieldError::from(#error_message)).map_err(std::convert::Into::into); + } + #[cfg(all(#(feature = #features),*))] + { + #block + } + }); + + method.block = syn::parse2::(new_block).expect("invalid block"); + } + schema_fields.push(quote! { fields.insert(#field_name.to_string(), #crate_name::registry::MetaField { name: #field_name.to_string(), diff --git a/src/lib.rs b/src/lib.rs index 4e857ede..996fa3d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -204,6 +204,7 @@ pub use types::{EnumItem, EnumType}; /// | provides | Annotate the expected returned fieldset from a field on a base type that is guaranteed to be selectable by the gateway. | string | Y | /// | requires | Annotate the required input fieldset from a base type for a resolver. It is used to develop a query plan where the required fields may not be needed by the client, but the service may need additional information from other services. | string | Y | /// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y | +/// | feature | It's like a `#[cfg(feature = "foo")]` attribute but instead of not compiling this field it will just return a proper `FieldError` to tell you this feature is not enabled | string ("feature1,feature2") | Y | /// /// # Field argument parameters /// @@ -655,6 +656,7 @@ pub use async_graphql_derive::Union; /// | desc | Field description | string | Y | /// | deprecation | Field deprecation reason | string | Y | /// | guard | Field of guard | [`Guard`](guard/trait.Guard.html) | Y | +/// | feature | It's like a `#[cfg(feature = "foo")]` attribute but instead of not compiling this field it will just return a proper `FieldError` to tell you this feature is not enabled | string ("feature1,feature2") | Y | /// /// # Field argument parameters ///