Impl field resolving, vol.1
This commit is contained in:
parent
e6861bc516
commit
c75d33ae0a
11 changed files with 384 additions and 47 deletions
|
@ -89,6 +89,35 @@ impl<'r, 'a, CX: ?Sized, SV> Executor<'r, 'a, CX, SV> {
|
|||
pub(crate) fn current_type_reworked(&self) -> &TypeType<'a, SV> {
|
||||
&self.current_type
|
||||
}
|
||||
|
||||
/// Resolves the specified single arbitrary `Type` `value` as
|
||||
/// [`graphql::Value`].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Whenever [`Type::resolve_value()`] errors.
|
||||
///
|
||||
/// [`graphql::Value`]: crate::graphql::Value
|
||||
/// [`Type::resolve_value()`]: resolve::Value::resolve_value
|
||||
pub fn resolve_value<BH, Type, TI>(&self, value: &Type, type_info: &TI) -> ExecutionResult<SV>
|
||||
where
|
||||
Type: resolve::Value<TI, CX, SV, BH> + ?Sized,
|
||||
TI: ?Sized,
|
||||
BH: ?Sized,
|
||||
{
|
||||
value.resolve_value(self.current_selection_set, type_info, self)
|
||||
}
|
||||
|
||||
/// Returns the current context of this [`Executor`].
|
||||
///
|
||||
/// Context is usually provided when the top-level [`execute()`] function is
|
||||
/// called.
|
||||
///
|
||||
/// [`execute()`]: crate::execute
|
||||
#[must_use]
|
||||
pub fn context(&self) -> &'r CX {
|
||||
self.context
|
||||
}
|
||||
}
|
||||
|
||||
/// Error type for errors that occur during query execution
|
||||
|
@ -635,14 +664,6 @@ where
|
|||
self.current_selection_set
|
||||
}
|
||||
|
||||
/// Access the current context
|
||||
///
|
||||
/// You usually provide the context when calling the top-level `execute`
|
||||
/// function, or using the context factory in the Iron integration.
|
||||
pub fn context(&self) -> &'r CtxT {
|
||||
self.context
|
||||
}
|
||||
|
||||
/// The currently executing schema
|
||||
pub fn schema(&self) -> &'a SchemaType<S> {
|
||||
self.schema
|
||||
|
|
|
@ -369,6 +369,8 @@ mod threads_context_correctly {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: Remove as should be unnecessary with generic context.
|
||||
/*
|
||||
mod dynamic_context_switching {
|
||||
use indexmap::IndexMap;
|
||||
|
||||
|
@ -672,7 +674,7 @@ mod dynamic_context_switching {
|
|||
assert_eq!(result, graphql_value!({"first": {"value": "First value"}}));
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
mod propagates_errors_to_nullable_fields {
|
||||
use crate::{
|
||||
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
|
||||
|
|
|
@ -96,19 +96,15 @@ mod interface {
|
|||
|
||||
mod union {
|
||||
use crate::{
|
||||
graphql_object, graphql_union, graphql_value,
|
||||
graphql_object, GraphQLUnion, graphql_value,
|
||||
schema::model::RootNode,
|
||||
types::scalars::{EmptyMutation, EmptySubscription},
|
||||
};
|
||||
|
||||
#[graphql_union]
|
||||
trait Pet {
|
||||
fn as_dog(&self) -> Option<&Dog> {
|
||||
None
|
||||
}
|
||||
fn as_cat(&self) -> Option<&Cat> {
|
||||
None
|
||||
}
|
||||
#[derive(GraphQLUnion)]
|
||||
enum Pet {
|
||||
Dog(Dog),
|
||||
Cat(Cat),
|
||||
}
|
||||
|
||||
struct Dog {
|
||||
|
@ -116,12 +112,6 @@ mod union {
|
|||
woofs: bool,
|
||||
}
|
||||
|
||||
impl Pet for Dog {
|
||||
fn as_dog(&self) -> Option<&Dog> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql_object]
|
||||
impl Dog {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -137,12 +127,6 @@ mod union {
|
|||
meows: bool,
|
||||
}
|
||||
|
||||
impl Pet for Cat {
|
||||
fn as_cat(&self) -> Option<&Cat> {
|
||||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql_object]
|
||||
impl Cat {
|
||||
fn name(&self) -> &str {
|
||||
|
@ -154,13 +138,13 @@ mod union {
|
|||
}
|
||||
|
||||
struct Schema {
|
||||
pets: Vec<Box<dyn Pet + Send + Sync>>,
|
||||
pets: Vec<Pet>,
|
||||
}
|
||||
|
||||
#[graphql_object]
|
||||
impl Schema {
|
||||
fn pets(&self) -> Vec<&(dyn Pet + Send + Sync)> {
|
||||
self.pets.iter().map(|p| p.as_ref()).collect()
|
||||
fn pets(&self) -> &[Pet] {
|
||||
&self.pets
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,11 +153,11 @@ mod union {
|
|||
let schema = RootNode::new(
|
||||
Schema {
|
||||
pets: vec![
|
||||
Box::new(Dog {
|
||||
Pet::Dog(Dog {
|
||||
name: "Odie".into(),
|
||||
woofs: true,
|
||||
}),
|
||||
Box::new(Cat {
|
||||
Pet::Cat(Cat {
|
||||
name: "Garfield".into(),
|
||||
meows: false,
|
||||
}),
|
||||
|
|
9
juniper/src/extract.rs
Normal file
9
juniper/src/extract.rs
Normal file
|
@ -0,0 +1,9 @@
|
|||
pub trait Extract<T: ?Sized> {
|
||||
fn extract(&self) -> &T;
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Extract<T> for T {
|
||||
fn extract(&self) -> &Self {
|
||||
self
|
||||
}
|
||||
}
|
|
@ -33,6 +33,7 @@ pub mod macros;
|
|||
mod ast;
|
||||
pub mod behavior;
|
||||
pub mod executor;
|
||||
pub mod extract;
|
||||
pub mod graphql;
|
||||
pub mod http;
|
||||
pub mod integrations;
|
||||
|
@ -94,6 +95,7 @@ pub use crate::{
|
|||
},
|
||||
validation::RuleError,
|
||||
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
||||
extract::Extract,
|
||||
};
|
||||
|
||||
/// An error that prevented query execution
|
||||
|
|
|
@ -2,8 +2,10 @@ use crate::{
|
|||
behavior, graphql,
|
||||
meta::MetaType,
|
||||
parser::{self, ParseError},
|
||||
reflect, Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, Selection,
|
||||
reflect, Arguments, BoxFuture, ExecutionResult, Executor, FieldResult, IntoFieldError,
|
||||
Registry, Selection,
|
||||
};
|
||||
use juniper::resolve;
|
||||
|
||||
pub trait Type<TypeInfo: ?Sized, ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||
fn meta<'r, 'ti: 'r>(
|
||||
|
@ -110,9 +112,9 @@ pub trait StaticField<
|
|||
{
|
||||
fn resolve_static_field(
|
||||
&self,
|
||||
arguments: &Arguments<ScalarValue>,
|
||||
arguments: &Arguments<'_, ScalarValue>,
|
||||
type_info: &TypeInfo,
|
||||
executor: &Executor<Context, ScalarValue>,
|
||||
executor: &Executor<'_, '_, Context, ScalarValue>,
|
||||
) -> ExecutionResult<ScalarValue>;
|
||||
}
|
||||
|
||||
|
@ -202,3 +204,75 @@ pub trait InputValueAsRef<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
|||
pub trait ScalarToken<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||
fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result<ScalarValue, ParseError>;
|
||||
}
|
||||
|
||||
/*
|
||||
pub trait IntoResolvable<
|
||||
ScalarValue,
|
||||
>
|
||||
{
|
||||
type Type;
|
||||
|
||||
fn into_resolvable(self) -> FieldResult<Self::Type, ScalarValue>;
|
||||
}
|
||||
|
||||
impl<T, SV> IntoResolvable<SV> for T
|
||||
where
|
||||
T: crate::GraphQLValue<SV>,
|
||||
SV: crate::ScalarValue,
|
||||
{
|
||||
type Type = Self;
|
||||
|
||||
fn into_resolvable(self) -> FieldResult<Self, SV> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, E, SV> IntoResolvable<SV> for Result<T, E>
|
||||
where
|
||||
T: crate::GraphQLValue<SV>,
|
||||
SV: crate::ScalarValue,
|
||||
E: IntoFieldError<SV>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into_resolvable(self) -> FieldResult<Self::Type, SV> {
|
||||
self.map_err(IntoFieldError::into_field_error)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
#[doc(hidden)]
|
||||
pub trait IntoResolvable<S, T>
|
||||
where
|
||||
T: crate::GraphQLValue<S>,
|
||||
S: crate::ScalarValue,
|
||||
{
|
||||
type Type;
|
||||
|
||||
#[doc(hidden)]
|
||||
fn into_resolvable(self) -> FieldResult<T, S>;
|
||||
}
|
||||
|
||||
impl<'a, S, T> IntoResolvable<S, T> for T
|
||||
where
|
||||
T: crate::GraphQLValue<S>,
|
||||
S: crate::ScalarValue,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into_resolvable(self) -> FieldResult<T, S> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, T, E: IntoFieldError<S>> IntoResolvable<S, T> for Result<T, E>
|
||||
where
|
||||
S: crate::ScalarValue,
|
||||
T: crate::GraphQLValue<S>,
|
||||
{
|
||||
type Type = T;
|
||||
|
||||
fn into_resolvable(self) -> FieldResult<T, S> {
|
||||
self.map_err(IntoFieldError::into_field_error)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ use crate::{
|
|||
ast::{Directive, FromInputValue, InputValue, Selection},
|
||||
executor::{ExecutionResult, Executor, Registry, Variables},
|
||||
parser::Spanning,
|
||||
resolve,
|
||||
schema::meta::{Argument, MetaType},
|
||||
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
||||
FieldResult, GraphQLEnum, IntoFieldError,
|
||||
|
@ -98,6 +99,8 @@ impl<'a, S> Arguments<'a, S> {
|
|||
Self { args }
|
||||
}
|
||||
|
||||
/// TODO: DEPRECATED!
|
||||
///
|
||||
/// Gets an argument by the given `name` and converts it into the desired
|
||||
/// type.
|
||||
///
|
||||
|
@ -121,6 +124,38 @@ impl<'a, S> Arguments<'a, S> {
|
|||
.transpose()
|
||||
.map_err(IntoFieldError::into_field_error)
|
||||
}
|
||||
|
||||
/// Resolves an argument with the provided `name` as the specified type `T`.
|
||||
///
|
||||
/// If [`None`] argument is found, then `T` is
|
||||
/// [tried to be resolved from implicit `null`][0].
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the [`resolve::InputValue`] conversion fails.
|
||||
///
|
||||
/// [0]: resolve::InputValue::try_from_implicit_null
|
||||
pub fn resolve<'s, T, BH>(&'s self, name: &str) -> FieldResult<T, S>
|
||||
where
|
||||
T: resolve::InputValue<'s, S, BH>,
|
||||
BH: ?Sized,
|
||||
{
|
||||
self.args
|
||||
.as_ref()
|
||||
.and_then(|args| args.get(name))
|
||||
.map(<T as resolve::InputValue<'s, S, BH>>::try_from_input_value)
|
||||
.transpose()
|
||||
.map_err(IntoFieldError::into_field_error)?
|
||||
.map_or_else(
|
||||
|| {
|
||||
<T as resolve::InputValue<'s, S, BH>>::try_from_implicit_null().map_err(|e| {
|
||||
IntoFieldError::into_field_error(e)
|
||||
.map_message(|m| format!("Missing argument `{name}`: {m}"))
|
||||
})
|
||||
},
|
||||
Ok,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Primary trait used to resolve GraphQL values.
|
||||
|
|
|
@ -352,11 +352,36 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
|||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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 context [`syn::GenericParam`] into the provided
|
||||
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||
fn mix_context(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||
let ty = parse_quote! { __Context };
|
||||
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)
|
||||
}
|
||||
|
||||
/// Returns generated code implementing [`marker::IsOutputType`] trait for
|
||||
/// this [GraphQL object][1].
|
||||
///
|
||||
|
@ -574,7 +599,108 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
|||
})
|
||||
.collect()
|
||||
}
|
||||
/*
|
||||
/// Returns generated code implementing [`resolve::StaticField`] trait for
|
||||
/// each [field][1] of this [GraphQL object][0].
|
||||
///
|
||||
/// [`resolve::StaticField`]: juniper::resolve::StaticField
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Objects
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Language.Fields
|
||||
#[must_use]
|
||||
pub(crate) fn impl_resolve_static_field(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (inf, generics) = self.mix_type_info(generics);
|
||||
let (cx, generics) = self.mix_context(generics);
|
||||
let (sv, generics) = self.mix_scalar_value(generics);
|
||||
|
||||
self.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
let mut generics = generics.clone();
|
||||
let (f_name, f_bh) = (&field.name, &field.behavior);
|
||||
let (f_ident, f_ty) = (&field.ident, &field.ty);
|
||||
|
||||
let body = if !field.is_async {
|
||||
generics.make_where_clause().predicates.push(parse_quote! {
|
||||
#f_ty: ::juniper::resolve::Value<#inf, #cx, #sv, #f_bh>
|
||||
});
|
||||
|
||||
let res = if field.is_method() {
|
||||
let args = field.arguments.as_ref().unwrap().iter().map(|arg| {
|
||||
match arg {
|
||||
field::MethodArgument::Regular(arg) => {
|
||||
let (a_ty, a_bh) = (&arg.ty, &arg.behavior);
|
||||
generics.make_where_clause().predicates.push(parse_quote! {
|
||||
#a_ty: ::juniper::resolve::InputValueOwned<#sv, #a_bh>
|
||||
});
|
||||
quote! {
|
||||
args.resolve::<#a_ty, #a_bh>(#name)?
|
||||
}
|
||||
}
|
||||
field::MethodArgument::Context(cx_ty) => {
|
||||
generics.make_where_clause().predicates.push(parse_quote! {
|
||||
#cx: ::juniper::Extract<#cx_ty>
|
||||
});
|
||||
quote! {
|
||||
<#cx as ::juniper::Extract<#cx_ty>>
|
||||
::extract(executor.context())
|
||||
}
|
||||
}
|
||||
field::MethodArgument::Executor => {
|
||||
quote! {
|
||||
executor
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let rcv = field.has_receiver.then(|| {
|
||||
quote! { self, }
|
||||
});
|
||||
|
||||
quote! { Self::#ident(#rcv #( #args ),*) }
|
||||
} else {
|
||||
quote! {
|
||||
&self.#f_ident
|
||||
}
|
||||
};
|
||||
|
||||
quote! {
|
||||
executor.resolve_value::<#f_bh, _, _>(#res, type_info)
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
::std::panic!(
|
||||
"Tried to resolve async field `{}` on type `{}` with a sync resolver",
|
||||
#f_name,
|
||||
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_gens ::juniper::resolve::StaticField<
|
||||
{ ::juniper::reflect::fnv1a128(#f_name) },
|
||||
#inf, #cx, #sv, #bh,
|
||||
> for #ty #where_clause {
|
||||
fn resolve_static_field(
|
||||
&self,
|
||||
args: &::juniper::Arguments<'_, #sv>,
|
||||
type_info: &#inf,
|
||||
executor: &::juniper::Executor<'_, '_, #cx, #sv>,
|
||||
) -> ::juniper::ExecutionResult<#sv> {
|
||||
#body
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
*/
|
||||
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||
/// [GraphQL object][1].
|
||||
///
|
||||
|
@ -655,8 +781,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);
|
||||
self.impl_reflect_field().to_tokens(into);
|
||||
//self.impl_resolve_static_field().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,7 @@ fn expand_on_trait(
|
|||
description: attr.description.map(SpanContainer::into_inner),
|
||||
context,
|
||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||
behavior: attr.behavior.into(),
|
||||
generics: ast.generics.clone(),
|
||||
variants,
|
||||
};
|
||||
|
@ -210,6 +211,7 @@ fn parse_variant_from_trait_method(
|
|||
ty,
|
||||
resolver_code,
|
||||
resolver_check,
|
||||
behavior: attr.behavior.into(),
|
||||
context: method_context_ty,
|
||||
})
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ fn expand_enum(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
|||
.map(SpanContainer::into_inner)
|
||||
.unwrap_or_else(|| parse_quote! { () }),
|
||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||
behavior: attr.behavior.into(),
|
||||
generics: ast.generics,
|
||||
variants,
|
||||
})
|
||||
|
@ -163,6 +164,7 @@ fn parse_variant_from_enum_variant(
|
|||
ty,
|
||||
resolver_code,
|
||||
resolver_check,
|
||||
behavior: attr.behavior.into(),
|
||||
context: None,
|
||||
})
|
||||
}
|
||||
|
@ -214,6 +216,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
|||
.map(SpanContainer::into_inner)
|
||||
.unwrap_or_else(|| parse_quote! { () }),
|
||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||
behavior: attr.behavior.into(),
|
||||
generics: ast.generics,
|
||||
variants,
|
||||
})
|
||||
|
|
|
@ -18,7 +18,7 @@ use syn::{
|
|||
};
|
||||
|
||||
use crate::common::{
|
||||
filter_attrs, gen,
|
||||
filter_attrs, gen, behavior,
|
||||
parse::{
|
||||
attr::{err, OptionExt as _},
|
||||
ParseBufferExt as _,
|
||||
|
@ -74,6 +74,17 @@ struct Attr {
|
|||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||
|
||||
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||
/// [GraphQL union][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-Unions
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified external resolver functions for [GraphQL union][1]
|
||||
/// variants.
|
||||
///
|
||||
|
@ -128,6 +139,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))?
|
||||
}
|
||||
"on" => {
|
||||
let ty = input.parse::<syn::Type>()?;
|
||||
input.parse::<token::Eq>()?;
|
||||
|
@ -160,6 +178,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),
|
||||
external_resolvers: try_merge_hashmap!(
|
||||
external_resolvers: self, another => span_joined
|
||||
),
|
||||
|
@ -188,6 +207,19 @@ impl Attr {
|
|||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||
#[derive(Debug, Default)]
|
||||
struct VariantAttr {
|
||||
/// Explicitly specified type of the custom [`Behavior`] this
|
||||
/// [GraphQL union][0] member 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-Unions
|
||||
/// [coerce]: juniper::behavior::Coerce
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified marker for the variant/field being ignored and not
|
||||
/// included into [GraphQL union][1].
|
||||
///
|
||||
|
@ -210,6 +242,13 @@ impl Parse for VariantAttr {
|
|||
while !input.is_empty() {
|
||||
let ident = input.parse::<syn::Ident>()?;
|
||||
match ident.to_string().as_str() {
|
||||
"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()))
|
||||
|
@ -236,6 +275,7 @@ impl VariantAttr {
|
|||
/// duplicates, if any.
|
||||
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
||||
Ok(Self {
|
||||
behavior: try_merge_opt!(behavior: self, another),
|
||||
ignore: try_merge_opt!(ignore: self, another),
|
||||
external_resolver: try_merge_opt!(external_resolver: self, another),
|
||||
})
|
||||
|
@ -301,6 +341,13 @@ struct Definition {
|
|||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||
scalar: scalar::Type,
|
||||
|
||||
/// [`Behavior`] parametrization to generate code with for this
|
||||
/// [GraphQL union][0].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||
behavior: behavior::Type,
|
||||
|
||||
/// Variants definitions of this [GraphQL union][1].
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||
|
@ -316,7 +363,7 @@ impl ToTokens for Definition {
|
|||
self.impl_graphql_value_async_tokens().to_tokens(into);
|
||||
self.impl_reflection_traits_tokens().to_tokens(into);
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
//self.impl_reflect().to_tokens(into);
|
||||
self.impl_reflect().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,7 +694,7 @@ impl Definition {
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
|
||||
/// Returns generated code implementing [`reflect::BaseType`],
|
||||
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||
/// [GraphQL union][0].
|
||||
|
@ -663,6 +710,15 @@ impl Definition {
|
|||
|
||||
let name = &self.name;
|
||||
|
||||
let member_names = self.variants.iter().map(|m| {
|
||||
let m_ty = &m.ty;
|
||||
let m_bh = &m.behavior;
|
||||
|
||||
quote! {
|
||||
<#m_ty as ::juniper::reflect::BaseType<#m_bh>>::NAME
|
||||
}
|
||||
});
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||
|
@ -675,8 +731,10 @@ impl Definition {
|
|||
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||
for #ty #where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Types =
|
||||
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||
const NAMES: ::juniper::reflect::Types = &[
|
||||
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||
#( #member_names ),*
|
||||
];
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
|
@ -687,7 +745,19 @@ impl Definition {
|
|||
::juniper::reflect::wrap::SINGULAR;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// 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.ty;
|
||||
let (_, ty_gen, _) = generics.split_for_impl();
|
||||
parse_quote! { #ident #ty_gen }
|
||||
};
|
||||
(ty, generics)
|
||||
}
|
||||
}
|
||||
|
||||
/// Definition of [GraphQL union][1] variant for code generation.
|
||||
|
@ -710,6 +780,14 @@ struct VariantDefinition {
|
|||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||
resolver_check: syn::Expr,
|
||||
|
||||
/// [`Behavior`] parametrization of this [GraphQL union][0] member
|
||||
/// implementation to [coerce] from in the generated code.
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Unions
|
||||
/// [coerce]: juniper::behavior::Coerce
|
||||
behavior: behavior::Type,
|
||||
|
||||
/// Rust type of [`Context`] that this [GraphQL union][1] variant requires
|
||||
/// for resolution.
|
||||
///
|
||||
|
@ -826,6 +904,7 @@ fn emerge_union_variants_from_attr(
|
|||
ty,
|
||||
resolver_code,
|
||||
resolver_check,
|
||||
behavior: behavior::Type::default(), // TODO: remove at all
|
||||
context: None,
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue