Add derive macro GQLMergeObject. #231

This commit is contained in:
Sunli 2020-08-10 14:11:46 +08:00
parent b0bce9ec32
commit d280a13b70
5 changed files with 64 additions and 32 deletions

View File

@ -19,7 +19,7 @@ mod utils;
use crate::utils::{add_container_attrs, parse_derive};
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use syn::parse_macro_input;
use syn::{AttributeArgs, ItemImpl};
#[proc_macro_attribute]
@ -207,11 +207,25 @@ pub fn Scalar(args: TokenStream, input: TokenStream) -> TokenStream {
#[proc_macro_attribute]
#[allow(non_snake_case)]
pub fn MergedObject(args: TokenStream, input: TokenStream) -> TokenStream {
add_container_attrs(
quote!(GQLMergedObject),
parse_macro_input!(args as AttributeArgs),
input.into(),
)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
#[proc_macro_derive(GQLMergedObject, attributes(item, graphql))]
pub fn derive_merged_object(input: TokenStream) -> TokenStream {
let (args, input) = match parse_derive(input.into()) {
Ok(r) => r,
Err(err) => return err.to_compile_error().into(),
};
let object_args = match args::Object::parse(parse_macro_input!(args as AttributeArgs)) {
Ok(object_args) => object_args,
Err(err) => return err.to_compile_error().into(),
};
let input = parse_macro_input!(input as DeriveInput);
match merged_object::generate(&object_args, &input) {
Ok(expanded) => expanded,
Err(err) => err.to_compile_error().into(),

View File

@ -1,9 +1,9 @@
use crate::args;
use crate::utils::{get_crate_name, get_rustdoc};
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::spanned::Spanned;
use syn::{Data, DeriveInput, Error, Ident, Result};
use syn::{Data, DeriveInput, Error, LitInt, Result};
pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<TokenStream> {
let crate_name = get_crate_name(object_args.internal);
@ -13,8 +13,6 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
.name
.clone()
.unwrap_or_else(|| ident.to_string());
let vis = &input.vis;
let attrs = &input.attrs;
let desc = object_args
.desc
@ -33,23 +31,14 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
types.push(&field.ty);
}
let new_args = {
let mut new_args = Vec::new();
for (i, ty) in types.iter().enumerate() {
let name = Ident::new(&format!("obj{}", i + 1), ty.span());
new_args.push(quote! { #name: #ty })
}
new_args
};
let new_func = {
let create_merged_obj = {
let mut obj = quote! { #crate_name::MergedObjectTail };
for (i, ty) in types.iter().enumerate() {
let name = Ident::new(&format!("obj{}", i + 1), ty.span());
obj = quote! { #crate_name::MergedObject(#name, #obj) };
for i in 0..types.len() {
let n = LitInt::new(&format!("{}", i), Span::call_site());
obj = quote! { #crate_name::MergedObject(&self.#n, #obj) };
}
quote! {
Self(#obj)
#obj
}
};
@ -62,15 +51,6 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
};
let expanded = quote! {
#(#attrs)*
#vis struct #ident(#merged_type);
impl #ident {
pub fn new(#(#new_args),*) -> Self {
#new_func
}
}
#[allow(clippy::all, clippy::pedantic)]
impl #crate_name::Type for #ident {
fn type_name() -> ::std::borrow::Cow<'static, str> {
@ -109,7 +89,7 @@ pub fn generate(object_args: &args::Object, input: &DeriveInput) -> Result<Token
#[#crate_name::async_trait::async_trait]
impl #crate_name::ObjectType for #ident {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::Result<#crate_name::serde_json::Value> {
self.0.resolve_field(ctx).await
#create_merged_obj.resolve_field(ctx).await
}
}

View File

@ -166,6 +166,13 @@ impl<T: OutputValueType + Send + Sync> OutputValueType for &T {
}
}
#[async_trait::async_trait]
impl<T: ObjectType + Send + Sync> ObjectType for &T {
async fn resolve_field(&self, ctx: &Context<'_>) -> Result<serde_json::Value> {
T::resolve_field(*self, ctx).await
}
}
impl<T: Type + Send + Sync> Type for Box<T> {
fn type_name() -> Cow<'static, str> {
T::type_name()

View File

@ -786,6 +786,9 @@ pub use async_graphql_derive::Scalar;
/// #[MergedObject]
/// struct MyObj(Object1, Object2, Object3);
///
/// let obj = MyObj::new(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 });
/// let obj = MyObj(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 });
/// ```
pub use async_graphql_derive::MergedObject;
/// Derive a GraphQL Merged object
pub use async_graphql_derive::GQLMergedObject;

View File

@ -64,7 +64,35 @@ pub async fn test_merged_object_macro() {
#[Object]
impl Query {
async fn obj(&self) -> MyObj {
MyObj::new(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 })
MyObj(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 })
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "{ obj { a b c } }";
assert_eq!(
schema.execute(&query).await.unwrap().data,
serde_json::json!({
"obj": {
"a": 10,
"b": 20,
"c": 30,
}
})
);
}
#[async_std::test]
pub async fn test_merged_object_derive() {
#[derive(GQLMergedObject)]
struct MyObj(Object1, Object2, Object3);
struct Query;
#[Object]
impl Query {
async fn obj(&self) -> MyObj {
MyObj(Object1 { a: 10 }, Object2 { b: 20 }, Object3 { c: 30 })
}
}