Improve and polish codegen for scalars
This commit is contained in:
parent
224b04973a
commit
b381c696ff
2 changed files with 192 additions and 31 deletions
|
@ -66,7 +66,10 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: ScalarValue> resolve::ScalarToken<S> for str {
|
impl<S> resolve::ScalarToken<S> for str
|
||||||
|
where
|
||||||
|
String: resolve::ScalarToken<S>,
|
||||||
|
{
|
||||||
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
|
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
|
||||||
<String as resolve::ScalarToken<S>>::parse_scalar_token(token)
|
<String as resolve::ScalarToken<S>>::parse_scalar_token(token)
|
||||||
}
|
}
|
||||||
|
|
|
@ -332,6 +332,7 @@ impl ToTokens for Definition {
|
||||||
self.impl_reflection_traits_tokens().to_tokens(into);
|
self.impl_reflection_traits_tokens().to_tokens(into);
|
||||||
////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////
|
||||||
self.impl_resolve_type_name().to_tokens(into);
|
self.impl_resolve_type_name().to_tokens(into);
|
||||||
|
self.impl_resolve_type().to_tokens(into);
|
||||||
self.impl_resolve_input_value().to_tokens(into);
|
self.impl_resolve_input_value().to_tokens(into);
|
||||||
self.impl_resolve_scalar_token().to_tokens(into);
|
self.impl_resolve_scalar_token().to_tokens(into);
|
||||||
}
|
}
|
||||||
|
@ -409,29 +410,76 @@ impl Definition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`resolve::TypeName`] trait for this
|
/// Returns generated code implementing [`resolve::TypeName`] trait for this
|
||||||
/// [GraphQL scalar][1].
|
/// [GraphQL scalar][0].
|
||||||
///
|
///
|
||||||
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
/// [`resolve::TypeName`]: juniper::resolve::TypeName
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||||
fn impl_resolve_type_name(&self) -> TokenStream {
|
fn impl_resolve_type_name(&self) -> TokenStream {
|
||||||
let name = &self.name;
|
|
||||||
|
|
||||||
let (ty, generics) = self.ty_and_generics();
|
let (ty, generics) = self.ty_and_generics();
|
||||||
let (info_ty, generics) = self.mix_info_ty(generics);
|
let (info, generics) = self.mix_info(generics);
|
||||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let name = &self.name;
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl#impl_gens ::juniper::resolve::TypeName<#info_ty> for #ty
|
impl#impl_gens ::juniper::resolve::TypeName<#info> for #ty
|
||||||
#where_clause
|
#where_clause
|
||||||
{
|
{
|
||||||
fn type_name(_: &#info_ty) -> &'static str {
|
fn type_name(_: &#info) -> &'static str {
|
||||||
#name
|
#name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns generated code implementing [`resolve::Type`] trait for this
|
||||||
|
/// [GraphQL scalar][0].
|
||||||
|
///
|
||||||
|
/// [`resolve::Type`]: juniper::resolve::Type
|
||||||
|
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||||
|
fn impl_resolve_type(&self) -> TokenStream {
|
||||||
|
let (ty, generics) = self.ty_and_generics();
|
||||||
|
let (info, generics) = self.mix_info(generics);
|
||||||
|
let (scalar, mut generics) = self.mix_scalar(generics);
|
||||||
|
generics.make_where_clause().predicates.push(parse_quote! {
|
||||||
|
Self: ::juniper::resolve::TypeName<#info>
|
||||||
|
+ ::juniper::resolve::ScalarToken<#scalar>
|
||||||
|
+ ::juniper::resolve::InputValueOwned<#scalar>
|
||||||
|
});
|
||||||
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let description = self
|
||||||
|
.description
|
||||||
|
.as_ref()
|
||||||
|
.map(|val| quote! { .description(#val) });
|
||||||
|
|
||||||
|
let specified_by_url = self.specified_by_url.as_ref().map(|url| {
|
||||||
|
let url_lit = url.as_str();
|
||||||
|
quote! { .specified_by_url(#url_lit) }
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[automatically_derived]
|
||||||
|
impl#impl_gens ::juniper::resolve::Type<#info, #scalar> for #ty
|
||||||
|
#where_clause
|
||||||
|
{
|
||||||
|
fn meta<'r>(
|
||||||
|
registry: &mut ::juniper::Registry<'r, #scalar>,
|
||||||
|
info: &#info,
|
||||||
|
) -> ::juniper::meta::MetaType<'r, #scalar>
|
||||||
|
where
|
||||||
|
#scalar: 'r,
|
||||||
|
{
|
||||||
|
registry.build_scalar_type_new::<Self, _>(info)
|
||||||
|
#description
|
||||||
|
#specified_by_url
|
||||||
|
.into_meta()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
/// Returns generated code implementing [`GraphQLValue`] trait for this
|
||||||
/// [GraphQL scalar][1].
|
/// [GraphQL scalar][1].
|
||||||
///
|
///
|
||||||
|
@ -553,19 +601,23 @@ impl Definition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`resolve::InputValue`] trait for
|
/// Returns generated code implementing [`resolve::InputValue`] trait for
|
||||||
/// this [GraphQL scalar][1].
|
/// this [GraphQL scalar][0].
|
||||||
///
|
///
|
||||||
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||||
fn impl_resolve_input_value(&self) -> TokenStream {
|
fn impl_resolve_input_value(&self) -> TokenStream {
|
||||||
let conversion = self.methods.expand_try_from_input_value(&self.scalar);
|
|
||||||
|
|
||||||
let (ty, generics) = self.ty_and_generics();
|
let (ty, generics) = self.ty_and_generics();
|
||||||
let (scalar, mut generics) = self.mix_scalar_ty(generics);
|
let (scalar, mut generics) = self.mix_scalar(generics);
|
||||||
let lt: syn::GenericParam = parse_quote! { '__inp };
|
let lt: syn::GenericParam = parse_quote! { '__inp };
|
||||||
generics.params.push(lt.clone());
|
generics.params.push(lt.clone());
|
||||||
|
generics
|
||||||
|
.make_where_clause()
|
||||||
|
.predicates
|
||||||
|
.push(self.methods.bound_try_from_input_value(scalar, <));
|
||||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let conversion = self.methods.expand_try_from_input_value(scalar);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl#impl_gens ::juniper::resolve::InputValue<#lt, #scalar> for #ty
|
impl#impl_gens ::juniper::resolve::InputValue<#lt, #scalar> for #ty
|
||||||
|
@ -575,7 +627,7 @@ impl Definition {
|
||||||
|
|
||||||
fn try_from_input_value(
|
fn try_from_input_value(
|
||||||
input: &#lt ::juniper::graphql::InputValue<#scalar>,
|
input: &#lt ::juniper::graphql::InputValue<#scalar>,
|
||||||
) -> Result<Self, Self::Error> {
|
) -> ::std::result::Result<Self, Self::Error> {
|
||||||
#conversion.map_err(
|
#conversion.map_err(
|
||||||
::juniper::IntoFieldError::<#scalar>::into_field_error,
|
::juniper::IntoFieldError::<#scalar>::into_field_error,
|
||||||
)
|
)
|
||||||
|
@ -612,17 +664,21 @@ impl Definition {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns generated code implementing [`resolve::ScalarToken`] trait for
|
/// Returns generated code implementing [`resolve::ScalarToken`] trait for
|
||||||
/// this [GraphQL scalar][1].
|
/// this [GraphQL scalar][0].
|
||||||
///
|
///
|
||||||
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||||
fn impl_resolve_scalar_token(&self) -> TokenStream {
|
fn impl_resolve_scalar_token(&self) -> TokenStream {
|
||||||
let body = self.methods.expand_parse_scalar_token(&self.scalar);
|
|
||||||
|
|
||||||
let (ty, generics) = self.ty_and_generics();
|
let (ty, generics) = self.ty_and_generics();
|
||||||
let (scalar, generics) = self.mix_scalar_ty(generics);
|
let (scalar, mut generics) = self.mix_scalar(generics);
|
||||||
|
generics
|
||||||
|
.make_where_clause()
|
||||||
|
.predicates
|
||||||
|
.extend(self.methods.bound_parse_scalar_token(scalar));
|
||||||
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
let (impl_gens, _, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
let body = self.methods.expand_parse_scalar_token(scalar);
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
#[automatically_derived]
|
#[automatically_derived]
|
||||||
impl#impl_gens ::juniper::resolve::ScalarToken<#scalar> for #ty
|
impl#impl_gens ::juniper::resolve::ScalarToken<#scalar> for #ty
|
||||||
|
@ -757,6 +813,8 @@ impl Definition {
|
||||||
(ty, generics)
|
(ty, generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait
|
||||||
|
/// implementation.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
fn ty_and_generics(&self) -> (syn::Type, syn::Generics) {
|
||||||
let mut generics = self.generics.clone();
|
let mut generics = self.generics.clone();
|
||||||
|
@ -779,8 +837,10 @@ impl Definition {
|
||||||
(ty, generics)
|
(ty, generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mixes a type info [`syn::GenericParam`] into the provided
|
||||||
|
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn mix_info_ty(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
fn mix_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||||
let ty = parse_quote! { __Info };
|
let ty = parse_quote! { __Info };
|
||||||
|
|
||||||
generics.params.push(parse_quote! { #ty: ?Sized });
|
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||||
|
@ -788,19 +848,32 @@ impl Definition {
|
||||||
(ty, generics)
|
(ty, generics)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Mixes a context [`syn::GenericParam`] into the provided
|
||||||
|
/// [`syn::Generics`] and returns its [`syn::Ident`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn mix_scalar_ty(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) {
|
fn mix_context(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) {
|
||||||
|
let ty = parse_quote! { __Ctx };
|
||||||
|
|
||||||
|
generics.params.push(parse_quote! { #ty: ?Sized });
|
||||||
|
|
||||||
|
(ty, generics)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mixes a [`ScalarValue`] [`syn::GenericParam`] into the provided
|
||||||
|
/// [`syn::Generics`] and returns its [`scalar::Type`].
|
||||||
|
///
|
||||||
|
/// [`ScalarValue`] trait bound is not made here, because some trait
|
||||||
|
/// implementations may not require it depending on the generated code or
|
||||||
|
/// even at all.
|
||||||
|
///
|
||||||
|
/// [`ScalarValue`]: juniper::ScalarValue
|
||||||
|
#[must_use]
|
||||||
|
fn mix_scalar(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) {
|
||||||
let scalar = &self.scalar;
|
let scalar = &self.scalar;
|
||||||
|
|
||||||
if scalar.is_implicit_generic() {
|
if scalar.is_implicit_generic() {
|
||||||
generics.params.push(parse_quote! { #scalar });
|
generics.params.push(parse_quote! { #scalar });
|
||||||
}
|
}
|
||||||
if scalar.is_generic() {
|
|
||||||
generics
|
|
||||||
.make_where_clause()
|
|
||||||
.predicates
|
|
||||||
.push(parse_quote! { #scalar: ::juniper::ScalarValue });
|
|
||||||
}
|
|
||||||
if let Some(bound) = scalar.bounds() {
|
if let Some(bound) = scalar.bounds() {
|
||||||
generics.make_where_clause().predicates.push(bound);
|
generics.make_where_clause().predicates.push(bound);
|
||||||
}
|
}
|
||||||
|
@ -957,6 +1030,38 @@ impl Methods {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates additional trait bounds for [`resolve::InputValue`]
|
||||||
|
/// implementation allowing to execute
|
||||||
|
/// [`resolve::InputValue::try_from_input_value()`][0] method.
|
||||||
|
///
|
||||||
|
/// [`resolve::InputValue`]: juniper::resolve::InputValue
|
||||||
|
/// [0]: juniper::resolve::InputValue::try_from_input_value
|
||||||
|
fn bound_try_from_input_value(
|
||||||
|
&self,
|
||||||
|
scalar: &scalar::Type,
|
||||||
|
lt: &syn::GenericParam,
|
||||||
|
) -> syn::WherePredicate {
|
||||||
|
match self {
|
||||||
|
Self::Custom { .. }
|
||||||
|
| Self::Delegated {
|
||||||
|
from_input: Some(_),
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
parse_quote! {
|
||||||
|
#scalar: ::juniper::ScalarValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Delegated { field, .. } => {
|
||||||
|
let field_ty = field.ty();
|
||||||
|
|
||||||
|
parse_quote! {
|
||||||
|
#field_ty: ::juniper::resolve::InputValue<#lt, #scalar>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Expands [`ParseScalarValue::from_str`] method.
|
/// Expands [`ParseScalarValue::from_str`] method.
|
||||||
///
|
///
|
||||||
/// [`ParseScalarValue::from_str`]: juniper::ParseScalarValue::from_str
|
/// [`ParseScalarValue::from_str`]: juniper::ParseScalarValue::from_str
|
||||||
|
@ -982,7 +1087,7 @@ impl Methods {
|
||||||
/// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0]
|
/// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0]
|
||||||
/// method.
|
/// method.
|
||||||
///
|
///
|
||||||
/// [0]: resolve::ScalarToken::parse_scalar_token
|
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||||
fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream {
|
fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom { parse_token, .. }
|
Self::Custom { parse_token, .. }
|
||||||
|
@ -990,8 +1095,10 @@ impl Methods {
|
||||||
parse_token: Some(parse_token),
|
parse_token: Some(parse_token),
|
||||||
..
|
..
|
||||||
} => parse_token.expand_parse_scalar_token(scalar),
|
} => parse_token.expand_parse_scalar_token(scalar),
|
||||||
|
|
||||||
Self::Delegated { field, .. } => {
|
Self::Delegated { field, .. } => {
|
||||||
let field_ty = field.ty();
|
let field_ty = field.ty();
|
||||||
|
|
||||||
quote! {
|
quote! {
|
||||||
<#field_ty as ::juniper::resolve::ScalarToken<#scalar>>
|
<#field_ty as ::juniper::resolve::ScalarToken<#scalar>>
|
||||||
::parse_scalar_token(token)
|
::parse_scalar_token(token)
|
||||||
|
@ -999,6 +1106,30 @@ impl Methods {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates additional trait bounds for [`resolve::ScalarToken`]
|
||||||
|
/// implementation allowing to execute
|
||||||
|
/// [`resolve::ScalarToken::parse_scalar_token()`][0] method.
|
||||||
|
///
|
||||||
|
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||||
|
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||||
|
fn bound_parse_scalar_token(&self, scalar: &scalar::Type) -> Vec<syn::WherePredicate> {
|
||||||
|
match self {
|
||||||
|
Self::Custom { parse_token, .. }
|
||||||
|
| Self::Delegated {
|
||||||
|
parse_token: Some(parse_token),
|
||||||
|
..
|
||||||
|
} => parse_token.bound_parse_scalar_token(scalar),
|
||||||
|
|
||||||
|
Self::Delegated { field, .. } => {
|
||||||
|
let field_ty = field.ty();
|
||||||
|
|
||||||
|
vec![parse_quote! {
|
||||||
|
#field_ty: ::juniper::resolve::ScalarToken<#scalar>
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Representation of [`ParseScalarValue::from_str`] method.
|
/// Representation of [`ParseScalarValue::from_str`] method.
|
||||||
|
@ -1046,12 +1177,13 @@ impl ParseToken {
|
||||||
/// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0]
|
/// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0]
|
||||||
/// method.
|
/// method.
|
||||||
///
|
///
|
||||||
/// [0]: resolve::ScalarToken::parse_scalar_token
|
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||||
fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream {
|
fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream {
|
||||||
match self {
|
match self {
|
||||||
Self::Custom(parse_token) => {
|
Self::Custom(parse_token) => {
|
||||||
quote! { #parse_token(token) }
|
quote! { #parse_token(token) }
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Delegated(delegated) => delegated
|
Self::Delegated(delegated) => delegated
|
||||||
.iter()
|
.iter()
|
||||||
.fold(None, |acc, ty| {
|
.fold(None, |acc, ty| {
|
||||||
|
@ -1075,6 +1207,31 @@ impl ParseToken {
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Generates additional trait bounds for [`resolve::ScalarToken`]
|
||||||
|
/// implementation allowing to execute
|
||||||
|
/// [`resolve::ScalarToken::parse_scalar_token()`][0] method.
|
||||||
|
///
|
||||||
|
/// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken
|
||||||
|
/// [0]: juniper::resolve::ScalarToken::parse_scalar_token
|
||||||
|
fn bound_parse_scalar_token(&self, scalar: &scalar::Type) -> Vec<syn::WherePredicate> {
|
||||||
|
match self {
|
||||||
|
Self::Custom(_) => {
|
||||||
|
vec![parse_quote! {
|
||||||
|
#scalar: ::juniper::ScalarValue
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
Self::Delegated(delegated) => delegated
|
||||||
|
.iter()
|
||||||
|
.map(|ty| {
|
||||||
|
parse_quote! {
|
||||||
|
#ty: ::juniper::resolve::ScalarToken<#scalar>
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Struct field to resolve not provided methods.
|
/// Struct field to resolve not provided methods.
|
||||||
|
@ -1103,9 +1260,10 @@ impl Field {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Closure to construct [GraphQL scalar][1] struct from [`Field`].
|
/// Generates closure to construct a [GraphQL scalar][0] struct from a
|
||||||
|
/// [`Field`] value.
|
||||||
///
|
///
|
||||||
/// [1]: https://spec.graphql.org/October2021#sec-Scalars
|
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
|
||||||
fn closure_constructor(&self) -> TokenStream {
|
fn closure_constructor(&self) -> TokenStream {
|
||||||
match self {
|
match self {
|
||||||
Field::Named(syn::Field { ident, .. }) => {
|
Field::Named(syn::Field { ident, .. }) => {
|
||||||
|
|
Loading…
Add table
Reference in a new issue