Impl reflect
traits for objects and interfaces [skip ci]
This commit is contained in:
parent
42e2780142
commit
23e01956b3
8 changed files with 241 additions and 7 deletions
|
@ -6,7 +6,7 @@ use crate::{
|
|||
graphql,
|
||||
meta::MetaType,
|
||||
parser::{ParseError, ScalarToken},
|
||||
resolve, Registry,
|
||||
reflect, resolve, Registry,
|
||||
};
|
||||
|
||||
/// Default standard behavior of GraphQL types implementation.
|
||||
|
@ -92,3 +92,39 @@ where
|
|||
T::parse_scalar_token(token)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B1, B2> reflect::BaseType<B1> for Coerce<T, B2>
|
||||
where
|
||||
T: reflect::BaseType<B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
const NAME: reflect::Type = T::NAME;
|
||||
}
|
||||
|
||||
impl<T, B1, B2> reflect::BaseSubTypes<B1> for Coerce<T, B2>
|
||||
where
|
||||
T: reflect::BaseSubTypes<B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
const NAMES: reflect::Types = T::NAMES;
|
||||
}
|
||||
|
||||
impl<T, B1, B2> reflect::WrappedType<B1> for Coerce<T, B2>
|
||||
where
|
||||
T: reflect::WrappedType<B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
const VALUE: reflect::WrappedValue = T::VALUE;
|
||||
}
|
||||
|
||||
impl<T, B1, B2> reflect::Implements<B1> for Coerce<T, B2>
|
||||
where
|
||||
T: reflect::Implements<B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
const NAMES: reflect::Types = T::NAMES;
|
||||
}
|
||||
|
|
|
@ -123,6 +123,7 @@ fn expand_on_trait(
|
|||
description: attr.description.as_deref().cloned(),
|
||||
context,
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
fields,
|
||||
implemented_for: attr
|
||||
.implemented_for
|
||||
|
@ -304,6 +305,7 @@ fn expand_on_derive_input(
|
|||
description: attr.description.as_deref().cloned(),
|
||||
context,
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
fields,
|
||||
implemented_for: attr
|
||||
.implemented_for
|
||||
|
|
|
@ -96,6 +96,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
|||
description: attr.description.as_deref().cloned(),
|
||||
context,
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
fields,
|
||||
implemented_for: attr
|
||||
.implemented_for
|
||||
|
|
|
@ -21,7 +21,7 @@ use syn::{
|
|||
|
||||
use crate::{
|
||||
common::{
|
||||
field, gen,
|
||||
behavior, field, gen,
|
||||
parse::{
|
||||
attr::{err, OptionExt as _},
|
||||
GenericsExt as _, ParseBufferExt as _,
|
||||
|
@ -111,6 +111,17 @@ struct Attr {
|
|||
/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||
|
||||
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||
/// [GraphQL interface][0] implementation with.
|
||||
///
|
||||
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||
/// code.
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified marker indicating that the Rust trait should be
|
||||
/// transformed into [`async_trait`].
|
||||
///
|
||||
|
@ -173,6 +184,13 @@ impl Parse for Attr {
|
|||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"behave" | "behavior" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let bh = input.parse::<behavior::Type>()?;
|
||||
out.behavior
|
||||
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"for" | "implementers" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
for impler in input.parse_maybe_wrapped_and_punctuated::<
|
||||
|
@ -231,6 +249,7 @@ impl Attr {
|
|||
description: try_merge_opt!(description: self, another),
|
||||
context: try_merge_opt!(context: self, another),
|
||||
scalar: try_merge_opt!(scalar: self, another),
|
||||
behavior: try_merge_opt!(behavior: self, another),
|
||||
implemented_for: try_merge_hashset!(implemented_for: self, another => span_joined),
|
||||
r#enum: try_merge_opt!(r#enum: self, another),
|
||||
asyncness: try_merge_opt!(asyncness: self, another),
|
||||
|
@ -309,6 +328,13 @@ struct Definition {
|
|||
/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
scalar: scalar::Type,
|
||||
|
||||
/// [`Behavior`] parametrization to generate code with for this
|
||||
/// [GraphQL interface][0].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||
behavior: behavior::Type,
|
||||
|
||||
/// Defined [GraphQL fields][2] of this [GraphQL interface][1].
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/June2018/#sec-Interfaces
|
||||
|
@ -347,6 +373,8 @@ impl ToTokens for Definition {
|
|||
self.impl_field_meta_tokens().to_tokens(into);
|
||||
self.impl_field_tokens().to_tokens(into);
|
||||
self.impl_async_field_tokens().to_tokens(into);
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
self.impl_reflect().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -847,6 +875,59 @@ impl Definition {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`reflect::BaseType`],
|
||||
/// [`reflect::BaseSubTypes`], [`reflect::WrappedType`] and
|
||||
/// [`reflect::Fields`] traits for this [GraphQL interface][0].
|
||||
///
|
||||
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||
/// [`reflect::Fields`]: juniper::reflect::Fields
|
||||
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Interfaces
|
||||
fn impl_reflect(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let name = &self.name;
|
||||
let implers = &self.implemented_for;
|
||||
let fields = self.fields.iter().map(|f| &f.name);
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::BaseType<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAME: ::juniper::reflect::Type = #name;
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::BaseSubTypes<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Types = &[
|
||||
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||
#( <#implers as ::juniper::reflect::BaseType<#bh>>::NAME ),*
|
||||
];
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const VALUE: ::juniper::reflect::WrappedValue =
|
||||
::juniper::reflect::wrap::SINGULAR;
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::Fields<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Names = &[#( #fields ),*];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`FieldMeta`] for each field of this
|
||||
/// [GraphQL interface][1].
|
||||
///
|
||||
|
@ -1267,6 +1348,20 @@ impl Definition {
|
|||
generics
|
||||
}
|
||||
|
||||
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||
/// implementation.
|
||||
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||
let generics = self.generics.clone();
|
||||
|
||||
let ty = {
|
||||
let ident = &self.enum_alias_ident;
|
||||
let (_, ty_gen, _) = generics.split_for_impl();
|
||||
parse_quote! { #ident#ty_gen }
|
||||
};
|
||||
|
||||
(ty, generics)
|
||||
}
|
||||
|
||||
/// Indicates whether this enum has non-exhaustive phantom variant to hold
|
||||
/// type parameters.
|
||||
#[must_use]
|
||||
|
|
|
@ -121,6 +121,7 @@ where
|
|||
description: attr.description.map(SpanContainer::into_inner),
|
||||
context,
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
fields,
|
||||
interfaces: attr
|
||||
.interfaces
|
||||
|
|
|
@ -98,6 +98,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
|
|||
.map(SpanContainer::into_inner)
|
||||
.unwrap_or_else(|| parse_quote! { () }),
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
fields,
|
||||
interfaces: attr
|
||||
.interfaces
|
||||
|
|
|
@ -18,7 +18,7 @@ use syn::{
|
|||
|
||||
use crate::{
|
||||
common::{
|
||||
field, gen,
|
||||
behavior, field, gen,
|
||||
parse::{
|
||||
attr::{err, OptionExt as _},
|
||||
GenericsExt as _, ParseBufferExt as _, TypeExt,
|
||||
|
@ -73,6 +73,17 @@ pub(crate) struct Attr {
|
|||
/// [1]: https://spec.graphql.org/June2018/#sec-Objects
|
||||
pub(crate) scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||
|
||||
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||
/// [GraphQL object][0] type implementation with.
|
||||
///
|
||||
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||
/// code.
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified [GraphQL interfaces][2] this [GraphQL object][1]
|
||||
/// type implements.
|
||||
///
|
||||
|
@ -135,6 +146,13 @@ impl Parse for Attr {
|
|||
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"behave" | "behavior" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let bh = input.parse::<behavior::Type>()?;
|
||||
out.behavior
|
||||
.replace(SpanContainer::new(ident.span(), Some(bh.span()), bh))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"impl" | "implements" | "interfaces" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
for iface in input.parse_maybe_wrapped_and_punctuated::<
|
||||
|
@ -180,6 +198,7 @@ impl Attr {
|
|||
description: try_merge_opt!(description: self, another),
|
||||
context: try_merge_opt!(context: self, another),
|
||||
scalar: try_merge_opt!(scalar: self, another),
|
||||
behavior: try_merge_opt!(behavior: self, another),
|
||||
interfaces: try_merge_hashset!(interfaces: self, another => span_joined),
|
||||
rename_fields: try_merge_opt!(rename_fields: self, another),
|
||||
is_internal: self.is_internal || another.is_internal,
|
||||
|
@ -245,6 +264,13 @@ pub(crate) struct Definition<Operation: ?Sized> {
|
|||
/// [1]: https://spec.graphql.org/June2018/#sec-Objects
|
||||
pub(crate) scalar: scalar::Type,
|
||||
|
||||
/// [`Behavior`] parametrization to generate code with for this
|
||||
/// [GraphQL object][0].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||
pub(crate) behavior: behavior::Type,
|
||||
|
||||
/// Defined [GraphQL fields][2] of this [GraphQL object][1].
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/June2018/#sec-Objects
|
||||
|
@ -329,6 +355,13 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
|||
(quote! { #impl_generics }, where_clause.cloned())
|
||||
}
|
||||
|
||||
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||
/// implementation.
|
||||
#[must_use]
|
||||
fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) {
|
||||
(&self.ty, self.generics.clone())
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`marker::IsOutputType`] trait for
|
||||
/// this [GraphQL object][1].
|
||||
///
|
||||
|
@ -423,6 +456,69 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`reflect::BaseType`],
|
||||
/// [`reflect::BaseSubTypes`], [`reflect::Implements`],
|
||||
/// [`reflect::WrappedType`] and [`reflect::Fields`] traits for this
|
||||
/// [GraphQL object][0].
|
||||
///
|
||||
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||
/// [`reflect::Fields`]: juniper::reflect::Fields
|
||||
/// [`reflect::Implements`]: juniper::reflect::Implements
|
||||
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||
#[must_use]
|
||||
pub(crate) fn impl_reflect(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let name = &self.name;
|
||||
let interfaces = self.interfaces.iter();
|
||||
let fields = self.fields.iter().map(|f| &f.name);
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::BaseType<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAME: ::juniper::reflect::Type = #name;
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::BaseSubTypes<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Types =
|
||||
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::Implements<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Types = &[#(
|
||||
<#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||
),*];
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const VALUE: ::juniper::reflect::WrappedValue =
|
||||
::juniper::reflect::wrap::SINGULAR;
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::Fields<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Names = &[#( #fields ),*];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||
/// [GraphQL object][1].
|
||||
///
|
||||
|
@ -504,6 +600,8 @@ impl ToTokens for Definition<Query> {
|
|||
self.impl_field_meta_tokens().to_tokens(into);
|
||||
self.impl_field_tokens().to_tokens(into);
|
||||
self.impl_async_field_tokens().to_tokens(into);
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
self.impl_reflect().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -971,9 +971,9 @@ impl Definition {
|
|||
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||
/// [GraphQL scalar][0].
|
||||
///
|
||||
/// [`reflect::BaseSubTypes`]: juniper::reflection::BaseSubTypes
|
||||
/// [`reflect::BaseType`]: juniper::reflection::BaseType
|
||||
/// [`reflect::WrappedType`]: juniper::reflection::WrappedType
|
||||
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn impl_reflect(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
|
@ -1087,7 +1087,7 @@ impl Definition {
|
|||
|
||||
let ty = {
|
||||
let ident = &self.ident;
|
||||
let (_, ty_gen, _) = self.generics.split_for_impl();
|
||||
let (_, ty_gen, _) = generics.split_for_impl();
|
||||
parse_quote! { #ident#ty_gen }
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue