diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 2026f97b..73d8b6a4 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -85,6 +85,12 @@ where field_path: Arc<FieldPath<'a>>, } +impl<'r, 'a, CX: ?Sized, SV> Executor<'r, 'a, CX, SV> { + pub(crate) fn current_type_reworked(&self) -> &TypeType<'a, SV> { + &self.current_type + } +} + /// Error type for errors that occur during query execution /// /// All execution errors contain the source position in the query of the field @@ -1381,17 +1387,17 @@ impl<'r, S: 'r> Registry<'r, S> { /// values of this type matches it. /// /// [`graphql::Type`]: resolve::Type - pub fn build_list_type_new<T, Info>( + pub fn wrap_list<'ti, T, TI>( &mut self, - info: &Info, + type_info: &'ti TI, expected_size: Option<usize>, - ) -> ListMeta<'r> + ) -> MetaType<'r, S> where - T: resolve::Type<Info, S> + ?Sized, - Info: ?Sized, + T: resolve::Type<TI, S> + ?Sized, + TI: ?Sized, + 'ti: 'r, { - todo!() - //ListMeta::new(T::meta(self, info).as_type(), expected_size) + ListMeta::new(T::meta(self, type_info).into(), expected_size).into_meta() } /// Creates a [`NullableMeta`] type. diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 1113cffe..47e7f705 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -563,7 +563,7 @@ impl<'a, S> ScalarMeta<'a, S> { } impl<'a> ListMeta<'a> { - /// Build a new [`ListMeta`] type by wrapping the specified [`Type`]. + /// Builds a new [`ListMeta`] type by wrapping the specified [`Type`]. /// /// Specifying `expected_size` will be used to ensure that values of this /// type will always match it. @@ -581,7 +581,7 @@ impl<'a> ListMeta<'a> { } impl<'a> NullableMeta<'a> { - /// Build a new [`NullableMeta`] type by wrapping the specified [`Type`]. + /// Builds a new [`NullableMeta`] type by wrapping the specified [`Type`]. pub fn new(of_type: Type<'a>) -> Self { Self { of_type } } diff --git a/juniper/src/types/iter.rs b/juniper/src/types/iter.rs index 8d6aad9d..f0f599a0 100644 --- a/juniper/src/types/iter.rs +++ b/juniper/src/types/iter.rs @@ -2,28 +2,28 @@ use crate::{graphql, resolve, ExecutionResult, Executor, Selection}; -/* -pub fn resolve_list<'t, T, S, Info, Ctx, I>( +pub fn resolve_list<'t, T, TI, CX, SV, BH, I>( iter: I, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor<Ctx, S>, -) -> ExecutionResult<S> + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor<CX, SV>, +) -> ExecutionResult<SV> where I: Iterator<Item = &'t T> + ExactSizeIterator, - T: resolve::Value<Info, Ctx, S> + ?Sized + 't, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value<TI, CX, SV, BH> + ?Sized + 't, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { let is_non_null = executor - .current_type_new() + .current_type_reworked() .list_contents() .ok_or("Iterating over non-list type")? .is_non_null(); let mut values = Vec::with_capacity(iter.len()); for v in iter { - let val = v.resolve_value(selection_set, info, executor)?; + let val = v.resolve_value(selection_set, type_info, executor)?; if is_non_null && val.is_null() { return Err("Resolved `null` on non-null type".into()); } @@ -32,28 +32,32 @@ where Ok(graphql::Value::list(values)) } -pub async fn resolve_list_async<'a, 't, T, S, Info, Ctx, I>( +pub async fn resolve_list_async<'t, 'r, T, TI, CX, SV, BH, I>( iter: I, - selection_set: Option<&[Selection<'_, S>]>, - info: &'a Info, - executor: &'a Executor<'a, 'a, Ctx, S>, -) -> ExecutionResult<S> + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor<'r, '_, CX, SV>, +) -> ExecutionResult<SV> where I: Iterator<Item = &'t T> + ExactSizeIterator, - T: resolve::ValueAsync<Info, Ctx, S> + ?Sized + 't, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync<TI, CX, SV, BH> + ?Sized + 't, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { use futures::stream::{FuturesOrdered, StreamExt as _}; let is_non_null = executor - .current_type_new() + .current_type_reworked() .list_contents() .ok_or("Iterating over non-list type")? .is_non_null(); let mut futs = iter - .map(|v| async move { v.resolve_value_async(selection_set, info, executor).await }) + .map(|v| async move { + v.resolve_value_async(selection_set, type_info, executor) + .await + }) .collect::<FuturesOrdered<_>>(); let mut values = Vec::with_capacity(futs.len()); @@ -66,4 +70,3 @@ where } Ok(graphql::Value::list(values)) } -*/ diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index d331959a..44635ccd 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -10,7 +10,7 @@ mod ref_mut; mod result; mod slice; mod r#str; -mod vec; +pub mod vec; pub mod async_await; pub mod base; diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index ad4cd8ed..df2d87a6 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -1,114 +1,175 @@ //! GraphQL implementation for [`Vec`]. use crate::{ + behavior, executor::{ExecutionResult, Executor, Registry}, graphql, reflect, resolve, schema::meta::MetaType, - BoxFuture, Selection, + BoxFuture, FieldError, IntoFieldError, Selection, }; use super::iter; -/* -impl<T, Info, S> resolve::Type<Info, S> for Vec<T> +impl<T, TI, SV, BH> resolve::Type<TI, SV, BH> for Vec<T> where - T: resolve::Type<Info, S>, - Info: ?Sized, + T: resolve::Type<TI, SV, BH>, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { - registry.build_list_type_new::<T, _>(info, None).into_meta() + registry.wrap_list::<behavior::Coerce<T, BH>, _>(type_info, None) } } -impl<T, Info, Ctx, S> resolve::Value<Info, Ctx, S> for Vec<T> +impl<T, TI, CX, SV, BH> resolve::Value<TI, CX, SV, BH> for Vec<T> where - T: resolve::Value<Info, Ctx, S>, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value<TI, CX, SV, BH>, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor<Ctx, S>, - ) -> ExecutionResult<S> { - iter::resolve_list(self.iter(), selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor<CX, SV>, + ) -> ExecutionResult<SV> { + iter::resolve_list(self.iter(), selection_set, type_info, executor) } } -impl<T, Info, Ctx, S> resolve::ValueAsync<Info, Ctx, S> for Vec<T> +impl<T, TI, CX, SV, BH> resolve::ValueAsync<TI, CX, SV, BH> for Vec<T> where - T: resolve::ValueAsync<Info, Ctx, S> + Sync, - Info: Sync + ?Sized, - Ctx: Sync + ?Sized, - S: Send + Sync, + T: resolve::ValueAsync<TI, CX, SV, BH> + Sync, + TI: Sync + ?Sized, + CX: Sync + ?Sized, + SV: Send + Sync, + BH: ?Sized + 'static, // TODO: Lift `'static` bound if possible. { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor<Ctx, S>, - ) -> BoxFuture<'r, ExecutionResult<S>> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor<CX, SV>, + ) -> BoxFuture<'r, ExecutionResult<SV>> { Box::pin(iter::resolve_list_async( self.iter(), selection_set, - info, + type_info, executor, )) } } -impl<T, S> resolve::ToInputValue<S> for Vec<T> +impl<T, SV, BH> resolve::ToInputValue<SV, BH> for Vec<T> where - T: resolve::ToInputValue<S>, + T: resolve::ToInputValue<SV, BH>, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue<S> { + fn to_input_value(&self) -> graphql::InputValue<SV> { graphql::InputValue::list(self.iter().map(T::to_input_value)) } } - -impl<'i, T, Info, S> graphql::InputType<'i, Info, S> for Vec<T> +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Vec<T> where - T: graphql::InputType<'i, Info, S>, - Info: ?Sized, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = TryFromInputValueError<T::Error>; + + fn try_from_input_value(v: &'i graphql::InputValue<SV>) -> Result<Self, Self::Error> { + match v { + graphql::InputValue::List(l) => l + .iter() + .map(|i| T::try_from_input_value(&i.item).map_err(TryFromInputValueError::Item)) + .collect(), + // See "Input Coercion" on List types: + // https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null + graphql::InputValue::Null => Err(TryFromInputValueError::IsNull), + other => T::try_from_input_value(other) + .map(|e| vec![e]) + .map_err(TryFromInputValueError::Item), + } + } +} + +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Vec<T> +where + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } - -impl<T, S> graphql::OutputType<S> for Vec<T> +impl<T, TI, CX, SV, BH> graphql::OutputType<TI, CX, SV, BH> for Vec<T> where - T: graphql::OutputType<S>, + T: graphql::OutputType<TI, CX, SV, BH>, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync<TI, CX, SV, BH>, { fn assert_output_type() { T::assert_output_type() } } -impl<T, S> reflect::BaseType<S> for Vec<T> +impl<T, BH> reflect::BaseType<BH> for Vec<T> where - T: reflect::BaseType<S>, + T: reflect::BaseType<BH>, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl<T, S> reflect::BaseSubTypes<S> for Vec<T> +impl<T, BH> reflect::BaseSubTypes<BH> for Vec<T> where - T: reflect::BaseSubTypes<S>, + T: reflect::BaseSubTypes<BH>, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl<T, S> reflect::WrappedType<S> for Vec<T> +impl<T, BH> reflect::WrappedType<BH> for Vec<T> where - T: reflect::WrappedType<S>, + T: reflect::WrappedType<BH>, + BH: ?Sized, { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } -*/ + +/// Possible errors of converting a [`graphql::InputValue`] into a [`Vec`]. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TryFromInputValueError<E> { + /// [`graphql::InputValue`] cannot be [`Null`]. + /// + /// See ["Combining List and Non-Null" section of spec][0]. + /// + /// [`Null`]: [`InputValue::Null`] + /// [0]: https://spec.graphql.org/October2021#sec-Combining-List-and-Non-Null + IsNull, + + /// Error of converting a [`graphql::InputValue::List`]'s item. + Item(E), +} + +impl<E, SV> IntoFieldError<SV> for TryFromInputValueError<E> +where + E: IntoFieldError<SV>, +{ + fn into_field_error(self) -> FieldError<SV> { + match self { + Self::IsNull => "Failed to convert into `Vec`: Value cannot be `null`".into(), + Self::Item(e) => e.into_field_error(), + } + } +} diff --git a/juniper_codegen/src/common/mod.rs b/juniper_codegen/src/common/mod.rs index ae2e9257..638042dd 100644 --- a/juniper_codegen/src/common/mod.rs +++ b/juniper_codegen/src/common/mod.rs @@ -1,7 +1,7 @@ //! Common functions, definitions and extensions for code generation, used by this crate. +pub(crate) mod behavior; pub(crate) mod field; pub(crate) mod gen; pub(crate) mod parse; pub(crate) mod scalar; -pub(crate) mod behavior;