Impl macro for scalars, vol.1 [skip ci]
This commit is contained in:
parent
25404eb4a7
commit
f2718cc01a
9 changed files with 303 additions and 104 deletions
|
@ -2,24 +2,35 @@
|
|||
|
||||
use std::{marker::PhantomData, sync::atomic::AtomicPtr};
|
||||
|
||||
use crate::{meta::MetaType, resolve, Registry};
|
||||
use crate::{
|
||||
graphql,
|
||||
meta::MetaType,
|
||||
parser::{ParseError, ScalarToken},
|
||||
resolve, Registry,
|
||||
};
|
||||
|
||||
/// Default standard behavior of GraphQL types implementation.
|
||||
#[derive(Debug)]
|
||||
pub enum Standard {}
|
||||
|
||||
/// Coercion of behavior types and type parameters.
|
||||
pub struct Coerce<T: ?Sized, From: ?Sized = Standard>(PhantomData<AtomicPtr<Box<From>>>, T);
|
||||
#[repr(transparent)]
|
||||
pub struct Coerce<T: ?Sized, To: ?Sized = Standard>(PhantomData<AtomicPtr<Box<To>>>, T);
|
||||
|
||||
impl<T, From: ?Sized> Coerce<T, From> {
|
||||
impl<T, To: ?Sized> Coerce<T, To> {
|
||||
#[must_use]
|
||||
pub const fn wrap(value: T) -> Self {
|
||||
Self(PhantomData, value)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.1
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub const fn coerce<T, From: ?Sized>(value: T) -> Coerce<T, From> {
|
||||
pub const fn coerce<T, To: ?Sized>(value: T) -> Coerce<T, To> {
|
||||
Coerce::wrap(value)
|
||||
}
|
||||
|
||||
|
@ -37,3 +48,44 @@ where
|
|||
T::meta(registry, type_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, TI, B1, B2> resolve::TypeName<TI, B1> for Coerce<T, B2>
|
||||
where
|
||||
T: resolve::TypeName<TI, B2> + ?Sized,
|
||||
TI: ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
fn type_name(type_info: &TI) -> &str {
|
||||
T::type_name(type_info)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'i, T, SV, B1, B2> resolve::InputValue<'i, SV, B1> for Coerce<T, B2>
|
||||
where
|
||||
T: resolve::InputValue<'i, SV, B2>,
|
||||
SV: 'i,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
type Error = T::Error;
|
||||
|
||||
fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> {
|
||||
T::try_from_input_value(v).map(Self::wrap)
|
||||
}
|
||||
|
||||
fn try_from_implicit_null() -> Result<Self, Self::Error> {
|
||||
T::try_from_implicit_null().map(Self::wrap)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, SV, B1, B2> resolve::ScalarToken<SV, B1> for Coerce<T, B2>
|
||||
where
|
||||
T: resolve::ScalarToken<SV, B2> + ?Sized,
|
||||
B1: ?Sized,
|
||||
B2: ?Sized,
|
||||
{
|
||||
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError<'_>> {
|
||||
T::parse_scalar_token(token)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1277,19 +1277,37 @@ impl<'r, S: 'r> Registry<'r, S> {
|
|||
ScalarMeta::new::<T>(Cow::Owned(name.to_string()))
|
||||
}
|
||||
|
||||
/*
|
||||
/// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`].
|
||||
/// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`],
|
||||
/// allowing to `customize` the created [`ScalarMeta`], and stores it in
|
||||
/// this [`Registry`].
|
||||
///
|
||||
/// # Idempotent
|
||||
///
|
||||
/// If this [`Registry`] contains a [`MetaType`] with such [`TypeName`]
|
||||
/// already, then just returns it without doing anything.
|
||||
///
|
||||
/// [`graphql::Type`]: resolve::Type
|
||||
pub fn build_scalar_type_new<T, Info>(&mut self, info: &Info) -> ScalarMeta<'r, S>
|
||||
/// [`TypeName`]: resolve::TypeName
|
||||
pub fn register_scalar_with<'ti, T, TI, F>(
|
||||
&mut self,
|
||||
type_info: &'ti TI,
|
||||
customize: F,
|
||||
) -> MetaType<'r, S>
|
||||
where
|
||||
T: resolve::TypeName<Info> + resolve::ScalarToken<S> + resolve::InputValueOwned<S>,
|
||||
Info: ?Sized,
|
||||
T: resolve::TypeName<TI> + resolve::InputValueOwned<S> + resolve::ScalarToken<S>,
|
||||
TI: ?Sized,
|
||||
'ti: 'r,
|
||||
F: FnOnce(&mut ScalarMeta<'r, S>),
|
||||
S: Clone,
|
||||
{
|
||||
// TODO: Allow using references.
|
||||
ScalarMeta::new_new::<T, _>(T::type_name(info).to_owned())
|
||||
self.entry_type::<T, _>(type_info)
|
||||
.or_insert_with(move || {
|
||||
let mut scalar = ScalarMeta::new_reworked::<T, _>(T::type_name(type_info));
|
||||
customize(&mut scalar);
|
||||
scalar.into_meta()
|
||||
})
|
||||
.clone()
|
||||
}
|
||||
*/
|
||||
|
||||
/// Builds a [`ScalarMeta`] information for the specified non-[`Sized`]
|
||||
/// [`graphql::Type`], and stores it in this [`Registry`].
|
||||
|
|
|
@ -499,12 +499,11 @@ impl<'a, S> ScalarMeta<'a, S> {
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/// Builds a new [`ScalarMeta`] information with the specified `name`.
|
||||
// TODO: Use `impl Into<Cow<'a, str>>` argument once feature
|
||||
// `explicit_generic_args_with_impl_trait` hits stable:
|
||||
// https://github.com/rust-lang/rust/issues/83701
|
||||
pub fn new_new<T, N>(name: N) -> Self
|
||||
pub fn new_reworked<T, N>(name: N) -> Self
|
||||
where
|
||||
T: resolve::InputValueOwned<S> + resolve::ScalarToken<S>,
|
||||
Cow<'a, str>: From<N>,
|
||||
|
@ -513,13 +512,13 @@ impl<'a, S> ScalarMeta<'a, S> {
|
|||
name: name.into(),
|
||||
description: None,
|
||||
specified_by_url: None,
|
||||
try_parse_fn: try_parse_fn_new::<S, T>,
|
||||
try_parse_fn: try_parse_fn_reworked::<T, S>,
|
||||
parse_fn: <T as resolve::ScalarToken<S>>::parse_scalar_token,
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/// Builds a new [`ScalarMeta`] information with the specified `name` for
|
||||
/// the [`?Sized`] `T`ype that may only be parsed as a reference.
|
||||
/// the non-[`Sized`] `T`ype that may only be parsed as a reference.
|
||||
// TODO: Use `impl Into<Cow<'a, str>>` argument once feature
|
||||
// `explicit_generic_args_with_impl_trait` hits stable:
|
||||
// https://github.com/rust-lang/rust/issues/83701
|
||||
|
@ -889,16 +888,14 @@ where
|
|||
.map_err(T::Error::into_field_error)
|
||||
}
|
||||
|
||||
/*
|
||||
fn try_parse_fn_new<S, T>(v: &InputValue<S>) -> Result<(), FieldError<S>>
|
||||
fn try_parse_fn_reworked<T, SV>(v: &InputValue<SV>) -> Result<(), FieldError<SV>>
|
||||
where
|
||||
T: resolve::InputValueOwned<S>,
|
||||
T: resolve::InputValueOwned<SV>,
|
||||
{
|
||||
T::try_from_input_value(v)
|
||||
.map(drop)
|
||||
.map_err(T::Error::into_field_error)
|
||||
}
|
||||
*/
|
||||
|
||||
fn try_parse_unsized_fn<T, SV>(v: &InputValue<SV>) -> Result<(), FieldError<SV>>
|
||||
where
|
||||
|
|
|
@ -121,13 +121,11 @@ where
|
|||
}
|
||||
|
||||
impl<SV> resolve::ScalarToken<SV> for str
|
||||
//TODO: where String: resolve::ScalarToken<SV>,
|
||||
where
|
||||
String: crate::ParseScalarValue<SV>,
|
||||
String: resolve::ScalarToken<SV>,
|
||||
{
|
||||
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<SV, ParseError<'_>> {
|
||||
// TODO: <String as resolve::ScalarToken<SV>>::parse_scalar_token(token)
|
||||
<String as crate::ParseScalarValue<SV>>::from_str(token)
|
||||
<String as resolve::ScalarToken<SV>>::parse_scalar_token(token)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,7 +226,7 @@ where
|
|||
}
|
||||
|
||||
impl reflect::BaseType for str {
|
||||
const NAME: reflect::Type = "String"; // TODO: <String as reflect::BaseType<BH>>::NAME;
|
||||
const NAME: reflect::Type = <String as reflect::BaseType>::NAME;
|
||||
}
|
||||
|
||||
impl reflect::BaseSubTypes for str {
|
||||
|
|
57
juniper_codegen/src/common/behavior.rs
Normal file
57
juniper_codegen/src/common/behavior.rs
Normal file
|
@ -0,0 +1,57 @@
|
|||
//! Common functions, definitions and extensions for parsing and code generation
|
||||
//! related to [`Behaviour`] type parameter.
|
||||
//!
|
||||
//! [`Behaviour`]: juniper::behavior
|
||||
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote,
|
||||
};
|
||||
|
||||
/// [`Behaviour`] parametrization of the code generation.
|
||||
///
|
||||
/// [`Behaviour`]: juniper::behavior
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum Type {
|
||||
/// [`behavior::Standard`] should be used in the generated code.
|
||||
///
|
||||
/// [`behavior::Standard`]: juniper::behavior::Standard
|
||||
Standard,
|
||||
|
||||
/// Concrete custom Rust type should be used as [`Behaviour`] in the
|
||||
/// generated code.
|
||||
///
|
||||
/// [`Behaviour`]: juniper::behavior
|
||||
Custom(syn::Type),
|
||||
}
|
||||
|
||||
impl Default for Type {
|
||||
fn default() -> Self {
|
||||
Self::Standard
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Type {
|
||||
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
|
||||
input.parse::<syn::Type>().map(Self::Custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Type {
|
||||
fn to_tokens(&self, into: &mut TokenStream) {
|
||||
self.ty().to_tokens(into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Type {
|
||||
/// Returns a Rust type representing this [`Type`].
|
||||
#[must_use]
|
||||
pub(crate) fn ty(&self) -> syn::Type {
|
||||
match self {
|
||||
Self::Standard => parse_quote! { ::juniper::behavior::Standard },
|
||||
Self::Custom(ty) => ty.clone(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@ pub(crate) mod field;
|
|||
pub(crate) mod gen;
|
||||
pub(crate) mod parse;
|
||||
pub(crate) mod scalar;
|
||||
pub(crate) mod behavior;
|
||||
|
|
|
@ -64,6 +64,7 @@ fn expand_on_type_alias(
|
|||
description: attr.description.as_deref().cloned(),
|
||||
specified_by_url: attr.specified_by_url.as_deref().cloned(),
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
|
@ -96,6 +97,7 @@ fn expand_on_derive_input(
|
|||
description: attr.description.as_deref().cloned(),
|
||||
specified_by_url: attr.specified_by_url.as_deref().cloned(),
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
};
|
||||
|
||||
Ok(quote! {
|
||||
|
|
|
@ -33,6 +33,7 @@ pub fn expand(input: TokenStream) -> syn::Result<TokenStream> {
|
|||
description: attr.description.as_deref().cloned(),
|
||||
specified_by_url: attr.specified_by_url.as_deref().cloned(),
|
||||
scalar,
|
||||
behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(),
|
||||
}
|
||||
.to_token_stream())
|
||||
}
|
||||
|
@ -82,7 +83,11 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn:
|
|||
.first()
|
||||
.filter(|_| fields.unnamed.len() == 1)
|
||||
.cloned()
|
||||
.map(Field::Unnamed)
|
||||
.map(|f| Field {
|
||||
itself: f,
|
||||
is_named: false,
|
||||
behavior: None.unwrap_or_default(), // TODO: Parse attribute!
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
ERR.custom_error(
|
||||
ast.span(),
|
||||
|
@ -95,7 +100,11 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn:
|
|||
.first()
|
||||
.filter(|_| fields.named.len() == 1)
|
||||
.cloned()
|
||||
.map(Field::Named)
|
||||
.map(|f| Field {
|
||||
itself: f,
|
||||
is_named: true,
|
||||
behavior: None.unwrap_or_default(), // TODO: Parse attribute!
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
ERR.custom_error(
|
||||
ast.span(),
|
||||
|
|
|
@ -16,6 +16,7 @@ use url::Url;
|
|||
|
||||
use crate::{
|
||||
common::{
|
||||
behavior,
|
||||
parse::{
|
||||
attr::{err, OptionExt as _},
|
||||
ParseBufferExt as _,
|
||||
|
@ -63,6 +64,17 @@ struct Attr {
|
|||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
scalar: Option<SpanContainer<scalar::AttrValue>>,
|
||||
|
||||
/// Explicitly specified type of the custom [`Behavior`] to parametrize this
|
||||
/// [GraphQL scalar][0] type 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-Scalars
|
||||
behavior: Option<SpanContainer<behavior::Type>>,
|
||||
|
||||
/// Explicitly specified function to be used as
|
||||
/// [`ToInputValue::to_input_value`] implementation.
|
||||
///
|
||||
|
@ -88,7 +100,7 @@ struct Attr {
|
|||
/// Explicit where clause added to [`syn::WhereClause`].
|
||||
where_clause: Option<SpanContainer<Vec<syn::WherePredicate>>>,
|
||||
|
||||
/// Indicator for single-field structs allowing to delegate implmemntations
|
||||
/// Indicator for single-field structs allowing to delegate implementations
|
||||
/// of non-provided resolvers to that field.
|
||||
transparent: bool,
|
||||
}
|
||||
|
@ -138,6 +150,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))?
|
||||
}
|
||||
"to_output_with" => {
|
||||
input.parse::<token::Eq>()?;
|
||||
let scl = input.parse::<syn::ExprPath>()?;
|
||||
|
@ -238,6 +257,7 @@ impl Attr {
|
|||
description: try_merge_opt!(description: self, another),
|
||||
specified_by_url: try_merge_opt!(specified_by_url: self, another),
|
||||
scalar: try_merge_opt!(scalar: self, another),
|
||||
behavior: try_merge_opt!(behavior: self, another),
|
||||
to_output: try_merge_opt!(to_output: self, another),
|
||||
from_input: try_merge_opt!(from_input: self, another),
|
||||
parse_token: try_merge_opt!(parse_token: self, another),
|
||||
|
@ -318,6 +338,13 @@ struct Definition {
|
|||
/// [`ScalarValue`]: juniper::ScalarValue
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
scalar: scalar::Type,
|
||||
|
||||
/// [`Behavior`] parametrization to generate code with for this
|
||||
/// [GraphQL scalar][0].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
behavior: behavior::Type,
|
||||
}
|
||||
|
||||
impl ToTokens for Definition {
|
||||
|
@ -331,17 +358,17 @@ impl ToTokens for Definition {
|
|||
self.impl_parse_scalar_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_type().to_tokens(into);
|
||||
self.impl_resolve_type_name().to_tokens(into);
|
||||
//self.impl_resolve_value().to_tokens(into);
|
||||
//self.impl_resolve_value_async().to_tokens(into);
|
||||
//self.impl_resolve_to_input_value().to_tokens(into);
|
||||
//self.impl_resolve_input_value().to_tokens(into);
|
||||
//self.impl_resolve_scalar_token().to_tokens(into);
|
||||
self.impl_resolve_input_value().to_tokens(into);
|
||||
self.impl_resolve_scalar_token().to_tokens(into);
|
||||
//self.impl_graphql_output_type().to_tokens(into);
|
||||
//self.impl_graphql_output_type().to_tokens(into);
|
||||
//self.impl_graphql_scalar().to_tokens(into);
|
||||
//self.impl_reflect().to_tokens(into);
|
||||
self.impl_reflect().to_tokens(into);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -472,17 +499,18 @@ impl Definition {
|
|||
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
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> for #ty
|
||||
impl#impl_gens ::juniper::resolve::TypeName<#inf, #bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
fn type_name(_: &#inf) -> &'static str {
|
||||
<Self as ::juniper::reflect::BaseType<()>>::NAME
|
||||
<Self as ::juniper::reflect::BaseType<#bh>>::NAME
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -494,13 +522,16 @@ impl Definition {
|
|||
/// [`resolve::Type`]: juniper::resolve::Type
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn impl_resolve_type(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (inf, generics) = self.mix_type_info(generics);
|
||||
let (sv, mut generics) = self.mix_scalar_value(generics);
|
||||
generics.make_where_clause().predicates.push(parse_quote! {
|
||||
Self: ::juniper::resolve::TypeName<#inf>
|
||||
+ ::juniper::resolve::ScalarToken<#sv>
|
||||
+ ::juniper::resolve::InputValueOwned<#sv>
|
||||
let predicates = &mut generics.make_where_clause().predicates;
|
||||
predicates.push(parse_quote! { #sv: Clone });
|
||||
predicates.push(parse_quote! {
|
||||
Self: ::juniper::resolve::TypeName<#inf, #bh>
|
||||
+ ::juniper::resolve::ScalarToken<#sv, #bh>
|
||||
+ ::juniper::resolve::InputValueOwned<#sv, #bh>
|
||||
});
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
|
@ -516,20 +547,22 @@ impl Definition {
|
|||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::resolve::Type<#inf, #sv> for #ty
|
||||
impl#impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
fn meta<'__r>(
|
||||
fn meta<'__r, '__ti: '__r>(
|
||||
registry: &mut ::juniper::Registry<'__r, #sv>,
|
||||
info: &#inf,
|
||||
type_info: &'__ti #inf,
|
||||
) -> ::juniper::meta::MetaType<'__r, #sv>
|
||||
where
|
||||
#sv: '__r,
|
||||
{
|
||||
registry.build_scalar_type_new::<Self, _>(info)
|
||||
#description
|
||||
#specified_by_url
|
||||
.into_meta()
|
||||
registry.register_scalar_with::<
|
||||
::juniper::behavior::Coerce<Self, #bh>, _, _,
|
||||
>(type_info, |meta| {
|
||||
meta#description
|
||||
#specified_by_url;
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -762,6 +795,7 @@ impl Definition {
|
|||
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn impl_resolve_input_value(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (sv, mut generics) = self.mix_scalar_value(generics);
|
||||
let lt: syn::GenericParam = parse_quote! { '__inp };
|
||||
|
@ -769,14 +803,14 @@ impl Definition {
|
|||
generics
|
||||
.make_where_clause()
|
||||
.predicates
|
||||
.push(self.methods.bound_try_from_input_value(sv, <));
|
||||
.push(self.methods.bound_try_from_input_value(<, sv, bh));
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let conversion = self.methods.expand_try_from_input_value(sv);
|
||||
let conversion = self.methods.expand_try_from_input_value(sv, bh);
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::resolve::InputValue<#lt, #sv> for #ty
|
||||
impl#impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
type Error = ::juniper::FieldError<#sv>;
|
||||
|
@ -825,19 +859,20 @@ impl Definition {
|
|||
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn impl_resolve_scalar_token(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (sv, mut generics) = self.mix_scalar_value(generics);
|
||||
generics
|
||||
.make_where_clause()
|
||||
.predicates
|
||||
.extend(self.methods.bound_parse_scalar_token(sv));
|
||||
.extend(self.methods.bound_parse_scalar_token(sv, bh));
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let body = self.methods.expand_parse_scalar_token(sv);
|
||||
let body = self.methods.expand_parse_scalar_token(sv, bh);
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::resolve::ScalarToken<#sv> for #ty
|
||||
impl#impl_gens ::juniper::resolve::ScalarToken<#sv, #bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
fn parse_scalar_token(
|
||||
|
@ -900,30 +935,30 @@ impl Definition {
|
|||
/// [`reflect::WrappedType`]: juniper::reflection::WrappedType
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn impl_reflect(&self) -> TokenStream {
|
||||
let bh = &self.behavior;
|
||||
let (ty, generics) = self.ty_and_generics();
|
||||
let (sv, generics) = self.mix_scalar_value(generics);
|
||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||
|
||||
let name = &self.name;
|
||||
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::BaseType<#sv> for #ty
|
||||
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<#sv> for #ty
|
||||
impl#impl_gens ::juniper::reflect::BaseSubTypes<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const NAMES: ::juniper::reflect::Types =
|
||||
&[<Self as ::juniper::reflect::BaseType<#sv>>::NAME];
|
||||
&[<Self as ::juniper::reflect::BaseType<#bh>>::NAME];
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl#impl_gens ::juniper::reflect::WrappedType<#sv> for #ty
|
||||
impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty
|
||||
#where_clause
|
||||
{
|
||||
const VALUE: ::juniper::reflect::WrappedValue =
|
||||
|
@ -1038,7 +1073,7 @@ impl Definition {
|
|||
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||
#[must_use]
|
||||
fn mix_type_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||
let ty = parse_quote! { __Info };
|
||||
let ty = parse_quote! { __TypeInfo };
|
||||
|
||||
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||
|
||||
|
@ -1049,7 +1084,7 @@ impl Definition {
|
|||
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||
#[must_use]
|
||||
fn mix_context(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||
let ty = parse_quote! { __Ctx };
|
||||
let ty = parse_quote! { __Context };
|
||||
|
||||
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||
|
||||
|
@ -1089,29 +1124,30 @@ impl VisitMut for ModifyLifetimes {
|
|||
}
|
||||
}
|
||||
|
||||
/// Methods representing [GraphQL scalar][1].
|
||||
/// User-provided methods for implementing a [GraphQL scalar][0].
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
enum Methods {
|
||||
/// [GraphQL scalar][1] represented with only custom resolvers.
|
||||
/// [GraphQL scalar][0] represented with custom resolving methods only.
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
Custom {
|
||||
/// Function provided with `#[graphql(to_output_with = ...)]`.
|
||||
/// Function provided with `#[graphql(to_output_with = ...)]` attribute.
|
||||
to_output: syn::ExprPath,
|
||||
|
||||
/// Function provided with `#[graphql(from_input_with = ...)]`.
|
||||
/// Function provided with `#[graphql(from_input_with = ...)]`
|
||||
/// attribute.
|
||||
from_input: syn::ExprPath,
|
||||
|
||||
/// [`ParseToken`] provided with `#[graphql(parse_token_with = ...)]`
|
||||
/// or `#[graphql(parse_token(...))]`.
|
||||
/// or `#[graphql(parse_token(...))]` attribute.
|
||||
parse_token: ParseToken,
|
||||
},
|
||||
|
||||
/// [GraphQL scalar][1] maybe partially represented with custom resolver.
|
||||
/// Other methods are used from [`Field`].
|
||||
/// [GraphQL scalar][0] maybe partially represented with custom resolving
|
||||
/// methods. Other methods are re-used from its inner [`Field`].
|
||||
///
|
||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
Delegated {
|
||||
/// Function provided with `#[graphql(to_output_with = ...)]`.
|
||||
to_output: Option<syn::ExprPath>,
|
||||
|
@ -1323,7 +1359,7 @@ impl Methods {
|
|||
/// method.
|
||||
///
|
||||
/// [0]: juniper::resolve::InputValue::try_from_input_value
|
||||
fn expand_try_from_input_value(&self, sv: &scalar::Type) -> TokenStream {
|
||||
fn expand_try_from_input_value(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream {
|
||||
match self {
|
||||
Self::Custom { from_input, .. }
|
||||
| Self::Delegated {
|
||||
|
@ -1335,11 +1371,14 @@ impl Methods {
|
|||
|
||||
Self::Delegated { field, .. } => {
|
||||
let field_ty = field.ty();
|
||||
let field_bh = &field.behavior;
|
||||
let self_constructor = field.closure_constructor();
|
||||
|
||||
quote! {
|
||||
<#field_ty as ::juniper::resolve::InputValue<'_, #sv>>
|
||||
<::juniper::behavior::Coerce<#field_ty, #bh> as
|
||||
::juniper::resolve::InputValue<'_, #sv, #field_bh>>
|
||||
::try_from_input_value(input)
|
||||
.into_inner()
|
||||
.map(#self_constructor)
|
||||
}
|
||||
}
|
||||
|
@ -1354,8 +1393,9 @@ impl Methods {
|
|||
/// [0]: juniper::resolve::InputValue::try_from_input_value
|
||||
fn bound_try_from_input_value(
|
||||
&self,
|
||||
sv: &scalar::Type,
|
||||
lt: &syn::GenericParam,
|
||||
sv: &scalar::Type,
|
||||
bh: &behavior::Type,
|
||||
) -> syn::WherePredicate {
|
||||
match self {
|
||||
Self::Custom { .. }
|
||||
|
@ -1370,9 +1410,11 @@ impl Methods {
|
|||
|
||||
Self::Delegated { field, .. } => {
|
||||
let field_ty = field.ty();
|
||||
let field_bh = &field.behavior;
|
||||
|
||||
parse_quote! {
|
||||
#field_ty: ::juniper::resolve::InputValue<#lt, #sv>
|
||||
::juniper::behavior::Coerce<#field_ty, #bh>:
|
||||
::juniper::resolve::InputValue<#lt, #sv, #field_bh>
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1404,19 +1446,21 @@ impl Methods {
|
|||
/// method.
|
||||
///
|
||||
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||
fn expand_parse_scalar_token(&self, sv: &scalar::Type) -> TokenStream {
|
||||
fn expand_parse_scalar_token(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream {
|
||||
match self {
|
||||
Self::Custom { parse_token, .. }
|
||||
| Self::Delegated {
|
||||
parse_token: Some(parse_token),
|
||||
..
|
||||
} => parse_token.expand_parse_scalar_token(sv),
|
||||
} => parse_token.expand_parse_scalar_token(sv, bh),
|
||||
|
||||
Self::Delegated { field, .. } => {
|
||||
let field_ty = field.ty();
|
||||
let field_bh = &field.behavior;
|
||||
|
||||
quote! {
|
||||
<#field_ty as ::juniper::resolve::ScalarToken<#sv>>
|
||||
<::juniper::behavior::Coerce<#field_ty, #bh> as
|
||||
::juniper::resolve::ScalarToken<#sv, #field_bh>>
|
||||
::parse_scalar_token(token)
|
||||
}
|
||||
}
|
||||
|
@ -1429,19 +1473,25 @@ impl Methods {
|
|||
///
|
||||
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||
fn bound_parse_scalar_token(&self, sv: &scalar::Type) -> Vec<syn::WherePredicate> {
|
||||
fn bound_parse_scalar_token(
|
||||
&self,
|
||||
sv: &scalar::Type,
|
||||
bh: &behavior::Type,
|
||||
) -> Vec<syn::WherePredicate> {
|
||||
match self {
|
||||
Self::Custom { parse_token, .. }
|
||||
| Self::Delegated {
|
||||
parse_token: Some(parse_token),
|
||||
..
|
||||
} => parse_token.bound_parse_scalar_token(sv),
|
||||
} => parse_token.bound_parse_scalar_token(sv, bh),
|
||||
|
||||
Self::Delegated { field, .. } => {
|
||||
let field_ty = field.ty();
|
||||
let field_bh = &field.behavior;
|
||||
|
||||
vec![parse_quote! {
|
||||
#field_ty: ::juniper::resolve::ScalarToken<#sv>
|
||||
::juniper::behavior::Coerce<#field_ty, #bh>:
|
||||
::juniper::resolve::ScalarToken<#sv, #field_bh>
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
@ -1494,7 +1544,7 @@ impl ParseToken {
|
|||
/// method.
|
||||
///
|
||||
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||
fn expand_parse_scalar_token(&self, sv: &scalar::Type) -> TokenStream {
|
||||
fn expand_parse_scalar_token(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream {
|
||||
match self {
|
||||
Self::Custom(parse_token) => {
|
||||
quote! { #parse_token(token) }
|
||||
|
@ -1506,14 +1556,16 @@ impl ParseToken {
|
|||
acc.map_or_else(
|
||||
|| {
|
||||
Some(quote! {
|
||||
<#ty as ::juniper::resolve::ScalarToken<#sv>>
|
||||
<::juniper::behavior::Coerce<#ty, #bh> as
|
||||
::juniper::resolve::ScalarToken<#sv>>
|
||||
::parse_scalar_token(token)
|
||||
})
|
||||
},
|
||||
|prev| {
|
||||
Some(quote! {
|
||||
#prev.or_else(|_| {
|
||||
<#ty as ::juniper::resolve::ScalarToken<#sv>>
|
||||
<::juniper::behavior::Coerce<#ty, #bh> as
|
||||
::juniper::resolve::ScalarToken<#sv>>
|
||||
::parse_scalar_token(token)
|
||||
})
|
||||
})
|
||||
|
@ -1530,7 +1582,11 @@ impl ParseToken {
|
|||
///
|
||||
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||
fn bound_parse_scalar_token(&self, sv: &scalar::Type) -> Vec<syn::WherePredicate> {
|
||||
fn bound_parse_scalar_token(
|
||||
&self,
|
||||
sv: &scalar::Type,
|
||||
bh: &behavior::Type,
|
||||
) -> Vec<syn::WherePredicate> {
|
||||
match self {
|
||||
Self::Custom(_) => {
|
||||
vec![parse_quote! {
|
||||
|
@ -1542,7 +1598,8 @@ impl ParseToken {
|
|||
.iter()
|
||||
.map(|ty| {
|
||||
parse_quote! {
|
||||
#ty: ::juniper::resolve::ScalarToken<#sv>
|
||||
::juniper::behavior::Coerce<#ty, #bh>:
|
||||
::juniper::resolve::ScalarToken<#sv>
|
||||
}
|
||||
})
|
||||
.collect(),
|
||||
|
@ -1550,20 +1607,29 @@ impl ParseToken {
|
|||
}
|
||||
}
|
||||
|
||||
/// Struct field to resolve not provided methods.
|
||||
enum Field {
|
||||
/// Named [`Field`].
|
||||
Named(syn::Field),
|
||||
/// Inner field of a type implementing [GraphQL scalar][0], that the
|
||||
/// implementation delegates calls to.
|
||||
///
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
struct Field {
|
||||
/// This [`Field`] itself.
|
||||
itself: syn::Field,
|
||||
|
||||
/// Unnamed [`Field`].
|
||||
Unnamed(syn::Field),
|
||||
/// Indicator whether this [`Field`] is named.
|
||||
is_named: bool,
|
||||
|
||||
/// [`Behavior`] parametrization of this [`Field`].
|
||||
///
|
||||
/// [`Behavior`]: juniper::behavior
|
||||
behavior: behavior::Type,
|
||||
}
|
||||
|
||||
impl ToTokens for Field {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
Self::Named(f) => f.ident.to_tokens(tokens),
|
||||
Self::Unnamed(_) => tokens.append(Literal::u8_unsuffixed(0)),
|
||||
if self.is_named {
|
||||
self.itself.ident.to_tokens(tokens)
|
||||
} else {
|
||||
tokens.append(Literal::u8_unsuffixed(0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1571,21 +1637,20 @@ impl ToTokens for Field {
|
|||
impl Field {
|
||||
/// [`syn::Type`] of this [`Field`].
|
||||
fn ty(&self) -> &syn::Type {
|
||||
match self {
|
||||
Self::Named(f) | Self::Unnamed(f) => &f.ty,
|
||||
}
|
||||
&self.itself.ty
|
||||
}
|
||||
|
||||
/// Generates closure to construct a [GraphQL scalar][0] struct from a
|
||||
/// [`Field`] value.
|
||||
/// Generates closure to construct a [GraphQL scalar][0] struct from an
|
||||
/// inner [`Field`] value.
|
||||
///
|
||||
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||
fn closure_constructor(&self) -> TokenStream {
|
||||
match self {
|
||||
Field::Named(syn::Field { ident, .. }) => {
|
||||
quote! { |v| Self { #ident: v } }
|
||||
}
|
||||
Field::Unnamed(_) => quote! { Self },
|
||||
if self.is_named {
|
||||
let ident = &self.itself.ident;
|
||||
|
||||
quote! { |v| Self { #ident: v } }
|
||||
} else {
|
||||
quote! { Self }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue