Impl input objects, vol.1 [skip ci]
This commit is contained in:
parent
d8813aff13
commit
464ff10c14
3 changed files with 233 additions and 22 deletions
|
@ -64,6 +64,17 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, SV, B1, B2> resolve::ToInputValue<SV, B1> for Coerce<T, B2>
|
||||
where
|
||||
T: resolve::ToInputValue<SV, B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
fn to_input_value(&self) -> graphql::InputValue<SV> {
|
||||
self.1.to_input_value()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, T, SV, B1, B2> resolve::InputValue<'i, SV, B1> for Coerce<T, B2>
|
||||
where
|
||||
T: resolve::InputValue<'i, SV, B2>,
|
||||
|
|
|
@ -80,6 +80,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
|||
description: attr.description.map(SpanContainer::into_inner),
|
||||
context,
|
||||
scalar,
|
||||
behavior: attr.behavior.into(),
|
||||
fields,
|
||||
};
|
||||
|
||||
|
@ -94,13 +95,13 @@ fn parse_field(
|
|||
renaming: rename::Policy,
|
||||
is_internal: bool,
|
||||
) -> Option<FieldDefinition> {
|
||||
let field_attr = FieldAttr::from_attrs("graphql", &f.attrs)
|
||||
let attr = FieldAttr::from_attrs("graphql", &f.attrs)
|
||||
.map_err(|e| proc_macro_error::emit_error!(e))
|
||||
.ok()?;
|
||||
|
||||
let ident = f.ident.as_ref().or_else(|| err_unnamed_field(f))?;
|
||||
|
||||
let name = field_attr
|
||||
let name = attr
|
||||
.name
|
||||
.map_or_else(
|
||||
|| renaming.apply(&ident.unraw().to_string()),
|
||||
|
@ -114,10 +115,11 @@ fn parse_field(
|
|||
Some(FieldDefinition {
|
||||
ident: ident.clone(),
|
||||
ty: f.ty.clone(),
|
||||
default: field_attr.default.map(SpanContainer::into_inner),
|
||||
default: attr.default.map(SpanContainer::into_inner),
|
||||
behavior: attr.behavior.into(),
|
||||
name,
|
||||
description: field_attr.description.map(SpanContainer::into_inner),
|
||||
ignored: field_attr.ignore.is_some(),
|
||||
description: attr.description.map(SpanContainer::into_inner),
|
||||
ignored: attr.ignore.is_some(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ use syn::{
|
|||
};
|
||||
|
||||
use crate::common::{
|
||||
default, filter_attrs,
|
||||
behavior, default, filter_attrs,
|
||||
parse::{
|
||||
attr::{err, OptionExt as _},
|
||||
ParseBufferExt as _,
|
||||
|
@ -66,6 +66,17 @@ struct ContainerAttr {
|
|||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||
|
||||
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||
/// [GraphQL input object][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-Input-Objects
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified [`rename::Policy`] for all fields of this
|
||||
/// [GraphQL input object][0].
|
||||
///
|
||||
|
@ -118,6 +129,13 @@ impl Parse for ContainerAttr {
|
|||
.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))?
|
||||
}
|
||||
"rename_all" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let val = input.parse::<syn::LitStr>()?;
|
||||
|
@ -151,6 +169,7 @@ impl ContainerAttr {
|
|||
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),
|
||||
rename_fields: try_merge_opt!(rename_fields: self, another),
|
||||
is_internal: self.is_internal || another.is_internal,
|
||||
})
|
||||
|
@ -185,6 +204,16 @@ struct FieldAttr {
|
|||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||
name: Option<SpanContainer<String>>,
|
||||
|
||||
/// Explicitly specified [description][2] of this
|
||||
/// [GraphQL input object field][1].
|
||||
///
|
||||
/// If [`None`], then Rust doc comment will be used as the [description][2],
|
||||
/// if any.
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||
/// [2]: https://spec.graphql.org/October2021#sec-Descriptions
|
||||
description: Option<SpanContainer<Description>>,
|
||||
|
||||
/// Explicitly specified [default value][2] of this
|
||||
/// [GraphQL input object field][1] to be used used in case a field value is
|
||||
/// not provided.
|
||||
|
@ -195,15 +224,18 @@ struct FieldAttr {
|
|||
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
||||
default: Option<SpanContainer<default::Value>>,
|
||||
|
||||
/// Explicitly specified [description][2] of this
|
||||
/// [GraphQL input object field][1].
|
||||
/// Explicitly specified type of the custom [`Behavior`] this
|
||||
/// [GraphQL input object field][1] implementation is parametrized with, to
|
||||
/// [coerce] in the generated code from.
|
||||
///
|
||||
/// If [`None`], then Rust doc comment will be used as the [description][2],
|
||||
/// if any.
|
||||
/// If [`None`], then [`behavior::Standard`] will be used for the generated
|
||||
/// code.
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||
/// [2]: https://spec.graphql.org/October2021#sec-Descriptions
|
||||
description: Option<SpanContainer<Description>>,
|
||||
/// [coerce]: juniper::behavior::Coerce
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified marker for the Rust struct field to be ignored and
|
||||
/// not included into the code generated for a [GraphQL input object][0]
|
||||
|
@ -234,17 +266,24 @@ impl Parse for FieldAttr {
|
|||
))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"desc" | "description" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let desc = input.parse::<Description>()?;
|
||||
out.description
|
||||
.replace(SpanContainer::new(ident.span(), Some(desc.span()), desc))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"default" => {
|
||||
let val = input.parse::<default::Value>()?;
|
||||
out.default
|
||||
.replace(SpanContainer::new(ident.span(), Some(val.span()), val))
|
||||
.none_or_else(|_| err::dup_arg(&ident))?
|
||||
}
|
||||
"desc" | "description" => {
|
||||
"behave" | "behavior" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let desc = input.parse::<Description>()?;
|
||||
out.description
|
||||
.replace(SpanContainer::new(ident.span(), Some(desc.span()), desc))
|
||||
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
|
||||
|
@ -267,8 +306,9 @@ impl FieldAttr {
|
|||
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
name: try_merge_opt!(name: self, another),
|
||||
default: try_merge_opt!(default: self, another),
|
||||
description: try_merge_opt!(description: self, another),
|
||||
default: try_merge_opt!(default: self, another),
|
||||
behavior: try_merge_opt!(behavior: self, another),
|
||||
ignore: try_merge_opt!(ignore: self, another),
|
||||
})
|
||||
}
|
||||
|
@ -314,6 +354,14 @@ struct FieldDefinition {
|
|||
/// [2]: https://spec.graphql.org/October2021#DefaultValue
|
||||
default: Option<default::Value>,
|
||||
|
||||
/// [`Behavior`] parametrization of this [GraphQL input object field][1]
|
||||
/// implementation to [coerce] from in the generated code.
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||
/// [coerce]: juniper::behavior::Coerce
|
||||
behavior: behavior::Type,
|
||||
|
||||
/// Name of this [GraphQL input object field][1] in GraphQL schema.
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#InputValueDefinition
|
||||
|
@ -383,6 +431,13 @@ struct Definition {
|
|||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
scalar: scalar::Type,
|
||||
|
||||
/// [`Behavior`] parametrization to generate code with for this
|
||||
/// [GraphQL input object][0].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
behavior: behavior::Type,
|
||||
|
||||
/// [Fields][1] of this [GraphQL input object][0].
|
||||
///
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
|
@ -399,6 +454,14 @@ impl ToTokens for Definition {
|
|||
self.impl_from_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_resolve_type().to_tokens(into);
|
||||
self.impl_resolve_type_name().to_tokens(into);
|
||||
self.impl_resolve_to_input_value().to_tokens(into);
|
||||
//self.impl_resolve_input_value().to_tokens(into);
|
||||
//self.impl_graphql_input_type().to_tokens(into);
|
||||
//self.impl_graphql_input_object().to_tokens(into);
|
||||
self.impl_reflect().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -500,6 +563,29 @@ impl Definition {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`resolve::TypeName`] trait for this
|
||||
/// [GraphQL input object][0].
|
||||
///
|
||||
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
fn impl_resolve_type_name(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (inf, generics) = self.mix_type_info(generics);
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh>
|
||||
for #ty #where_clause
|
||||
{
|
||||
fn type_name(_: &#inf) -> &'static str {
|
||||
<Self as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
||||
/// [GraphQL input object][0].
|
||||
///
|
||||
|
@ -663,11 +749,52 @@ impl Definition {
|
|||
#where_clause
|
||||
{
|
||||
fn to_input_value(&self) -> ::juniper::InputValue<#scalar> {
|
||||
::juniper::InputValue::object(
|
||||
#[allow(deprecated)]
|
||||
::std::array::IntoIter::new([#( #fields ),*])
|
||||
.collect()
|
||||
)
|
||||
::juniper::InputValue::object([#( #fields ),*])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`resolve::ToInputValue`] trait for
|
||||
/// this [GraphQL input object][0].
|
||||
///
|
||||
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
fn impl_resolve_to_input_value(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (sv, mut generics) = self.mix_scalar_value(generics);
|
||||
for f in &self.fields {
|
||||
let field_ty = &f.ty;
|
||||
let field_bh = &f.behavior;
|
||||
generics.make_where_clause().predicates.push(parse_quote! {
|
||||
#field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh>
|
||||
});
|
||||
}
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let fields = self.fields.iter().filter_map(|f| {
|
||||
let field = &f.ident;
|
||||
let field_ty = &f.ty;
|
||||
let field_bh = &f.behavior;
|
||||
let name = &f.name;
|
||||
|
||||
(!f.ignored).then(|| {
|
||||
quote! {
|
||||
(#name, <#field_ty as
|
||||
::juniper::resolve::ToInputValue<#sv, #field_bh>>
|
||||
::to_input_value(&self.#field))
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh>
|
||||
for #ty #where_clause
|
||||
{
|
||||
fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> {
|
||||
::juniper::InputValue::object([#( #fields ),*])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -716,6 +843,47 @@ impl Definition {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`reflect::BaseType`],
|
||||
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||
/// [GraphQL input object][0].
|
||||
///
|
||||
/// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes
|
||||
/// [`reflect::BaseType`]: juniper::reflect::BaseType
|
||||
/// [`reflect::WrappedType`]: juniper::reflect::WrappedType
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Input-Objects
|
||||
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
|
||||
/// similar) implementation of this struct.
|
||||
///
|
||||
|
@ -775,4 +943,34 @@ 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.ident;
|
||||
let (_, ty_gen, _) = generics.split_for_impl();
|
||||
parse_quote! { #ident #ty_gen }
|
||||
};
|
||||
(ty, generics)
|
||||
}
|
||||
|
||||
/// Mixes a type info [`syn::GenericParam`] into the provided
|
||||
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||
fn mix_type_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||
let ty = parse_quote! { __TypeInfo };
|
||||
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||
(ty, generics)
|
||||
}
|
||||
|
||||
/// Mixes a [`ScalarValue`] [`syn::GenericParam`] into the provided
|
||||
/// [`syn::Generics`] and returns it.
|
||||
///
|
||||
/// [`ScalarValue`]: juniper::ScalarValue
|
||||
fn mix_scalar_value(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||
let sv = parse_quote! { __ScalarValue };
|
||||
generics.params.push(parse_quote! { #sv });
|
||||
(sv, generics)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue