#[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_macro2::Span;
use quote::quote;
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::output_type::OutputType;
@ -18,12 +19,14 @@ pub fn generate(
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 generics = &item_impl.generics;
let generics_params = &generics.params;
let where_clause = &item_impl.generics.where_clause;
let extends = object_args.extends;
let gql_typename = object_args
.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 {
quote! { ::std::option::Option::Some(<Self as #crate_name::Description>::description()) }
@ -466,8 +469,10 @@ pub fn generate(
let block = &method.block;
let new_block = quote!({
{
let value:#inner_ty = async move #block.await;
::std::result::Result::Ok(value)
::std::result::Result::Ok(async move {
let value:#inner_ty = #block;
value
}.await)
}
});
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 expanded = quote! {
#item_impl
#[allow(non_snake_case)]
type #shadow_type<#generics_params> = #self_ty;
#[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> {
::std::borrow::Cow::Borrowed(#gql_typename)
}
@ -566,7 +575,7 @@ pub fn generate(
#[allow(clippy::all, clippy::pedantic, clippy::suspicious_else_formatting)]
#[allow(unused_braces, unused_variables, unused_parens, unused_mut)]
#[#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>> {
#(#resolvers)*
::std::result::Result::Ok(::std::option::Option::None)
@ -592,13 +601,16 @@ pub fn generate(
#[allow(clippy::all, clippy::pedantic)]
#[#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> {
#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())
}

View File

@ -7,7 +7,7 @@ use quote::quote;
use syn::visit::Visit;
use syn::{
Attribute, Error, Expr, ExprPath, Ident, Lit, LitStr, Meta, NestedMeta, Type, TypeGroup,
TypePath,
TypeParamBound,
};
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 {
Type::Path(path) => Ok((
path,
ty,
path.path
.segments
.last()
@ -391,6 +391,19 @@ pub fn get_type_path_and_name(ty: &Type) -> GeneratorResult<(&TypePath, String)>
.unwrap(),
)),
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()),
}
}

View File

@ -1,4 +1,5 @@
use std::borrow::Cow;
use std::sync::Arc;
use crate::parser::types::Field;
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> {
fn type_name() -> Cow<'static, str> {
T::type_name()

View File

@ -316,6 +316,8 @@ pub type FieldResult<T> = Result<T>;
///
/// # Examples
///
/// Implements GraphQL Object for struct.
///
/// ```rust
/// 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;
/// Define a GraphQL object with fields

View File

@ -297,7 +297,7 @@ pub struct 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 f: F,
) -> String {

View File

@ -36,7 +36,7 @@ pub trait ContainerType: OutputType {
fields: &mut Fields<'a>,
) -> ServerResult<()>
where
Self: Sized + Send + Sync,
Self: Send + Sync,
{
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.
pub async fn resolve_container<'a, T: ContainerType + Send + Sync>(
pub async fn resolve_container<'a, T: ContainerType + Send + Sync + ?Sized>(
ctx: &ContextSelectionSet<'a>,
root: &'a T,
) -> 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.
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>,
root: &'a T,
) -> 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>,
root: &'a T,
parallel: bool,
@ -134,7 +134,7 @@ pub struct Fields<'a>(Vec<BoxFieldFuture<'a>>);
impl<'a> Fields<'a> {
/// 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,
ctx: &ContextSelectionSet<'a>,
root: &'a T,