#[Object] on impl dyn TraitObj. #381

This commit is contained in:
Sunli 2021-01-10 11:21:47 +08:00
parent 1897859b91
commit c547252121
6 changed files with 139 additions and 16 deletions

View File

@ -1,7 +1,8 @@
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote; use quote::quote;
use syn::ext::IdentExt; use syn::ext::IdentExt;
use syn::{Block, Error, FnArg, ImplItem, ItemImpl, Pat, ReturnType, Type, TypeReference}; use syn::{Block, Error, FnArg, Ident, ImplItem, ItemImpl, Pat, ReturnType, Type, TypeReference};
use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget}; use crate::args::{self, ComplexityType, RenameRuleExt, RenameTarget};
use crate::output_type::OutputType; use crate::output_type::OutputType;
@ -18,12 +19,14 @@ pub fn generate(
let crate_name = get_crate_name(object_args.internal); let crate_name = get_crate_name(object_args.internal);
let (self_ty, self_name) = get_type_path_and_name(item_impl.self_ty.as_ref())?; let (self_ty, self_name) = get_type_path_and_name(item_impl.self_ty.as_ref())?;
let generics = &item_impl.generics; let generics = &item_impl.generics;
let generics_params = &generics.params;
let where_clause = &item_impl.generics.where_clause; let where_clause = &item_impl.generics.where_clause;
let extends = object_args.extends; let extends = object_args.extends;
let gql_typename = object_args let gql_typename = object_args
.name .name
.clone() .clone()
.unwrap_or_else(|| RenameTarget::Type.rename(self_name.clone())); .unwrap_or_else(|| RenameTarget::Type.rename(self_name.clone()));
let shadow_type = Ident::new(&format!("__Shadow{}", gql_typename), Span::call_site());
let desc = if object_args.use_type_description { let desc = if object_args.use_type_description {
quote! { ::std::option::Option::Some(<Self as #crate_name::Description>::description()) } quote! { ::std::option::Option::Some(<Self as #crate_name::Description>::description()) }
@ -466,8 +469,10 @@ pub fn generate(
let block = &method.block; let block = &method.block;
let new_block = quote!({ let new_block = quote!({
{ {
let value:#inner_ty = async move #block.await; ::std::result::Result::Ok(async move {
::std::result::Result::Ok(value) let value:#inner_ty = #block;
value
}.await)
} }
}); });
method.block = syn::parse2::<Block>(new_block).expect("invalid block"); method.block = syn::parse2::<Block>(new_block).expect("invalid block");
@ -534,11 +539,15 @@ pub fn generate(
} }
let visible = visible_fn(&object_args.visible); let visible = visible_fn(&object_args.visible);
let expanded = quote! { let expanded = quote! {
#item_impl #item_impl
#[allow(non_snake_case)]
type #shadow_type<#generics_params> = #self_ty;
#[allow(clippy::all, clippy::pedantic)] #[allow(clippy::all, clippy::pedantic)]
impl #generics #crate_name::Type for #self_ty #where_clause { impl #generics #crate_name::Type for #shadow_type<#generics_params> #where_clause {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> { fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed(#gql_typename) ::std::borrow::Cow::Borrowed(#gql_typename)
} }
@ -566,7 +575,7 @@ pub fn generate(
#[allow(clippy::all, clippy::pedantic, clippy::suspicious_else_formatting)] #[allow(clippy::all, clippy::pedantic, clippy::suspicious_else_formatting)]
#[allow(unused_braces, unused_variables, unused_parens, unused_mut)] #[allow(unused_braces, unused_variables, unused_parens, unused_mut)]
#[#crate_name::async_trait::async_trait] #[#crate_name::async_trait::async_trait]
impl#generics #crate_name::resolver_utils::ContainerType for #self_ty #where_clause { impl#generics #crate_name::resolver_utils::ContainerType for #shadow_type<#generics_params> #where_clause {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> { async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
#(#resolvers)* #(#resolvers)*
::std::result::Result::Ok(::std::option::Option::None) ::std::result::Result::Ok(::std::option::Option::None)
@ -592,13 +601,16 @@ pub fn generate(
#[allow(clippy::all, clippy::pedantic)] #[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait] #[#crate_name::async_trait::async_trait]
impl #generics #crate_name::OutputType for #self_ty #where_clause { impl #generics #crate_name::OutputType for #shadow_type<#generics_params> #where_clause {
async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> { async fn resolve(&self, ctx: &#crate_name::ContextSelectionSet<'_>, _field: &#crate_name::Positioned<#crate_name::parser::types::Field>) -> #crate_name::ServerResult<#crate_name::Value> {
#crate_name::resolver_utils::resolve_container(ctx, self).await #crate_name::resolver_utils::resolve_container(ctx, self).await
} }
} }
impl #generics #crate_name::ObjectType for #self_ty #where_clause {} impl #generics #crate_name::ObjectType for #shadow_type<#generics_params> #where_clause {}
}; };
if gql_typename == "QueryRoot11" {
println!("{}", expanded);
}
Ok(expanded.into()) Ok(expanded.into())
} }

View File

@ -7,7 +7,7 @@ use quote::quote;
use syn::visit::Visit; use syn::visit::Visit;
use syn::{ use syn::{
Attribute, Error, Expr, ExprPath, Ident, Lit, LitStr, Meta, NestedMeta, Type, TypeGroup, Attribute, Error, Expr, ExprPath, Ident, Lit, LitStr, Meta, NestedMeta, Type, TypeGroup,
TypePath, TypeParamBound,
}; };
use thiserror::Error; use thiserror::Error;
@ -380,10 +380,10 @@ pub fn remove_graphql_attrs(attrs: &mut Vec<Attribute>) {
} }
} }
pub fn get_type_path_and_name(ty: &Type) -> GeneratorResult<(&TypePath, String)> { pub fn get_type_path_and_name(ty: &Type) -> GeneratorResult<(&Type, String)> {
match ty { match ty {
Type::Path(path) => Ok(( Type::Path(path) => Ok((
path, ty,
path.path path.path
.segments .segments
.last() .last()
@ -391,6 +391,19 @@ pub fn get_type_path_and_name(ty: &Type) -> GeneratorResult<(&TypePath, String)>
.unwrap(), .unwrap(),
)), )),
Type::Group(TypeGroup { elem, .. }) => get_type_path_and_name(&elem), Type::Group(TypeGroup { elem, .. }) => get_type_path_and_name(&elem),
Type::TraitObject(trait_object) => Ok((
ty,
trait_object
.bounds
.iter()
.find_map(|bound| match bound {
TypeParamBound::Trait(t) => {
Some(t.path.segments.last().map(|s| s.ident.to_string()).unwrap())
}
_ => None,
})
.unwrap(),
)),
_ => Err(Error::new_spanned(ty, "Invalid type").into()), _ => Err(Error::new_spanned(ty, "Invalid type").into()),
} }
} }

View File

@ -1,4 +1,5 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::sync::Arc;
use crate::parser::types::Field; use crate::parser::types::Field;
use crate::registry::Registry; use crate::registry::Registry;
@ -77,6 +78,50 @@ impl<T: OutputType + Send + Sync + ?Sized> OutputType for &T {
} }
} }
impl<T: Type + Send + Sync + ?Sized> Type for Box<T> {
fn type_name() -> Cow<'static, str> {
T::type_name()
}
fn create_type_info(registry: &mut Registry) -> String {
T::create_type_info(registry)
}
}
#[async_trait::async_trait]
impl<T: OutputType + Send + Sync + ?Sized> OutputType for Box<T> {
#[allow(clippy::trivially_copy_pass_by_ref)]
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
field: &Positioned<Field>,
) -> ServerResult<Value> {
T::resolve(&**self, ctx, field).await
}
}
impl<T: Type + Send + Sync + ?Sized> Type for Arc<T> {
fn type_name() -> Cow<'static, str> {
T::type_name()
}
fn create_type_info(registry: &mut Registry) -> String {
T::create_type_info(registry)
}
}
#[async_trait::async_trait]
impl<T: OutputType + Send + Sync + ?Sized> OutputType for Arc<T> {
#[allow(clippy::trivially_copy_pass_by_ref)]
async fn resolve(
&self,
ctx: &ContextSelectionSet<'_>,
field: &Positioned<Field>,
) -> ServerResult<Value> {
T::resolve(&**self, ctx, field).await
}
}
impl<T: Type> Type for Result<T> { impl<T: Type> Type for Result<T> {
fn type_name() -> Cow<'static, str> { fn type_name() -> Cow<'static, str> {
T::type_name() T::type_name()

View File

@ -316,6 +316,8 @@ pub type FieldResult<T> = Result<T>;
/// ///
/// # Examples /// # Examples
/// ///
/// Implements GraphQL Object for struct.
///
/// ```rust /// ```rust
/// use async_graphql::*; /// use async_graphql::*;
/// ///
@ -363,6 +365,57 @@ pub type FieldResult<T> = Result<T>;
/// })); /// }));
/// }); /// });
/// ``` /// ```
///
/// # Examples
///
/// Implements GraphQL Object for trait object.
///
/// ```rust
/// use async_graphql::*;
///
/// trait MyTrait: Send + Sync {
/// fn name(&self) -> &str;
/// }
///
/// #[Object]
/// impl dyn MyTrait {
/// #[graphql(name = "name")]
/// async fn gql_name(&self) -> &str {
/// self.name()
/// }
/// }
///
/// struct MyObj(String);
///
/// impl MyTrait for MyObj {
/// fn name(&self) -> &str {
/// &self.0
/// }
/// }
///
/// struct QueryRoot;
///
/// #[Object]
/// impl QueryRoot {
/// async fn objs(&self) -> Vec<Box<dyn MyTrait>> {
/// vec![
/// Box::new(MyObj("a".to_string())),
/// Box::new(MyObj("b".to_string())),
/// ]
/// }
/// }
///
/// async_std::task::block_on(async move {
/// let schema = Schema::new(QueryRoot, EmptyMutation, EmptySubscription);
/// let res = schema.execute("{ objs { name } }").await.into_result().unwrap().data;
/// assert_eq!(res, value!({
/// "objs": [
/// { "name": "a" },
/// { "name": "b" },
/// ]
/// }));
/// });
/// ```
pub use async_graphql_derive::Object; pub use async_graphql_derive::Object;
/// Define a GraphQL object with fields /// Define a GraphQL object with fields

View File

@ -297,7 +297,7 @@ pub struct Registry {
} }
impl Registry { impl Registry {
pub fn create_type<T: crate::Type, F: FnMut(&mut Registry) -> MetaType>( pub fn create_type<T: crate::Type + ?Sized, F: FnMut(&mut Registry) -> MetaType>(
&mut self, &mut self,
mut f: F, mut f: F,
) -> String { ) -> String {

View File

@ -36,7 +36,7 @@ pub trait ContainerType: OutputType {
fields: &mut Fields<'a>, fields: &mut Fields<'a>,
) -> ServerResult<()> ) -> ServerResult<()>
where where
Self: Sized + Send + Sync, Self: Send + Sync,
{ {
fields.add_set(ctx, self) fields.add_set(ctx, self)
} }
@ -61,7 +61,7 @@ impl<T: ContainerType + Send + Sync> ContainerType for &T {
} }
/// Resolve an container by executing each of the fields concurrently. /// Resolve an container by executing each of the fields concurrently.
pub async fn resolve_container<'a, T: ContainerType + Send + Sync>( pub async fn resolve_container<'a, T: ContainerType + Send + Sync + ?Sized>(
ctx: &ContextSelectionSet<'a>, ctx: &ContextSelectionSet<'a>,
root: &'a T, root: &'a T,
) -> ServerResult<Value> { ) -> ServerResult<Value> {
@ -69,7 +69,7 @@ pub async fn resolve_container<'a, T: ContainerType + Send + Sync>(
} }
/// Resolve an container by executing each of the fields serially. /// Resolve an container by executing each of the fields serially.
pub async fn resolve_container_serial<'a, T: ContainerType + Send + Sync>( pub async fn resolve_container_serial<'a, T: ContainerType + Send + Sync + ?Sized>(
ctx: &ContextSelectionSet<'a>, ctx: &ContextSelectionSet<'a>,
root: &'a T, root: &'a T,
) -> ServerResult<Value> { ) -> ServerResult<Value> {
@ -102,7 +102,7 @@ fn insert_value(target: &mut BTreeMap<Name, Value>, name: Name, value: Value) {
} }
} }
async fn resolve_container_inner<'a, T: ContainerType + Send + Sync>( async fn resolve_container_inner<'a, T: ContainerType + Send + Sync + ?Sized>(
ctx: &ContextSelectionSet<'a>, ctx: &ContextSelectionSet<'a>,
root: &'a T, root: &'a T,
parallel: bool, parallel: bool,
@ -134,7 +134,7 @@ pub struct Fields<'a>(Vec<BoxFieldFuture<'a>>);
impl<'a> Fields<'a> { impl<'a> Fields<'a> {
/// Add another set of fields to this set of fields using the given container. /// Add another set of fields to this set of fields using the given container.
pub fn add_set<T: ContainerType + Send + Sync>( pub fn add_set<T: ContainerType + Send + Sync + ?Sized>(
&mut self, &mut self,
ctx: &ContextSelectionSet<'a>, ctx: &ContextSelectionSet<'a>,
root: &'a T, root: &'a T,