Impl reflect traits for enums

This commit is contained in:
tyranron 2022-06-27 15:14:05 +02:00
parent 505e25cea2
commit 48631e9b25
No known key found for this signature in database
GPG key ID: 762E144FB230A4F0
12 changed files with 111 additions and 17 deletions

View file

@ -10,6 +10,8 @@ use syn::{
parse_quote, parse_quote,
}; };
use crate::util::span_container::SpanContainer;
/// [`Behaviour`] parametrization of the code generation. /// [`Behaviour`] parametrization of the code generation.
/// ///
/// [`Behaviour`]: juniper::behavior /// [`Behaviour`]: juniper::behavior
@ -55,3 +57,9 @@ impl Type {
} }
} }
} }
impl From<Option<SpanContainer<Self>>> for Type {
fn from(attr: Option<SpanContainer<Self>>) -> Self {
attr.map(SpanContainer::into_inner).unwrap_or_default()
}
}

View file

@ -494,7 +494,7 @@ impl OnMethod {
ty: argument.ty.as_ref().clone(), ty: argument.ty.as_ref().clone(),
description: attr.description.as_ref().map(|d| d.as_ref().value()), description: attr.description.as_ref().map(|d| d.as_ref().value()),
default: attr.default.as_ref().map(|v| v.as_ref().clone()), default: attr.default.as_ref().map(|v| v.as_ref().clone()),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
}))) })))
} }
} }

View file

@ -92,6 +92,7 @@ pub(crate) fn expand(input: TokenStream) -> syn::Result<TokenStream> {
description, description,
context, context,
scalar, scalar,
behavior: attr.behavior.into(),
values, values,
has_ignored_variants, has_ignored_variants,
}; };

View file

@ -18,6 +18,7 @@ use syn::{
use crate::{ use crate::{
common::{ common::{
behavior,
parse::{ parse::{
attr::{err, OptionExt as _}, attr::{err, OptionExt as _},
ParseBufferExt as _, ParseBufferExt as _,
@ -71,6 +72,17 @@ struct ContainerAttr {
/// [0]: https://spec.graphql.org/October2021#sec-Enums /// [0]: https://spec.graphql.org/October2021#sec-Enums
scalar: Option<SpanContainer<scalar::AttrValue>>, scalar: Option<SpanContainer<scalar::AttrValue>>,
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
/// [GraphQL enum][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-Enums
behavior: Option<SpanContainer<behavior::Type>>,
/// Explicitly specified [`RenameRule`] for all [values][1] of this /// Explicitly specified [`RenameRule`] for all [values][1] of this
/// [GraphQL enum][0]. /// [GraphQL enum][0].
/// ///
@ -128,6 +140,13 @@ impl Parse for ContainerAttr {
.replace(SpanContainer::new(ident.span(), Some(scl.span()), scl)) .replace(SpanContainer::new(ident.span(), Some(scl.span()), scl))
.none_or_else(|_| err::dup_arg(&ident))? .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))?
}
"rename_all" => { "rename_all" => {
input.parse::<token::Eq>()?; input.parse::<token::Eq>()?;
let val = input.parse::<syn::LitStr>()?; let val = input.parse::<syn::LitStr>()?;
@ -161,6 +180,7 @@ impl ContainerAttr {
description: try_merge_opt!(description: self, another), description: try_merge_opt!(description: self, another),
context: try_merge_opt!(context: self, another), context: try_merge_opt!(context: self, another),
scalar: try_merge_opt!(scalar: self, another), scalar: try_merge_opt!(scalar: self, another),
behavior: try_merge_opt!(behavior: self, another),
rename_values: try_merge_opt!(rename_values: self, another), rename_values: try_merge_opt!(rename_values: self, another),
is_internal: self.is_internal || another.is_internal, is_internal: self.is_internal || another.is_internal,
}) })
@ -389,6 +409,13 @@ struct Definition {
/// [0]: https://spec.graphql.org/October2021#sec-Enums /// [0]: https://spec.graphql.org/October2021#sec-Enums
scalar: scalar::Type, scalar: scalar::Type,
/// [`Behavior`] parametrization to generate code with for this
/// [GraphQL enum][0].
///
/// [`Behavior`]: juniper::behavior
/// [0]: https://spec.graphql.org/October2021#sec-Enums
behavior: behavior::Type,
/// [Values][1] of this [GraphQL enum][0]. /// [Values][1] of this [GraphQL enum][0].
/// ///
/// [0]: https://spec.graphql.org/October2021#sec-Enums /// [0]: https://spec.graphql.org/October2021#sec-Enums
@ -411,6 +438,8 @@ impl ToTokens for Definition {
self.impl_from_input_value_tokens().to_tokens(into); self.impl_from_input_value_tokens().to_tokens(into);
self.impl_to_input_value_tokens().to_tokens(into); self.impl_to_input_value_tokens().to_tokens(into);
self.impl_reflection_traits_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into);
////////////////////////////////////////////////////////////////////////
self.impl_reflect().to_tokens(into);
} }
} }
@ -729,6 +758,47 @@ impl Definition {
} }
} }
/// Returns generated code implementing [`reflect::BaseType`],
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
/// [GraphQL enum][0].
///
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
/// [`reflect::BaseType`]: juniper::reflect::BaseType
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
/// [0]: https://spec.graphql.org/October2021#sec-Enums
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;
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::WrappedType<#bh> for #ty
#where_clause
{
const VALUE: ::juniper::reflect::WrappedValue =
::juniper::reflect::wrap::SINGULAR;
}
}
}
/// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and
/// similar) implementation of this enum. /// similar) implementation of this enum.
/// ///
@ -787,4 +857,19 @@ impl Definition {
generics generics
} }
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
/// implementation.
#[must_use]
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
let generics = self.generics.clone();
let ty = {
let ident = &self.ident;
let (_, ty_gen, _) = generics.split_for_impl();
parse_quote! { #ident#ty_gen }
};
(ty, generics)
}
} }

View file

@ -124,7 +124,7 @@ fn expand_on_trait(
description: attr.description.map(|d| d.into_inner().into_boxed_str()), description: attr.description.map(|d| d.into_inner().into_boxed_str()),
context, context,
scalar, scalar,
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
fields, fields,
implemented_for: attr implemented_for: attr
.implemented_for .implemented_for
@ -218,7 +218,7 @@ fn parse_trait_method(
description, description,
deprecated, deprecated,
ident: method_ident.clone(), ident: method_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
arguments: Some(arguments), arguments: Some(arguments),
has_receiver: method.sig.receiver().is_some(), has_receiver: method.sig.receiver().is_some(),
is_async: method.sig.asyncness.is_some(), is_async: method.sig.asyncness.is_some(),
@ -313,7 +313,7 @@ fn expand_on_derive_input(
description: attr.description.map(|d| d.into_inner().into_boxed_str()), description: attr.description.map(|d| d.into_inner().into_boxed_str()),
context, context,
scalar, scalar,
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
fields, fields,
implemented_for: attr implemented_for: attr
.implemented_for .implemented_for
@ -388,7 +388,7 @@ fn parse_struct_field(field: &mut syn::Field, renaming: &RenameRule) -> Option<f
description, description,
deprecated, deprecated,
ident: field_ident.clone(), ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
arguments: None, arguments: None,
has_receiver: false, has_receiver: false,
is_async: false, is_async: false,

View file

@ -97,7 +97,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
description: attr.description.map(|d| d.into_inner().into_boxed_str()), description: attr.description.map(|d| d.into_inner().into_boxed_str()),
context, context,
scalar, scalar,
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
fields, fields,
implemented_for: attr implemented_for: attr
.implemented_for .implemented_for
@ -160,7 +160,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option<field::Defin
description, description,
deprecated, deprecated,
ident: field_ident.clone(), ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
arguments: None, arguments: None,
has_receiver: false, has_receiver: false,
is_async: false, is_async: false,

View file

@ -121,7 +121,7 @@ where
description: attr.description.map(SpanContainer::into_inner), description: attr.description.map(SpanContainer::into_inner),
context, context,
scalar, scalar,
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
fields, fields,
interfaces: attr interfaces: attr
.interfaces .interfaces
@ -229,7 +229,7 @@ fn parse_field(
description, description,
deprecated, deprecated,
ident: method_ident.clone(), ident: method_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
arguments: Some(arguments), arguments: Some(arguments),
has_receiver: method.sig.receiver().is_some(), has_receiver: method.sig.receiver().is_some(),
is_async: method.sig.asyncness.is_some(), is_async: method.sig.asyncness.is_some(),

View file

@ -98,7 +98,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition<Query>> {
.map(SpanContainer::into_inner) .map(SpanContainer::into_inner)
.unwrap_or_else(|| parse_quote! { () }), .unwrap_or_else(|| parse_quote! { () }),
scalar, scalar,
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
fields, fields,
interfaces: attr interfaces: attr
.interfaces .interfaces
@ -154,7 +154,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option<field::Defin
description, description,
deprecated, deprecated,
ident: field_ident.clone(), ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
arguments: None, arguments: None,
has_receiver: false, has_receiver: false,
is_async: false, is_async: false,

View file

@ -74,7 +74,7 @@ pub(crate) struct Attr {
pub(crate) scalar: Option<SpanContainer<scalar::AttrValue>>, pub(crate) scalar: Option<SpanContainer<scalar::AttrValue>>,
/// Explicitly specified type of the custom [`Behavior`] to parametrize this /// Explicitly specified type of the custom [`Behavior`] to parametrize this
/// [GraphQL object][0] type implementation with. /// [GraphQL object][0] implementation with.
/// ///
/// If [`None`], then [`behavior::Standard`] will be used for the generated /// If [`None`], then [`behavior::Standard`] will be used for the generated
/// code. /// code.

View file

@ -64,7 +64,7 @@ fn expand_on_type_alias(
specified_by_url: attr.specified_by_url.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(),
scalar, scalar,
scalar_value: attr.scalar.as_deref().into(), scalar_value: attr.scalar.as_deref().into(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
}; };
Ok(quote! { Ok(quote! {
@ -99,7 +99,7 @@ fn expand_on_derive_input(
specified_by_url: attr.specified_by_url.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(),
scalar, scalar,
scalar_value: attr.scalar.as_deref().into(), scalar_value: attr.scalar.as_deref().into(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
}; };
Ok(quote! { Ok(quote! {

View file

@ -38,7 +38,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
specified_by_url: attr.specified_by_url.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(),
scalar, scalar,
scalar_value: attr.scalar.as_deref().into(), scalar_value: attr.scalar.as_deref().into(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), behavior: attr.behavior.into(),
} }
.to_token_stream()) .to_token_stream())
} }

View file

@ -67,7 +67,7 @@ struct Attr {
scalar: Option<SpanContainer<scalar::AttrValue>>, scalar: Option<SpanContainer<scalar::AttrValue>>,
/// Explicitly specified type of the custom [`Behavior`] to parametrize this /// Explicitly specified type of the custom [`Behavior`] to parametrize this
/// [GraphQL scalar][0] type implementation with. /// [GraphQL scalar][0] implementation with.
/// ///
/// If [`None`], then [`behavior::Standard`] will be used for the generated /// If [`None`], then [`behavior::Standard`] will be used for the generated
/// code. /// code.
@ -1721,7 +1721,7 @@ impl ParseToken {
#[derive(Debug, Default)] #[derive(Debug, Default)]
struct FieldAttr { struct FieldAttr {
/// Explicitly specified type of the custom [`Behavior`] used for /// Explicitly specified type of the custom [`Behavior`] used for
/// [GraphQL scalar][0] implementation by the [`Field`]. /// [GraphQL scalar][0] implementation by this [`Field`].
/// ///
/// If [`None`], then [`behavior::Standard`] will be used for the generated /// If [`None`], then [`behavior::Standard`] will be used for the generated
/// code. /// code.