Impl codegen for scalars (resolve::ToInputValue trait), vol.5

This commit is contained in:
tyranron 2022-06-01 17:31:02 +02:00
parent 740aa9061e
commit 97d2da581a
No known key found for this signature in database
GPG key ID: 762E144FB230A4F0
17 changed files with 265 additions and 40 deletions

View file

@ -256,13 +256,17 @@ impl<S> InputValue<S> {
Self::Variable(v.as_ref().to_owned())
}
/// Construct a [`Spanning::unlocated`] list.
/// Constructs a [`Spanning::unlocated`] [`InputValue::List`].
///
/// Convenience function to make each [`InputValue`] in the input vector
/// not contain any location information. Can be used from [`ToInputValue`]
/// implementations, where no source code position information is available.
pub fn list(l: Vec<Self>) -> Self {
Self::List(l.into_iter().map(Spanning::unlocated).collect())
/// Convenience function to make each [`InputValue`] in the input `list` to
/// not contain any location information.
///
/// Intended for [`resolve::ToInputValue`] implementations, where no source
/// code position information is available.
///
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
pub fn list(list: impl IntoIterator<Item = Self>) -> Self {
Self::List(list.into_iter().map(Spanning::unlocated).collect())
}
/// Construct a located list.
@ -270,16 +274,25 @@ impl<S> InputValue<S> {
Self::List(l)
}
/// Construct aa [`Spanning::unlocated`] object.
/// Construct a [`Spanning::unlocated`] [`InputValue::Onject`].
///
/// Similarly to [`InputValue::list`] it makes each key and value in the
/// given hash map not contain any location information.
pub fn object<K>(o: IndexMap<K, Self>) -> Self
/// Similarly to [`InputValue::list()`] it makes each key and value in the
/// given `obj`ect to not contain any location information.
///
/// Intended for [`resolve::ToInputValue`] implementations, where no source
/// code position information is available.
///
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
// TODO: Use `impl IntoIterator<Item = (K, Self)>` argument once feature
// `explicit_generic_args_with_impl_trait` hits stable:
// https://github.com/rust-lang/rust/issues/83701
pub fn object<K, O>(obj: O) -> Self
where
K: AsRef<str> + Eq + Hash,
O: IntoIterator<Item = (K, Self)>,
{
Self::Object(
o.into_iter()
obj.into_iter()
.map(|(k, v)| {
(
Spanning::unlocated(k.as_ref().to_owned()),

View file

@ -2,9 +2,7 @@
use futures::future::BoxFuture;
use crate::{
Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue,
};
use crate::{Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue};
/// Alias for a [GraphQL object][1], [scalar][2] or [interface][3] type's name
/// in a GraphQL schema.

View file

@ -101,3 +101,7 @@ pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized {
pub trait InputValueOwned<S = DefaultScalarValue>: for<'i> InputValue<'i, S> {}
impl<T, S> InputValueOwned<S> for T where T: for<'i> InputValue<'i, S> {}
pub trait ToInputValue<S> {
fn to_input_value(&self) -> graphql::InputValue<S>;
}

View file

@ -143,12 +143,12 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Arc<T>
impl<T, S> resolve::ToInputValue<S> for Arc<T>
where
T: resolve::ScalarToken<S> + ?Sized,
T: resolve::ToInputValue<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
fn to_input_value(&self) -> graphql::InputValue<S> {
(**self).to_input_value()
}
}
@ -194,6 +194,15 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Arc<T>
where
T: resolve::ScalarToken<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
}
}
impl<T, S> graphql::InputType<S> for Arc<T>
where
T: graphql::InputType<S> + ?Sized,

View file

@ -64,6 +64,15 @@ where
}
}
impl<T, S, const N: usize> resolve::ToInputValue<S> for [T; N]
where
T: resolve::ToInputValue<S>,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
graphql::InputValue::list(self.iter().map(T::to_input_value))
}
}
impl<T, S, const N: usize> graphql::InputType<S> for [T; N]
where
T: graphql::InputType<S>,

View file

@ -141,12 +141,12 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Box<T>
impl<T, S> resolve::ToInputValue<S> for Box<T>
where
T: resolve::ScalarToken<S> + ?Sized,
T: resolve::ToInputValue<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
fn to_input_value(&self) -> graphql::InputValue<S> {
(**self).to_input_value()
}
}
@ -192,6 +192,15 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Box<T>
where
T: resolve::ScalarToken<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
}
}
impl<T, S> graphql::InputType<S> for Box<T>
where
T: graphql::InputType<S> + ?Sized,

View file

@ -183,7 +183,7 @@ where
S: ScalarValue,
{
fn to_input_value(&self) -> InputValue<S> {
InputValue::list(self.iter().map(T::to_input_value).collect())
InputValue::list(self.iter().map(T::to_input_value))
}
}
@ -283,7 +283,7 @@ where
S: ScalarValue,
{
fn to_input_value(&self) -> InputValue<S> {
InputValue::list(self.iter().map(T::to_input_value).collect())
InputValue::list(self.iter().map(T::to_input_value))
}
}
@ -481,7 +481,7 @@ where
S: ScalarValue,
{
fn to_input_value(&self) -> InputValue<S> {
InputValue::list(self.iter().map(T::to_input_value).collect())
InputValue::list(self.iter().map(T::to_input_value))
}
}

View file

@ -324,6 +324,18 @@ where
}
}
impl<T, S> resolve::ToInputValue<S> for Nullable<T>
where
T: resolve::ToInputValue<S>,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
match self {
Self::Some(v) => v.to_input_value(),
Self::ExplicitNull | Self::ImplicitNull => graphql::InputValue::Null,
}
}
}
impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Nullable<T>
where
T: resolve::InputValue<'inp, S>,

View file

@ -61,6 +61,18 @@ where
}
}
impl<T, S> resolve::ToInputValue<S> for Option<T>
where
T: resolve::ToInputValue<S>,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
match self {
Some(v) => v.to_input_value(),
None => graphql::InputValue::Null,
}
}
}
impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Option<T>
where
T: resolve::InputValue<'inp, S>,

View file

@ -143,12 +143,12 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Rc<T>
impl<T, S> resolve::ToInputValue<S> for Rc<T>
where
T: resolve::ScalarToken<S> + ?Sized,
T: resolve::ToInputValue<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
fn to_input_value(&self) -> graphql::InputValue<S> {
(**self).to_input_value()
}
}
@ -194,6 +194,15 @@ where
}
}
impl<T, S> resolve::ScalarToken<S> for Rc<T>
where
T: resolve::ScalarToken<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
}
}
impl<T, S> graphql::InputType<S> for Rc<T>
where
T: graphql::InputType<S> + ?Sized,

View file

@ -143,12 +143,12 @@ where
}
}
impl<'me, T, S> resolve::ScalarToken<S> for &'me T
impl<'me, T, S> resolve::ToInputValue<S> for &'me T
where
T: resolve::ScalarToken<S> + ?Sized,
T: resolve::ToInputValue<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
fn to_input_value(&self) -> graphql::InputValue<S> {
(**self).to_input_value()
}
}
@ -180,6 +180,15 @@ pub trait TryFromInputValue<S = DefaultScalarValue> {
}
}
impl<'me, T, S> resolve::ScalarToken<S> for &'me T
where
T: resolve::ScalarToken<S> + ?Sized,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
T::parse_scalar_token(token)
}
}
impl<'me, T, S> graphql::InputType<S> for &'me T
where
T: graphql::InputType<S> + ?Sized,

View file

@ -142,6 +142,15 @@ where
}
}
impl<'me, T, S> resolve::ToInputValue<S> for &'me mut T
where
T: resolve::ToInputValue<S> + ?Sized,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
(**self).to_input_value()
}
}
impl<'me, T, S> resolve::ScalarToken<S> for &'me mut T
where
T: resolve::ScalarToken<S> + ?Sized,

View file

@ -62,6 +62,15 @@ where
}
}
impl<T, S> resolve::ToInputValue<S> for [T]
where
T: resolve::ToInputValue<S>,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
graphql::InputValue::list(self.iter().map(T::to_input_value))
}
}
impl<T, S> graphql::InputType<S> for [T]
where
T: graphql::InputType<S>,

View file

@ -66,12 +66,12 @@ where
}
}
impl<S> resolve::ScalarToken<S> for str
impl<S> resolve::ToInputValue<S> for str
where
String: resolve::ScalarToken<S>,
S: From<String>,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
<String as resolve::ScalarToken<S>>::parse_scalar_token(token)
fn to_input_value(&self) -> graphql::InputValue<S> {
graphql::InputValue::scalar(self.to_owned())
}
}
@ -108,6 +108,15 @@ impl<'inp, S: ScalarValue> resolve::InputValueAsRc<'inp, S> for str {
}
}
impl<S> resolve::ScalarToken<S> for str
where
String: resolve::ScalarToken<S>,
{
fn parse_scalar_token(token: ScalarToken<'_>) -> Result<S, ParseError<'_>> {
<String as resolve::ScalarToken<S>>::parse_scalar_token(token)
}
}
impl<S> graphql::InputType<S> for str {
fn assert_input_type() {}
}

View file

@ -60,6 +60,15 @@ where
}
}
impl<T, S> resolve::ToInputValue<S> for Vec<T>
where
T: resolve::ToInputValue<S>,
{
fn to_input_value(&self) -> graphql::InputValue<S> {
graphql::InputValue::list(self.iter().map(T::to_input_value))
}
}
impl<T, S> graphql::InputType<S> for Vec<T>
where
T: graphql::InputType<S>,

View file

@ -6,6 +6,7 @@ use std::{any::TypeId, borrow::Cow, fmt, mem};
use crate::{
ast::{InputValue, ToInputValue},
parser::Spanning,
resolve,
};
pub use self::{
@ -190,6 +191,27 @@ impl<S: Clone> ToInputValue<S> for Value<S> {
}
}
impl<S: Clone> resolve::ToInputValue<S> for Value<S> {
fn to_input_value(&self) -> InputValue<S> {
// TODO: Simplify recursive calls syntax, once old `ToInputValue` trait
// is removed.
match self {
Self::Null => InputValue::Null,
Self::Scalar(s) => InputValue::Scalar(s.clone()),
Self::List(l) => InputValue::list(
l.iter()
.map(<Self as resolve::ToInputValue<S>>::to_input_value),
),
Self::Object(o) => InputValue::object(o.iter().map(|(k, v)| {
(
k.clone(),
<Self as resolve::ToInputValue<S>>::to_input_value(v),
)
})),
}
}
}
impl<S: ScalarValue> fmt::Display for Value<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {

View file

@ -334,6 +334,7 @@ impl ToTokens for Definition {
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_input_and_output_type().to_tokens(into);
@ -680,7 +681,7 @@ impl Definition {
fn impl_to_input_value_tokens(&self) -> TokenStream {
let scalar = &self.scalar;
let to_input_value = self.methods.expand_to_input_value(scalar);
let to_input_value = self.methods.expand_old_to_input_value(scalar);
let (ty, generics) = self.impl_self_and_generics(false);
let (impl_gens, _, where_clause) = generics.split_for_impl();
@ -697,6 +698,34 @@ impl Definition {
}
}
/// Returns generated code implementing [`resolve::ToInputValue`] trait for
/// this [GraphQL scalar][0].
///
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
/// [0]: https://spec.graphql.org/October2021#sec-Scalars
fn impl_resolve_to_input_value(&self) -> TokenStream {
let (ty, generics) = self.ty_and_generics();
let (sv, mut generics) = self.mix_scalar_value(generics);
generics
.make_where_clause()
.predicates
.push(self.methods.bound_to_input_value(sv));
let (impl_gens, _, where_clause) = generics.split_for_impl();
let body = self.methods.expand_to_input_value(sv);
quote! {
#[automatically_derived]
impl#impl_gens ::juniper::resolve::ToInputValue<#sv> for #ty
#where_clause
{
fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> {
#body
}
}
}
}
/// Returns generated code implementing [`FromInputValue`] trait for this
/// [GraphQL scalar][1].
///
@ -1084,7 +1113,7 @@ impl Methods {
}
}
/// Expands [`resolve::Value::resolve_value()`][0] method.
/// Expands body of [`resolve::Value::resolve_value()`][0] method.
///
/// [0]: juniper::resolve::Value::resolve_value
fn expand_resolve_value(
@ -1152,7 +1181,7 @@ impl Methods {
/// Expands [`ToInputValue::to_input_value`] method.
///
/// [`ToInputValue::to_input_value`]: juniper::ToInputValue::to_input_value
fn expand_to_input_value(&self, scalar: &scalar::Type) -> TokenStream {
fn expand_old_to_input_value(&self, scalar: &scalar::Type) -> TokenStream {
match self {
Self::Custom { to_output, .. }
| Self::Delegated {
@ -1172,6 +1201,60 @@ impl Methods {
}
}
/// Expands body of [`resolve::ToInputValue::to_input_value()`][0] method.
///
/// [0]: juniper::resolve::ToInputValue::to_input_value
fn expand_to_input_value(&self, sv: &scalar::Type) -> TokenStream {
match self {
Self::Custom { to_output, .. }
| Self::Delegated {
to_output: Some(to_output),
..
} => {
quote! {
let v = #to_output(self);
::juniper::resolve::ToInputValue::<#sv>::to_input_value(&v)
}
}
Self::Delegated { field, .. } => {
let field_ty = field.ty();
quote! {
<#field_ty as ::juniper::resolve::ToInputValue<#sv>>
::to_input_value(&self.#field)
}
}
}
}
/// Generates additional trait bounds for [`resolve::ToInputValue`]
/// implementation allowing to execute
/// [`resolve::ToInputValue::to_input_value()`][0] method.
///
/// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue
/// [0]: juniper::resolve::ToInputValue::to_input_value
fn bound_to_input_value(&self, sv: &scalar::Type) -> syn::WherePredicate {
match self {
Self::Custom { .. }
| Self::Delegated {
to_output: Some(_), ..
} => {
parse_quote! {
#sv: ::juniper::ScalarValue
}
}
Self::Delegated { field, .. } => {
let field_ty = field.ty();
parse_quote! {
#field_ty: ::juniper::resolve::ToInputValue<#sv>>
}
}
}
}
/// Expands [`FromInputValue::from_input_value`][1] method.
///
/// [1]: juniper::FromInputValue::from_input_value