Make Object and Subscription macros support #cfg(...)
attribute. #281
This commit is contained in:
parent
b47d08c5b5
commit
7d3eb9b62c
|
@ -205,7 +205,6 @@ pub struct Field {
|
|||
pub owned: bool,
|
||||
pub guard: Option<TokenStream>,
|
||||
pub post_guard: Option<TokenStream>,
|
||||
pub features: Vec<String>,
|
||||
}
|
||||
|
||||
impl Field {
|
||||
|
@ -217,7 +216,6 @@ impl Field {
|
|||
let mut external = false;
|
||||
let mut provides = None;
|
||||
let mut requires = None;
|
||||
let mut features = Vec::new();
|
||||
let mut owned = false;
|
||||
let mut guard = None;
|
||||
let mut post_guard = None;
|
||||
|
@ -290,20 +288,6 @@ 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(',')
|
||||
.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)) => {
|
||||
|
@ -334,7 +318,6 @@ impl Field {
|
|||
owned,
|
||||
guard,
|
||||
post_guard,
|
||||
features,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::args;
|
||||
use crate::output_type::OutputType;
|
||||
use crate::utils::{feature_block, get_crate_name, get_param_getter_ident, get_rustdoc};
|
||||
use crate::utils::{get_cfg_attrs, get_crate_name, get_param_getter_ident, get_rustdoc};
|
||||
use inflector::Inflector;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
@ -45,6 +45,8 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
for item in &mut item_impl.items {
|
||||
if let ImplItem::Method(method) = item {
|
||||
if args::Entity::parse(&crate_name, &method.attrs)?.is_some() {
|
||||
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
||||
|
||||
if method.sig.asyncness.is_none() {
|
||||
return Err(Error::new_spanned(&method, "Must be asynchronous"));
|
||||
}
|
||||
|
@ -182,6 +184,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
find_entities.push((
|
||||
args.len(),
|
||||
quote! {
|
||||
#(#cfg_attrs)*
|
||||
if typename == &<#entity_type as #crate_name::Type>::type_name() {
|
||||
if let (#(#key_pat),*) = (#(#key_getter),*) {
|
||||
#(#requires_getter)*
|
||||
|
@ -221,7 +224,6 @@ 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 },
|
||||
|
@ -246,6 +248,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
}
|
||||
}
|
||||
};
|
||||
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
||||
|
||||
let mut create_ctx = true;
|
||||
let mut args = Vec::new();
|
||||
|
@ -357,6 +360,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
let schema_ty = ty.value_type();
|
||||
|
||||
schema_fields.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
fields.insert(#field_name.to_string(), #crate_name::registry::MetaField {
|
||||
name: #field_name.to_string(),
|
||||
description: #field_desc,
|
||||
|
@ -390,13 +394,6 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
.expect("invalid result type");
|
||||
}
|
||||
|
||||
method.block =
|
||||
syn::parse2::<Block>(feature_block(&crate_name, &features, &field_name, {
|
||||
let block = &method.block;
|
||||
quote! { #block }
|
||||
}))
|
||||
.expect("invalid block");
|
||||
|
||||
let resolve_obj = quote! {
|
||||
{
|
||||
let res = self.#field_ident(ctx, #(#use_params),*).await;
|
||||
|
@ -418,6 +415,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
});
|
||||
|
||||
resolvers.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
if ctx.item.node.name.node == #field_name {
|
||||
#(#get_params)*
|
||||
#guard
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::args;
|
||||
use crate::utils::{feature_block, get_crate_name, get_rustdoc};
|
||||
use crate::utils::{get_crate_name, get_rustdoc};
|
||||
use inflector::Inflector;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
@ -103,33 +103,20 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
|
|||
.post_guard
|
||||
.map(|guard| quote! { #guard.check(ctx, &res).await.map_err(|err| err.into_error_with_path(ctx.item.pos, ctx.path_node.as_ref()))?; });
|
||||
|
||||
let features = &field.features;
|
||||
getters.push(if !field.owned {
|
||||
let block = feature_block(
|
||||
&crate_name,
|
||||
&features,
|
||||
&field_name,
|
||||
quote! { Ok(&self.#ident) },
|
||||
);
|
||||
quote! {
|
||||
#[inline]
|
||||
#[allow(missing_docs)]
|
||||
#vis async fn #ident(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::FieldResult<&#ty> {
|
||||
#block
|
||||
Ok(&self.#ident)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let block = feature_block(
|
||||
&crate_name,
|
||||
&features,
|
||||
&field_name,
|
||||
quote! { Ok(self.#ident.clone()) },
|
||||
);
|
||||
quote! {
|
||||
#[inline]
|
||||
#[allow(missing_docs)]
|
||||
#vis async fn #ident(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::FieldResult<#ty> {
|
||||
#block
|
||||
Ok(self.#ident.clone())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::args;
|
||||
use crate::output_type::OutputType;
|
||||
use crate::utils::{feature_block, get_crate_name, get_param_getter_ident, get_rustdoc};
|
||||
use crate::utils::{get_cfg_attrs, get_crate_name, get_param_getter_ident, get_rustdoc};
|
||||
use inflector::Inflector;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
|
@ -59,7 +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;
|
||||
let cfg_attrs = get_cfg_attrs(&method.attrs);
|
||||
|
||||
if method.sig.asyncness.is_none() {
|
||||
return Err(Error::new_spanned(
|
||||
|
@ -207,14 +207,8 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
.expect("invalid result type");
|
||||
}
|
||||
|
||||
method.block =
|
||||
syn::parse2::<Block>(feature_block(&crate_name, &features, &field_name, {
|
||||
let block = &method.block;
|
||||
quote! { #block }
|
||||
}))
|
||||
.expect("invalid block");
|
||||
|
||||
schema_fields.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
fields.insert(#field_name.to_string(), #crate_name::registry::MetaField {
|
||||
name: #field_name.to_string(),
|
||||
description: #field_desc,
|
||||
|
@ -327,6 +321,7 @@ pub fn generate(object_args: &args::Object, item_impl: &mut ItemImpl) -> Result<
|
|||
};
|
||||
|
||||
create_stream.push(quote! {
|
||||
#(#cfg_attrs)*
|
||||
if ctx.item.node.name.node == #field_name {
|
||||
return ::std::boxed::Box::pin(
|
||||
#crate_name::futures::TryStreamExt::try_flatten(
|
||||
|
|
|
@ -278,29 +278,10 @@ pub fn get_param_getter_ident(name: &str) -> Ident {
|
|||
Ident::new(&format!("__{}_getter", name), Span::call_site())
|
||||
}
|
||||
|
||||
pub fn feature_block(
|
||||
crate_name: &TokenStream,
|
||||
features: &[String],
|
||||
field_name: &str,
|
||||
block: TokenStream,
|
||||
) -> TokenStream {
|
||||
if !features.is_empty() {
|
||||
let error_message = format!(
|
||||
"`{}` is only available if the features `{}` are enabled",
|
||||
field_name,
|
||||
features.join(",")
|
||||
);
|
||||
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
|
||||
}
|
||||
})
|
||||
} else {
|
||||
block
|
||||
}
|
||||
pub fn get_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|attr| !attr.path.segments.is_empty() && attr.path.segments[0].ident == "cfg")
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
|
|
@ -205,7 +205,6 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||
/// | 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
|
||||
///
|
||||
|
@ -319,7 +318,6 @@ pub use async_graphql_derive::Object;
|
|||
/// | 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 |
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
@ -672,7 +670,6 @@ 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
|
||||
///
|
||||
|
|
|
@ -2,35 +2,32 @@
|
|||
|
||||
use async_graphql::*;
|
||||
use futures::{Stream, StreamExt};
|
||||
use std::pin::Pin;
|
||||
|
||||
#[async_std::test]
|
||||
pub async fn test_field_features() {
|
||||
#[derive(SimpleObject)]
|
||||
struct MyObj {
|
||||
value: i32,
|
||||
|
||||
#[field(feature = "bson")]
|
||||
#[cfg(feature = "bson")]
|
||||
value_bson: i32,
|
||||
|
||||
#[field(feature = "abc")]
|
||||
#[cfg(feature = "abc")]
|
||||
value_abc: i32,
|
||||
}
|
||||
|
||||
struct Subscription;
|
||||
struct SubscriptionRoot;
|
||||
|
||||
#[Subscription]
|
||||
impl Subscription {
|
||||
impl SubscriptionRoot {
|
||||
async fn values(&self) -> impl Stream<Item = i32> {
|
||||
futures::stream::once(async move { 10 })
|
||||
}
|
||||
|
||||
#[field(feature = "bson")]
|
||||
#[cfg(feature = "bson")]
|
||||
async fn values_bson(&self) -> impl Stream<Item = i32> {
|
||||
futures::stream::once(async move { 10 })
|
||||
}
|
||||
|
||||
#[field(feature = "abc")]
|
||||
#[cfg(feature = "abc")]
|
||||
async fn values_abc(
|
||||
&self,
|
||||
) -> Pin<Box<dyn async_graphql::futures::Stream<Item = i32> + Send + 'static>> {
|
||||
|
@ -46,12 +43,12 @@ pub async fn test_field_features() {
|
|||
10
|
||||
}
|
||||
|
||||
#[field(feature = "bson")]
|
||||
#[cfg(feature = "bson")]
|
||||
async fn value_bson(&self) -> i32 {
|
||||
10
|
||||
}
|
||||
|
||||
#[field(feature = "abc")]
|
||||
#[cfg(feature = "abc")]
|
||||
async fn value_abc(&self) -> i32 {
|
||||
10
|
||||
}
|
||||
|
@ -59,13 +56,15 @@ pub async fn test_field_features() {
|
|||
async fn obj(&self) -> MyObj {
|
||||
MyObj {
|
||||
value: 10,
|
||||
#[cfg(feature = "bson")]
|
||||
value_bson: 10,
|
||||
#[cfg(feature = "abc")]
|
||||
value_abc: 10,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let schema = Schema::new(QueryRoot, EmptyMutation, Subscription);
|
||||
let schema = Schema::new(QueryRoot, EmptyMutation, SubscriptionRoot);
|
||||
let query = "{ value }";
|
||||
assert_eq!(
|
||||
schema.execute(query).await.data,
|
||||
|
@ -85,13 +84,13 @@ pub async fn test_field_features() {
|
|||
let query = "{ valueAbc }";
|
||||
assert_eq!(
|
||||
schema.execute(query).await.into_result().unwrap_err(),
|
||||
Error::Query {
|
||||
pos: Pos { column: 3, line: 1 },
|
||||
path: Some(serde_json::json!(["valueAbc"])),
|
||||
err: QueryError::FieldError {
|
||||
err: "`valueAbc` is only available if the features `abc` are enabled".to_string(),
|
||||
extended_error: None
|
||||
}
|
||||
Error::Rule {
|
||||
errors: vec![RuleError {
|
||||
locations: vec![Pos { line: 1, column: 3 }],
|
||||
message: r#"Unknown field "valueAbc" on type "QueryRoot". Did you mean "value"?"#
|
||||
.to_string(),
|
||||
}]
|
||||
.into(),
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -114,13 +113,13 @@ pub async fn test_field_features() {
|
|||
let query = "{ obj { valueAbc } }";
|
||||
assert_eq!(
|
||||
schema.execute(query).await.into_result().unwrap_err(),
|
||||
Error::Query {
|
||||
pos: Pos { column: 9, line: 1 },
|
||||
path: Some(serde_json::json!(["obj", "valueAbc"])),
|
||||
err: QueryError::FieldError {
|
||||
err: "`valueAbc` is only available if the features `abc` are enabled".to_string(),
|
||||
extended_error: None
|
||||
}
|
||||
Error::Rule {
|
||||
errors: vec![RuleError {
|
||||
locations: vec![Pos { line: 1, column: 9 }],
|
||||
message: r#"Unknown field "valueAbc" on type "MyObj". Did you mean "value"?"#
|
||||
.to_string(),
|
||||
}]
|
||||
.into(),
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -152,16 +151,17 @@ pub async fn test_field_features() {
|
|||
.unwrap()
|
||||
.error
|
||||
.unwrap(),
|
||||
Error::Query {
|
||||
pos: Pos {
|
||||
column: 16,
|
||||
line: 1
|
||||
},
|
||||
path: Some(serde_json::json!(["valuesAbc"])),
|
||||
err: QueryError::FieldError {
|
||||
err: "`valuesAbc` is only available if the features `abc` are enabled".to_string(),
|
||||
extended_error: None
|
||||
}
|
||||
Error::Rule {
|
||||
errors: vec![RuleError {
|
||||
locations: vec![Pos {
|
||||
line: 1,
|
||||
column: 16
|
||||
}],
|
||||
message:
|
||||
r#"Unknown field "valuesAbc" on type "SubscriptionRoot". Did you mean "values", "valuesBson"?"#
|
||||
.to_string(),
|
||||
}]
|
||||
.into(),
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user