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> {
|
pub(crate) fn current_type_reworked(&self) -> &TypeType<'a, SV> {
|
||||||
&self.current_type
|
&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
|
/// Error type for errors that occur during query execution
|
||||||
|
@ -635,14 +664,6 @@ where
|
||||||
self.current_selection_set
|
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
|
/// The currently executing schema
|
||||||
pub fn schema(&self) -> &'a SchemaType<S> {
|
pub fn schema(&self) -> &'a SchemaType<S> {
|
||||||
self.schema
|
self.schema
|
||||||
|
|
|
@ -369,6 +369,8 @@ mod threads_context_correctly {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Remove as should be unnecessary with generic context.
|
||||||
|
/*
|
||||||
mod dynamic_context_switching {
|
mod dynamic_context_switching {
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
|
|
||||||
|
@ -672,7 +674,7 @@ mod dynamic_context_switching {
|
||||||
assert_eq!(result, graphql_value!({"first": {"value": "First value"}}));
|
assert_eq!(result, graphql_value!({"first": {"value": "First value"}}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
mod propagates_errors_to_nullable_fields {
|
mod propagates_errors_to_nullable_fields {
|
||||||
use crate::{
|
use crate::{
|
||||||
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
|
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
|
||||||
|
|
|
@ -96,19 +96,15 @@ mod interface {
|
||||||
|
|
||||||
mod union {
|
mod union {
|
||||||
use crate::{
|
use crate::{
|
||||||
graphql_object, graphql_union, graphql_value,
|
graphql_object, GraphQLUnion, graphql_value,
|
||||||
schema::model::RootNode,
|
schema::model::RootNode,
|
||||||
types::scalars::{EmptyMutation, EmptySubscription},
|
types::scalars::{EmptyMutation, EmptySubscription},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[graphql_union]
|
#[derive(GraphQLUnion)]
|
||||||
trait Pet {
|
enum Pet {
|
||||||
fn as_dog(&self) -> Option<&Dog> {
|
Dog(Dog),
|
||||||
None
|
Cat(Cat),
|
||||||
}
|
|
||||||
fn as_cat(&self) -> Option<&Cat> {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Dog {
|
struct Dog {
|
||||||
|
@ -116,12 +112,6 @@ mod union {
|
||||||
woofs: bool,
|
woofs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pet for Dog {
|
|
||||||
fn as_dog(&self) -> Option<&Dog> {
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Dog {
|
impl Dog {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
@ -137,12 +127,6 @@ mod union {
|
||||||
meows: bool,
|
meows: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Pet for Cat {
|
|
||||||
fn as_cat(&self) -> Option<&Cat> {
|
|
||||||
Some(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Cat {
|
impl Cat {
|
||||||
fn name(&self) -> &str {
|
fn name(&self) -> &str {
|
||||||
|
@ -154,13 +138,13 @@ mod union {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Schema {
|
struct Schema {
|
||||||
pets: Vec<Box<dyn Pet + Send + Sync>>,
|
pets: Vec<Pet>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[graphql_object]
|
#[graphql_object]
|
||||||
impl Schema {
|
impl Schema {
|
||||||
fn pets(&self) -> Vec<&(dyn Pet + Send + Sync)> {
|
fn pets(&self) -> &[Pet] {
|
||||||
self.pets.iter().map(|p| p.as_ref()).collect()
|
&self.pets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,11 +153,11 @@ mod union {
|
||||||
let schema = RootNode::new(
|
let schema = RootNode::new(
|
||||||
Schema {
|
Schema {
|
||||||
pets: vec![
|
pets: vec![
|
||||||
Box::new(Dog {
|
Pet::Dog(Dog {
|
||||||
name: "Odie".into(),
|
name: "Odie".into(),
|
||||||
woofs: true,
|
woofs: true,
|
||||||
}),
|
}),
|
||||||
Box::new(Cat {
|
Pet::Cat(Cat {
|
||||||
name: "Garfield".into(),
|
name: "Garfield".into(),
|
||||||
meows: false,
|
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;
|
mod ast;
|
||||||
pub mod behavior;
|
pub mod behavior;
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
|
pub mod extract;
|
||||||
pub mod graphql;
|
pub mod graphql;
|
||||||
pub mod http;
|
pub mod http;
|
||||||
pub mod integrations;
|
pub mod integrations;
|
||||||
|
@ -94,6 +95,7 @@ pub use crate::{
|
||||||
},
|
},
|
||||||
validation::RuleError,
|
validation::RuleError,
|
||||||
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
|
||||||
|
extract::Extract,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An error that prevented query execution
|
/// An error that prevented query execution
|
||||||
|
|
|
@ -2,8 +2,10 @@ use crate::{
|
||||||
behavior, graphql,
|
behavior, graphql,
|
||||||
meta::MetaType,
|
meta::MetaType,
|
||||||
parser::{self, ParseError},
|
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> {
|
pub trait Type<TypeInfo: ?Sized, ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
fn meta<'r, 'ti: 'r>(
|
fn meta<'r, 'ti: 'r>(
|
||||||
|
@ -110,9 +112,9 @@ pub trait StaticField<
|
||||||
{
|
{
|
||||||
fn resolve_static_field(
|
fn resolve_static_field(
|
||||||
&self,
|
&self,
|
||||||
arguments: &Arguments<ScalarValue>,
|
arguments: &Arguments<'_, ScalarValue>,
|
||||||
type_info: &TypeInfo,
|
type_info: &TypeInfo,
|
||||||
executor: &Executor<Context, ScalarValue>,
|
executor: &Executor<'_, '_, Context, ScalarValue>,
|
||||||
) -> ExecutionResult<ScalarValue>;
|
) -> ExecutionResult<ScalarValue>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,3 +204,75 @@ pub trait InputValueAsRef<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
pub trait ScalarToken<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
pub trait ScalarToken<ScalarValue, Behavior: ?Sized = behavior::Standard> {
|
||||||
fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result<ScalarValue, ParseError>;
|
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},
|
ast::{Directive, FromInputValue, InputValue, Selection},
|
||||||
executor::{ExecutionResult, Executor, Registry, Variables},
|
executor::{ExecutionResult, Executor, Registry, Variables},
|
||||||
parser::Spanning,
|
parser::Spanning,
|
||||||
|
resolve,
|
||||||
schema::meta::{Argument, MetaType},
|
schema::meta::{Argument, MetaType},
|
||||||
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
value::{DefaultScalarValue, Object, ScalarValue, Value},
|
||||||
FieldResult, GraphQLEnum, IntoFieldError,
|
FieldResult, GraphQLEnum, IntoFieldError,
|
||||||
|
@ -98,6 +99,8 @@ impl<'a, S> Arguments<'a, S> {
|
||||||
Self { args }
|
Self { args }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO: DEPRECATED!
|
||||||
|
///
|
||||||
/// Gets an argument by the given `name` and converts it into the desired
|
/// Gets an argument by the given `name` and converts it into the desired
|
||||||
/// type.
|
/// type.
|
||||||
///
|
///
|
||||||
|
@ -121,6 +124,38 @@ impl<'a, S> Arguments<'a, S> {
|
||||||
.transpose()
|
.transpose()
|
||||||
.map_err(IntoFieldError::into_field_error)
|
.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.
|
/// 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
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
/// implementation.
|
/// implementation.
|
||||||
#[must_use]
|
|
||||||
fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) {
|
fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) {
|
||||||
(&self.ty, self.generics.clone())
|
(&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
|
/// Returns generated code implementing [`marker::IsOutputType`] trait for
|
||||||
/// this [GraphQL object][1].
|
/// this [GraphQL object][1].
|
||||||
///
|
///
|
||||||
|
@ -574,7 +599,108 @@ impl<Operation: ?Sized + 'static> Definition<Operation> {
|
||||||
})
|
})
|
||||||
.collect()
|
.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
|
/// Returns generated code implementing [`GraphQLType`] trait for this
|
||||||
/// [GraphQL object][1].
|
/// [GraphQL object][1].
|
||||||
///
|
///
|
||||||
|
@ -655,8 +781,8 @@ impl ToTokens for Definition<Query> {
|
||||||
self.impl_async_field_tokens().to_tokens(into);
|
self.impl_async_field_tokens().to_tokens(into);
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
self.impl_reflect().to_tokens(into);
|
self.impl_reflect().to_tokens(into);
|
||||||
//self.impl_reflect_field().to_tokens(into);
|
self.impl_reflect_field().to_tokens(into);
|
||||||
//self.impl_resolve_field_static().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),
|
description: attr.description.map(SpanContainer::into_inner),
|
||||||
context,
|
context,
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics.clone(),
|
generics: ast.generics.clone(),
|
||||||
variants,
|
variants,
|
||||||
};
|
};
|
||||||
|
@ -210,6 +211,7 @@ fn parse_variant_from_trait_method(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
context: method_context_ty,
|
context: method_context_ty,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,7 @@ fn expand_enum(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
||||||
.map(SpanContainer::into_inner)
|
.map(SpanContainer::into_inner)
|
||||||
.unwrap_or_else(|| parse_quote! { () }),
|
.unwrap_or_else(|| parse_quote! { () }),
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics,
|
generics: ast.generics,
|
||||||
variants,
|
variants,
|
||||||
})
|
})
|
||||||
|
@ -163,6 +164,7 @@ fn parse_variant_from_enum_variant(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
context: None,
|
context: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -214,6 +216,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result<Definition> {
|
||||||
.map(SpanContainer::into_inner)
|
.map(SpanContainer::into_inner)
|
||||||
.unwrap_or_else(|| parse_quote! { () }),
|
.unwrap_or_else(|| parse_quote! { () }),
|
||||||
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics),
|
||||||
|
behavior: attr.behavior.into(),
|
||||||
generics: ast.generics,
|
generics: ast.generics,
|
||||||
variants,
|
variants,
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,7 @@ use syn::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::common::{
|
use crate::common::{
|
||||||
filter_attrs, gen,
|
filter_attrs, gen, behavior,
|
||||||
parse::{
|
parse::{
|
||||||
attr::{err, OptionExt as _},
|
attr::{err, OptionExt as _},
|
||||||
ParseBufferExt as _,
|
ParseBufferExt as _,
|
||||||
|
@ -74,6 +74,17 @@ struct Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
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]
|
/// Explicitly specified external resolver functions for [GraphQL union][1]
|
||||||
/// variants.
|
/// variants.
|
||||||
///
|
///
|
||||||
|
@ -128,6 +139,13 @@ impl Parse for Attr {
|
||||||
.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))?
|
||||||
|
}
|
||||||
"on" => {
|
"on" => {
|
||||||
let ty = input.parse::<syn::Type>()?;
|
let ty = input.parse::<syn::Type>()?;
|
||||||
input.parse::<token::Eq>()?;
|
input.parse::<token::Eq>()?;
|
||||||
|
@ -160,6 +178,7 @@ impl Attr {
|
||||||
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),
|
||||||
external_resolvers: try_merge_hashmap!(
|
external_resolvers: try_merge_hashmap!(
|
||||||
external_resolvers: self, another => span_joined
|
external_resolvers: self, another => span_joined
|
||||||
),
|
),
|
||||||
|
@ -188,6 +207,19 @@ impl Attr {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
struct VariantAttr {
|
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
|
/// Explicitly specified marker for the variant/field being ignored and not
|
||||||
/// included into [GraphQL union][1].
|
/// included into [GraphQL union][1].
|
||||||
///
|
///
|
||||||
|
@ -210,6 +242,13 @@ impl Parse for VariantAttr {
|
||||||
while !input.is_empty() {
|
while !input.is_empty() {
|
||||||
let ident = input.parse::<syn::Ident>()?;
|
let ident = input.parse::<syn::Ident>()?;
|
||||||
match ident.to_string().as_str() {
|
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" | "skip" => out
|
||||||
.ignore
|
.ignore
|
||||||
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
.replace(SpanContainer::new(ident.span(), None, ident.clone()))
|
||||||
|
@ -236,6 +275,7 @@ impl VariantAttr {
|
||||||
/// duplicates, if any.
|
/// duplicates, if any.
|
||||||
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
fn try_merge(self, mut another: Self) -> syn::Result<Self> {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
behavior: try_merge_opt!(behavior: self, another),
|
||||||
ignore: try_merge_opt!(ignore: self, another),
|
ignore: try_merge_opt!(ignore: self, another),
|
||||||
external_resolver: try_merge_opt!(external_resolver: 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
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
scalar: scalar::Type,
|
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].
|
/// Variants definitions of this [GraphQL union][1].
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [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_graphql_value_async_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);
|
self.impl_reflect().to_tokens(into);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,7 +694,7 @@ impl Definition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
|
||||||
/// Returns generated code implementing [`reflect::BaseType`],
|
/// Returns generated code implementing [`reflect::BaseType`],
|
||||||
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
/// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this
|
||||||
/// [GraphQL union][0].
|
/// [GraphQL union][0].
|
||||||
|
@ -663,6 +710,15 @@ impl Definition {
|
||||||
|
|
||||||
let name = &self.name;
|
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! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
impl #impl_gens ::juniper::reflect::BaseType<#bh>
|
||||||
|
@ -675,8 +731,10 @@ impl Definition {
|
||||||
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh>
|
||||||
for #ty #where_clause
|
for #ty #where_clause
|
||||||
{
|
{
|
||||||
const NAMES: ::juniper::reflect::Types =
|
const NAMES: ::juniper::reflect::Types = &[
|
||||||
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
<Self as ::juniper::reflect::BaseType<#bh>>::NAME,
|
||||||
|
#( #member_names ),*
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
|
@ -687,7 +745,19 @@ impl Definition {
|
||||||
::juniper::reflect::wrap::SINGULAR;
|
::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.
|
/// Definition of [GraphQL union][1] variant for code generation.
|
||||||
|
@ -710,6 +780,14 @@ struct VariantDefinition {
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
/// [1]: https://spec.graphql.org/October2021#sec-Unions
|
||||||
resolver_check: syn::Expr,
|
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
|
/// Rust type of [`Context`] that this [GraphQL union][1] variant requires
|
||||||
/// for resolution.
|
/// for resolution.
|
||||||
///
|
///
|
||||||
|
@ -826,6 +904,7 @@ fn emerge_union_variants_from_attr(
|
||||||
ty,
|
ty,
|
||||||
resolver_code,
|
resolver_code,
|
||||||
resolver_check,
|
resolver_check,
|
||||||
|
behavior: behavior::Type::default(), // TODO: remove at all
|
||||||
context: None,
|
context: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue