Impl reflect traits for objects and interfaces fields, vol.1 [skip ci]

This commit is contained in:
tyranron 2022-06-23 18:37:14 +02:00
parent 23e01956b3
commit 1819ab6e12
No known key found for this signature in database
GPG key ID: 762E144FB230A4F0
7 changed files with 132 additions and 0 deletions

View file

@ -16,6 +16,7 @@ use syn::{
use crate::{
common::{
behavior,
parse::{
attr::{err, OptionExt as _},
ParseBufferExt as _, TypeExt as _,
@ -58,6 +59,19 @@ pub(crate) struct Attr {
/// [2]: https://spec.graphql.org/June2018/#sec-Required-Arguments
pub(crate) default: Option<SpanContainer<Option<syn::Expr>>>,
/// Explicitly specified type of the custom [`Behavior`] this
/// [GraphQL argument][0] implementation is parametrized with, to [coerce]
/// in the generated code from.
///
/// 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-Language.Arguments
/// [coerce]: juniper::behavior::Coerce
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
/// Explicitly specified marker indicating that this method argument doesn't
/// represent a [GraphQL argument][1], but is a [`Context`] being injected
/// into a [GraphQL field][2] resolving function.
@ -121,6 +135,13 @@ impl Parse for Attr {
))
.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))?
}
"ctx" | "context" | "Context" => {
let span = ident.span();
out.context
@ -151,6 +172,7 @@ impl Attr {
name: try_merge_opt!(name: self, another),
description: try_merge_opt!(description: self, another),
default: try_merge_opt!(default: self, another),
behavior: try_merge_opt!(behavior: self, another),
context: try_merge_opt!(context: self, another),
executor: try_merge_opt!(executor: self, another),
})
@ -253,6 +275,14 @@ pub(crate) struct OnField {
/// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments
/// [2]: https://spec.graphql.org/June2018/#sec-Required-Arguments
pub(crate) default: Option<Option<syn::Expr>>,
/// [`Behavior`] parametrization of this [GraphQL argument][0]
/// implementation to [coerce] from in the generated code.
///
/// [`Behavior`]: juniper::behavior
/// [0]: https://spec.graphql.org/October2021#sec-Language.Arguments
/// [coerce]: juniper::behavior::Coerce
pub(crate) behavior: behavior::Type,
}
/// Possible kinds of Rust method arguments for code generation.
@ -464,6 +494,7 @@ impl OnMethod {
ty: argument.ty.as_ref().clone(),
description: attr.description.as_ref().map(|d| d.as_ref().value()),
default: attr.default.as_ref().map(|v| v.as_ref().clone()),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
})))
}
}

View file

@ -16,6 +16,7 @@ use syn::{
use crate::{
common::{
behavior,
parse::{
attr::{err, OptionExt as _},
ParseBufferExt as _,
@ -58,6 +59,19 @@ pub(crate) struct Attr {
/// [2]: https://spec.graphql.org/June2018/#sec-Deprecation
pub(crate) deprecated: Option<SpanContainer<Option<syn::LitStr>>>,
/// Explicitly specified type of the custom [`Behavior`] this
/// [GraphQL field][0] implementation is parametrized with, to [coerce] in
/// the generated code from.
///
/// 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-Language.Fields
/// [coerce]: juniper::behavior::Coerce
pub(crate) behavior: Option<SpanContainer<behavior::Type>>,
/// Explicitly specified marker indicating that this method (or struct
/// field) should be omitted by code generation and not considered as the
/// [GraphQL field][1] definition.
@ -100,6 +114,13 @@ impl Parse for Attr {
))
.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))?
}
"ignore" | "skip" => out
.ignore
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
@ -122,6 +143,7 @@ impl Attr {
name: try_merge_opt!(name: self, another),
description: try_merge_opt!(description: self, another),
deprecated: try_merge_opt!(deprecated: self, another),
behavior: try_merge_opt!(behavior: self, another),
ignore: try_merge_opt!(ignore: self, another),
})
}
@ -199,6 +221,14 @@ pub(crate) struct Definition {
/// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields
pub(crate) ident: syn::Ident,
/// [`Behavior`] parametrization of this [GraphQL field][0] implementation
/// to [coerce] from in the generated code.
///
/// [`Behavior`]: juniper::behavior
/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields
/// [coerce]: juniper::behavior::Coerce
pub(crate) behavior: behavior::Type,
/// Rust [`MethodArgument`]s required to call the method representing this
/// [GraphQL field][1].
///

View file

@ -212,6 +212,7 @@ fn parse_trait_method(
description,
deprecated,
ident: method_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
arguments: Some(arguments),
has_receiver: method.sig.receiver().is_some(),
is_async: method.sig.asyncness.is_some(),
@ -375,6 +376,7 @@ fn parse_struct_field(field: &mut syn::Field, renaming: &RenameRule) -> Option<f
description,
deprecated,
ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
arguments: None,
has_receiver: false,
is_async: false,

View file

@ -154,6 +154,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option<field::Defin
description,
deprecated,
ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
arguments: None,
has_receiver: false,
is_async: false,

View file

@ -229,6 +229,7 @@ fn parse_field(
description,
deprecated,
ident: method_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
arguments: Some(arguments),
has_receiver: method.sig.receiver().is_some(),
is_async: method.sig.asyncness.is_some(),

View file

@ -154,6 +154,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option<field::Defin
description,
deprecated,
ident: field_ident.clone(),
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
arguments: None,
has_receiver: false,
is_async: false,

View file

@ -519,6 +519,70 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
}
}
/// Returns generated code implementing [`reflect::Field`] trait for each
/// [field][1] of this [GraphQL object][0].
///
/// [`reflect::Field`]: juniper::reflect::Field
/// [0]: https://spec.graphql.org/October2021#sec-Objects
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
#[must_use]
pub(crate) fn impl_reflect_field(&self) -> TokenStream {
let bh = &self.behavior;
let (ty, generics) = self.ty_and_generics();
let (impl_gens, _, where_clause) = generics.split_for_impl();
self.fields
.iter()
.map(|field| {
let (f_name, f_ty, f_bh) = (&field.name, &field.ty, &field.behavior);
let arguments = field
.arguments
.as_ref()
.iter()
.flat_map(|vec| vec.iter().filter_map(field::MethodArgument::as_regular))
.map(|arg| {
let (a_name, a_ty, a_bh) = (&arg.name, &arg.ty, &arg.behavior);
quote! {(
#a_name,
<#a_ty as ::juniper::reflect::BaseType<#a_bh>>
::NAME,
<#a_ty as ::juniper::reflect::WrappedType<#a_bh>>
::VALUE,
)}
})
.collect::<Vec<_>>();
quote! {
#[allow(deprecated, non_snake_case)]
#[automatically_derived]
impl#impl_gens ::juniper::reflect::Field<
{ ::juniper::reflect::fnv1a128(#f_name) }, #bh,
> for #ty #where_clause {
const TYPE: ::juniper::reflect::Type =
<#f_ty as ::juniper::reflect::BaseType<#f_bh>>
::NAME;
const SUB_TYPES: ::juniper::reflect::Types =
<#f_ty as ::juniper::reflect::BaseSubTypes<#f_bh>>
::NAMES;
const WRAPPED_VALUE: juniper::reflect::WrappedValue =
<#f_ty as ::juniper::reflect::WrappedType<#f_bh>>
::VALUE;
const ARGUMENTS: &'static [(
::juniper::reflect::Name,
::juniper::reflect::Type,
::juniper::reflect::WrappedValue,
)] = &[#( #arguments ),*];
}
}
})
.collect()
}
/// Returns generated code implementing [`GraphQLType`] trait for this
/// [GraphQL object][1].
///
@ -602,6 +666,8 @@ impl ToTokens for Definition<Query> {
self.impl_async_field_tokens().to_tokens(into);
////////////////////////////////////////////////////////////////////////
self.impl_reflect().to_tokens(into);
self.impl_reflect_field().to_tokens(into);
//self.impl_resolve_field_static().to_tokens(into);
}
}