Add support for generic SimpleObject. #387

This commit is contained in:
Sunli 2021-01-14 13:16:30 +08:00
parent b054fc0704
commit 85a0ab0da1
2 changed files with 149 additions and 47 deletions

View File

@ -153,54 +153,126 @@ pub fn generate(object_args: &args::SimpleObject) -> GeneratorResult<TokenStream
let visible = visible_fn(&object_args.visible); let visible = visible_fn(&object_args.visible);
let expanded = quote! { let expanded = if object_args.concretes.is_empty() {
#[allow(clippy::all, clippy::pedantic)] quote! {
impl #impl_generics #ident #ty_generics #where_clause { #[allow(clippy::all, clippy::pedantic)]
#(#getters)* impl #impl_generics #ident #ty_generics #where_clause {
} #(#getters)*
#[allow(clippy::all, clippy::pedantic)]
impl #impl_generics #crate_name::Type for #ident #ty_generics #where_clause {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed(#gql_typename)
} }
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String { #[allow(clippy::all, clippy::pedantic)]
registry.create_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object { impl #impl_generics #crate_name::Type for #ident #ty_generics #where_clause {
name: ::std::borrow::ToOwned::to_owned(#gql_typename), fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
description: #desc, ::std::borrow::Cow::Borrowed(#gql_typename)
fields: { }
let mut fields = #crate_name::indexmap::IndexMap::new();
#(#schema_fields)* fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
fields registry.create_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
}, name: ::std::borrow::ToOwned::to_owned(#gql_typename),
cache_control: #cache_control, description: #desc,
extends: #extends, fields: {
keys: ::std::option::Option::None, let mut fields = #crate_name::indexmap::IndexMap::new();
visible: #visible, #(#schema_fields)*
}) fields
},
cache_control: #cache_control,
extends: #extends,
keys: ::std::option::Option::None,
visible: #visible,
})
}
} }
#[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::resolver_utils::ContainerType for #ident #ty_generics #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)
}
}
#[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::OutputType for #ident #ty_generics #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 #impl_generics #crate_name::ObjectType for #ident #ty_generics #where_clause {}
}
} else {
let mut code = Vec::new();
code.push(quote! {
impl #impl_generics #ident #ty_generics #where_clause {
#(#getters)*
fn __internal_create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String where Self: #crate_name::OutputType {
registry.create_type::<Self, _>(|registry| #crate_name::registry::MetaType::Object {
name: ::std::borrow::ToOwned::to_owned(#gql_typename),
description: #desc,
fields: {
let mut fields = #crate_name::indexmap::IndexMap::new();
#(#schema_fields)*
fields
},
cache_control: #cache_control,
extends: #extends,
keys: ::std::option::Option::None,
visible: #visible,
})
}
async fn __internal_resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> where Self: #crate_name::ContainerType {
#(#resolvers)*
::std::result::Result::Ok(::std::option::Option::None)
}
}
});
for concrete in &object_args.concretes {
let gql_typename = &concrete.name;
let params = &concrete.params.0;
let concrete_type = quote! { #ident<#(#params),*> };
let expanded = quote! {
#[allow(clippy::all, clippy::pedantic)]
impl #crate_name::Type for #concrete_type {
fn type_name() -> ::std::borrow::Cow<'static, ::std::primitive::str> {
::std::borrow::Cow::Borrowed(#gql_typename)
}
fn create_type_info(registry: &mut #crate_name::registry::Registry) -> ::std::string::String {
Self::__internal_create_type_info(registry)
}
}
#[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait]
impl #crate_name::resolver_utils::ContainerType for #concrete_type {
async fn resolve_field(&self, ctx: &#crate_name::Context<'_>) -> #crate_name::ServerResult<::std::option::Option<#crate_name::Value>> {
self.__internal_resolve_field(ctx).await
}
}
#[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait]
impl #crate_name::OutputType for #concrete_type {
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 #crate_name::ObjectType for #concrete_type {}
};
code.push(expanded);
} }
#[allow(clippy::all, clippy::pedantic)] quote!(#(#code)*)
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::resolver_utils::ContainerType for #ident #ty_generics #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)
}
}
#[allow(clippy::all, clippy::pedantic)]
#[#crate_name::async_trait::async_trait]
impl #impl_generics #crate_name::OutputType for #ident #ty_generics #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 #impl_generics #crate_name::ObjectType for #ident #ty_generics #where_clause {}
}; };
Ok(expanded.into()) Ok(expanded.into())
} }

View File

@ -90,12 +90,42 @@ pub async fn test_input_object_generic() {
#[async_std::test] #[async_std::test]
pub async fn test_generic_simple_object() { pub async fn test_generic_simple_object() {
#[derive(SimpleObject)] #[derive(SimpleObject)]
#[graphql( #[graphql(concrete(name = "MyObjIntString", params(i32, String)))]
concrete(name = "IntEqualityFilter", params(i32)), #[graphql(concrete(name = "MyObji64f32", params(i64, u8)))]
concrete(name = "StringEqualityFilter", params(String))
)]
struct MyObj<A: OutputType, B: OutputType> { struct MyObj<A: OutputType, B: OutputType> {
a: A, a: A,
b: B, b: B,
} }
struct Query;
#[Object]
impl Query {
async fn q1(&self) -> MyObj<i32, String> {
MyObj {
a: 100,
b: "abc".to_string(),
}
}
async fn q2(&self) -> MyObj<i64, u8> {
MyObj { a: 100, b: 28 }
}
}
let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
let query = "{ q1 { a b } q2 { a b } }";
assert_eq!(
schema.execute(query).await.into_result().unwrap().data,
value!({
"q1": {
"a": 100,
"b": "abc",
},
"q2": {
"a": 100,
"b": 28,
}
})
);
} }