From 56a68a9e24f2be488225d62c28f28ce6f883d3d1 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 19 Apr 2022 18:47:42 +0300 Subject: [PATCH 01/58] Bootstrap, vol.1 --- juniper/src/graphql/mod.rs | 1 + juniper/src/graphql/resolve.rs | 33 +++++++++++++++++++++++++++++++++ juniper/src/lib.rs | 9 +++++---- juniper/src/resolve/mod.rs | 12 ++++++++++++ 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 juniper/src/graphql/mod.rs create mode 100644 juniper/src/graphql/resolve.rs create mode 100644 juniper/src/resolve/mod.rs diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs new file mode 100644 index 000000000..581d1dcd4 --- /dev/null +++ b/juniper/src/graphql/mod.rs @@ -0,0 +1 @@ +pub mod resolve; diff --git a/juniper/src/graphql/resolve.rs b/juniper/src/graphql/resolve.rs new file mode 100644 index 000000000..ef411da2e --- /dev/null +++ b/juniper/src/graphql/resolve.rs @@ -0,0 +1,33 @@ +use crate::{executor::Registry, resolve, schema::meta::MetaType, DefaultScalarValue}; + +pub trait TypeName { + fn type_name(info: &Info) -> &str + where + Self: resolve::TypeName; +} + +impl TypeName for T { + fn type_name(info: &Info) -> &str + where + Self: resolve::TypeName, + { + >::type_name(info) + } +} + +pub trait Type { + fn meta<'r, Info: ?Sized>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + Self: resolve::Type; +} + +impl Type for T { + fn meta<'r, Info: ?Sized>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + Self: resolve::Type, + { + >::meta(info, registry) + } +} diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 0373175e2..fa869cbd0 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -28,19 +28,20 @@ pub use juniper_codegen::{ #[doc(hidden)] #[macro_use] pub mod macros; + mod ast; pub mod executor; +pub mod graphql; +pub mod http; +pub mod integrations; mod introspection; pub mod parser; +pub mod resolve; pub(crate) mod schema; mod types; mod util; pub mod validation; mod value; -// This needs to be public until docs have support for private modules: -// https://github.com/rust-lang/cargo/issues/1520 -pub mod http; -pub mod integrations; #[cfg(all(test, not(feature = "expose-test-schema")))] mod tests; diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs new file mode 100644 index 000000000..9016b9ac8 --- /dev/null +++ b/juniper/src/resolve/mod.rs @@ -0,0 +1,12 @@ +use crate::{executor::Registry, schema::meta::MetaType, DefaultScalarValue, ScalarValue}; + +pub trait TypeName { + fn type_name(info: &Info) -> &str; +} + +pub trait Type { + fn meta<'r>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r; +} + From 4ef63e0b7c26b7b39e7e184eaba49ddb36cf0b53 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 19 Apr 2022 19:04:34 +0300 Subject: [PATCH 02/58] Bootstrap, vol.2 --- juniper/src/executor/mod.rs | 2 +- juniper/src/graphql/resolve.rs | 51 +++++++++++++++++++++++++++++++++- juniper/src/resolve/mod.rs | 19 ++++++++++++- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 47d5fead2..cf35af155 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -69,7 +69,7 @@ pub enum FieldPath<'a> { /// of the current field stack, context, variables, and errors. pub struct Executor<'r, 'a, CtxT, S = DefaultScalarValue> where - CtxT: 'a, + CtxT: ?Sized + 'a, S: 'a, { fragments: &'r HashMap<&'a str, Fragment<'a, S>>, diff --git a/juniper/src/graphql/resolve.rs b/juniper/src/graphql/resolve.rs index ef411da2e..068115a86 100644 --- a/juniper/src/graphql/resolve.rs +++ b/juniper/src/graphql/resolve.rs @@ -1,4 +1,9 @@ -use crate::{executor::Registry, resolve, schema::meta::MetaType, DefaultScalarValue}; +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + resolve, + schema::meta::MetaType, + Arguments, DefaultScalarValue, +}; pub trait TypeName { fn type_name(info: &Info) -> &str @@ -31,3 +36,47 @@ impl Type for T { >::meta(info, registry) } } + +pub trait Field { + fn resolve_field( + &self, + info: &Info, + field_name: &str, + arguments: &Arguments, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::Field; +} + +impl Field for T { + fn resolve_field( + &self, + info: &Info, + field_name: &str, + arguments: &Arguments, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::Field, + { + >::resolve_field( + self, info, field_name, arguments, executor, + ) + } +} + +pub trait ConcreteTypeName { + fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str + where + Self: resolve::ConcreteTypeName; +} + +impl ConcreteTypeName for T { + fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str + where + Self: resolve::ConcreteTypeName, + { + >::concrete_type_name(self, info) + } +} diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 9016b9ac8..27b28a196 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -1,4 +1,8 @@ -use crate::{executor::Registry, schema::meta::MetaType, DefaultScalarValue, ScalarValue}; +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + schema::meta::MetaType, + Arguments, DefaultScalarValue, +}; pub trait TypeName { fn type_name(info: &Info) -> &str; @@ -10,3 +14,16 @@ pub trait Type { S: 'r; } +pub trait Field { + fn resolve_field( + &self, + info: &Info, + field_name: &str, + arguments: &Arguments, + executor: &Executor, + ) -> ExecutionResult; +} + +pub trait ConcreteTypeName { + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str; +} From 2d1e5d38b753859401ad634fddc0ecd75ad3da21 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 22 Apr 2022 16:48:19 +0300 Subject: [PATCH 03/58] Impl some basic types --- juniper/src/executor/mod.rs | 63 +++++++++++ juniper/src/graphql/mod.rs | 55 ++++++++++ juniper/src/graphql/resolve.rs | 194 +++++++++++++++++++++++++++++---- juniper/src/lib.rs | 2 +- juniper/src/resolve/mod.rs | 67 ++++++++++-- juniper/src/types/arc.rs | 186 +++++++++++++++++++++++++++++++ juniper/src/types/box.rs | 184 +++++++++++++++++++++++++++++++ juniper/src/types/iter.rs | 65 +++++++++++ juniper/src/types/mod.rs | 9 ++ juniper/src/types/option.rs | 78 +++++++++++++ juniper/src/types/rc.rs | 186 +++++++++++++++++++++++++++++++ juniper/src/types/ref.rs | 182 +++++++++++++++++++++++++++++++ juniper/src/types/ref_mut.rs | 182 +++++++++++++++++++++++++++++++ juniper/src/types/vec.rs | 77 +++++++++++++ 14 files changed, 1496 insertions(+), 34 deletions(-) create mode 100644 juniper/src/types/arc.rs create mode 100644 juniper/src/types/box.rs create mode 100644 juniper/src/types/iter.rs create mode 100644 juniper/src/types/option.rs create mode 100644 juniper/src/types/rc.rs create mode 100644 juniper/src/types/ref.rs create mode 100644 juniper/src/types/ref_mut.rs create mode 100644 juniper/src/types/vec.rs diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index cf35af155..23073222f 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -17,6 +17,7 @@ use crate::{ Selection, ToInputValue, Type, }, parser::{SourcePosition, Spanning}, + resolve, schema::{ meta::{ Argument, DeprecationStatus, EnumMeta, EnumValue, Field, InputObjectMeta, @@ -83,6 +84,12 @@ where field_path: Arc>, } +impl<'r, 'a, Ctx: ?Sized, S> Executor<'r, 'a, Ctx, S> { + pub(crate) fn current_type_new(&self) -> &TypeType<'a, S> { + &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 @@ -1183,6 +1190,32 @@ impl<'r, S: 'r> Registry<'r, S> { } } + /// Returns a [`Type`] meta information for the specified [`graphql::Type`], + /// registered in this [`Registry`]. + /// + /// If this [`Registry`] doesn't contain a [`Type`] meta information with + /// such [`TypeName`] before, it will construct the one and store it. + /// + /// [`graphql::Type`]: resolve::Type + /// [`TypeName`]: resolve::TypeName + pub fn get_type_new(&mut self, info: &Info) -> Type<'r> + where + T: resolve::Type + resolve::TypeName + ?Sized, + Info: ?Sized, + { + let name = T::type_name(info); + let validated_name = name.parse::().unwrap(); + if !self.types.contains_key(name) { + self.insert_placeholder( + validated_name.clone(), + Type::NonNullNamed(Cow::Owned(name.to_string())), + ); + let meta = T::meta(self, info); + self.types.insert(validated_name, meta); + } + self.types[name].as_type() + } + /// Creates a [`Field`] with the provided `name`. pub fn field(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S> where @@ -1278,6 +1311,24 @@ impl<'r, S: 'r> Registry<'r, S> { ListMeta::new(of_type, expected_size) } + /// Builds a [`ListMeta`] information for the specified [`graphql::Type`]. + /// + /// Specifying `expected_size` will be used in validation to ensure that + /// values of this type matches it. + /// + /// [`graphql::Type`]: resolve::Type + pub fn build_list_type_new( + &mut self, + info: &Info, + expected_size: Option, + ) -> ListMeta<'r> + where + T: resolve::Type + ?Sized, + Info: ?Sized, + { + ListMeta::new(T::meta(self, info).as_type(), expected_size) + } + /// Creates a [`NullableMeta`] type. pub fn build_nullable_type(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> where @@ -1288,6 +1339,18 @@ impl<'r, S: 'r> Registry<'r, S> { NullableMeta::new(of_type) } + /// Builds a [`NullableMeta`] information for the specified + /// [`graphql::Type`]. + /// + /// [`graphql::Type`]: resolve::Type + pub fn build_nullable_type_new(&mut self, info: &Info) -> NullableMeta<'r> + where + T: resolve::Type + ?Sized, + Info: ?Sized, + { + NullableMeta::new(T::meta(self, info).as_type()) + } + /// Creates an [`ObjectMeta`] type with the given `fields`. pub fn build_object_type( &mut self, diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 581d1dcd4..4d87480d9 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -1 +1,56 @@ pub mod resolve; + +use crate::DefaultScalarValue; + +pub use crate::value::Value; + +pub use self::resolve::Type; + +pub trait Interface: + OutputType + + Type + + resolve::TypeName + + resolve::ConcreteTypeName + + resolve::Value + + resolve::ValueAsync + + resolve::ConcreteValue + + resolve::ConcreteValueAsync + + resolve::Field + + resolve::FieldAsync +{ + fn assert_interface(); +} + +pub trait Object: + OutputType + + Type + + resolve::TypeName + + resolve::ConcreteTypeName + + resolve::Value + + resolve::ValueAsync + + resolve::Field + + resolve::FieldAsync +{ + fn assert_object(); +} + +pub trait Union: + OutputType + + Type + + resolve::TypeName + + resolve::ConcreteTypeName + + resolve::Value + + resolve::ValueAsync + + resolve::ConcreteValue + + resolve::ConcreteValueAsync +{ + fn assert_union(); +} + +pub trait InputType { + fn assert_input_type(); +} + +pub trait OutputType { + fn assert_output_type(); +} diff --git a/juniper/src/graphql/resolve.rs b/juniper/src/graphql/resolve.rs index 068115a86..22fe5342b 100644 --- a/juniper/src/graphql/resolve.rs +++ b/juniper/src/graphql/resolve.rs @@ -1,10 +1,25 @@ use crate::{ - executor::{ExecutionResult, Executor, Registry}, - resolve, - schema::meta::MetaType, - Arguments, DefaultScalarValue, + meta::MetaType, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, + Registry, Selection, }; +pub trait Type { + fn meta<'r, Info: ?Sized>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + Self: resolve::Type; +} + +impl Type for T { + fn meta<'r, Info: ?Sized>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + Self: resolve::Type, + { + >::meta(registry, info) + } +} + pub trait TypeName { fn type_name(info: &Info) -> &str where @@ -20,29 +35,148 @@ impl TypeName for T { } } -pub trait Type { - fn meta<'r, Info: ?Sized>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> +pub trait ConcreteTypeName { + fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str where - S: 'r, - Self: resolve::Type; + Self: resolve::ConcreteTypeName; } -impl Type for T { - fn meta<'r, Info: ?Sized>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> +impl ConcreteTypeName for T { + fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str where - S: 'r, - Self: resolve::Type, + Self: resolve::ConcreteTypeName, + { + >::concrete_type_name(self, info) + } +} + +pub trait Value { + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::Value; +} + +impl Value for T { + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::Value, + { + >::resolve_value(self, selection_set, info, executor) + } +} + +pub trait ValueAsync { + fn resolve_value_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> + where + Self: resolve::ValueAsync; +} + +impl ValueAsync for T { + fn resolve_value_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> + where + Self: resolve::ValueAsync, { - >::meta(info, registry) + >::resolve_value_async( + self, + selection_set, + info, + executor, + ) + } +} + +pub trait ConcreteValue { + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::ConcreteValue; +} + +impl ConcreteValue for T { + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult + where + Self: resolve::ConcreteValue, + { + >::resolve_concrete_value( + self, + type_name, + selection_set, + info, + executor, + ) + } +} + +pub trait ConcreteValueAsync { + fn resolve_concrete_value_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> + where + Self: resolve::ConcreteValueAsync; +} + +impl ConcreteValueAsync for T { + fn resolve_concrete_value_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> + where + Self: resolve::ConcreteValueAsync, + { + >::resolve_concrete_value_async( + self, + type_name, + selection_set, + info, + executor, + ) } } pub trait Field { fn resolve_field( &self, - info: &Info, field_name: &str, arguments: &Arguments, + info: &Info, executor: &Executor, ) -> ExecutionResult where @@ -52,31 +186,45 @@ pub trait Field { impl Field for T { fn resolve_field( &self, - info: &Info, field_name: &str, arguments: &Arguments, + info: &Info, executor: &Executor, ) -> ExecutionResult where Self: resolve::Field, { >::resolve_field( - self, info, field_name, arguments, executor, + self, field_name, arguments, info, executor, ) } } -pub trait ConcreteTypeName { - fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str +pub trait FieldAsync { + fn resolve_field_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> where - Self: resolve::ConcreteTypeName; + Self: resolve::FieldAsync; } -impl ConcreteTypeName for T { - fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str +impl FieldAsync for T { + fn resolve_field_async<'r, Info: ?Sized, Ctx: ?Sized>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> where - Self: resolve::ConcreteTypeName, + Self: resolve::FieldAsync, { - >::concrete_type_name(self, info) + >::resolve_field_async( + self, field_name, arguments, info, executor, + ) } } diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index fa869cbd0..d9b4979cf 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -41,7 +41,7 @@ pub(crate) mod schema; mod types; mod util; pub mod validation; -mod value; +pub(crate) mod value; #[cfg(all(test, not(feature = "expose-test-schema")))] mod tests; diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 27b28a196..e027d0cf5 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -1,29 +1,76 @@ use crate::{ - executor::{ExecutionResult, Executor, Registry}, - schema::meta::MetaType, - Arguments, DefaultScalarValue, + meta::MetaType, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, Registry, + Selection, }; +pub trait Type { + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r; +} + pub trait TypeName { fn type_name(info: &Info) -> &str; } -pub trait Type { - fn meta<'r>(info: &Info, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r; +pub trait ConcreteTypeName { + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str; +} + +pub trait Value { + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult; +} + +pub trait ValueAsync { + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; +} + +pub trait ConcreteValue { + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult; +} + +pub trait ConcreteValueAsync { + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; } pub trait Field { fn resolve_field( &self, - info: &Info, field_name: &str, arguments: &Arguments, + info: &Info, executor: &Executor, ) -> ExecutionResult; } -pub trait ConcreteTypeName { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str; +pub trait FieldAsync { + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; } diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs new file mode 100644 index 000000000..8300eec92 --- /dev/null +++ b/juniper/src/types/arc.rs @@ -0,0 +1,186 @@ +use std::sync::Arc; + +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + Arguments, BoxFuture, Selection, +}; + +impl resolve::Type for Arc +where + T: resolve::Type + ?Sized, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + T::meta(registry, info) + } +} + +impl resolve::TypeName for Arc +where + T: resolve::TypeName + ?Sized, + Info: ?Sized, +{ + fn type_name(info: &Info) -> &str { + T::type_name(info) + } +} + +impl resolve::ConcreteTypeName for Arc +where + T: resolve::ConcreteTypeName + ?Sized, + Info: ?Sized, +{ + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + (**self).concrete_type_name(info) + } +} + +impl resolve::Value for Arc +where + T: resolve::Value + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, info, executor) + } +} + +impl resolve::ValueAsync for Arc +where + T: resolve::ValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, info, executor) + } +} + +impl resolve::ConcreteValue for Arc +where + T: resolve::ConcreteValue + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, info, executor) + } +} + +impl resolve::ConcreteValueAsync for Arc +where + T: resolve::ConcreteValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + } +} + +impl resolve::Field for Arc +where + T: resolve::Field + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, info, executor) + } +} + +impl resolve::FieldAsync for Arc +where + T: resolve::FieldAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, info, executor) + } +} + +impl graphql::InputType for Arc +where + T: graphql::InputType + ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Arc +where + T: graphql::OutputType + ?Sized, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl graphql::Interface for Arc +where + T: graphql::Interface + ?Sized, +{ + fn assert_interface() { + T::assert_interface() + } +} + +impl graphql::Object for Arc +where + T: graphql::Object + ?Sized, +{ + fn assert_object() { + T::assert_object() + } +} + +impl graphql::Union for Arc +where + T: graphql::Union + ?Sized, +{ + fn assert_union() { + T::assert_union() + } +} diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs new file mode 100644 index 000000000..5f0b0ce15 --- /dev/null +++ b/juniper/src/types/box.rs @@ -0,0 +1,184 @@ +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + Arguments, BoxFuture, Selection, +}; + +impl resolve::Type for Box +where + T: resolve::Type + ?Sized, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + T::meta(registry, info) + } +} + +impl resolve::TypeName for Box +where + T: resolve::TypeName + ?Sized, + Info: ?Sized, +{ + fn type_name(info: &Info) -> &str { + T::type_name(info) + } +} + +impl resolve::ConcreteTypeName for Box +where + T: resolve::ConcreteTypeName + ?Sized, + Info: ?Sized, +{ + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + (**self).concrete_type_name(info) + } +} + +impl resolve::Value for Box +where + T: resolve::Value + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, info, executor) + } +} + +impl resolve::ValueAsync for Box +where + T: resolve::ValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, info, executor) + } +} + +impl resolve::ConcreteValue for Box +where + T: resolve::ConcreteValue + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, info, executor) + } +} + +impl resolve::ConcreteValueAsync for Box +where + T: resolve::ConcreteValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + } +} + +impl resolve::Field for Box +where + T: resolve::Field + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, info, executor) + } +} + +impl resolve::FieldAsync for Box +where + T: resolve::FieldAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, info, executor) + } +} + +impl graphql::InputType for Box +where + T: graphql::InputType + ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Box +where + T: graphql::OutputType + ?Sized, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl graphql::Interface for Box +where + T: graphql::Interface + ?Sized, +{ + fn assert_interface() { + T::assert_interface() + } +} + +impl graphql::Object for Box +where + T: graphql::Object + ?Sized, +{ + fn assert_object() { + T::assert_object() + } +} + +impl graphql::Union for Box +where + T: graphql::Union + ?Sized, +{ + fn assert_union() { + T::assert_union() + } +} diff --git a/juniper/src/types/iter.rs b/juniper/src/types/iter.rs new file mode 100644 index 000000000..2b4e5edde --- /dev/null +++ b/juniper/src/types/iter.rs @@ -0,0 +1,65 @@ +use crate::{graphql, resolve, ExecutionResult, Executor, Selection}; + +pub fn resolve_list<'t, T, S, Info, Ctx, I>( + iter: I, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, +) -> ExecutionResult +where + I: Iterator + ExactSizeIterator, + T: resolve::Value + ?Sized + 't, + Info: ?Sized, + Ctx: ?Sized, +{ + let is_non_null = executor + .current_type_new() + .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)?; + if is_non_null && val.is_null() { + return Err("Resolved `null` on non-null type".into()); + } + values.push(val); + } + Ok(graphql::Value::list(values)) +} + +pub async fn resolve_list_async<'a, 't, T, S, Info, Ctx, I>( + iter: I, + selection_set: Option<&[Selection<'_, S>]>, + info: &'a Info, + executor: &'a Executor<'a, 'a, Ctx, S>, +) -> ExecutionResult +where + I: Iterator + ExactSizeIterator, + T: resolve::ValueAsync + ?Sized + 't, + Info: ?Sized, + Ctx: ?Sized, +{ + use futures::stream::{FuturesOrdered, StreamExt as _}; + + let is_non_null = executor + .current_type_new() + .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 }) + .collect::>(); + + let mut values = Vec::with_capacity(futs.len()); + while let Some(res) = futs.next().await { + let val = res?; + if is_non_null && val.is_null() { + return Err("Resolved `null` on non-null type".into()); + } + values.push(val); + } + Ok(graphql::Value::list(values)) +} diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 8ac005ba0..55451b5aa 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,3 +1,12 @@ +mod arc; +mod r#box; +pub mod iter; +mod option; +mod rc; +mod r#ref; +mod ref_mut; +mod vec; + pub mod async_await; pub mod base; pub mod containers; diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs new file mode 100644 index 000000000..98f995c48 --- /dev/null +++ b/juniper/src/types/option.rs @@ -0,0 +1,78 @@ +use futures::future; + +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + BoxFuture, Selection, +}; + +impl resolve::Type for Option +where + T: resolve::Type, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry.build_nullable_type_new::(info).into_meta() + } +} + +impl resolve::Value for Option +where + T: resolve::Value, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + match self { + Some(v) => v.resolve_value(selection_set, info, executor), + None => Ok(graphql::Value::Null), + } + } +} + +impl resolve::ValueAsync for Option +where + T: resolve::ValueAsync, + Info: ?Sized, + Ctx: ?Sized, + S: Send, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + match self { + Some(v) => v.resolve_value_async(selection_set, info, executor), + None => Box::pin(future::ok(graphql::Value::Null)), + } + } +} + +impl graphql::InputType for Option +where + T: graphql::InputType, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Option +where + T: graphql::OutputType, +{ + fn assert_output_type() { + T::assert_output_type() + } +} diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs new file mode 100644 index 000000000..0ac2d2639 --- /dev/null +++ b/juniper/src/types/rc.rs @@ -0,0 +1,186 @@ +use std::rc::Rc; + +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + Arguments, BoxFuture, Selection, +}; + +impl resolve::Type for Rc +where + T: resolve::Type + ?Sized, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + T::meta(registry, info) + } +} + +impl resolve::TypeName for Rc +where + T: resolve::TypeName + ?Sized, + Info: ?Sized, +{ + fn type_name(info: &Info) -> &str { + T::type_name(info) + } +} + +impl resolve::ConcreteTypeName for Rc +where + T: resolve::ConcreteTypeName + ?Sized, + Info: ?Sized, +{ + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + (**self).concrete_type_name(info) + } +} + +impl resolve::Value for Rc +where + T: resolve::Value + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, info, executor) + } +} + +impl resolve::ValueAsync for Rc +where + T: resolve::ValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, info, executor) + } +} + +impl resolve::ConcreteValue for Rc +where + T: resolve::ConcreteValue + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, info, executor) + } +} + +impl resolve::ConcreteValueAsync for Rc +where + T: resolve::ConcreteValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + } +} + +impl resolve::Field for Rc +where + T: resolve::Field + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, info, executor) + } +} + +impl resolve::FieldAsync for Rc +where + T: resolve::FieldAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, info, executor) + } +} + +impl graphql::InputType for Rc +where + T: graphql::InputType + ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Rc +where + T: graphql::OutputType + ?Sized, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl graphql::Interface for Rc +where + T: graphql::Interface + ?Sized, +{ + fn assert_interface() { + T::assert_interface() + } +} + +impl graphql::Object for Rc +where + T: graphql::Object + ?Sized, +{ + fn assert_object() { + T::assert_object() + } +} + +impl graphql::Union for Rc +where + T: graphql::Union + ?Sized, +{ + fn assert_union() { + T::assert_union() + } +} diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs new file mode 100644 index 000000000..2f0867947 --- /dev/null +++ b/juniper/src/types/ref.rs @@ -0,0 +1,182 @@ +use crate::{ + graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, + Selection, +}; + +impl<'me, T, Info, S> resolve::Type for &'me T +where + T: resolve::Type + ?Sized, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + T::meta(registry, info) + } +} + +impl<'me, T, Info> resolve::TypeName for &'me T +where + T: resolve::TypeName + ?Sized, + Info: ?Sized, +{ + fn type_name(info: &Info) -> &str { + T::type_name(info) + } +} + +impl<'me, T, Info> resolve::ConcreteTypeName for &'me T +where + T: resolve::ConcreteTypeName + ?Sized, + Info: ?Sized, +{ + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + (**self).concrete_type_name(info) + } +} + +impl<'me, T, Info, Ctx, S> resolve::Value for &'me T +where + T: resolve::Value + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ValueAsync for &'me T +where + T: resolve::ValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ConcreteValue for &'me T +where + T: resolve::ConcreteValue + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ConcreteValueAsync for &'me T +where + T: resolve::ConcreteValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::Field for &'me T +where + T: resolve::Field + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::FieldAsync for &'me T +where + T: resolve::FieldAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, info, executor) + } +} + +impl<'me, T, S> graphql::InputType for &'me T +where + T: graphql::InputType + ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl<'me, T, S> graphql::OutputType for &'me T +where + T: graphql::OutputType + ?Sized, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl<'me, T, S> graphql::Interface for &'me T +where + T: graphql::Interface + ?Sized, +{ + fn assert_interface() { + T::assert_interface() + } +} + +impl<'me, T, S> graphql::Object for &'me T +where + T: graphql::Object + ?Sized, +{ + fn assert_object() { + T::assert_object() + } +} + +impl<'me, T, S> graphql::Union for &'me T +where + T: graphql::Union + ?Sized, +{ + fn assert_union() { + T::assert_union() + } +} diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs new file mode 100644 index 000000000..566359b65 --- /dev/null +++ b/juniper/src/types/ref_mut.rs @@ -0,0 +1,182 @@ +use crate::{ + graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, + Selection, +}; + +impl<'me, T, Info, S> resolve::Type for &'me mut T +where + T: resolve::Type + ?Sized, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + T::meta(registry, info) + } +} + +impl<'me, T, Info> resolve::TypeName for &'me mut T +where + T: resolve::TypeName + ?Sized, + Info: ?Sized, +{ + fn type_name(info: &Info) -> &str { + T::type_name(info) + } +} + +impl<'me, T, Info> resolve::ConcreteTypeName for &'me mut T +where + T: resolve::ConcreteTypeName + ?Sized, + Info: ?Sized, +{ + fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + (**self).concrete_type_name(info) + } +} + +impl<'me, T, Info, Ctx, S> resolve::Value for &'me mut T +where + T: resolve::Value + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ValueAsync for &'me mut T +where + T: resolve::ValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ConcreteValue for &'me mut T +where + T: resolve::ConcreteValue + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::ConcreteValueAsync for &'me mut T +where + T: resolve::ConcreteValueAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::Field for &'me mut T +where + T: resolve::Field + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, info, executor) + } +} + +impl<'me, T, Info, Ctx, S> resolve::FieldAsync for &'me mut T +where + T: resolve::FieldAsync + ?Sized, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, info, executor) + } +} + +impl<'me, T, S> graphql::InputType for &'me mut T +where + T: graphql::InputType + ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl<'me, T, S> graphql::OutputType for &'me mut T +where + T: graphql::OutputType + ?Sized, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl<'me, T, S> graphql::Interface for &'me mut T +where + T: graphql::Interface + ?Sized, +{ + fn assert_interface() { + T::assert_interface() + } +} + +impl<'me, T, S> graphql::Object for &'me mut T +where + T: graphql::Object + ?Sized, +{ + fn assert_object() { + T::assert_object() + } +} + +impl<'me, T, S> graphql::Union for &'me mut T +where + T: graphql::Union + ?Sized, +{ + fn assert_union() { + T::assert_union() + } +} diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs new file mode 100644 index 000000000..e2374e27f --- /dev/null +++ b/juniper/src/types/vec.rs @@ -0,0 +1,77 @@ +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + BoxFuture, Selection, +}; + +use super::iter; + +impl resolve::Type for Vec +where + T: resolve::Type, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry.build_list_type_new::(info, None).into_meta() + } +} + +impl resolve::Value for Vec +where + T: resolve::Value, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, info, executor) + } +} + +impl resolve::ValueAsync for Vec +where + T: resolve::ValueAsync + Sync, + Info: Sync + ?Sized, + Ctx: Sync + ?Sized, + S: Send + Sync, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + Box::pin(iter::resolve_list_async( + self.iter(), + selection_set, + info, + executor, + )) + } +} + +impl graphql::InputType for Vec +where + T: graphql::InputType, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Vec +where + T: graphql::OutputType, +{ + fn assert_output_type() { + T::assert_output_type() + } +} From c8759cd16e98ae79f4b16bf1bf6794bc04a37af3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 25 Apr 2022 15:50:28 +0300 Subject: [PATCH 04/58] Impl basic types, vol.2 --- juniper/src/lib.rs | 2 +- juniper/src/types/arc.rs | 2 + juniper/src/types/array.rs | 83 +++++++++++ juniper/src/types/box.rs | 2 + juniper/src/types/iter.rs | 2 + juniper/src/types/mod.rs | 7 +- juniper/src/types/nullable.rs | 255 +++++++++++++++++++++++++--------- juniper/src/types/option.rs | 2 + juniper/src/types/rc.rs | 2 + juniper/src/types/ref.rs | 4 + juniper/src/types/ref_mut.rs | 4 + juniper/src/types/slice.rs | 81 +++++++++++ juniper/src/types/vec.rs | 2 + 13 files changed, 382 insertions(+), 66 deletions(-) create mode 100644 juniper/src/types/array.rs create mode 100644 juniper/src/types/slice.rs diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index d9b4979cf..9ee509bbc 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -82,7 +82,7 @@ pub use crate::{ async_await::{GraphQLTypeAsync, GraphQLValueAsync}, base::{Arguments, GraphQLType, GraphQLValue, TypeKind}, marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion}, - nullable::Nullable, + Nullable, scalars::{EmptyMutation, EmptySubscription, ID}, subscriptions::{ ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue, diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 8300eec92..467951c88 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Arc`]. + use std::sync::Arc; use crate::{ diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs new file mode 100644 index 000000000..e018148da --- /dev/null +++ b/juniper/src/types/array.rs @@ -0,0 +1,83 @@ +//! GraphQL implementation for [array]. +//! +//! [array]: primitive@std::array + +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + BoxFuture, Selection, +}; + +use super::iter; + +impl resolve::Type for [T; N] +where + T: resolve::Type, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry + .build_list_type_new::(info, Some(N)) + .into_meta() + } +} + +impl resolve::Value for [T; N] +where + T: resolve::Value, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, info, executor) + } +} + +impl resolve::ValueAsync for [T; N] +where + T: resolve::ValueAsync + Sync, + Info: Sync + ?Sized, + Ctx: Sync + ?Sized, + S: Send + Sync, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + Box::pin(iter::resolve_list_async( + self.iter(), + selection_set, + info, + executor, + )) + } +} + +impl graphql::InputType for [T; N] +where + T: graphql::InputType, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for [T; N] +where + T: graphql::OutputType, +{ + fn assert_output_type() { + T::assert_output_type() + } +} diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 5f0b0ce15..c8140f3e2 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Box`]. + use crate::{ executor::{ExecutionResult, Executor, Registry}, graphql, resolve, diff --git a/juniper/src/types/iter.rs b/juniper/src/types/iter.rs index 2b4e5edde..c3979b088 100644 --- a/juniper/src/types/iter.rs +++ b/juniper/src/types/iter.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Iterator`]. + use crate::{graphql, resolve, ExecutionResult, Executor, Selection}; pub fn resolve_list<'t, T, S, Info, Ctx, I>( diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 55451b5aa..5fceeb2ba 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,10 +1,13 @@ +mod array; mod arc; mod r#box; pub mod iter; +mod nullable; mod option; mod rc; mod r#ref; mod ref_mut; +mod slice; mod vec; pub mod async_await; @@ -12,8 +15,10 @@ pub mod base; pub mod containers; pub mod marker; pub mod name; -pub mod nullable; pub mod pointers; pub mod scalars; pub mod subscriptions; pub mod utilities; + +#[doc(inline)] +pub use self::nullable::Nullable; diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index 7e8d01cc1..1d2f55839 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -1,40 +1,52 @@ +//! GraphQL implementation for [`Nullable`]. + +use std::mem; + +use futures::future; + use crate::{ - ast::{FromInputValue, InputValue, Selection, ToInputValue}, + ast::{FromInputValue, InputValue, ToInputValue}, executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, schema::meta::MetaType, types::{ async_await::GraphQLValueAsync, base::{GraphQLType, GraphQLValue}, marker::IsInputType, }, - value::{ScalarValue, Value}, + BoxFuture, ScalarValue, Selection, }; -/// `Nullable` can be used in situations where you need to distinguish between an implicitly and -/// explicitly null input value. +/// [`Nullable`] wrapper allowing to distinguish between an implicit and +/// explicit `null` input value. /// -/// The GraphQL spec states that these two field calls are similar, but are not identical: +/// [GraphQL spec states][0] that these two field calls are similar, but are not +/// identical: /// -/// ```graphql -/// { -/// field(arg: null) -/// field -/// } -/// ``` +/// > ```graphql +/// > { +/// > field(arg: null) +/// > field +/// > } +/// > ``` +/// > The first has explicitly provided `null` to the argument "arg", while the +/// > second has implicitly not provided a value to the argument "arg". These +/// > two forms may be interpreted differently. For example, a mutation +/// > representing deleting a field vs not altering a field, respectively. /// -/// The first has explicitly provided null to the argument “arg”, while the second has implicitly -/// not provided a value to the argument “arg”. These two forms may be interpreted differently. For -/// example, a mutation representing deleting a field vs not altering a field, respectively. +/// In cases where there is no need to distinguish between the two types of +/// `null`, it's better to simply use [`Option`]. /// -/// In cases where you do not need to be able to distinguish between the two types of null, you -/// should simply use `Option`. +/// [0]: https://spec.graphql.org/October2021#example-1c7eb #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] pub enum Nullable { - /// No value + /// No value specified. ImplicitNull, - /// No value, explicitly specified to be null + + /// Value explicitly specified to be `null`. ExplicitNull, - /// Some value `T` + + /// Explicitly specified non-`null` value of `T`. Some(T), } @@ -45,101 +57,134 @@ impl Default for Nullable { } impl Nullable { - /// Returns `true` if the nullable is a `ExplicitNull` value. + /// Indicates whether this [`Nullable`] represents an [`ExplicitNull`]. + /// + /// [`ExplicitNull`]: Nullable::ExplicitNull #[inline] pub fn is_explicit_null(&self) -> bool { matches!(self, Self::ExplicitNull) } - /// Returns `true` if the nullable is a `ImplicitNull` value. + /// Indicates whether this [`Nullable`] represents an [`ImplicitNull`]. + /// + /// [`ImplicitNull`]: Nullable::ImplicitNull #[inline] pub fn is_implicit_null(&self) -> bool { matches!(self, Self::ImplicitNull) } - /// Returns `true` if the nullable is a `Some` value. + /// Indicates whether this [`Nullable`] contains a non-`null` value. #[inline] pub fn is_some(&self) -> bool { matches!(self, Self::Some(_)) } - /// Returns `true` if the nullable is not a `Some` value. + /// Indicates whether this [`Nullable`] represents a `null`. #[inline] pub fn is_null(&self) -> bool { !matches!(self, Self::Some(_)) } + /// Converts from `&Nullable` to `Nullable<&T>`. + #[inline] + pub fn as_ref(&self) -> Nullable<&T> { + match self { + Self::Some(x) => Nullable::Some(x), + Self::ImplicitNull => Nullable::ImplicitNull, + Self::ExplicitNull => Nullable::ExplicitNull, + } + } + /// Converts from `&mut Nullable` to `Nullable<&mut T>`. #[inline] pub fn as_mut(&mut self) -> Nullable<&mut T> { - match *self { - Self::Some(ref mut x) => Nullable::Some(x), + match self { + Self::Some(x) => Nullable::Some(x), Self::ImplicitNull => Nullable::ImplicitNull, Self::ExplicitNull => Nullable::ExplicitNull, } } - /// Returns the contained `Some` value, consuming the `self` value. + /// Returns the contained non-`null` value, consuming the `self` value. /// /// # Panics /// - /// Panics if the value is not a `Some` with a custom panic message provided by `msg`. + /// With a custom `msg` if this [`Nullable`] represents a `null`. #[inline] #[track_caller] pub fn expect(self, msg: &str) -> T { self.some().expect(msg) } - /// Returns the contained `Some` value or a provided default. + /// Returns the contained non-`null` value or the provided `default` one. #[inline] pub fn unwrap_or(self, default: T) -> T { self.some().unwrap_or(default) } - /// Returns the contained `Some` value or computes it from a closure. + /// Returns thecontained non-`null` value or computes it from the provided + /// `func`tion. + #[inline] + pub fn unwrap_or_else T>(self, func: F) -> T { + self.some().unwrap_or_else(func) + } + + /// Returns the contained non-`null` value or the [`Default`] one. #[inline] - pub fn unwrap_or_else T>(self, f: F) -> T { - self.some().unwrap_or_else(f) + pub fn unwrap_or_default(self) -> T + where + T: Default, + { + self.some().unwrap_or_default() } - /// Maps a `Nullable` to `Nullable` by applying a function to a contained value. + /// Maps this `Nullable` to `Nullable` by applying the provided + /// `func`tion to the contained non-`null` value. #[inline] - pub fn map U>(self, f: F) -> Nullable { + pub fn map U>(self, func: F) -> Nullable { match self { - Self::Some(x) => Nullable::Some(f(x)), + Self::Some(x) => Nullable::Some(func(x)), Self::ImplicitNull => Nullable::ImplicitNull, Self::ExplicitNull => Nullable::ExplicitNull, } } - /// Applies a function to the contained value (if any), or returns the provided default (if - /// not). + /// Applies the provided `func`tion to the contained non-`null` value (if + /// any), or returns the provided `default` value (if not). #[inline] - pub fn map_or U>(self, default: U, f: F) -> U { - self.some().map_or(default, f) + pub fn map_or U>(self, default: U, func: F) -> U { + self.some().map_or(default, func) } - /// Applies a function to the contained value (if any), or computes a default (if not). + /// Applies the provided `func`tion to the contained non-`null` value (if + /// any), or computes the provided `default` one (if not). #[inline] - pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, f: F) -> U { - self.some().map_or_else(default, f) + pub fn map_or_else U, F: FnOnce(T) -> U>(self, default: D, func: F) -> U { + self.some().map_or_else(default, func) } - /// Transforms the `Nullable` into a `Result`, mapping `Some(v)` to `Ok(v)` and - /// `ImplicitNull` or `ExplicitNull` to `Err(err)`. + /// Transforms this `Nullable` into a `Result`, mapping `Some(v)` + /// to `Ok(v)` and [`ImplicitNull`] or [`ExplicitNull`] to `Err(err)`. + /// + /// [`ExplicitNull`]: Nullable::ExplicitNull + /// [`ImplicitNull`]: Nullable::ImplicitNull #[inline] pub fn ok_or(self, err: E) -> Result { self.some().ok_or(err) } - /// Transforms the `Nullable` into a `Result`, mapping `Some(v)` to `Ok(v)` and - /// `ImplicitNull` or `ExplicitNull` to `Err(err())`. + /// Transforms this `Nullable` into a `Result`, mapping `Some(v)` + /// to `Ok(v)` and [`ImplicitNull`] or [`ExplicitNull`] to `Err(err())`. + /// + /// [`ExplicitNull`]: Nullable::ExplicitNull + /// [`ImplicitNull`]: Nullable::ImplicitNull #[inline] pub fn ok_or_else E>(self, err: F) -> Result { self.some().ok_or_else(err) } - /// Returns the nullable if it contains a value, otherwise returns `b`. + /// Returns this [`Nullable`] if it contains a non-`null` value, otherwise + /// returns the specified `b` [`Nullable`] value. #[inline] #[must_use] pub fn or(self, b: Self) -> Self { @@ -149,35 +194,43 @@ impl Nullable { } } - /// Returns the nullable if it contains a value, otherwise calls `f` and - /// returns the result. + /// Returns this [`Nullable`] if it contains a non-`null` value, otherwise + /// computes a [`Nullable`] value from the specified `func`tion. #[inline] #[must_use] - pub fn or_else Nullable>(self, f: F) -> Nullable { + pub fn or_else Nullable>(self, func: F) -> Nullable { match self { Self::Some(_) => self, - _ => f(), + _ => func(), } } - /// Replaces the actual value in the nullable by the value given in parameter, returning the - /// old value if present, leaving a `Some` in its place without deinitializing either one. + /// Replaces the contained non-`null` value in this [`Nullable`] by the + /// provided `value`, returning the old one if present, leaving a [`Some`] + /// in its place without deinitializing either one. + /// + /// [`Some`]: Nullable::Some #[inline] #[must_use] pub fn replace(&mut self, value: T) -> Self { - std::mem::replace(self, Self::Some(value)) + mem::replace(self, Self::Some(value)) } - /// Converts from `Nullable` to `Option`. + /// Converts this [`Nullable`] to [Option]. + #[inline] pub fn some(self) -> Option { match self { Self::Some(v) => Some(v), - _ => None, + Self::ExplicitNull | Self::ImplicitNull => None, } } - /// Converts from `Nullable` to `Option>`, mapping `Some(v)` to `Some(Some(v))`, - /// `ExplicitNull` to `Some(None)`, and `ImplicitNull` to `None`. + /// Converts this [`Nullable`] to `Option>`, mapping `Some(v)` to + /// `Some(Some(v))`, [`ExplicitNull`] to `Some(None)`, and [`ImplicitNull`] + /// to [`None`]. + /// + /// [`ExplicitNull`]: Nullable::ExplicitNull + /// [`ImplicitNull`]: Nullable::ImplicitNull pub fn explicit(self) -> Option> { match self { Self::Some(v) => Some(Some(v)), @@ -188,33 +241,107 @@ impl Nullable { } impl Nullable<&T> { - /// Maps a `Nullable<&T>` to a `Nullable` by copying the contents of the nullable. + /// Maps this `Nullable<&T>` to a `Nullable` by [`Copy`]ing the contents + /// of this [`Nullable`]. pub fn copied(self) -> Nullable { self.map(|&t| t) } } impl Nullable<&mut T> { - /// Maps a `Nullable<&mut T>` to a `Nullable` by copying the contents of the nullable. + /// Maps this `Nullable<&mut T>` to a `Nullable` by [`Copy`]ing the + /// contents of this [`Nullable`]. pub fn copied(self) -> Nullable { self.map(|&mut t| t) } } impl Nullable<&T> { - /// Maps a `Nullable<&T>` to a `Nullable` by cloning the contents of the nullable. + /// Maps this `Nullable<&T>` to a `Nullable` by [`Clone`]ing the contents + /// of this [`Nullable`]. pub fn cloned(self) -> Nullable { self.map(|t| t.clone()) } } impl Nullable<&mut T> { - /// Maps a `Nullable<&mut T>` to a `Nullable` by cloning the contents of the nullable. + /// Maps this `Nullable<&mut T>` to a `Nullable` by [`Clone`]ing the + /// contents of this [`Nullable`]. pub fn cloned(self) -> Nullable { self.map(|t| t.clone()) } } +impl resolve::Type for Nullable +where + T: resolve::Type, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry.build_nullable_type_new::(info).into_meta() + } +} + +impl resolve::Value for Nullable +where + T: resolve::Value, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + match self { + Self::Some(v) => v.resolve_value(selection_set, info, executor), + Self::ExplicitNull | Self::ImplicitNull => Ok(graphql::Value::Null), + } + } +} + +impl resolve::ValueAsync for Nullable +where + T: resolve::ValueAsync, + Info: ?Sized, + Ctx: ?Sized, + S: Send, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + match self { + Self::Some(v) => v.resolve_value_async(selection_set, info, executor), + Self::ExplicitNull | Self::ImplicitNull => Box::pin(future::ok(graphql::Value::Null)), + } + } +} + +impl graphql::InputType for Nullable +where + T: graphql::InputType, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for Nullable +where + T: graphql::OutputType, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + impl GraphQLType for Nullable where T: GraphQLType, @@ -252,7 +379,7 @@ where ) -> ExecutionResult { match *self { Self::Some(ref obj) => executor.resolve(info, obj), - _ => Ok(Value::null()), + _ => Ok(graphql::Value::null()), } } } @@ -269,11 +396,11 @@ where info: &'a Self::TypeInfo, _: Option<&'a [Selection]>, executor: &'a Executor, - ) -> crate::BoxFuture<'a, ExecutionResult> { + ) -> BoxFuture<'a, ExecutionResult> { let f = async move { let value = match self { Self::Some(obj) => executor.resolve_into_value_async(info, obj).await, - _ => Value::null(), + _ => graphql::Value::null(), }; Ok(value) }; diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 98f995c48..be1ceb485 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Option`]. + use futures::future; use crate::{ diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 0ac2d2639..3cc1f7355 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Rc`]. + use std::rc::Rc; use crate::{ diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 2f0867947..36b27e4d1 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -1,3 +1,7 @@ +//! GraphQL implementation for [reference]. +//! +//! [reference]: primitive@std::reference + use crate::{ graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index 566359b65..eee1ae3b4 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -1,3 +1,7 @@ +//! GraphQL implementation for mutable [reference]. +//! +//! [reference]: primitive@std::reference + use crate::{ graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs new file mode 100644 index 000000000..e3ad5b49a --- /dev/null +++ b/juniper/src/types/slice.rs @@ -0,0 +1,81 @@ +//! GraphQL implementation for [slice]. +//! +//! [slice]: primitive@std::slice + +use crate::{ + executor::{ExecutionResult, Executor, Registry}, + graphql, resolve, + schema::meta::MetaType, + BoxFuture, Selection, +}; + +use super::iter; + +impl resolve::Type for [T] +where + T: resolve::Type, + Info: ?Sized, +{ + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry.build_list_type_new::(info, None).into_meta() + } +} + +impl resolve::Value for [T] +where + T: resolve::Value, + Info: ?Sized, + Ctx: ?Sized, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, S>]>, + info: &Info, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, info, executor) + } +} + +impl resolve::ValueAsync for [T] +where + T: resolve::ValueAsync + Sync, + Info: Sync + ?Sized, + Ctx: Sync + ?Sized, + S: Send + Sync, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, S>]>, + info: &'r Info, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + Box::pin(iter::resolve_list_async( + self.iter(), + selection_set, + info, + executor, + )) + } +} + +impl graphql::InputType for [T] +where + T: graphql::InputType, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl graphql::OutputType for [T] +where + T: graphql::OutputType, +{ + fn assert_output_type() { + T::assert_output_type() + } +} diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index e2374e27f..a085f90ff 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -1,3 +1,5 @@ +//! GraphQL implementation for [`Vec`]. + use crate::{ executor::{ExecutionResult, Executor, Registry}, graphql, resolve, From 64cf7adb4f5370f8edc0d59ec8f6f55565ebdeac Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 5 May 2022 18:35:42 +0300 Subject: [PATCH 05/58] Impl basic types, vol.3 --- juniper/src/executor/mod.rs | 16 ++++++ juniper/src/graphql/mod.rs | 15 +++++- juniper/src/resolve/mod.rs | 47 +++++++++++++++-- juniper/src/schema/meta.rs | 42 +++++++++++++--- juniper/src/types/arc.rs | 26 ++++++++-- juniper/src/types/box.rs | 26 ++++++++-- juniper/src/types/mod.rs | 1 + juniper/src/types/rc.rs | 26 ++++++++-- juniper/src/types/ref.rs | 24 ++++++++- juniper/src/types/ref_mut.rs | 24 ++++++++- juniper/src/types/str.rs | 97 ++++++++++++++++++++++++++++++++++++ 11 files changed, 318 insertions(+), 26 deletions(-) create mode 100644 juniper/src/types/str.rs diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 23073222f..d749fc4ae 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -4,6 +4,7 @@ use std::{ borrow::Cow, cmp::Ordering, collections::HashMap, + convert::TryFrom, fmt::{Debug, Display}, sync::{Arc, RwLock}, }; @@ -1294,6 +1295,21 @@ impl<'r, S: 'r> Registry<'r, S> { ScalarMeta::new::(Cow::Owned(name.to_string())) } + /// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`]. + /// + /// [`graphql::Type`]: resolve::Type + pub fn build_scalar_type_new<'info, T, Info>(&mut self, info: &Info) -> ScalarMeta<'r, S> + where + T: resolve::TypeName + + resolve::ScalarToken + + for<'inp> resolve::InputValue<'inp, S>, + for<'i> >>::Error: IntoFieldError, + Info: ?Sized, + { + // TODO: Allow using references. + ScalarMeta::new_new::(T::type_name(info).to_owned()) + } + /// Creates a [`ListMeta`] type. /// /// Specifying `expected_size` will be used to ensure that values of this diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 4d87480d9..fb1ac95fd 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -2,7 +2,9 @@ pub mod resolve; use crate::DefaultScalarValue; -pub use crate::value::Value; +pub use crate::{ + ast::InputValue, graphql_input_value as input_value, graphql_value as value, value::Value, +}; pub use self::resolve::Type; @@ -34,6 +36,17 @@ pub trait Object: fn assert_object(); } +pub trait Scalar: + InputType + + OutputType + + Type + + resolve::TypeName + + resolve::Value + + resolve::ValueAsync +{ + fn assert_scalar(); +} + pub trait Union: OutputType + Type diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index e027d0cf5..dd23fd4df 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -1,12 +1,16 @@ +use std::convert::TryFrom; + use crate::{ - meta::MetaType, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, Registry, - Selection, + graphql, + meta::MetaType, + parser::{self, ParseError}, + Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, Registry, Selection, }; pub trait Type { fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> where - S: 'r; + S: 'r; // TODO: remove? } pub trait TypeName { @@ -74,3 +78,40 @@ pub trait FieldAsync { executor: &'r Executor, ) -> BoxFuture<'r, ExecutionResult>; } + +pub trait InputValue<'inp, S: 'inp>: TryFrom<&'inp graphql::InputValue> { + fn try_from_implicit_null() -> Result { + Self::try_from(&graphql::InputValue::::Null) + } +} + +pub trait InputValueOwned: for<'inp> InputValue<'inp, S> {} + +impl InputValueOwned for T where T: for<'inp> InputValue<'inp, S> {} + +pub trait ValidateInputValue: Sized { + fn validate_input_value<'inp>( + v: &'inp graphql::InputValue, + ) -> Result<(), crate::FieldError> + where + Self: TryFrom<&'inp graphql::InputValue>, + >>::Error: crate::IntoFieldError; +} + +impl ValidateInputValue for T { + fn validate_input_value<'inp>( + v: &'inp graphql::InputValue, + ) -> Result<(), crate::FieldError> + where + Self: TryFrom<&'inp graphql::InputValue>, + >>::Error: crate::IntoFieldError, + { + Self::try_from(v) + .map(drop) + .map_err(crate::IntoFieldError::::into_field_error) + } +} + +pub trait ScalarToken { + fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; +} diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 2ba1faa49..f80afce35 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -1,18 +1,19 @@ //! Types used to describe a `GraphQL` schema -use juniper::IntoFieldError; use std::{ borrow::{Cow, ToOwned}, + convert::TryFrom, fmt, }; use crate::{ ast::{FromInputValue, InputValue, Type}, parser::{ParseError, ScalarToken}, + resolve, schema::model::SchemaType, types::base::TypeKind, value::{DefaultScalarValue, ParseScalarValue}, - FieldError, + FieldError, IntoFieldError, }; /// Whether an item is deprecated, with context. @@ -28,16 +29,16 @@ impl DeprecationStatus { /// If this deprecation status indicates the item is deprecated. pub fn is_deprecated(&self) -> bool { match self { - DeprecationStatus::Current => false, - DeprecationStatus::Deprecated(_) => true, + Self::Current => false, + Self::Deprecated(_) => true, } } /// An optional reason for the deprecation, or none if `Current`. pub fn reason(&self) -> Option<&str> { match self { - DeprecationStatus::Current => None, - DeprecationStatus::Deprecated(rsn) => rsn.as_deref(), + Self::Current => None, + Self::Deprecated(rsn) => rsn.as_deref(), } } } @@ -448,6 +449,27 @@ impl<'a, S> ScalarMeta<'a, S> { } } + /// Builds a new [`ScalarMeta`] information with the specified `name`. + // TODO: Use `impl Into>` argument once feature + // `explicit_generic_args_with_impl_trait` hits stable: + // https://github.com/rust-lang/rust/issues/83701 + pub fn new_new(name: N) -> Self + where + T: resolve::ValidateInputValue + resolve::ScalarToken, + //T: for<'inp> resolve::InputValue<'inp, S> + resolve::ScalarToken, + //for<'inp> >>::Error: IntoFieldError, + Cow<'a, str>: From, + { + Self { + name: name.into(), + description: None, + specified_by_url: None, + try_parse_fn: >::validate_input_value, + //try_parse_fn: |inp| try_parse_fn_new::(inp), + parse_fn: >::parse_scalar_token, + } + } + /// Sets the `description` of this [`ScalarMeta`] type. /// /// Overwrites any previously set description. @@ -799,3 +821,11 @@ where .map(drop) .map_err(T::Error::into_field_error) } + +fn try_parse_fn_new<'inp, 'b: 'inp, S: 'inp, T>(v: &'b InputValue) -> Result<(), FieldError> +where + T: resolve::InputValue<'inp, S>, + T::Error: IntoFieldError, +{ + T::try_from(v).map(drop).map_err(T::Error::into_field_error) +} diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 467951c88..309022d5e 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -3,10 +3,10 @@ use std::sync::Arc; use crate::{ - executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, - schema::meta::MetaType, - Arguments, BoxFuture, Selection, + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl resolve::Type for Arc @@ -142,6 +142,15 @@ where } } +impl resolve::ScalarToken for Arc +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Arc where T: graphql::InputType + ?Sized, @@ -178,6 +187,15 @@ where } } +impl graphql::Scalar for Arc +where + T: graphql::Scalar + ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + impl graphql::Union for Arc where T: graphql::Union + ?Sized, diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index c8140f3e2..91f2434b5 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -1,10 +1,10 @@ //! GraphQL implementation for [`Box`]. use crate::{ - executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, - schema::meta::MetaType, - Arguments, BoxFuture, Selection, + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl resolve::Type for Box @@ -140,6 +140,15 @@ where } } +impl resolve::ScalarToken for Box +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Box where T: graphql::InputType + ?Sized, @@ -176,6 +185,15 @@ where } } +impl graphql::Scalar for Box +where + T: graphql::Scalar + ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + impl graphql::Union for Box where T: graphql::Union + ?Sized, diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 5fceeb2ba..5823a8a51 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -8,6 +8,7 @@ mod rc; mod r#ref; mod ref_mut; mod slice; +mod r#str; mod vec; pub mod async_await; diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 3cc1f7355..430790c9e 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -3,10 +3,10 @@ use std::rc::Rc; use crate::{ - executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, - schema::meta::MetaType, - Arguments, BoxFuture, Selection, + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl resolve::Type for Rc @@ -142,6 +142,15 @@ where } } +impl resolve::ScalarToken for Rc +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Rc where T: graphql::InputType + ?Sized, @@ -178,6 +187,15 @@ where } } +impl graphql::Scalar for Rc +where + T: graphql::Scalar + ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + impl graphql::Union for Rc where T: graphql::Union + ?Sized, diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 36b27e4d1..42204d89e 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -3,8 +3,10 @@ //! [reference]: primitive@std::reference use crate::{ - graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, - Selection, + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl<'me, T, Info, S> resolve::Type for &'me T @@ -140,6 +142,15 @@ where } } +impl<'me, T, S> resolve::ScalarToken for &'me T +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl<'me, T, S> graphql::InputType for &'me T where T: graphql::InputType + ?Sized, @@ -176,6 +187,15 @@ where } } +impl<'me, T, S> graphql::Scalar for &'me T +where + T: graphql::Scalar + ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + impl<'me, T, S> graphql::Union for &'me T where T: graphql::Union + ?Sized, diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index eee1ae3b4..a9418a0f7 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -3,8 +3,10 @@ //! [reference]: primitive@std::reference use crate::{ - graphql, meta::MetaType, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, - Selection, + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl<'me, T, Info, S> resolve::Type for &'me mut T @@ -140,6 +142,15 @@ where } } +impl<'me, T, S> resolve::ScalarToken for &'me mut T +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl<'me, T, S> graphql::InputType for &'me mut T where T: graphql::InputType + ?Sized, @@ -176,6 +187,15 @@ where } } +impl<'me, T, S> graphql::Scalar for &'me mut T +where + T: graphql::Scalar + ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + impl<'me, T, S> graphql::Union for &'me mut T where T: graphql::Union + ?Sized, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs new file mode 100644 index 000000000..1b62c3c28 --- /dev/null +++ b/juniper/src/types/str.rs @@ -0,0 +1,97 @@ +//! GraphQL implementation for [`str`]. +//! +//! [`str`]: primitive@std::str + +use std::convert::TryFrom; + +use futures::future; + +use crate::{ + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + resolve, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, ScalarValue, + Selection, +}; + +impl resolve::Type for str { + fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + where + S: 'r, + { + registry.build_scalar_type_new::<&Self, _>(info).into_meta() + } +} + +impl resolve::TypeName for str { + fn type_name(_: &Info) -> &'static str { + // TODO: Reuse from `String`. + "String" + } +} + +impl resolve::Value for str +where + Info: ?Sized, + Ctx: ?Sized, + S: From, +{ + fn resolve_value( + &self, + _: Option<&[Selection<'_, S>]>, + _: &Info, + _: &Executor, + ) -> ExecutionResult { + // TODO: Remove redundant `.to_owned()` allocation by allowing + // `ScalarValue` creation from reference? + Ok(graphql::Value::scalar(self.to_owned())) + } +} + +impl resolve::ValueAsync for str +where + Info: ?Sized, + Ctx: ?Sized, + S: From + Send, +{ + fn resolve_value_async<'r>( + &'r self, + _: Option<&'r [Selection<'_, S>]>, + _: &'r Info, + _: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + // TODO: Remove redundant `.to_owned()` allocation by allowing + // `ScalarValue` creation from reference? + Box::pin(future::ok(graphql::Value::scalar(self.to_owned()))) + } +} + +impl<'me, S: ScalarValue> resolve::ScalarToken for str { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + // TODO: replace with `resolve::ScalarToken` + >::from_str(token) + } +} + +impl graphql::InputType for str { + fn assert_input_type() {} +} + +impl graphql::OutputType for str { + fn assert_output_type() {} +} + +impl graphql::Scalar for str { + fn assert_scalar() {} +} + +impl<'inp: 'me, 'me, S: ScalarValue> TryFrom<&'inp graphql::InputValue> for &'me str { + type Error = String; + + fn try_from(v: &'inp graphql::InputValue) -> Result { + v.as_string_value() + .ok_or_else(|| format!("Expected `String`, found: {}", v)) + } +} + +impl<'inp: 'me, 'me, S: ScalarValue> resolve::InputValue<'inp, S> for &'me str {} From bde5b83eafff83b35f8c28035ed36e7d39294115 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 11 May 2022 19:12:24 +0300 Subject: [PATCH 06/58] Upd --- juniper/src/executor/mod.rs | 2 ++ juniper/src/resolve/mod.rs | 45 ++++++++++++++++--------------------- juniper/src/schema/meta.rs | 4 ++++ juniper/src/types/ref.rs | 15 +++++++++++++ juniper/src/types/str.rs | 25 ++++++++++----------- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index d749fc4ae..3969befbd 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1295,6 +1295,7 @@ impl<'r, S: 'r> Registry<'r, S> { ScalarMeta::new::(Cow::Owned(name.to_string())) } + /* /// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type @@ -1309,6 +1310,7 @@ impl<'r, S: 'r> Registry<'r, S> { // TODO: Allow using references. ScalarMeta::new_new::(T::type_name(info).to_owned()) } + */ /// Creates a [`ListMeta`] type. /// diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index dd23fd4df..98bc3fb17 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -79,39 +79,32 @@ pub trait FieldAsync { ) -> BoxFuture<'r, ExecutionResult>; } -pub trait InputValue<'inp, S: 'inp>: TryFrom<&'inp graphql::InputValue> { +pub trait ScalarToken { + fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; +} + +pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized { + type Error; + + fn try_from_input_value(v: &'input graphql::InputValue) -> Result; + fn try_from_implicit_null() -> Result { - Self::try_from(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::::Null) } } -pub trait InputValueOwned: for<'inp> InputValue<'inp, S> {} +pub trait InputValueOwned: for<'i> InputValue<'i, S> {} -impl InputValueOwned for T where T: for<'inp> InputValue<'inp, S> {} +impl InputValueOwned for T where T: for<'i> InputValue<'i, S> {} -pub trait ValidateInputValue: Sized { - fn validate_input_value<'inp>( - v: &'inp graphql::InputValue, - ) -> Result<(), crate::FieldError> - where - Self: TryFrom<&'inp graphql::InputValue>, - >>::Error: crate::IntoFieldError; -} +pub trait InputValueAsRef { + type Error; -impl ValidateInputValue for T { - fn validate_input_value<'inp>( - v: &'inp graphql::InputValue, - ) -> Result<(), crate::FieldError> - where - Self: TryFrom<&'inp graphql::InputValue>, - >>::Error: crate::IntoFieldError, + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; + + fn try_from_implicit_null() -> Result<&'static Self, Self::Error> + where S: 'static { - Self::try_from(v) - .map(drop) - .map_err(crate::IntoFieldError::::into_field_error) + Self::try_from_input_value(&graphql::InputValue::::Null) } } - -pub trait ScalarToken { - fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; -} diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index f80afce35..fd9ff96e2 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -449,6 +449,7 @@ impl<'a, S> ScalarMeta<'a, S> { } } + /* /// Builds a new [`ScalarMeta`] information with the specified `name`. // TODO: Use `impl Into>` argument once feature // `explicit_generic_args_with_impl_trait` hits stable: @@ -469,6 +470,7 @@ impl<'a, S> ScalarMeta<'a, S> { parse_fn: >::parse_scalar_token, } } + */ /// Sets the `description` of this [`ScalarMeta`] type. /// @@ -822,6 +824,7 @@ where .map_err(T::Error::into_field_error) } +/* fn try_parse_fn_new<'inp, 'b: 'inp, S: 'inp, T>(v: &'b InputValue) -> Result<(), FieldError> where T: resolve::InputValue<'inp, S>, @@ -829,3 +832,4 @@ where { T::try_from(v).map(drop).map_err(T::Error::into_field_error) } +*/ diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 42204d89e..78407fdb2 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -151,6 +151,21 @@ where } } +impl<'inp: 'me, 'me, T, S: 'inp> resolve::InputValue<'inp, S> for &'me T +where + T: resolve::InputValueAsRef + ?Sized, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + >::try_from_input_value(v) + } + + fn try_from_implicit_null() -> Result { + >::try_from_implicit_null() + } +} + impl<'me, T, S> graphql::InputType for &'me T where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 1b62c3c28..08367508e 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -19,7 +19,8 @@ impl resolve::Type for str { where S: 'r, { - registry.build_scalar_type_new::<&Self, _>(info).into_meta() + // registry.build_scalar_type_new::<&Self, _>(info).into_meta() + unimplemented!() } } @@ -68,11 +69,20 @@ where impl<'me, S: ScalarValue> resolve::ScalarToken for str { fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - // TODO: replace with `resolve::ScalarToken` + // TODO: Replace with `resolve::ScalarToken` >::from_str(token) } } +impl<'me, S: ScalarValue> resolve::InputValueAsRef for str { + type Error = String; + + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { + v.as_string_value() + .ok_or_else(|| format!("Expected `String`, found: {}", v)) + } +} + impl graphql::InputType for str { fn assert_input_type() {} } @@ -84,14 +94,3 @@ impl graphql::OutputType for str { impl graphql::Scalar for str { fn assert_scalar() {} } - -impl<'inp: 'me, 'me, S: ScalarValue> TryFrom<&'inp graphql::InputValue> for &'me str { - type Error = String; - - fn try_from(v: &'inp graphql::InputValue) -> Result { - v.as_string_value() - .ok_or_else(|| format!("Expected `String`, found: {}", v)) - } -} - -impl<'inp: 'me, 'me, S: ScalarValue> resolve::InputValue<'inp, S> for &'me str {} From 912d31f66b652d342c7c0b4943acd55b9ef3221e Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 12 May 2022 18:53:19 +0300 Subject: [PATCH 07/58] Poking with parsing input --- juniper/src/executor/mod.rs | 23 ++++++++++------ juniper/src/lib.rs | 2 +- juniper/src/resolve/mod.rs | 24 ++++++++++++----- juniper/src/schema/meta.rs | 49 +++++++++++++++++++++++++---------- juniper/src/types/box.rs | 16 ++++++++++++ juniper/src/types/mod.rs | 2 +- juniper/src/types/nullable.rs | 19 ++++++++++++++ juniper/src/types/option.rs | 15 +++++++++++ juniper/src/types/str.rs | 10 +++---- 9 files changed, 123 insertions(+), 37 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 3969befbd..0dc3118b1 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -4,7 +4,6 @@ use std::{ borrow::Cow, cmp::Ordering, collections::HashMap, - convert::TryFrom, fmt::{Debug, Display}, sync::{Arc, RwLock}, }; @@ -1295,22 +1294,30 @@ impl<'r, S: 'r> Registry<'r, S> { ScalarMeta::new::(Cow::Owned(name.to_string())) } - /* /// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type - pub fn build_scalar_type_new<'info, T, Info>(&mut self, info: &Info) -> ScalarMeta<'r, S> + pub fn build_scalar_type_new(&mut self, info: &Info) -> ScalarMeta<'r, S> where - T: resolve::TypeName - + resolve::ScalarToken - + for<'inp> resolve::InputValue<'inp, S>, - for<'i> >>::Error: IntoFieldError, + T: resolve::TypeName + resolve::ScalarToken + resolve::InputValueOwned, Info: ?Sized, { // TODO: Allow using references. ScalarMeta::new_new::(T::type_name(info).to_owned()) } - */ + + /// Builds a [`ScalarMeta`] information for the [`?Sized`] specified + /// [`graphql::Type`]. + /// + /// [`graphql::Type`]: resolve::Type + pub fn build_scalar_type_unsized(&mut self, info: &Info) -> ScalarMeta<'r, S> + where + T: resolve::TypeName + resolve::ScalarToken + resolve::InputValueAsRef + ?Sized, + Info: ?Sized, + { + // TODO: Allow using references. + ScalarMeta::new_unsized::(T::type_name(info).to_owned()) + } /// Creates a [`ListMeta`] type. /// diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 9ee509bbc..6e35d2301 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -82,12 +82,12 @@ pub use crate::{ async_await::{GraphQLTypeAsync, GraphQLValueAsync}, base::{Arguments, GraphQLType, GraphQLValue, TypeKind}, marker::{self, GraphQLInterface, GraphQLObject, GraphQLUnion}, - Nullable, scalars::{EmptyMutation, EmptySubscription, ID}, subscriptions::{ ExecutionOutput, GraphQLSubscriptionType, GraphQLSubscriptionValue, SubscriptionConnection, SubscriptionCoordinator, }, + Nullable, }, validation::RuleError, value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value}, diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 98bc3fb17..12e108d6c 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -1,10 +1,9 @@ -use std::convert::TryFrom; - use crate::{ graphql, meta::MetaType, parser::{self, ParseError}, - Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, Registry, Selection, + Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, Registry, + Selection, }; pub trait Type { @@ -84,7 +83,7 @@ pub trait ScalarToken { } pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized { - type Error; + type Error: IntoFieldError; fn try_from_input_value(v: &'input graphql::InputValue) -> Result; @@ -98,13 +97,24 @@ pub trait InputValueOwned: for<'i> InputValue<'i, S> {} impl InputValueOwned for T where T: for<'i> InputValue<'i, S> {} pub trait InputValueAsRef { - type Error; + type Error: IntoFieldError; fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; - fn try_from_implicit_null() -> Result<&'static Self, Self::Error> - where S: 'static + fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error> + where + S: 'a, { Self::try_from_input_value(&graphql::InputValue::::Null) } } + +/* +impl InputValueAsRef for T where T: InputValueOwned { + type Error = >::Error; + + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { + >::try_from_input_value(v).as_ref() + } +} +*/ diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index fd9ff96e2..dfcc838d2 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -2,7 +2,6 @@ use std::{ borrow::{Cow, ToOwned}, - convert::TryFrom, fmt, }; @@ -449,28 +448,42 @@ impl<'a, S> ScalarMeta<'a, S> { } } - /* /// Builds a new [`ScalarMeta`] information with the specified `name`. // TODO: Use `impl Into>` argument once feature // `explicit_generic_args_with_impl_trait` hits stable: // https://github.com/rust-lang/rust/issues/83701 pub fn new_new(name: N) -> Self where - T: resolve::ValidateInputValue + resolve::ScalarToken, - //T: for<'inp> resolve::InputValue<'inp, S> + resolve::ScalarToken, - //for<'inp> >>::Error: IntoFieldError, + T: resolve::InputValueOwned + resolve::ScalarToken, Cow<'a, str>: From, { Self { name: name.into(), description: None, specified_by_url: None, - try_parse_fn: >::validate_input_value, - //try_parse_fn: |inp| try_parse_fn_new::(inp), + try_parse_fn: try_parse_fn_new::, + parse_fn: >::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. + // TODO: Use `impl Into>` argument once feature + // `explicit_generic_args_with_impl_trait` hits stable: + // https://github.com/rust-lang/rust/issues/83701 + pub fn new_unsized(name: N) -> Self + where + T: resolve::InputValueAsRef + resolve::ScalarToken + ?Sized, + Cow<'a, str>: From, + { + Self { + name: name.into(), + description: None, + specified_by_url: None, + try_parse_fn: try_parse_unsized_fn::, parse_fn: >::parse_scalar_token, } } - */ /// Sets the `description` of this [`ScalarMeta`] type. /// @@ -824,12 +837,20 @@ where .map_err(T::Error::into_field_error) } -/* -fn try_parse_fn_new<'inp, 'b: 'inp, S: 'inp, T>(v: &'b InputValue) -> Result<(), FieldError> +fn try_parse_fn_new(v: &InputValue) -> Result<(), FieldError> where - T: resolve::InputValue<'inp, S>, - T::Error: IntoFieldError, + T: resolve::InputValueOwned, { - T::try_from(v).map(drop).map_err(T::Error::into_field_error) + T::try_from_input_value(v) + .map(drop) + .map_err(T::Error::into_field_error) +} + +fn try_parse_unsized_fn(v: &InputValue) -> Result<(), FieldError> +where + T: resolve::InputValueAsRef + ?Sized, +{ + T::try_from_input_value(v) + .map(drop) + .map_err(T::Error::into_field_error) } -*/ diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 91f2434b5..3354d3991 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -149,6 +149,22 @@ where } } +// TODO: how to parse unsized? +impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Box +where + T: resolve::InputValue<'inp, S>, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + >::try_from_input_value(v).map(Self::new) + } + + fn try_from_implicit_null() -> Result { + >::try_from_implicit_null().map(Self::new) + } +} + impl graphql::InputType for Box where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 5823a8a51..31517c72d 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,5 +1,5 @@ -mod array; mod arc; +mod array; mod r#box; pub mod iter; mod nullable; diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index 1d2f55839..eb1ee10cb 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -324,6 +324,25 @@ where } } +impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Nullable +where + T: resolve::InputValue<'inp, S>, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp InputValue) -> Result { + if v.is_null() { + Ok(Self::ExplicitNull) + } else { + >::try_from_input_value(v).map(Self::Some) + } + } + + fn try_from_implicit_null() -> Result { + Ok(Self::ImplicitNull) + } +} + impl graphql::InputType for Nullable where T: graphql::InputType, diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index be1ceb485..ec3f06492 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -61,6 +61,21 @@ where } } +impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Option +where + T: resolve::InputValue<'inp, S>, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + if v.is_null() { + Ok(None) + } else { + >::try_from_input_value(v).map(Some) + } + } +} + impl graphql::InputType for Option where T: graphql::InputType, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 08367508e..b7eb4845a 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -2,16 +2,13 @@ //! //! [`str`]: primitive@std::str -use std::convert::TryFrom; - use futures::future; use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, ScalarValue, - Selection, + resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, }; impl resolve::Type for str { @@ -19,8 +16,9 @@ impl resolve::Type for str { where S: 'r, { - // registry.build_scalar_type_new::<&Self, _>(info).into_meta() - unimplemented!() + registry + .build_scalar_type_unsized::(info) + .into_meta() } } From 5a3bd6c8a9bc7becdad8bd753d5585a6e98cbcf9 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 17 May 2022 16:59:42 +0200 Subject: [PATCH 08/58] Pave way to parse `?Sized` types from `InputValue` --- juniper/src/resolve/mod.rs | 29 +++++------------------- juniper/src/types/arc.rs | 45 +++++++++++++++++++++++++++++++++++++- juniper/src/types/box.rs | 41 ++++++++++++++++++++++++++++------ juniper/src/types/mod.rs | 8 +++---- juniper/src/types/rc.rs | 45 +++++++++++++++++++++++++++++++++++++- juniper/src/types/ref.rs | 16 +++++++++++++- juniper/src/types/str.rs | 30 +++++++++++++++++++++++-- 7 files changed, 175 insertions(+), 39 deletions(-) diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 12e108d6c..39c693e3c 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -6,6 +6,12 @@ use crate::{ Selection, }; +#[doc(inline)] +pub use crate::types::{ + arc::TryFromInputValue as InputValueAsArc, r#box::TryFromInputValue as InputValueAsBox, + r#ref::TryFromInputValue as InputValueAsRef, rc::TryFromInputValue as InputValueAsRc, +}; + pub trait Type { fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> where @@ -95,26 +101,3 @@ pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized { pub trait InputValueOwned: for<'i> InputValue<'i, S> {} impl InputValueOwned for T where T: for<'i> InputValue<'i, S> {} - -pub trait InputValueAsRef { - type Error: IntoFieldError; - - fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; - - fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error> - where - S: 'a, - { - Self::try_from_input_value(&graphql::InputValue::::Null) - } -} - -/* -impl InputValueAsRef for T where T: InputValueOwned { - type Error = >::Error; - - fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { - >::try_from_input_value(v).as_ref() - } -} -*/ diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 309022d5e..6c9b8faf8 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -6,7 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, + resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, + Registry, Selection, }; impl resolve::Type for Arc @@ -151,6 +152,48 @@ where } } +impl<'inp, T, S> resolve::InputValue<'inp, S> for Arc +where + T: resolve::InputValueAsArc<'inp, S> + ?Sized, + S: 'inp, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + >::try_from_input_value(v) + } + + fn try_from_implicit_null() -> Result { + >::try_from_implicit_null() + } +} + +pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { + type Error: IntoFieldError; + + fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; + + fn try_from_implicit_null() -> Result, Self::Error> { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + +impl<'inp, T, S> TryFromInputValue<'inp, S> for T +where + T: resolve::InputValue<'inp, S>, + S: 'inp, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Arc::new) + } + + fn try_from_implicit_null() -> Result, Self::Error> { + >::try_from_implicit_null().map(Arc::new) + } +} + impl graphql::InputType for Arc where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 3354d3991..337d15403 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -4,7 +4,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, + resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, + Registry, Selection, }; impl resolve::Type for Box @@ -149,19 +150,45 @@ where } } -// TODO: how to parse unsized? -impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Box +impl<'inp, T, S> resolve::InputValue<'inp, S> for Box where - T: resolve::InputValue<'inp, S>, + T: resolve::InputValueAsBox<'inp, S> + ?Sized, + S: 'inp, { - type Error = >::Error; + type Error = >::Error; fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { - >::try_from_input_value(v).map(Self::new) + >::try_from_input_value(v) } fn try_from_implicit_null() -> Result { - >::try_from_implicit_null().map(Self::new) + >::try_from_implicit_null() + } +} + +pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { + type Error: IntoFieldError; + + fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; + + fn try_from_implicit_null() -> Result, Self::Error> { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + +impl<'inp, T, S> TryFromInputValue<'inp, S> for T +where + T: resolve::InputValue<'inp, S>, + S: 'inp, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Box::new) + } + + fn try_from_implicit_null() -> Result, Self::Error> { + >::try_from_implicit_null().map(Box::new) } } diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 31517c72d..58dfde7cb 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,11 +1,11 @@ -mod arc; +pub mod arc; mod array; -mod r#box; +pub mod r#box; pub mod iter; mod nullable; mod option; -mod rc; -mod r#ref; +pub mod rc; +pub mod r#ref; mod ref_mut; mod slice; mod r#str; diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 430790c9e..8bae30537 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -6,7 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, + resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, + Registry, Selection, }; impl resolve::Type for Rc @@ -151,6 +152,48 @@ where } } +impl<'inp, T, S> resolve::InputValue<'inp, S> for Rc +where + T: resolve::InputValueAsRc<'inp, S> + ?Sized, + S: 'inp, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + >::try_from_input_value(v) + } + + fn try_from_implicit_null() -> Result { + >::try_from_implicit_null() + } +} + +pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { + type Error: IntoFieldError; + + fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; + + fn try_from_implicit_null() -> Result, Self::Error> { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + +impl<'inp, T, S> TryFromInputValue<'inp, S> for T +where + T: resolve::InputValue<'inp, S>, + S: 'inp, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Rc::new) + } + + fn try_from_implicit_null() -> Result, Self::Error> { + >::try_from_implicit_null().map(Rc::new) + } +} + impl graphql::InputType for Rc where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 78407fdb2..83d33ac1b 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -6,7 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, + resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, + Registry, Selection, }; impl<'me, T, Info, S> resolve::Type for &'me T @@ -166,6 +167,19 @@ where } } +pub trait TryFromInputValue { + type Error: IntoFieldError; + + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; + + fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error> + where + S: 'a, + { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + impl<'me, T, S> graphql::InputType for &'me T where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index b7eb4845a..47b26a67f 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -2,6 +2,8 @@ //! //! [`str`]: primitive@std::str +use std::{sync::Arc, rc::Rc}; + use futures::future; use crate::{ @@ -65,14 +67,14 @@ where } } -impl<'me, S: ScalarValue> resolve::ScalarToken for str { +impl resolve::ScalarToken for str { fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { // TODO: Replace with `resolve::ScalarToken` >::from_str(token) } } -impl<'me, S: ScalarValue> resolve::InputValueAsRef for str { +impl resolve::InputValueAsRef for str { type Error = String; fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { @@ -81,6 +83,30 @@ impl<'me, S: ScalarValue> resolve::InputValueAsRef for str { } } +impl<'inp, S: ScalarValue> resolve::InputValueAsBox<'inp, S> for str { + type Error = String; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) + } +} + +impl<'inp, S: ScalarValue> resolve::InputValueAsArc<'inp, S> for str { + type Error = String; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) + } +} + +impl<'inp, S: ScalarValue> resolve::InputValueAsRc<'inp, S> for str { + type Error = String; + + fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) + } +} + impl graphql::InputType for str { fn assert_input_type() {} } From 21c7a3a65376cb1ced390389ffa1575cb0ca80f3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 17 May 2022 17:13:25 +0200 Subject: [PATCH 09/58] Bootstrap codegen for scalars --- juniper/src/types/str.rs | 2 +- juniper_codegen/src/graphql_scalar/mod.rs | 58 +++++++++++++++++++++++ 2 files changed, 59 insertions(+), 1 deletion(-) diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 47b26a67f..4177f4da3 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -2,7 +2,7 @@ //! //! [`str`]: primitive@std::str -use std::{sync::Arc, rc::Rc}; +use std::{rc::Rc, sync::Arc}; use futures::future; diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 556529805..a0632b9e2 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -330,6 +330,8 @@ impl ToTokens for Definition { self.impl_from_input_value_tokens().to_tokens(into); self.impl_parse_scalar_value_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); + //////////////////////////////////////////////////////////////////////// + self.impl_resolve_input_value_tokens().to_tokens(into); } } @@ -524,6 +526,37 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::InputValue`] trait for + /// this [GraphQL scalar][1]. + /// + /// [`resolve::InputValue`]: juniper::resolve::InputValue + /// [1]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_resolve_input_value_tokens(&self) -> TokenStream { + let scalar = &self.scalar; + + let conversion = self.methods.expand_try_from_input_value(scalar); + + let (ty, mut generics) = self.impl_self_and_generics(false); + generics.params.push(parse_quote! { '__inp }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::InputValue<'__inp, #scalar> for #ty + #where_clause + { + type Error = ::juniper::FieldError<#scalar>; + + fn try_from_input_value( + input: &'__inp ::juniper::graphql::InputValue<#scalar>, + ) -> Result { + #conversion + .map_err(::juniper::IntoFieldError::<#scalar>::into_field_error) + } + } + } + } + /// Returns generated code implementing [`ParseScalarValue`] trait for this /// [GraphQL scalar][1]. /// @@ -790,6 +823,31 @@ impl Methods { } } + /// Expands [`resolve::InputValue::try_from_input_value()`][0] method. + /// + /// [0]: juniper::resolve::InputValue::try_from_input_value + fn expand_try_from_input_value(&self, scalar: &scalar::Type) -> TokenStream { + match self { + Self::Custom { from_input, .. } + | Self::Delegated { + from_input: Some(from_input), + .. + } => { + quote! { #from_input(input) } + } + + Self::Delegated { field, .. } => { + let field_ty = field.ty(); + let self_constructor = field.closure_constructor(); + + quote! { + <#field_ty as ::juniper::resolve::InputValue<'__inp, #scalar>>::try_from_input_value(input) + .map(#self_constructor) + } + } + } + } + /// Expands [`ParseScalarValue::from_str`] method. /// /// [`ParseScalarValue::from_str`]: juniper::ParseScalarValue::from_str From 8ea231f395b210013e96802fee7f94fa9e5d5b71 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 19 May 2022 17:32:43 +0200 Subject: [PATCH 10/58] Building up codegen for scalars, vol.2 --- juniper/src/types/str.rs | 8 +- juniper_codegen/src/common/scalar.rs | 8 +- juniper_codegen/src/graphql_scalar/mod.rs | 191 ++++++++++++++++++++-- 3 files changed, 185 insertions(+), 22 deletions(-) diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 4177f4da3..e4f3e1e35 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -25,9 +25,8 @@ impl resolve::Type for str { } impl resolve::TypeName for str { - fn type_name(_: &Info) -> &'static str { - // TODO: Reuse from `String`. - "String" + fn type_name(info: &Info) -> &str { + >::type_name(info) } } @@ -69,8 +68,7 @@ where impl resolve::ScalarToken for str { fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - // TODO: Replace with `resolve::ScalarToken` - >::from_str(token) + >::parse_scalar_token(token) } } diff --git a/juniper_codegen/src/common/scalar.rs b/juniper_codegen/src/common/scalar.rs index 8f11833de..103c3ac07 100644 --- a/juniper_codegen/src/common/scalar.rs +++ b/juniper_codegen/src/common/scalar.rs @@ -63,15 +63,17 @@ pub(crate) enum Type { /// [`ScalarValue`]: juniper::ScalarValue Concrete(syn::Type), - /// One of type parameters of the original type is specified as [`ScalarValue`]. + /// One of type parameters of the original type is specified as + /// [`ScalarValue`]. /// /// The original type is the type that the code is generated for. /// /// [`ScalarValue`]: juniper::ScalarValue ExplicitGeneric(syn::Ident), - /// [`ScalarValue`] parametrization is assumed to be generic and is not specified - /// explicitly, or specified as bound predicate (like `S: ScalarValue + Send + Sync`). + /// [`ScalarValue`] parametrization is assumed to be generic and is not + /// specified explicitly, or specified as bound predicate (like + /// `S: ScalarValue + Send + Sync`). /// /// [`ScalarValue`]: juniper::ScalarValue ImplicitGeneric(Option), diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index a0632b9e2..e76540566 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -331,7 +331,9 @@ impl ToTokens for Definition { self.impl_parse_scalar_value_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// - self.impl_resolve_input_value_tokens().to_tokens(into); + self.impl_resolve_type_name().to_tokens(into); + self.impl_resolve_input_value().to_tokens(into); + self.impl_resolve_scalar_token().to_tokens(into); } } @@ -406,6 +408,30 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::TypeName`] trait for this + /// [GraphQL scalar][1]. + /// + /// [`resolve::TypeName`]: juniper::resolve::TypeName + /// [1]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_resolve_type_name(&self) -> TokenStream { + let name = &self.name; + + let (ty, generics) = self.ty_and_generics(); + let (info_ty, generics) = self.mix_info_ty(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::TypeName<#info_ty> for #ty + #where_clause + { + fn type_name(_: &#info_ty) -> &'static str { + #name + } + } + } + } + /// Returns generated code implementing [`GraphQLValue`] trait for this /// [GraphQL scalar][1]. /// @@ -531,27 +557,28 @@ impl Definition { /// /// [`resolve::InputValue`]: juniper::resolve::InputValue /// [1]: https://spec.graphql.org/October2021#sec-Scalars - fn impl_resolve_input_value_tokens(&self) -> TokenStream { - let scalar = &self.scalar; + fn impl_resolve_input_value(&self) -> TokenStream { + let conversion = self.methods.expand_try_from_input_value(&self.scalar); - let conversion = self.methods.expand_try_from_input_value(scalar); - - let (ty, mut generics) = self.impl_self_and_generics(false); - generics.params.push(parse_quote! { '__inp }); + let (ty, generics) = self.ty_and_generics(); + let (scalar, mut generics) = self.mix_scalar_ty(generics); + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::InputValue<'__inp, #scalar> for #ty + impl#impl_gens ::juniper::resolve::InputValue<#lt, #scalar> for #ty #where_clause { type Error = ::juniper::FieldError<#scalar>; fn try_from_input_value( - input: &'__inp ::juniper::graphql::InputValue<#scalar>, + input: &#lt ::juniper::graphql::InputValue<#scalar>, ) -> Result { - #conversion - .map_err(::juniper::IntoFieldError::<#scalar>::into_field_error) + #conversion.map_err( + ::juniper::IntoFieldError::<#scalar>::into_field_error, + ) } } } @@ -584,6 +611,35 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ScalarToken`] trait for + /// this [GraphQL scalar][1]. + /// + /// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken + /// [1]: https://spec.graphql.org/October2021#sec-Scalars + 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 (scalar, generics) = self.mix_scalar_ty(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ScalarToken<#scalar> for #ty + #where_clause + { + fn parse_scalar_token( + token: ::juniper::parser::ScalarToken<'_>, + ) -> ::std::result::Result< + #scalar, + ::juniper::parser::ParseError<'_>, + > { + #body + } + } + } + } + /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and /// [`WrappedType`] traits for this [GraphQL scalar][1]. /// @@ -700,6 +756,57 @@ impl Definition { (ty, generics) } + + #[must_use] + fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { + let mut generics = self.generics.clone(); + + let ty = match &self.ty { + TypeOrIdent::Type(ty) => (**ty).clone(), + TypeOrIdent::Ident(ident) => { + let (_, ty_gen, _) = self.generics.split_for_impl(); + parse_quote! { #ident#ty_gen } + } + }; + + if !self.where_clause.is_empty() { + generics + .make_where_clause() + .predicates + .extend(self.where_clause.clone()) + } + + (ty, generics) + } + + #[must_use] + fn mix_info_ty(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + let ty = parse_quote! { __Info }; + + generics.params.push(parse_quote! { #ty: ?Sized }); + + (ty, generics) + } + + #[must_use] + fn mix_scalar_ty(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) { + let scalar = &self.scalar; + + if scalar.is_implicit_generic() { + 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() { + generics.make_where_clause().predicates.push(bound); + } + + (scalar, generics) + } } /// Adds `__fa__` prefix to all lifetimes to avoid "lifetime name `'a` shadows a @@ -823,7 +930,8 @@ impl Methods { } } - /// Expands [`resolve::InputValue::try_from_input_value()`][0] method. + /// Expands body of [`resolve::InputValue::try_from_input_value()`][0] + /// method. /// /// [0]: juniper::resolve::InputValue::try_from_input_value fn expand_try_from_input_value(&self, scalar: &scalar::Type) -> TokenStream { @@ -841,8 +949,9 @@ impl Methods { let self_constructor = field.closure_constructor(); quote! { - <#field_ty as ::juniper::resolve::InputValue<'__inp, #scalar>>::try_from_input_value(input) - .map(#self_constructor) + <#field_ty as ::juniper::resolve::InputValue<'_, #scalar>> + ::try_from_input_value(input) + .map(#self_constructor) } } } @@ -869,6 +978,27 @@ impl Methods { } } } + + /// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0] + /// method. + /// + /// [0]: resolve::ScalarToken::parse_scalar_token + fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { + match self { + Self::Custom { parse_token, .. } + | Self::Delegated { + parse_token: Some(parse_token), + .. + } => parse_token.expand_parse_scalar_token(scalar), + Self::Delegated { field, .. } => { + let field_ty = field.ty(); + quote! { + <#field_ty as ::juniper::resolve::ScalarToken<#scalar>> + ::parse_scalar_token(token) + } + } + } + } } /// Representation of [`ParseScalarValue::from_str`] method. @@ -912,6 +1042,39 @@ impl ParseToken { .unwrap_or_default(), } } + + /// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0] + /// method. + /// + /// [0]: resolve::ScalarToken::parse_scalar_token + fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { + match self { + Self::Custom(parse_token) => { + quote! { #parse_token(token) } + } + Self::Delegated(delegated) => delegated + .iter() + .fold(None, |acc, ty| { + acc.map_or_else( + || { + Some(quote! { + <#ty as ::juniper::resolve::ScalarToken<#scalar>> + ::parse_scalar_token(token) + }) + }, + |prev| { + Some(quote! { + #prev.or_else(|_| { + <#ty as ::juniper::resolve::ScalarToken<#scalar>> + ::parse_scalar_token(token) + }) + }) + }, + ) + }) + .unwrap_or_default(), + } + } } /// Struct field to resolve not provided methods. From b381c696ffc5c530c75c7d8f13a4dcd60d86ec85 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 27 May 2022 18:11:40 +0200 Subject: [PATCH 11/58] Improve and polish codegen for scalars --- juniper/src/types/str.rs | 5 +- juniper_codegen/src/graphql_scalar/mod.rs | 216 +++++++++++++++++++--- 2 files changed, 191 insertions(+), 30 deletions(-) diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index e4f3e1e35..af7c966cb 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -66,7 +66,10 @@ where } } -impl resolve::ScalarToken for str { +impl resolve::ScalarToken for str +where + String: resolve::ScalarToken, +{ fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { >::parse_scalar_token(token) } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index e76540566..45a342917 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -332,6 +332,7 @@ impl ToTokens for Definition { self.impl_reflection_traits_tokens().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_scalar_token().to_tokens(into); } @@ -409,24 +410,71 @@ impl Definition { } /// Returns generated code implementing [`resolve::TypeName`] trait for this - /// [GraphQL scalar][1]. + /// [GraphQL scalar][0]. /// /// [`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 { + let (ty, generics) = self.ty_and_generics(); + let (info, generics) = self.mix_info(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + let name = &self.name; + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::TypeName<#info> for #ty + #where_clause + { + fn type_name(_: &#info) -> &'static str { + #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_ty, generics) = self.mix_info_ty(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::TypeName<#info_ty> for #ty + impl#impl_gens ::juniper::resolve::Type<#info, #scalar> for #ty #where_clause { - fn type_name(_: &#info_ty) -> &'static str { - #name + fn meta<'r>( + registry: &mut ::juniper::Registry<'r, #scalar>, + info: &#info, + ) -> ::juniper::meta::MetaType<'r, #scalar> + where + #scalar: 'r, + { + registry.build_scalar_type_new::(info) + #description + #specified_by_url + .into_meta() } } } @@ -553,19 +601,23 @@ impl Definition { } /// Returns generated code implementing [`resolve::InputValue`] trait for - /// this [GraphQL scalar][1]. + /// this [GraphQL scalar][0]. /// /// [`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 { - let conversion = self.methods.expand_try_from_input_value(&self.scalar); - 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 }; 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 conversion = self.methods.expand_try_from_input_value(scalar); + quote! { #[automatically_derived] impl#impl_gens ::juniper::resolve::InputValue<#lt, #scalar> for #ty @@ -575,7 +627,7 @@ impl Definition { fn try_from_input_value( input: &#lt ::juniper::graphql::InputValue<#scalar>, - ) -> Result { + ) -> ::std::result::Result { #conversion.map_err( ::juniper::IntoFieldError::<#scalar>::into_field_error, ) @@ -612,17 +664,21 @@ impl Definition { } /// Returns generated code implementing [`resolve::ScalarToken`] trait for - /// this [GraphQL scalar][1]. + /// this [GraphQL scalar][0]. /// /// [`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 { - let body = self.methods.expand_parse_scalar_token(&self.scalar); - 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 body = self.methods.expand_parse_scalar_token(scalar); + quote! { #[automatically_derived] impl#impl_gens ::juniper::resolve::ScalarToken<#scalar> for #ty @@ -757,6 +813,8 @@ impl Definition { (ty, generics) } + /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait + /// implementation. #[must_use] fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let mut generics = self.generics.clone(); @@ -779,8 +837,10 @@ impl Definition { (ty, generics) } + /// Mixes a type info [`syn::GenericParam`] into the provided + /// [`syn::Generics`] and returns its [`syn::Ident`]. #[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 }; generics.params.push(parse_quote! { #ty: ?Sized }); @@ -788,19 +848,32 @@ impl Definition { (ty, generics) } + /// Mixes a context [`syn::GenericParam`] into the provided + /// [`syn::Generics`] and returns its [`syn::Ident`]. #[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; if scalar.is_implicit_generic() { 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() { 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. /// /// [`ParseScalarValue::from_str`]: juniper::ParseScalarValue::from_str @@ -982,7 +1087,7 @@ impl Methods { /// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0] /// method. /// - /// [0]: resolve::ScalarToken::parse_scalar_token + /// [0]: juniper::resolve::ScalarToken::parse_scalar_token fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { match self { Self::Custom { parse_token, .. } @@ -990,8 +1095,10 @@ impl Methods { parse_token: Some(parse_token), .. } => parse_token.expand_parse_scalar_token(scalar), + Self::Delegated { field, .. } => { let field_ty = field.ty(); + quote! { <#field_ty as ::juniper::resolve::ScalarToken<#scalar>> ::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 { + 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. @@ -1046,12 +1177,13 @@ impl ParseToken { /// Expands body of [`resolve::ScalarToken::parse_scalar_token()`][0] /// method. /// - /// [0]: resolve::ScalarToken::parse_scalar_token + /// [0]: juniper::resolve::ScalarToken::parse_scalar_token fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { match self { Self::Custom(parse_token) => { quote! { #parse_token(token) } } + Self::Delegated(delegated) => delegated .iter() .fold(None, |acc, ty| { @@ -1075,6 +1207,31 @@ impl ParseToken { .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 { + 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. @@ -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 { match self { Field::Named(syn::Field { ident, .. }) => { From b1be8f1d29cdfb640ef1bfd4e205ac4a7798382f Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 30 May 2022 19:09:15 +0200 Subject: [PATCH 12/58] Improve codegen for scalars, vol.2 --- juniper/src/lib.rs | 1 + juniper/src/reflect/mod.rs | 1 + juniper/src/types/scalars.rs | 12 --- juniper/src/types/str.rs | 18 ++++- juniper_codegen/src/graphql_scalar/mod.rs | 93 ++++++++++++++++++----- 5 files changed, 90 insertions(+), 35 deletions(-) create mode 100644 juniper/src/reflect/mod.rs diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 6e35d2301..9340608a2 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -36,6 +36,7 @@ pub mod http; pub mod integrations; mod introspection; pub mod parser; +pub mod reflect; pub mod resolve; pub(crate) mod schema; mod types; diff --git a/juniper/src/reflect/mod.rs b/juniper/src/reflect/mod.rs new file mode 100644 index 000000000..e91f009a8 --- /dev/null +++ b/juniper/src/reflect/mod.rs @@ -0,0 +1 @@ +pub use crate::macros::reflect::*; diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 66e26e74f..ac7817705 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -196,18 +196,6 @@ where }) } -impl reflect::WrappedType for str { - const VALUE: reflect::WrappedValue = 1; -} - -impl reflect::BaseType for str { - const NAME: reflect::Type = "String"; -} - -impl reflect::BaseSubTypes for str { - const NAMES: reflect::Types = &[>::NAME]; -} - impl GraphQLType for str where S: ScalarValue, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index af7c966cb..24861e6bb 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -7,7 +7,7 @@ use std::{rc::Rc, sync::Arc}; use futures::future; use crate::{ - graphql, + graphql, reflect, meta::MetaType, parser::{ParseError, ScalarToken}, resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, @@ -25,8 +25,8 @@ impl resolve::Type for str { } impl resolve::TypeName for str { - fn type_name(info: &Info) -> &str { - >::type_name(info) + fn type_name(_: &Info) -> &'static str { + >::NAME } } @@ -119,3 +119,15 @@ impl graphql::OutputType for str { impl graphql::Scalar for str { fn assert_scalar() {} } + +impl reflect::BaseType for str { + const NAME: reflect::Type = >::NAME; +} + +impl reflect::BaseSubTypes for str { + const NAMES: reflect::Types = &[>::NAME]; +} + +impl reflect::WrappedType for str { + const VALUE: reflect::WrappedValue = 1; +} diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 45a342917..7e98eae3f 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -329,12 +329,14 @@ impl ToTokens for Definition { self.impl_to_input_value_tokens().to_tokens(into); self.impl_from_input_value_tokens().to_tokens(into); self.impl_parse_scalar_value_tokens().to_tokens(into); - self.impl_reflection_traits_tokens().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_input_value().to_tokens(into); self.impl_resolve_scalar_token().to_tokens(into); + self.impl_input_and_output_type().to_tokens(into); + //self.impl_scalar().to_tokens(into); + self.impl_reflect().to_tokens(into); } } @@ -363,6 +365,56 @@ impl Definition { } } + /// Returns generated code implementing [`graphql::InputType`] and + /// [`graphql::OutputType`] traits for this [GraphQL scalar][0]. + /// + /// [`graphql::InputType`]: juniper::graphql::InputType + /// [`graphql::OutputType`]: juniper::graphql::OutputType + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + #[must_use] + fn impl_input_and_output_type(&self) -> TokenStream { + let (ty, generics) = self.ty_and_generics(); + let (scalar, generics) = self.mix_scalar(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::graphql::InputType<#scalar> for #ty + #where_clause + { + fn assert_input_type() {} + } + + #[automatically_derived] + impl#impl_gens ::juniper::graphql::OutputType<#scalar> for #ty + #where_clause + { + fn assert_output_type() {} + } + } + } + + /// Returns generated code implementing [`graphql::Scalar`] trait for this + /// [GraphQL scalar][0]. + /// + /// [`graphql::Scalar`]: juniper::graphql::Scalar + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + #[must_use] + fn impl_scalar(&self) -> TokenStream { + let (ty, generics) = self.ty_and_generics(); + let (scalar, generics) = self.mix_scalar(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::graphql::Scalar<#scalar> for #ty + #where_clause + { + fn assert_scalar() {} + } + } + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL scalar][1]. /// @@ -696,41 +748,42 @@ impl Definition { } } - /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and - /// [`WrappedType`] traits for this [GraphQL scalar][1]. + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this + /// [GraphQL scalar][0]. /// - /// [`BaseSubTypes`]: juniper::macros::reflection::BaseSubTypes - /// [`BaseType`]: juniper::macros::reflection::BaseType - /// [`WrappedType`]: juniper::macros::reflection::WrappedType - /// [1]: https://spec.graphql.org/October2021#sec-Scalars - fn impl_reflection_traits_tokens(&self) -> TokenStream { - let scalar = &self.scalar; - let name = &self.name; - - let (ty, generics) = self.impl_self_and_generics(false); + /// [`reflect::BaseSubTypes`]: juniper::reflection::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflection::BaseType + /// [`reflect::WrappedType`]: juniper::reflection::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_reflect(&self) -> TokenStream { + let (ty, generics) = self.ty_and_generics(); + let (scalar, generics) = self.mix_scalar(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); + let name = &self.name; + quote! { #[automatically_derived] - impl#impl_gens ::juniper::macros::reflect::BaseType<#scalar> for #ty + impl#impl_gens ::juniper::reflect::BaseType<#scalar> for #ty #where_clause { - const NAME: ::juniper::macros::reflect::Type = #name; + const NAME: ::juniper::reflect::Type = #name; } #[automatically_derived] - impl#impl_gens ::juniper::macros::reflect::BaseSubTypes<#scalar> for #ty + impl#impl_gens ::juniper::reflect::BaseSubTypes<#scalar> for #ty #where_clause { - const NAMES: ::juniper::macros::reflect::Types = - &[>::NAME]; + const NAMES: ::juniper::reflect::Types = + &[>::NAME]; } #[automatically_derived] - impl#impl_gens ::juniper::macros::reflect::WrappedType<#scalar> for #ty + impl#impl_gens ::juniper::reflect::WrappedType<#scalar> for #ty #where_clause { - const VALUE: ::juniper::macros::reflect::WrappedValue = 1; + const VALUE: ::juniper::reflect::WrappedValue = 1; } } } From 22f3f1887ea8f0e93c0a3f49768a7af751274e79 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 31 May 2022 00:29:14 +0200 Subject: [PATCH 13/58] Bikeshed reflection, vol.1 --- juniper/src/macros/reflect.rs | 126 ++-------------------- juniper/src/types/arc.rs | 25 ++++- juniper/src/types/array.rs | 23 +++- juniper/src/types/box.rs | 25 ++++- juniper/src/types/mod.rs | 1 + juniper/src/types/nullable.rs | 25 ++++- juniper/src/types/option.rs | 23 +++- juniper/src/types/rc.rs | 25 ++++- juniper/src/types/ref.rs | 25 ++++- juniper/src/types/ref_mut.rs | 23 +++- juniper/src/types/result.rs | 24 +++++ juniper/src/types/slice.rs | 23 +++- juniper/src/types/str.rs | 6 +- juniper/src/types/vec.rs | 23 +++- juniper_codegen/src/graphql_scalar/mod.rs | 7 +- 15 files changed, 267 insertions(+), 137 deletions(-) create mode 100644 juniper/src/types/result.rs diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index 4cba097f8..912a25241 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -44,10 +44,6 @@ pub trait BaseType { const NAME: Type; } -impl<'a, S, T: BaseType + ?Sized> BaseType for &'a T { - const NAME: Type = T::NAME; -} - impl<'ctx, S, T> BaseType for (&'ctx T::Context, T) where S: ScalarValue, @@ -56,42 +52,6 @@ where const NAME: Type = T::NAME; } -impl> BaseType for Option { - const NAME: Type = T::NAME; -} - -impl> BaseType for Nullable { - const NAME: Type = T::NAME; -} - -impl, E> BaseType for Result { - const NAME: Type = T::NAME; -} - -impl> BaseType for Vec { - const NAME: Type = T::NAME; -} - -impl> BaseType for [T] { - const NAME: Type = T::NAME; -} - -impl, const N: usize> BaseType for [T; N] { - const NAME: Type = T::NAME; -} - -impl + ?Sized> BaseType for Box { - const NAME: Type = T::NAME; -} - -impl + ?Sized> BaseType for Arc { - const NAME: Type = T::NAME; -} - -impl + ?Sized> BaseType for Rc { - const NAME: Type = T::NAME; -} - /// [Sub-types][2] of a [GraphQL object][1]. /// /// This trait is transparent to [`Option`], [`Vec`] and other containers. @@ -105,10 +65,6 @@ pub trait BaseSubTypes { const NAMES: Types; } -impl<'a, S, T: BaseSubTypes + ?Sized> BaseSubTypes for &'a T { - const NAMES: Types = T::NAMES; -} - impl<'ctx, S, T> BaseSubTypes for (&'ctx T::Context, T) where S: ScalarValue, @@ -117,42 +73,6 @@ where const NAMES: Types = T::NAMES; } -impl> BaseSubTypes for Option { - const NAMES: Types = T::NAMES; -} - -impl> BaseSubTypes for Nullable { - const NAMES: Types = T::NAMES; -} - -impl, E> BaseSubTypes for Result { - const NAMES: Types = T::NAMES; -} - -impl> BaseSubTypes for Vec { - const NAMES: Types = T::NAMES; -} - -impl> BaseSubTypes for [T] { - const NAMES: Types = T::NAMES; -} - -impl, const N: usize> BaseSubTypes for [T; N] { - const NAMES: Types = T::NAMES; -} - -impl + ?Sized> BaseSubTypes for Box { - const NAMES: Types = T::NAMES; -} - -impl + ?Sized> BaseSubTypes for Arc { - const NAMES: Types = T::NAMES; -} - -impl + ?Sized> BaseSubTypes for Rc { - const NAMES: Types = T::NAMES; -} - /// Alias for a value of a [`WrappedType`] (composed GraphQL type). pub type WrappedValue = u128; @@ -163,7 +83,7 @@ pub type WrappedValue = u128; /// because of the [wrapping types][2]. To work around this we use a /// [`WrappedValue`] which is represented via [`u128`] number in the following /// encoding: -/// - In base case of non-nullable [object][1] [`VALUE`] is `1`. +/// - In base case of non-nullable singular [object][1] [`VALUE`] is `1`. /// - To represent nullability we "append" `2` to the [`VALUE`], so /// [`Option`]`<`[object][1]`>` has [`VALUE`] of `12`. /// - To represent list we "append" `3` to the [`VALUE`], so @@ -213,44 +133,18 @@ where const VALUE: u128 = T::VALUE; } -impl> WrappedType for Option { - const VALUE: u128 = T::VALUE * 10 + 2; -} +pub mod wrap { + use super::WrappedValue; -impl> WrappedType for Nullable { - const VALUE: u128 = T::VALUE * 10 + 2; -} + pub const SINGULAR: WrappedValue = 1; -impl, E> WrappedType for Result { - const VALUE: u128 = T::VALUE; -} - -impl> WrappedType for Vec { - const VALUE: u128 = T::VALUE * 10 + 3; -} - -impl> WrappedType for [T] { - const VALUE: u128 = T::VALUE * 10 + 3; -} - -impl, const N: usize> WrappedType for [T; N] { - const VALUE: u128 = T::VALUE * 10 + 3; -} - -impl<'a, S, T: WrappedType + ?Sized> WrappedType for &'a T { - const VALUE: u128 = T::VALUE; -} - -impl + ?Sized> WrappedType for Box { - const VALUE: u128 = T::VALUE; -} - -impl + ?Sized> WrappedType for Arc { - const VALUE: u128 = T::VALUE; -} + pub const fn nullable(val: WrappedValue) -> WrappedValue { + val * 10 + 2 + } -impl + ?Sized> WrappedType for Rc { - const VALUE: u128 = T::VALUE; + pub const fn list(val: WrappedValue) -> WrappedValue { + val * 10 + 3 + } } /// Alias for a [GraphQL object][1] or [interface][2] [field argument][3] name. diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 6c9b8faf8..33804c3fe 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -6,8 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, - Registry, Selection, + reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, + IntoFieldError, Registry, Selection, }; impl resolve::Type for Arc @@ -247,3 +247,24 @@ where T::assert_union() } } + +impl reflect::BaseType for Arc +where + T: reflect::BaseType + ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Arc +where + T: reflect::BaseSubTypes + ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Arc +where + T: reflect::WrappedType + ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index e018148da..4cd5856bb 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -4,7 +4,7 @@ use crate::{ executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, + graphql, reflect, resolve, schema::meta::MetaType, BoxFuture, Selection, }; @@ -81,3 +81,24 @@ where T::assert_output_type() } } + +impl reflect::BaseType for [T; N] +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for [T; N] +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for [T; N] +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); +} diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 337d15403..1237ed6ea 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -4,8 +4,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, - Registry, Selection, + reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, + IntoFieldError, Registry, Selection, }; impl resolve::Type for Box @@ -245,3 +245,24 @@ where T::assert_union() } } + +impl reflect::BaseType for Box +where + T: reflect::BaseType + ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Box +where + T: reflect::BaseSubTypes + ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Box +where + T: reflect::WrappedType + ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 58dfde7cb..5e529d61a 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -7,6 +7,7 @@ mod option; pub mod rc; pub mod r#ref; mod ref_mut; +mod result; mod slice; mod r#str; mod vec; diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index eb1ee10cb..4113ea812 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -7,7 +7,7 @@ use futures::future; use crate::{ ast::{FromInputValue, InputValue, ToInputValue}, executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, + graphql, reflect, resolve, schema::meta::MetaType, types::{ async_await::GraphQLValueAsync, @@ -361,6 +361,29 @@ where } } +impl reflect::BaseType for Nullable +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Nullable +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Nullable +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); +} + +//////////////////////////////////////////////////////////////////////////////// + impl GraphQLType for Nullable where T: GraphQLType, diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index ec3f06492..75320f82f 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -4,7 +4,7 @@ use futures::future; use crate::{ executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, + graphql, reflect, resolve, schema::meta::MetaType, BoxFuture, Selection, }; @@ -93,3 +93,24 @@ where T::assert_output_type() } } + +impl reflect::BaseType for Option +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Option +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Option +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); +} diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 8bae30537..515290b12 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -6,8 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, - Registry, Selection, + reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, + IntoFieldError, Registry, Selection, }; impl resolve::Type for Rc @@ -247,3 +247,24 @@ where T::assert_union() } } + +impl reflect::BaseType for Rc +where + T: reflect::BaseType + ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Rc +where + T: reflect::BaseSubTypes + ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Rc +where + T: reflect::WrappedType + ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 83d33ac1b..646b45661 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -6,8 +6,8 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, - Registry, Selection, + reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, + IntoFieldError, Registry, Selection, }; impl<'me, T, Info, S> resolve::Type for &'me T @@ -233,3 +233,24 @@ where T::assert_union() } } + +impl<'me, T, S> reflect::BaseType for &'me T +where + T: reflect::BaseType + ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl<'me, T, S> reflect::BaseSubTypes for &'me T +where + T: reflect::BaseSubTypes + ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl<'me, T, S> reflect::WrappedType for &'me T +where + T: reflect::WrappedType + ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index a9418a0f7..36a576d72 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -6,7 +6,7 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl<'me, T, Info, S> resolve::Type for &'me mut T @@ -204,3 +204,24 @@ where T::assert_union() } } + +impl<'me, T, S> reflect::BaseType for &'me mut T +where + T: reflect::BaseType + ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl<'me, T, S> reflect::BaseSubTypes for &'me mut T +where + T: reflect::BaseSubTypes + ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl<'me, T, S> reflect::WrappedType for &'me mut T +where + T: reflect::WrappedType + ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/result.rs b/juniper/src/types/result.rs new file mode 100644 index 000000000..1077f979c --- /dev/null +++ b/juniper/src/types/result.rs @@ -0,0 +1,24 @@ +//! GraphQL implementation for [`Result`]. + +use crate::reflect; + +impl reflect::BaseType for Result +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Result +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Result +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index e3ad5b49a..5a5b6b52b 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -4,7 +4,7 @@ use crate::{ executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, + graphql, reflect, resolve, schema::meta::MetaType, BoxFuture, Selection, }; @@ -79,3 +79,24 @@ where T::assert_output_type() } } + +impl reflect::BaseType for [T] +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for [T] +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for [T] +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); +} diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 24861e6bb..b8f269851 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -7,10 +7,10 @@ use std::{rc::Rc, sync::Arc}; use futures::future; use crate::{ - graphql, reflect, + graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, + reflect, resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, }; impl resolve::Type for str { @@ -129,5 +129,5 @@ impl reflect::BaseSubTypes for str { } impl reflect::WrappedType for str { - const VALUE: reflect::WrappedValue = 1; + const VALUE: reflect::WrappedValue = reflect::wrap::SINGULAR; } diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index a085f90ff..8a96f5ad7 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -2,7 +2,7 @@ use crate::{ executor::{ExecutionResult, Executor, Registry}, - graphql, resolve, + graphql, reflect, resolve, schema::meta::MetaType, BoxFuture, Selection, }; @@ -77,3 +77,24 @@ where T::assert_output_type() } } + +impl reflect::BaseType for Vec +where + T: reflect::BaseType, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Vec +where + T: reflect::BaseSubTypes, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Vec +where + T: reflect::WrappedType, +{ + const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); +} diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 7e98eae3f..d094b3d13 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -471,15 +471,13 @@ impl Definition { let (info, generics) = self.mix_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let name = &self.name; - quote! { #[automatically_derived] impl#impl_gens ::juniper::resolve::TypeName<#info> for #ty #where_clause { fn type_name(_: &#info) -> &'static str { - #name + >::NAME } } } @@ -783,7 +781,8 @@ impl Definition { impl#impl_gens ::juniper::reflect::WrappedType<#scalar> for #ty #where_clause { - const VALUE: ::juniper::reflect::WrappedValue = 1; + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; } } } From 113b112daf83e0d9090347885280efc6fc501e2f Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 31 May 2022 19:08:36 +0200 Subject: [PATCH 14/58] Impl codegen for scalars (`resolve::Value` trait), vol.3 --- juniper/src/macros/reflect.rs | 2 - juniper/src/types/scalars.rs | 1 - juniper_codegen/src/graphql_scalar/mod.rs | 107 ++++++++++++++++++++-- 3 files changed, 100 insertions(+), 10 deletions(-) diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index 912a25241..f40a8c7d8 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -1,7 +1,5 @@ //! Compile-time reflection of Rust types into GraphQL types. -use std::{rc::Rc, sync::Arc}; - use futures::future::BoxFuture; use crate::{ diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index ac7817705..0f8bb4089 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -8,7 +8,6 @@ use crate::{ ast::{InputValue, Selection, ToInputValue}, executor::{ExecutionResult, Executor, Registry}, graphql_scalar, - macros::reflect, parser::{LexerError, ParseError, ScalarToken, Token}, schema::meta::MetaType, types::{ diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index d094b3d13..d85069c70 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -332,6 +332,7 @@ impl ToTokens for Definition { //////////////////////////////////////////////////////////////////////// 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_input_value().to_tokens(into); self.impl_resolve_scalar_token().to_tokens(into); self.impl_input_and_output_type().to_tokens(into); @@ -468,15 +469,15 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_type_name(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (info, generics) = self.mix_info(generics); + let (inf, generics) = self.mix_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::TypeName<#info> for #ty + impl#impl_gens ::juniper::resolve::TypeName<#inf> for #ty #where_clause { - fn type_name(_: &#info) -> &'static str { + fn type_name(_: &#inf) -> &'static str { >::NAME } } @@ -490,10 +491,10 @@ impl Definition { /// [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 (inf, 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> + Self: ::juniper::resolve::TypeName<#inf> + ::juniper::resolve::ScalarToken<#scalar> + ::juniper::resolve::InputValueOwned<#scalar> }); @@ -511,12 +512,12 @@ impl Definition { quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::Type<#info, #scalar> for #ty + impl#impl_gens ::juniper::resolve::Type<#inf, #scalar> for #ty #where_clause { fn meta<'r>( registry: &mut ::juniper::Registry<'r, #scalar>, - info: &#info, + info: &#inf, ) -> ::juniper::meta::MetaType<'r, #scalar> where #scalar: 'r, @@ -567,6 +568,41 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL scalar][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_resolve_value(&self) -> TokenStream { + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_info(generics); + let (cx, generics) = self.mix_context(generics); + let (scalar, mut generics) = self.mix_scalar(generics); + generics + .make_where_clause() + .predicates + .push(self.methods.bound_resolve_value(&inf, &cx, scalar)); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let body = self.methods.expand_resolve_value(scalar); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #scalar> for #ty + #where_clause + { + fn resolve_value( + &self, + selection: Option<&[::juniper::Selection<'_, #scalar>]>, + info: &#inf, + executor: &::juniper::Executor<'_, '_, #cx, #scalar>, + ) -> ::juniper::ExecutionResult<#scalar> { + #body + } + } + } + } + /// Returns generated code implementing [`GraphQLValueAsync`] trait for this /// [GraphQL scalar][1]. /// @@ -1009,6 +1045,63 @@ impl Methods { } } + /// Expands [`resolve::Value::resolve_value()`][0] method. + /// + /// [0]: juniper::resolve::Value::resolve_value + fn expand_resolve_value(&self, scalar: &scalar::Type) -> TokenStream { + match self { + Self::Custom { to_output, .. } + | Self::Delegated { + to_output: Some(to_output), + .. + } => { + quote! { Ok(#to_output(self)) } + } + + Self::Delegated { field, .. } => { + quote! { + ::juniper::resolve::Value::<#scalar>::resolve_value( + &self.#field, + info, + selection, + executor, + ) + } + } + } + } + + /// Generates additional trait bounds for [`resolve::Value`] implementation + /// allowing to execute [`resolve::Value::resolve_value()`][0] method. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: juniper::resolve::Value::resolve_value + fn bound_resolve_value( + &self, + inf: &syn::Ident, + cx: &syn::Ident, + scalar: &scalar::Type, + ) -> syn::WherePredicate { + match self { + Self::Custom { .. } + | Self::Delegated { + to_output: Some(_), .. + } => { + parse_quote! { + #scalar: ::juniper::ScalarValue + } + } + + Self::Delegated { field, .. } => { + let field_ty = field.ty(); + + parse_quote! { + #field_ty: ::juniper::resolve::Value<#inf, #cx, #scalar> + } + } + } + } + /// Expands [`ToInputValue::to_input_value`] method. /// /// [`ToInputValue::to_input_value`]: juniper::ToInputValue::to_input_value From 740aa9061e112e909dc1e4579b304f95e026f6b3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 1 Jun 2022 16:20:19 +0200 Subject: [PATCH 15/58] Impl codegen for scalars (`resolve::ValueAsync` trait), vol.4 --- juniper_codegen/src/graphql_scalar/mod.rs | 185 ++++++++++++++-------- 1 file changed, 116 insertions(+), 69 deletions(-) diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index d85069c70..17508bc0a 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -333,6 +333,7 @@ impl ToTokens for Definition { 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_input_value().to_tokens(into); self.impl_resolve_scalar_token().to_tokens(into); self.impl_input_and_output_type().to_tokens(into); @@ -375,19 +376,19 @@ impl Definition { #[must_use] fn impl_input_and_output_type(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (scalar, generics) = self.mix_scalar(generics); + let (sv, generics) = self.mix_scalar_value(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::graphql::InputType<#scalar> for #ty + impl#impl_gens ::juniper::graphql::InputType<#sv> for #ty #where_clause { fn assert_input_type() {} } #[automatically_derived] - impl#impl_gens ::juniper::graphql::OutputType<#scalar> for #ty + impl#impl_gens ::juniper::graphql::OutputType<#sv> for #ty #where_clause { fn assert_output_type() {} @@ -403,12 +404,12 @@ impl Definition { #[must_use] fn impl_scalar(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (scalar, generics) = self.mix_scalar(generics); + let (sv, generics) = self.mix_scalar_value(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::graphql::Scalar<#scalar> for #ty + impl#impl_gens ::juniper::graphql::Scalar<#sv> for #ty #where_clause { fn assert_scalar() {} @@ -469,7 +470,7 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_type_name(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_info(generics); + let (inf, generics) = self.mix_type_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { @@ -491,12 +492,12 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_type(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_info(generics); - let (scalar, mut generics) = self.mix_scalar(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<#scalar> - + ::juniper::resolve::InputValueOwned<#scalar> + + ::juniper::resolve::ScalarToken<#sv> + + ::juniper::resolve::InputValueOwned<#sv> }); let (impl_gens, _, where_clause) = generics.split_for_impl(); @@ -512,15 +513,15 @@ impl Definition { quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::Type<#inf, #scalar> for #ty + impl#impl_gens ::juniper::resolve::Type<#inf, #sv> for #ty #where_clause { - fn meta<'r>( - registry: &mut ::juniper::Registry<'r, #scalar>, + fn meta<'__r>( + registry: &mut ::juniper::Registry<'__r, #sv>, info: &#inf, - ) -> ::juniper::meta::MetaType<'r, #scalar> + ) -> ::juniper::meta::MetaType<'__r, #sv> where - #scalar: 'r, + #sv: '__r, { registry.build_scalar_type_new::(info) #description @@ -575,28 +576,28 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_value(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_info(generics); + let (inf, generics) = self.mix_type_info(generics); let (cx, generics) = self.mix_context(generics); - let (scalar, mut generics) = self.mix_scalar(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); generics .make_where_clause() .predicates - .push(self.methods.bound_resolve_value(&inf, &cx, scalar)); + .push(self.methods.bound_resolve_value(&inf, &cx, sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let body = self.methods.expand_resolve_value(scalar); + let body = self.methods.expand_resolve_value(&inf, &cx, sv); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #scalar> for #ty + impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #sv> for #ty #where_clause { fn resolve_value( &self, - selection: Option<&[::juniper::Selection<'_, #scalar>]>, + selection: Option<&[::juniper::Selection<'_, #sv>]>, info: &#inf, - executor: &::juniper::Executor<'_, '_, #cx, #scalar>, - ) -> ::juniper::ExecutionResult<#scalar> { + executor: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { #body } } @@ -633,6 +634,44 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ValueAsync`] trait for + /// this [GraphQL scalar][0]. + /// + /// [`resolve::ValueAsync`]: juniper::resolve::ValueAsync + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_resolve_value_async(&self) -> TokenStream { + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (cx, generics) = self.mix_context(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { + Self: ::juniper::resolve::Value<#inf, #cx, #sv> + }); + preds.push(parse_quote! { + #sv: Send + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv> for #ty + #where_clause + { + fn resolve_value_async<'__r>( + &'__r self, + selection: Option<&'__r [::juniper::Selection<'_, #sv>]>, + info: &'__r #inf, + executor: &'__r ::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::BoxFuture<'__r, ::juniper::ExecutionResult<#sv>> { + let v = > + ::resolve_value(self, selection, info, executor); + ::std::boxed::Box::pin(::juniper::futures::future::ready(v)) + } + } + } + } + /// Returns generated code implementing [`InputValue`] trait for this /// [GraphQL scalar][1]. /// @@ -693,29 +732,29 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_input_value(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (scalar, mut generics) = self.mix_scalar(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); let lt: syn::GenericParam = parse_quote! { '__inp }; generics.params.push(lt.clone()); generics .make_where_clause() .predicates - .push(self.methods.bound_try_from_input_value(scalar, <)); + .push(self.methods.bound_try_from_input_value(sv, <)); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let conversion = self.methods.expand_try_from_input_value(scalar); + let conversion = self.methods.expand_try_from_input_value(sv); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::InputValue<#lt, #scalar> for #ty + impl#impl_gens ::juniper::resolve::InputValue<#lt, #sv> for #ty #where_clause { - type Error = ::juniper::FieldError<#scalar>; + type Error = ::juniper::FieldError<#sv>; fn try_from_input_value( - input: &#lt ::juniper::graphql::InputValue<#scalar>, + input: &#lt ::juniper::graphql::InputValue<#sv>, ) -> ::std::result::Result { #conversion.map_err( - ::juniper::IntoFieldError::<#scalar>::into_field_error, + ::juniper::IntoFieldError::<#sv>::into_field_error, ) } } @@ -756,24 +795,24 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_scalar_token(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (scalar, mut generics) = self.mix_scalar(generics); + let (sv, mut generics) = self.mix_scalar_value(generics); generics .make_where_clause() .predicates - .extend(self.methods.bound_parse_scalar_token(scalar)); + .extend(self.methods.bound_parse_scalar_token(sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let body = self.methods.expand_parse_scalar_token(scalar); + let body = self.methods.expand_parse_scalar_token(sv); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::ScalarToken<#scalar> for #ty + impl#impl_gens ::juniper::resolve::ScalarToken<#sv> for #ty #where_clause { fn parse_scalar_token( token: ::juniper::parser::ScalarToken<'_>, ) -> ::std::result::Result< - #scalar, + #sv, ::juniper::parser::ParseError<'_>, > { #body @@ -792,29 +831,29 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_reflect(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); - let (scalar, generics) = self.mix_scalar(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<#scalar> for #ty + impl#impl_gens ::juniper::reflect::BaseType<#sv> for #ty #where_clause { const NAME: ::juniper::reflect::Type = #name; } #[automatically_derived] - impl#impl_gens ::juniper::reflect::BaseSubTypes<#scalar> for #ty + impl#impl_gens ::juniper::reflect::BaseSubTypes<#sv> for #ty #where_clause { const NAMES: ::juniper::reflect::Types = - &[>::NAME]; + &[>::NAME]; } #[automatically_derived] - impl#impl_gens ::juniper::reflect::WrappedType<#scalar> for #ty + impl#impl_gens ::juniper::reflect::WrappedType<#sv> for #ty #where_clause { const VALUE: ::juniper::reflect::WrappedValue = @@ -928,7 +967,7 @@ impl Definition { /// Mixes a type info [`syn::GenericParam`] into the provided /// [`syn::Generics`] and returns its [`syn::Ident`]. #[must_use] - fn mix_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + fn mix_type_info(&self, mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { let ty = parse_quote! { __Info }; generics.params.push(parse_quote! { #ty: ?Sized }); @@ -956,7 +995,7 @@ impl Definition { /// /// [`ScalarValue`]: juniper::ScalarValue #[must_use] - fn mix_scalar(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) { + fn mix_scalar_value(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) { let scalar = &self.scalar; if scalar.is_implicit_generic() { @@ -1048,7 +1087,12 @@ impl Methods { /// Expands [`resolve::Value::resolve_value()`][0] method. /// /// [0]: juniper::resolve::Value::resolve_value - fn expand_resolve_value(&self, scalar: &scalar::Type) -> TokenStream { + fn expand_resolve_value( + &self, + inf: &syn::Ident, + cx: &syn::Ident, + sv: &scalar::Type, + ) -> TokenStream { match self { Self::Custom { to_output, .. } | Self::Delegated { @@ -1059,13 +1103,16 @@ impl Methods { } Self::Delegated { field, .. } => { + let field_ty = field.ty(); + quote! { - ::juniper::resolve::Value::<#scalar>::resolve_value( - &self.#field, - info, - selection, - executor, - ) + <#field_ty as ::juniper::resolve::Value<#inf, #cx, #sv>> + ::resolve_value( + &self.#field, + info, + selection, + executor, + ) } } } @@ -1080,7 +1127,7 @@ impl Methods { &self, inf: &syn::Ident, cx: &syn::Ident, - scalar: &scalar::Type, + sv: &scalar::Type, ) -> syn::WherePredicate { match self { Self::Custom { .. } @@ -1088,7 +1135,7 @@ impl Methods { to_output: Some(_), .. } => { parse_quote! { - #scalar: ::juniper::ScalarValue + #sv: ::juniper::ScalarValue } } @@ -1096,7 +1143,7 @@ impl Methods { let field_ty = field.ty(); parse_quote! { - #field_ty: ::juniper::resolve::Value<#inf, #cx, #scalar> + #field_ty: ::juniper::resolve::Value<#inf, #cx, #sv> } } } @@ -1152,7 +1199,7 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::InputValue::try_from_input_value - fn expand_try_from_input_value(&self, scalar: &scalar::Type) -> TokenStream { + fn expand_try_from_input_value(&self, sv: &scalar::Type) -> TokenStream { match self { Self::Custom { from_input, .. } | Self::Delegated { @@ -1167,7 +1214,7 @@ impl Methods { let self_constructor = field.closure_constructor(); quote! { - <#field_ty as ::juniper::resolve::InputValue<'_, #scalar>> + <#field_ty as ::juniper::resolve::InputValue<'_, #sv>> ::try_from_input_value(input) .map(#self_constructor) } @@ -1183,7 +1230,7 @@ impl Methods { /// [0]: juniper::resolve::InputValue::try_from_input_value fn bound_try_from_input_value( &self, - scalar: &scalar::Type, + sv: &scalar::Type, lt: &syn::GenericParam, ) -> syn::WherePredicate { match self { @@ -1193,7 +1240,7 @@ impl Methods { .. } => { parse_quote! { - #scalar: ::juniper::ScalarValue + #sv: ::juniper::ScalarValue } } @@ -1201,7 +1248,7 @@ impl Methods { let field_ty = field.ty(); parse_quote! { - #field_ty: ::juniper::resolve::InputValue<#lt, #scalar> + #field_ty: ::juniper::resolve::InputValue<#lt, #sv> } } } @@ -1233,19 +1280,19 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &scalar::Type) -> TokenStream { match self { Self::Custom { parse_token, .. } | Self::Delegated { parse_token: Some(parse_token), .. - } => parse_token.expand_parse_scalar_token(scalar), + } => parse_token.expand_parse_scalar_token(sv), Self::Delegated { field, .. } => { let field_ty = field.ty(); quote! { - <#field_ty as ::juniper::resolve::ScalarToken<#scalar>> + <#field_ty as ::juniper::resolve::ScalarToken<#sv>> ::parse_scalar_token(token) } } @@ -1258,19 +1305,19 @@ impl Methods { /// /// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn bound_parse_scalar_token(&self, scalar: &scalar::Type) -> Vec { + fn bound_parse_scalar_token(&self, sv: &scalar::Type) -> Vec { match self { Self::Custom { parse_token, .. } | Self::Delegated { parse_token: Some(parse_token), .. - } => parse_token.bound_parse_scalar_token(scalar), + } => parse_token.bound_parse_scalar_token(sv), Self::Delegated { field, .. } => { let field_ty = field.ty(); vec![parse_quote! { - #field_ty: ::juniper::resolve::ScalarToken<#scalar> + #field_ty: ::juniper::resolve::ScalarToken<#sv> }] } } @@ -1323,7 +1370,7 @@ impl ParseToken { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, scalar: &scalar::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &scalar::Type) -> TokenStream { match self { Self::Custom(parse_token) => { quote! { #parse_token(token) } @@ -1335,14 +1382,14 @@ impl ParseToken { acc.map_or_else( || { Some(quote! { - <#ty as ::juniper::resolve::ScalarToken<#scalar>> + <#ty as ::juniper::resolve::ScalarToken<#sv>> ::parse_scalar_token(token) }) }, |prev| { Some(quote! { #prev.or_else(|_| { - <#ty as ::juniper::resolve::ScalarToken<#scalar>> + <#ty as ::juniper::resolve::ScalarToken<#sv>> ::parse_scalar_token(token) }) }) @@ -1359,11 +1406,11 @@ impl ParseToken { /// /// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn bound_parse_scalar_token(&self, scalar: &scalar::Type) -> Vec { + fn bound_parse_scalar_token(&self, sv: &scalar::Type) -> Vec { match self { Self::Custom(_) => { vec![parse_quote! { - #scalar: ::juniper::ScalarValue + #sv: ::juniper::ScalarValue }] } @@ -1371,7 +1418,7 @@ impl ParseToken { .iter() .map(|ty| { parse_quote! { - #ty: ::juniper::resolve::ScalarToken<#scalar> + #ty: ::juniper::resolve::ScalarToken<#sv> } }) .collect(), From 97d2da581a2a7ff535f5b837775a74496a12ff35 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 1 Jun 2022 17:31:02 +0200 Subject: [PATCH 16/58] Impl codegen for scalars (`resolve::ToInputValue` trait), vol.5 --- juniper/src/ast.rs | 35 ++++++--- juniper/src/macros/reflect.rs | 4 +- juniper/src/resolve/mod.rs | 4 + juniper/src/types/arc.rs | 17 ++++- juniper/src/types/array.rs | 9 +++ juniper/src/types/box.rs | 17 ++++- juniper/src/types/containers.rs | 6 +- juniper/src/types/nullable.rs | 12 +++ juniper/src/types/option.rs | 12 +++ juniper/src/types/rc.rs | 17 ++++- juniper/src/types/ref.rs | 17 ++++- juniper/src/types/ref_mut.rs | 9 +++ juniper/src/types/slice.rs | 9 +++ juniper/src/types/str.rs | 17 ++++- juniper/src/types/vec.rs | 9 +++ juniper/src/value/mod.rs | 22 ++++++ juniper_codegen/src/graphql_scalar/mod.rs | 89 ++++++++++++++++++++++- 17 files changed, 265 insertions(+), 40 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index ea2c0fec2..b535f9215 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -256,13 +256,17 @@ impl InputValue { 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::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) -> Self { + Self::List(list.into_iter().map(Spanning::unlocated).collect()) } /// Construct a located list. @@ -270,16 +274,25 @@ impl InputValue { 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 `obj`ect to not contain any location information. + /// + /// Intended for [`resolve::ToInputValue`] implementations, where no source + /// code position information is available. /// - /// Similarly to [`InputValue::list`] it makes each key and value in the - /// given hash map not contain any location information. - pub fn object(o: IndexMap) -> Self + /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue + // TODO: Use `impl IntoIterator` argument once feature + // `explicit_generic_args_with_impl_trait` hits stable: + // https://github.com/rust-lang/rust/issues/83701 + pub fn object(obj: O) -> Self where K: AsRef + Eq + Hash, + O: IntoIterator, { Self::Object( - o.into_iter() + obj.into_iter() .map(|(k, v)| { ( Spanning::unlocated(k.as_ref().to_owned()), diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index f40a8c7d8..b967d958f 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -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. diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 39c693e3c..87564555e 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -101,3 +101,7 @@ pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized { pub trait InputValueOwned: for<'i> InputValue<'i, S> {} impl InputValueOwned for T where T: for<'i> InputValue<'i, S> {} + +pub trait ToInputValue { + fn to_input_value(&self) -> graphql::InputValue; +} diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 33804c3fe..cb5a44ee3 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -143,12 +143,12 @@ where } } -impl resolve::ScalarToken for Arc +impl resolve::ToInputValue for Arc where - T: resolve::ScalarToken + ?Sized, + T: resolve::ToInputValue + ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - T::parse_scalar_token(token) + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() } } @@ -194,6 +194,15 @@ where } } +impl resolve::ScalarToken for Arc +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Arc where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index 4cd5856bb..0f0197fac 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -64,6 +64,15 @@ where } } +impl resolve::ToInputValue for [T; N] +where + T: resolve::ToInputValue, +{ + fn to_input_value(&self) -> graphql::InputValue { + graphql::InputValue::list(self.iter().map(T::to_input_value)) + } +} + impl graphql::InputType for [T; N] where T: graphql::InputType, diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 1237ed6ea..be725c5d7 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -141,12 +141,12 @@ where } } -impl resolve::ScalarToken for Box +impl resolve::ToInputValue for Box where - T: resolve::ScalarToken + ?Sized, + T: resolve::ToInputValue + ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - T::parse_scalar_token(token) + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() } } @@ -192,6 +192,15 @@ where } } +impl resolve::ScalarToken for Box +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Box where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index ab5383a4f..bda6d4dbe 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -183,7 +183,7 @@ where S: ScalarValue, { fn to_input_value(&self) -> InputValue { - 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 { - 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 { - InputValue::list(self.iter().map(T::to_input_value).collect()) + InputValue::list(self.iter().map(T::to_input_value)) } } diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index 4113ea812..a87367810 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -324,6 +324,18 @@ where } } +impl resolve::ToInputValue for Nullable +where + T: resolve::ToInputValue, +{ + fn to_input_value(&self) -> graphql::InputValue { + 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 where T: resolve::InputValue<'inp, S>, diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 75320f82f..4b475182f 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -61,6 +61,18 @@ where } } +impl resolve::ToInputValue for Option +where + T: resolve::ToInputValue, +{ + fn to_input_value(&self) -> graphql::InputValue { + match self { + Some(v) => v.to_input_value(), + None => graphql::InputValue::Null, + } + } +} + impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Option where T: resolve::InputValue<'inp, S>, diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 515290b12..76d148d71 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -143,12 +143,12 @@ where } } -impl resolve::ScalarToken for Rc +impl resolve::ToInputValue for Rc where - T: resolve::ScalarToken + ?Sized, + T: resolve::ToInputValue + ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - T::parse_scalar_token(token) + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() } } @@ -194,6 +194,15 @@ where } } +impl resolve::ScalarToken for Rc +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl graphql::InputType for Rc where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 646b45661..55ed18bea 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -143,12 +143,12 @@ where } } -impl<'me, T, S> resolve::ScalarToken for &'me T +impl<'me, T, S> resolve::ToInputValue for &'me T where - T: resolve::ScalarToken + ?Sized, + T: resolve::ToInputValue + ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - T::parse_scalar_token(token) + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() } } @@ -180,6 +180,15 @@ pub trait TryFromInputValue { } } +impl<'me, T, S> resolve::ScalarToken for &'me T +where + T: resolve::ScalarToken + ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + impl<'me, T, S> graphql::InputType for &'me T where T: graphql::InputType + ?Sized, diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index 36a576d72..607b4ba45 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -142,6 +142,15 @@ where } } +impl<'me, T, S> resolve::ToInputValue for &'me mut T +where + T: resolve::ToInputValue + ?Sized, +{ + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() + } +} + impl<'me, T, S> resolve::ScalarToken for &'me mut T where T: resolve::ScalarToken + ?Sized, diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index 5a5b6b52b..7dcdfe356 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -62,6 +62,15 @@ where } } +impl resolve::ToInputValue for [T] +where + T: resolve::ToInputValue, +{ + fn to_input_value(&self) -> graphql::InputValue { + graphql::InputValue::list(self.iter().map(T::to_input_value)) + } +} + impl graphql::InputType for [T] where T: graphql::InputType, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index b8f269851..65ebcf615 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -66,12 +66,12 @@ where } } -impl resolve::ScalarToken for str +impl resolve::ToInputValue for str where - String: resolve::ScalarToken, + S: From, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - >::parse_scalar_token(token) + fn to_input_value(&self) -> graphql::InputValue { + graphql::InputValue::scalar(self.to_owned()) } } @@ -108,6 +108,15 @@ impl<'inp, S: ScalarValue> resolve::InputValueAsRc<'inp, S> for str { } } +impl resolve::ScalarToken for str +where + String: resolve::ScalarToken, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + >::parse_scalar_token(token) + } +} + impl graphql::InputType for str { fn assert_input_type() {} } diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index 8a96f5ad7..bf1735128 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -60,6 +60,15 @@ where } } +impl resolve::ToInputValue for Vec +where + T: resolve::ToInputValue, +{ + fn to_input_value(&self) -> graphql::InputValue { + graphql::InputValue::list(self.iter().map(T::to_input_value)) + } +} + impl graphql::InputType for Vec where T: graphql::InputType, diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index e3457c8eb..9d8ca2b34 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -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 ToInputValue for Value { } } +impl resolve::ToInputValue for Value { + fn to_input_value(&self) -> InputValue { + // 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(>::to_input_value), + ), + Self::Object(o) => InputValue::object(o.iter().map(|(k, v)| { + ( + k.clone(), + >::to_input_value(v), + ) + })), + } + } +} + impl fmt::Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 17508bc0a..daecafe81 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -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 From 8235ac22c03cd6729288e0c4b303dc1647620564 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 6 Jun 2022 19:06:59 +0200 Subject: [PATCH 17/58] Reworking base traits, vol.1 [skip ci] --- juniper/src/behavior.rs | 5 + juniper/src/graphql/mod.rs | 42 ++-- juniper/src/graphql/resolve.rs | 230 ---------------------- juniper/src/lib.rs | 1 + juniper/src/macros/reflect.rs | 23 +-- juniper/src/reflect/mod.rs | 55 +++++- juniper/src/resolve/mod.rs | 139 ++++++++----- juniper/src/types/arc.rs | 5 +- juniper/src/types/array.rs | 7 +- juniper/src/types/box.rs | 5 +- juniper/src/types/nullable.rs | 5 +- juniper/src/types/option.rs | 5 +- juniper/src/types/rc.rs | 5 +- juniper/src/types/ref.rs | 8 +- juniper/src/types/ref_mut.rs | 9 - juniper/src/types/slice.rs | 2 + juniper/src/types/str.rs | 8 +- juniper/src/types/vec.rs | 7 +- juniper_codegen/src/graphql_scalar/mod.rs | 8 +- 19 files changed, 216 insertions(+), 353 deletions(-) create mode 100644 juniper/src/behavior.rs delete mode 100644 juniper/src/graphql/resolve.rs diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs new file mode 100644 index 000000000..3c6c84a41 --- /dev/null +++ b/juniper/src/behavior.rs @@ -0,0 +1,5 @@ +//! Default GraphQL behaviors. + +/// Default standard behavior of GraphQL types implementation. +#[derive(Debug)] +pub enum Standard {} diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index fb1ac95fd..8c3d3b660 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -1,16 +1,10 @@ -pub mod resolve; - -use crate::DefaultScalarValue; - pub use crate::{ ast::InputValue, graphql_input_value as input_value, graphql_value as value, value::Value, }; -pub use self::resolve::Type; - -pub trait Interface: +pub trait Interface: OutputType - + Type +/* + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value @@ -19,51 +13,55 @@ pub trait Interface: + resolve::ConcreteValueAsync + resolve::Field + resolve::FieldAsync + + */ { fn assert_interface(); } -pub trait Object: +pub trait Object: OutputType - + Type +/* + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value + resolve::ValueAsync + resolve::Field + resolve::FieldAsync + + */ { fn assert_object(); } -pub trait Scalar: - InputType - + OutputType - + Type - + resolve::TypeName - + resolve::Value - + resolve::ValueAsync +pub trait Scalar: + OutputType + /* + resolve::TypeName + resolve::Value + resolve::ValueAsync */ { fn assert_scalar(); } -pub trait Union: +pub trait Union: OutputType - + Type +/* + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value + resolve::ValueAsync + resolve::ConcreteValue - + resolve::ConcreteValueAsync + + resolve::ConcreteValueAsync */ { fn assert_union(); } -pub trait InputType { +pub trait InputType<'inp, Info: ?Sized, S: 'inp> /*: + crate::resolve::Type + + crate::resolve::ToInputValue + + crate::resolve::InputValue<'inp, S>*/ +{ fn assert_input_type(); } -pub trait OutputType { +pub trait OutputType: /*Type*/ { fn assert_output_type(); } diff --git a/juniper/src/graphql/resolve.rs b/juniper/src/graphql/resolve.rs deleted file mode 100644 index 22fe5342b..000000000 --- a/juniper/src/graphql/resolve.rs +++ /dev/null @@ -1,230 +0,0 @@ -use crate::{ - meta::MetaType, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, - Registry, Selection, -}; - -pub trait Type { - fn meta<'r, Info: ?Sized>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> - where - S: 'r, - Self: resolve::Type; -} - -impl Type for T { - fn meta<'r, Info: ?Sized>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> - where - S: 'r, - Self: resolve::Type, - { - >::meta(registry, info) - } -} - -pub trait TypeName { - fn type_name(info: &Info) -> &str - where - Self: resolve::TypeName; -} - -impl TypeName for T { - fn type_name(info: &Info) -> &str - where - Self: resolve::TypeName, - { - >::type_name(info) - } -} - -pub trait ConcreteTypeName { - fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str - where - Self: resolve::ConcreteTypeName; -} - -impl ConcreteTypeName for T { - fn concrete_type_name<'i, Info: ?Sized>(&self, info: &'i Info) -> &'i str - where - Self: resolve::ConcreteTypeName, - { - >::concrete_type_name(self, info) - } -} - -pub trait Value { - fn resolve_value( - &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::Value; -} - -impl Value for T { - fn resolve_value( - &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::Value, - { - >::resolve_value(self, selection_set, info, executor) - } -} - -pub trait ValueAsync { - fn resolve_value_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::ValueAsync; -} - -impl ValueAsync for T { - fn resolve_value_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::ValueAsync, - { - >::resolve_value_async( - self, - selection_set, - info, - executor, - ) - } -} - -pub trait ConcreteValue { - fn resolve_concrete_value( - &self, - type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::ConcreteValue; -} - -impl ConcreteValue for T { - fn resolve_concrete_value( - &self, - type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::ConcreteValue, - { - >::resolve_concrete_value( - self, - type_name, - selection_set, - info, - executor, - ) - } -} - -pub trait ConcreteValueAsync { - fn resolve_concrete_value_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::ConcreteValueAsync; -} - -impl ConcreteValueAsync for T { - fn resolve_concrete_value_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::ConcreteValueAsync, - { - >::resolve_concrete_value_async( - self, - type_name, - selection_set, - info, - executor, - ) - } -} - -pub trait Field { - fn resolve_field( - &self, - field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::Field; -} - -impl Field for T { - fn resolve_field( - &self, - field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult - where - Self: resolve::Field, - { - >::resolve_field( - self, field_name, arguments, info, executor, - ) - } -} - -pub trait FieldAsync { - fn resolve_field_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::FieldAsync; -} - -impl FieldAsync for T { - fn resolve_field_async<'r, Info: ?Sized, Ctx: ?Sized>( - &'r self, - field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> - where - Self: resolve::FieldAsync, - { - >::resolve_field_async( - self, field_name, arguments, info, executor, - ) - } -} diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 9340608a2..04ac5d69c 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -30,6 +30,7 @@ pub use juniper_codegen::{ pub mod macros; mod ast; +pub mod behavior; pub mod executor; pub mod graphql; pub mod http; diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index b967d958f..2a1ccca80 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -2,22 +2,10 @@ use futures::future::BoxFuture; -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. -/// -/// See [`BaseType`] for more info. -/// -/// [1]: https://spec.graphql.org/October2021#sec-Objects -/// [2]: https://spec.graphql.org/October2021#sec-Scalars -/// [3]: https://spec.graphql.org/October2021#sec-Interfaces -pub type Type = &'static str; - -/// Alias for a slice of [`Type`]s. -/// -/// See [`BaseSubTypes`] for more info. -pub type Types = &'static [Type]; +use crate::{ + reflect::{Type, Types, WrappedValue}, + Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue, +}; /// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`]. /// @@ -69,9 +57,6 @@ where const NAMES: Types = T::NAMES; } -/// Alias for a value of a [`WrappedType`] (composed GraphQL type). -pub type WrappedValue = u128; - // TODO: Just use `&str`s once they're allowed in `const` generics. /// Encoding of a composed GraphQL type in numbers. /// diff --git a/juniper/src/reflect/mod.rs b/juniper/src/reflect/mod.rs index e91f009a8..d4e1d8b67 100644 --- a/juniper/src/reflect/mod.rs +++ b/juniper/src/reflect/mod.rs @@ -1 +1,54 @@ -pub use crate::macros::reflect::*; +//! Compile-time reflection of Rust types into GraphQL types. + +use crate::behavior; + +/// Alias for a [GraphQL type][0]'s name in a GraphQL schema. +/// +/// See [`BaseType`] for more info. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Types +pub type Type = &'static str; + +/// Alias for a slice of [`Type`]s. +/// +/// See [`BaseSubTypes`] for more info. +pub type Types = &'static [Type]; + +/// Basic reflection of a [GraphQL type][0]. +/// +/// This trait is transparent to [`Option`], [`Vec`] and other containers, so to +/// fully represent a [GraphQL object][1] we additionally use [`WrappedType`]. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Types +pub trait BaseType { + /// [`Type`] of this [GraphQL type][0]. + /// + /// Different Rust types may have the same [`NAME`]. For example, [`String`] + /// and [`&str`](prim@str) share the `String!` GraphQL [`Type`]. + /// + /// [`NAME`]: Self::NAME + /// [0]: https://spec.graphql.org/October2021#sec-Types + const NAME: Type; +} + +/// Reflection of [sub-types][2] of a [GraphQL type][0]. +/// +/// This trait is transparent to [`Option`], [`Vec`] and other containers. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Types +/// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC +pub trait BaseSubTypes { + /// Sub-[`Types`] of this [GraphQL type][0]. + /// + /// Contains [at least][2] the [`BaseType::NAME`] of this [GraphQL type][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Types + /// [2]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC + const NAMES: Types; +} + +/// Alias for a value of a [`WrappedType`] (composed +/// [GraphQL wrapping type][0]). +/// +/// [0]: https://spec.graphql.org/October2021#sec-Wrapping-Types +pub type WrappedValue = u128; diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 87564555e..8e823117e 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -1,9 +1,8 @@ use crate::{ - graphql, + behavior, graphql, meta::MetaType, parser::{self, ParseError}, - Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, IntoFieldError, Registry, - Selection, + Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, Selection, }; #[doc(inline)] @@ -12,96 +11,136 @@ pub use crate::types::{ r#ref::TryFromInputValue as InputValueAsRef, rc::TryFromInputValue as InputValueAsRc, }; -pub trait Type { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> +pub trait Type { + fn meta<'r>( + registry: &mut Registry<'r, ScalarValue>, + type_info: &TypeInfo, + ) -> MetaType<'r, ScalarValue> where - S: 'r; // TODO: remove? + ScalarValue: 'r; // TODO: remove? } -pub trait TypeName { - fn type_name(info: &Info) -> &str; +pub trait TypeName { + fn type_name(type_info: &TypeInfo) -> &str; } -pub trait ConcreteTypeName { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str; +pub trait ConcreteTypeName { + fn concrete_type_name<'i>(&self, type_info: &'i TypeInfo) -> &'i str; } -pub trait Value { +pub trait Value< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult; + selection_set: Option<&[Selection<'_, ScalarValue>]>, + type_info: &TypeInfo, + executor: &Executor, + ) -> ExecutionResult; } -pub trait ValueAsync { +pub trait ValueAsync< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult>; + selection_set: Option<&'r [Selection<'_, ScalarValue>]>, + type_info: &'r TypeInfo, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; } -pub trait ConcreteValue { +pub trait ConcreteValue< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult; + selection_set: Option<&[Selection<'_, ScalarValue>]>, + type_info: &TypeInfo, + executor: &Executor, + ) -> ExecutionResult; } -pub trait ConcreteValueAsync { +pub trait ConcreteValueAsync { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult>; + selection_set: Option<&'r [Selection<'_, ScalarValue>]>, + type_info: &'r TypeInfo, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; } -pub trait Field { +pub trait Field< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult; + arguments: &Arguments, + type_info: &TypeInfo, + executor: &Executor, + ) -> ExecutionResult; } -pub trait FieldAsync { +pub trait FieldAsync< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult>; + arguments: &'r Arguments, + type_info: &'r TypeInfo, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; } -pub trait ScalarToken { - fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; +pub trait ToInputValue { + fn to_input_value(&self) -> graphql::InputValue; } -pub trait InputValue<'input, S: 'input = DefaultScalarValue>: Sized { - type Error: IntoFieldError; +pub trait InputValue<'input, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard>: + Sized +{ + type Error: IntoFieldError; - fn try_from_input_value(v: &'input graphql::InputValue) -> Result; + fn try_from_input_value( + v: &'input graphql::InputValue, + ) -> Result; fn try_from_implicit_null() -> Result { - Self::try_from_input_value(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::::Null) } } -pub trait InputValueOwned: for<'i> InputValue<'i, S> {} +pub trait InputValueOwned: + for<'i> InputValue<'i, ScalarValue, Behavior> +{ +} -impl InputValueOwned for T where T: for<'i> InputValue<'i, S> {} +impl InputValueOwned for T where T: for<'i> InputValue<'i, S, B> {} -pub trait ToInputValue { - fn to_input_value(&self) -> graphql::InputValue; +pub trait ScalarToken { + fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; } diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index cb5a44ee3..38f0f2187 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -203,9 +203,10 @@ where } } -impl graphql::InputType for Arc +impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Arc where - T: graphql::InputType + ?Sized, + T: graphql::InputType<'i, Info, S> + ?Sized, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index 0f0197fac..77ef5f9be 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -73,14 +73,17 @@ where } } -impl graphql::InputType for [T; N] +/* +impl<'i, T, Info, S, const N: usize> graphql::InputType<'i, Info, S> for [T; N] where - T: graphql::InputType, + T: graphql::InputType<'i, Info, S>, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() } } +*/ impl graphql::OutputType for [T; N] where diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index be725c5d7..b30936972 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -201,9 +201,10 @@ where } } -impl graphql::InputType for Box +impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Box where - T: graphql::InputType + ?Sized, + T: graphql::InputType<'i, Info, S> + resolve::InputValueAsBox<'i, S> + ?Sized, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index a87367810..04165f3bf 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -355,9 +355,10 @@ where } } -impl graphql::InputType for Nullable +impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Nullable where - T: graphql::InputType, + T: graphql::InputType<'i, Info, S>, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 4b475182f..0d8ebb6aa 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -88,9 +88,10 @@ where } } -impl graphql::InputType for Option +impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Option where - T: graphql::InputType, + T: graphql::InputType<'i, Info, S>, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 76d148d71..ebecc3f27 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -203,9 +203,10 @@ where } } -impl graphql::InputType for Rc +impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Rc where - T: graphql::InputType + ?Sized, + T: graphql::InputType<'i, Info, S> + ?Sized, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 55ed18bea..5b84d54f6 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -189,14 +189,16 @@ where } } -impl<'me, T, S> graphql::InputType for &'me T +/* +impl<'me, 'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for &'me T where - T: graphql::InputType + ?Sized, + Self: resolve::Type + resolve::ToInputValue + resolve::InputValue<'i, S>, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() } -} +}*/ impl<'me, T, S> graphql::OutputType for &'me T where diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index 607b4ba45..77f8b602e 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -160,15 +160,6 @@ where } } -impl<'me, T, S> graphql::InputType for &'me mut T -where - T: graphql::InputType + ?Sized, -{ - fn assert_input_type() { - T::assert_input_type() - } -} - impl<'me, T, S> graphql::OutputType for &'me mut T where T: graphql::OutputType + ?Sized, diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index 7dcdfe356..022a335bb 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -71,6 +71,7 @@ where } } +/* impl graphql::InputType for [T] where T: graphql::InputType, @@ -79,6 +80,7 @@ where T::assert_input_type() } } +*/ impl graphql::OutputType for [T] where diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 65ebcf615..c1a559a0c 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -117,9 +117,15 @@ where } } -impl graphql::InputType for str { +/* +impl<'i, Info, S: 'i> graphql::InputType<'i, Info, S> for str +where + Self: resolve::Type + resolve::ToInputValue + resolve::InputValue<'i, S>, + Info: ?Sized, +{ fn assert_input_type() {} } +*/ impl graphql::OutputType for str { fn assert_output_type() {} diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index bf1735128..a3022e412 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -69,14 +69,17 @@ where } } -impl graphql::InputType for Vec +/* +impl<'i, T, Info, S> graphql::InputType<'i, Info, S> for Vec where - T: graphql::InputType, + T: graphql::InputType<'i, Info, S>, + Info: ?Sized, { fn assert_input_type() { T::assert_input_type() } } + */ impl graphql::OutputType for Vec where diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index daecafe81..9e6e49cff 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -337,8 +337,8 @@ impl ToTokens for Definition { 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); - //self.impl_scalar().to_tokens(into); + //self.impl_graphql_input_and_output_type().to_tokens(into); + //self.impl_graphql_scalar().to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -375,7 +375,7 @@ impl Definition { /// [`graphql::OutputType`]: juniper::graphql::OutputType /// [0]: https://spec.graphql.org/October2021#sec-Scalars #[must_use] - fn impl_input_and_output_type(&self) -> TokenStream { + fn impl_graphql_input_and_output_type(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); let (sv, generics) = self.mix_scalar_value(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); @@ -403,7 +403,7 @@ impl Definition { /// [`graphql::Scalar`]: juniper::graphql::Scalar /// [0]: https://spec.graphql.org/October2021#sec-Scalars #[must_use] - fn impl_scalar(&self) -> TokenStream { + fn impl_graphql_scalar(&self) -> TokenStream { let (ty, generics) = self.ty_and_generics(); let (sv, generics) = self.mix_scalar_value(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); From 97c88d219cc9587fe47545624a6b698ce711f302 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 7 Jun 2022 18:19:44 +0200 Subject: [PATCH 18/58] Reworking base traits, vol.2 --- juniper/src/macros/reflect.rs | 309 +------------ juniper/src/reflect/mod.rs | 796 +++++++++++++++++++++++++++++++++- juniper/src/resolve/mod.rs | 34 +- 3 files changed, 843 insertions(+), 296 deletions(-) diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index 2a1ccca80..b24ba4f58 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -3,7 +3,10 @@ use futures::future::BoxFuture; use crate::{ - reflect::{Type, Types, WrappedValue}, + reflect::{ + can_be_subtype, fnv1a128, str_eq, str_exists_in_arr, type_len_with_wrapped_val, wrap, + Argument, Arguments, FieldName, Name, Names, Type, Types, WrappedValue, + }, Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue, }; @@ -78,7 +81,7 @@ where /// /// ```rust /// # use juniper::{ -/// # format_type, +/// # reflect::format_type, /// # macros::reflect::{WrappedType, BaseType, WrappedValue, Type}, /// # DefaultScalarValue, /// # }; @@ -114,48 +117,6 @@ where const VALUE: u128 = T::VALUE; } -pub mod wrap { - use super::WrappedValue; - - pub const SINGULAR: WrappedValue = 1; - - pub const fn nullable(val: WrappedValue) -> WrappedValue { - val * 10 + 2 - } - - pub const fn list(val: WrappedValue) -> WrappedValue { - val * 10 + 3 - } -} - -/// Alias for a [GraphQL object][1] or [interface][2] [field argument][3] name. -/// -/// See [`Fields`] for more info. -/// -/// [1]: https://spec.graphql.org/October2021#sec-Objects -/// [2]: https://spec.graphql.org/October2021#sec-Interfaces -/// [3]: https://spec.graphql.org/October2021#sec-Language.Arguments -pub type Name = &'static str; - -/// Alias for a slice of [`Name`]s. -/// -/// See [`Fields`] for more info. -pub type Names = &'static [Name]; - -/// Alias for [field argument][1]s [`Name`], [`Type`] and [`WrappedValue`]. -/// -/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments -pub type Argument = (Name, Type, WrappedValue); - -/// Alias for a slice of [field argument][1]s [`Name`], [`Type`] and -/// [`WrappedValue`]. -/// -/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments -pub type Arguments = &'static [(Name, Type, WrappedValue)]; - -/// Alias for a `const`-hashed [`Name`] used in a `const` context. -pub type FieldName = u128; - /// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`]. /// /// [1]: https://spec.graphql.org/October2021#sec-Objects @@ -269,107 +230,6 @@ pub trait AsyncField: FieldMeta { ) -> BoxFuture<'b, ExecutionResult>; } -/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in -/// `const` generics. See [spec] for more info. -/// -/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html -#[must_use] -pub const fn fnv1a128(str: Name) -> u128 { - const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d; - const FNV_PRIME: u128 = 0x0000000001000000000000000000013b; - - let bytes = str.as_bytes(); - let mut hash = FNV_OFFSET_BASIS; - let mut i = 0; - while i < bytes.len() { - hash ^= bytes[i] as u128; - hash = hash.wrapping_mul(FNV_PRIME); - i += 1; - } - hash -} - -/// Length __in bytes__ of the [`format_type!`] macro result. -#[must_use] -pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize { - let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type! - - let mut curr = val; - while curr % 10 != 0 { - match curr % 10 { - 2 => len -= "!".as_bytes().len(), // remove ! - 3 => len += "[]!".as_bytes().len(), // [Type]! - _ => {} - } - curr /= 10; - } - - len -} - -/// Checks whether the given GraphQL [object][1] represents a `subtype` of the -/// given GraphQL `ty`pe, basing on the [`WrappedType`] encoding. -/// -/// To fully determine the sub-typing relation the [`Type`] should be one of the -/// [`BaseSubTypes::NAMES`]. -/// -/// [1]: https://spec.graphql.org/October2021#sec-Objects -#[must_use] -pub const fn can_be_subtype(ty: WrappedValue, subtype: WrappedValue) -> bool { - let ty_curr = ty % 10; - let sub_curr = subtype % 10; - - if ty_curr == sub_curr { - if ty_curr == 1 { - true - } else { - can_be_subtype(ty / 10, subtype / 10) - } - } else if ty_curr == 2 { - can_be_subtype(ty / 10, subtype) - } else { - false - } -} - -/// Checks whether the given `val` exists in the given `arr`. -#[must_use] -pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool { - let mut i = 0; - while i < arr.len() { - if str_eq(val, arr[i]) { - return true; - } - i += 1; - } - false -} - -/// Compares strings in a `const` context. -/// -/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to -/// write custom comparison function. -/// -/// [`Eq`]: std::cmp::Eq -// TODO: Remove once `Eq` trait is allowed in `const` context. -pub const fn str_eq(l: &str, r: &str) -> bool { - let (l, r) = (l.as_bytes(), r.as_bytes()); - - if l.len() != r.len() { - return false; - } - - let mut i = 0; - while i < l.len() { - if l[i] != r[i] { - return false; - } - i += 1; - } - - true -} - /// Asserts that `#[graphql_interface(for = ...)]` has all the types referencing /// this interface in the `impl = ...` attribute argument. /// @@ -384,7 +244,7 @@ macro_rules! assert_implemented_for { <$interfaces as ::juniper::macros::reflect::BaseSubTypes<$scalar>>::NAMES, ); if !is_present { - const MSG: &str = $crate::const_concat!( + const MSG: &str = $crate::reflect::const_concat!( "Failed to implement interface `", <$interfaces as $crate::macros::reflect::BaseType<$scalar>>::NAME, "` on `", @@ -412,7 +272,7 @@ macro_rules! assert_interfaces_impls { <$implementers as ::juniper::macros::reflect::Implements<$scalar>>::NAMES, ); if !is_present { - const MSG: &str = $crate::const_concat!( + const MSG: &str = $crate::reflect::const_concat!( "Failed to implement interface `", <$interface as $crate::macros::reflect::BaseType<$scalar>>::NAME, "` on `", @@ -465,7 +325,7 @@ macro_rules! assert_subtype { <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME; const IMPL_TY: $crate::macros::reflect::Type = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME; - const ERR_PREFIX: &str = $crate::const_concat!( + const ERR_PREFIX: &str = $crate::reflect::const_concat!( "Failed to implement interface `", BASE_TY, "` on `", @@ -507,14 +367,14 @@ macro_rules! assert_subtype { let is_subtype = $crate::macros::reflect::str_exists_in_arr(IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES) && $crate::macros::reflect::can_be_subtype(BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL); if !is_subtype { - const MSG: &str = $crate::const_concat!( + const MSG: &str = $crate::reflect::const_concat!( ERR_PREFIX, "Field `", FIELD_NAME, "`: implementor is expected to return a subtype of interface's return object: `", - $crate::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL), + $crate::reflect::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL), "` is not a subtype of `", - $crate::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL), + $crate::reflect::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL), "`.", ); ::std::panic!("{}", MSG); @@ -538,7 +398,7 @@ macro_rules! assert_field_args { const _: () = { const BASE_NAME: &str = <$base_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME; const IMPL_NAME: &str = <$impl_ty as $crate::macros::reflect::BaseType<$scalar>>::NAME; - const ERR_PREFIX: &str = $crate::const_concat!( + const ERR_PREFIX: &str = $crate::reflect::const_concat!( "Failed to implement interface `", BASE_NAME, "` on `", @@ -661,13 +521,14 @@ macro_rules! assert_field_args { const BASE_ARG_NAME: &str = ERROR.base.0; const IMPL_ARG_NAME: &str = ERROR.implementation.0; - const BASE_TYPE_FORMATTED: &str = $crate::format_type!(ERROR.base.1, ERROR.base.2); + const BASE_TYPE_FORMATTED: &str = + $crate::reflect::format_type!(ERROR.base.1, ERROR.base.2); const IMPL_TYPE_FORMATTED: &str = - $crate::format_type!(ERROR.implementation.1, ERROR.implementation.2); + $crate::reflect::format_type!(ERROR.implementation.1, ERROR.implementation.2); const MSG: &str = match ERROR.cause { Cause::TypeMismatch => { - $crate::const_concat!( + $crate::reflect::const_concat!( "Argument `", BASE_ARG_NAME, "`: expected type `", @@ -678,7 +539,7 @@ macro_rules! assert_field_args { ) } Cause::RequiredField => { - $crate::const_concat!( + $crate::reflect::const_concat!( "Argument `", BASE_ARG_NAME, "` of type `", @@ -687,7 +548,7 @@ macro_rules! assert_field_args { ) } Cause::AdditionalNonNullableField => { - $crate::const_concat!( + $crate::reflect::const_concat!( "Argument `", IMPL_ARG_NAME, "` of type `", @@ -697,43 +558,13 @@ macro_rules! assert_field_args { } }; const ERROR_MSG: &str = - $crate::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG); + $crate::reflect::const_concat!(ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG); ::std::panic!("{}", ERROR_MSG); } }; }; } -/// Concatenates `const` [`str`](prim@str)s in a `const` context. -#[macro_export] -macro_rules! const_concat { - ($($s:expr),* $(,)?) => {{ - const LEN: usize = 0 $(+ $s.as_bytes().len())*; - const CNT: usize = [$($s),*].len(); - const fn concat(input: [&str; CNT]) -> [u8; LEN] { - let mut bytes = [0; LEN]; - let (mut i, mut byte) = (0, 0); - while i < CNT { - let mut b = 0; - while b < input[i].len() { - bytes[byte] = input[i].as_bytes()[b]; - byte += 1; - b += 1; - } - i += 1; - } - bytes - } - const CON: [u8; LEN] = concat([$($s),*]); - - // TODO: Use `str::from_utf8()` once it becomes `const`. - // SAFETY: This is safe, as we concatenate multiple UTF-8 strings one - // after another byte-by-byte. - #[allow(unsafe_code)] - unsafe { ::std::str::from_utf8_unchecked(&CON) } - }}; -} - /// Ensures that the given `$impl_ty` implements [`Field`] and returns a /// [`fnv1a128`] hash for it, otherwise panics with understandable message. #[macro_export] @@ -746,7 +577,7 @@ macro_rules! checked_hash { if exists { $crate::macros::reflect::fnv1a128(FIELD_NAME) } else { - const MSG: &str = $crate::const_concat!( + const MSG: &str = $crate::reflect::const_concat!( $($prefix,)? "Field `", $field_name, @@ -758,103 +589,3 @@ macro_rules! checked_hash { } }}; } - -/// Formats the given [`Type`] and [`WrappedValue`] into a readable GraphQL type -/// name. -/// -/// # Examples -/// -/// ```rust -/// # use juniper::format_type; -/// # -/// assert_eq!(format_type!("String", 123), "[String]!"); -/// assert_eq!(format_type!("🦀", 123), "[🦀]!"); -/// ``` -#[macro_export] -macro_rules! format_type { - ($ty: expr, $wrapped_value: expr $(,)?) => {{ - const TYPE: ( - $crate::macros::reflect::Type, - $crate::macros::reflect::WrappedValue, - ) = ($ty, $wrapped_value); - const RES_LEN: usize = $crate::macros::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1); - - const OPENING_BRACKET: &str = "["; - const CLOSING_BRACKET: &str = "]"; - const BANG: &str = "!"; - - const fn format_type_arr() -> [u8; RES_LEN] { - let (ty, wrap_val) = TYPE; - let mut type_arr: [u8; RES_LEN] = [0; RES_LEN]; - - let mut current_start = 0; - let mut current_end = RES_LEN - 1; - let mut current_wrap_val = wrap_val; - let mut is_null = false; - while current_wrap_val % 10 != 0 { - match current_wrap_val % 10 { - 2 => is_null = true, // Skips writing `BANG` later. - 3 => { - // Write `OPENING_BRACKET` at `current_start`. - let mut i = 0; - while i < OPENING_BRACKET.as_bytes().len() { - type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i]; - i += 1; - } - current_start += i; - if !is_null { - // Write `BANG` at `current_end`. - i = 0; - while i < BANG.as_bytes().len() { - type_arr[current_end - BANG.as_bytes().len() + i + 1] = - BANG.as_bytes()[i]; - i += 1; - } - current_end -= i; - } - // Write `CLOSING_BRACKET` at `current_end`. - i = 0; - while i < CLOSING_BRACKET.as_bytes().len() { - type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] = - CLOSING_BRACKET.as_bytes()[i]; - i += 1; - } - current_end -= i; - is_null = false; - } - _ => {} - } - - current_wrap_val /= 10; - } - - // Writes `Type` at `current_start`. - let mut i = 0; - while i < ty.as_bytes().len() { - type_arr[current_start + i] = ty.as_bytes()[i]; - i += 1; - } - i = 0; - if !is_null { - // Writes `BANG` at `current_end`. - while i < BANG.as_bytes().len() { - type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i]; - i += 1; - } - } - - type_arr - } - - const TYPE_ARR: [u8; RES_LEN] = format_type_arr(); - - // TODO: Use `str::from_utf8()` once it becomes `const`. - // SAFETY: This is safe, as we concatenate multiple UTF-8 strings one - // after another byte-by-byte. - #[allow(unsafe_code)] - const TYPE_FORMATTED: &str = - unsafe { ::std::str::from_utf8_unchecked(TYPE_ARR.as_slice()) }; - - TYPE_FORMATTED - }}; -} diff --git a/juniper/src/reflect/mod.rs b/juniper/src/reflect/mod.rs index d4e1d8b67..2304e6a2b 100644 --- a/juniper/src/reflect/mod.rs +++ b/juniper/src/reflect/mod.rs @@ -2,16 +2,22 @@ use crate::behavior; -/// Alias for a [GraphQL type][0]'s name in a GraphQL schema. +#[doc(inline)] +pub use self::macros::{ + assert_field, assert_field_args, assert_field_type, assert_has_field, assert_implemented_for, + assert_interfaces_impls, checked_hash, const_concat, format_type, +}; + +/// Name of a [GraphQL type][0] in a GraphQL schema. /// -/// See [`BaseType`] for more info. +/// See [`BaseType`] for details. /// /// [0]: https://spec.graphql.org/October2021#sec-Types pub type Type = &'static str; -/// Alias for a slice of [`Type`]s. +/// List of [`Type`]s. /// -/// See [`BaseSubTypes`] for more info. +/// See [`BaseSubTypes`] for details. pub type Types = &'static [Type]; /// Basic reflection of a [GraphQL type][0]. @@ -47,8 +53,786 @@ pub trait BaseSubTypes { const NAMES: Types; } -/// Alias for a value of a [`WrappedType`] (composed -/// [GraphQL wrapping type][0]). +/// Reflection of [GraphQL interfaces][1] implementations for a +/// [GraphQL type][0]. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Types +/// [1]: https://spec.graphql.org/October2021#sec-Interfaces +pub trait Implements { + /// [`Types`] of the [GraphQL interfaces][1] implemented by this + /// [GraphQL type][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Types + /// [1]: https://spec.graphql.org/October2021#sec-Interfaces + const NAMES: Types; +} + +/// Encoded value of a [`WrappedType`] (composed [GraphQL wrapping type][0]). +/// +/// See [`WrappedType`] for details. /// /// [0]: https://spec.graphql.org/October2021#sec-Wrapping-Types +// TODO: Just use `&str`s once they're allowed in `const` generics. pub type WrappedValue = u128; + +/// [`WrappedValue`] encoding helpers. +pub mod wrap { + use super::WrappedValue; + + /// [`WrappedValue`] of a singular non-nullable [GraphQL type][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Types + pub const SINGULAR: WrappedValue = 1; + + /// Performs wrapping into a nullable [`WrappedValue`]. + pub const fn nullable(val: WrappedValue) -> WrappedValue { + val * 10 + 2 + } + + /// Performs wrapping into a list [`WrappedValue`]. + pub const fn list(val: WrappedValue) -> WrappedValue { + val * 10 + 3 + } +} + +/// Reflection of a composed [GraphQL wrapping type][1], encoded in numbers. +/// +/// To fully represent a [GraphQL type][0] it's not enough to use [`Type`], +/// because of the [wrapping types][1]. To work around this, a [`WrappedValue`] +/// is used, which is represented via [`u128`] number in the following encoding: +/// - In base case of non-nullable singular [type][0] [`VALUE`] is `1`. +/// - To represent nullability we "append" `2` to the [`VALUE`], so +/// [`Option`]`<`[type][0]`>` has [`VALUE`] of `12`. +/// - To represent a list we "append" `3` to the [`VALUE`], so +/// [`Vec`]`<`[type][0]`>` has [`VALUE`] of `13`. +/// +/// Note, that due to Rust type system, the encoding here differs from the one +/// of [GraphQL wrapping types][1], as it takes nullability as wrapping, while +/// GraphQL [does the opposite][1] (takes non-nullability as wrapping). +/// +/// This approach allows to uniquely represent any [GraphQL type][0] with a +/// combination of a [`Type`] and a [`WrappedValue`], and even format it via +/// [`format_type!`] macro in a `const` context. +/// +/// # Example +/// +/// ```rust +/// # use juniper::reflect::{ +/// # format_type, BaseType, Type, WrappedType, WrappedValue, +/// # }; +/// # +/// assert_eq!( as WrappedType>::VALUE, 12); +/// assert_eq!( as WrappedType>::VALUE, 13); +/// assert_eq!(> as WrappedType>::VALUE, 123); +/// assert_eq!(> as WrappedType>::VALUE, 132); +/// assert_eq!(>> as WrappedType>::VALUE, 1232); +/// +/// const TYPE_STRING: Type = >> as BaseType>::NAME; +/// const WRAP_VAL_STRING: WrappedValue = >> as WrappedType>::VALUE; +/// assert_eq!(format_type!(TYPE_STRING, WRAP_VAL_STRING), "[String]"); +/// +/// const TYPE_STR: Type = >> as BaseType>::NAME; +/// const WRAP_VAL_STR: WrappedValue = >> as WrappedType>::VALUE; +/// assert_eq!(format_type!(TYPE_STR, WRAP_VAL_STR), "[String]"); +/// ``` +/// +/// [`VALUE`]: Self::VALUE +/// [0]: https://spec.graphql.org/October2021#sec-Types +/// [1]: https://spec.graphql.org/October2021#sec-Wrapping-Types +pub trait WrappedType { + /// [`WrappedValue`] of this this [GraphQL type][0], encoded in a number. + /// + /// Use [`format_type!`] macro on this number to represent it as a + /// human-readable [GraphQL type][0] string. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Types + const VALUE: WrappedValue; +} + +/// Name of a [GraphQL field][0] or a [field argument][1]. +/// +/// See [`Fields`] for details. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields +/// [1]: https://spec.graphql.org/October2021#sec-Language.Arguments +pub type Name = &'static str; + +/// List of [`Name`]s. +/// +/// See [`Fields`] for details. +pub type Names = &'static [Name]; + +/// Reflection of [fields][0] for a [GraphQL object][1] or an [interface][2]. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields +/// [1]: https://spec.graphql.org/October2021#sec-Objects +/// [2]: https://spec.graphql.org/October2021#sec-Interfaces +pub trait Fields { + /// [`Names`] of this [GraphQL object][1]/[interface][2] [fields][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + /// [1]: https://spec.graphql.org/October2021#sec-Objects + /// [2]: https://spec.graphql.org/October2021#sec-Interfaces + const NAMES: Names; +} + +/// [GraphQL field argument][0], represented as its [`Name`], [`Type`] and +/// [`WrappedValue`]. +/// +/// See [`Field`] for details. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Language.Arguments +pub type Argument = (Name, Type, WrappedValue); + +/// List of [`Argument`]s. +/// +/// See [`Field`] for details. +pub type Arguments = &'static [(Name, Type, WrappedValue)]; + +/// Alias for a `const`-hashed [`Name`] used in a `const` context. +// TODO: Just use `&str`s once they're allowed in `const` generics. +pub type FieldName = u128; + +/// Reflection of a single [GraphQL field][0]. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Language.Fields +pub trait Field { + /// [`Type`] of this [GraphQL field][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + const TYPE: Type; + + /// [Sub-types][1] this [GraphQL field][0] is coercible into. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + /// [1]: BaseSubTypes + const SUB_TYPES: Types; + + /// [`WrappedValue`] of this [GraphQL field][0]. + /// + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + const WRAPPED_VALUE: WrappedValue; + + /// [`Arguments`] of this [GraphQL field][0] . + /// + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + const ARGUMENTS: Arguments; +} + +/// Non-cryptographic hash with good dispersion to use as a [`str`](prim@str) in +/// `const` generics. See [spec] for more info. +/// +/// [spec]: https://datatracker.ietf.org/doc/html/draft-eastlake-fnv-17.html +#[must_use] +pub const fn fnv1a128(str: Name) -> FieldName { + const FNV_OFFSET_BASIS: u128 = 0x6c62272e07bb014262b821756295c58d; + const FNV_PRIME: u128 = 0x0000000001000000000000000000013b; + + let bytes = str.as_bytes(); + let mut hash = FNV_OFFSET_BASIS; + let mut i = 0; + while i < bytes.len() { + hash ^= bytes[i] as u128; + hash = hash.wrapping_mul(FNV_PRIME); + i += 1; + } + hash +} + +/// Length __in bytes__ of the [`format_type!`] macro result. +#[must_use] +pub const fn type_len_with_wrapped_val(ty: Type, val: WrappedValue) -> usize { + let mut len = ty.as_bytes().len() + "!".as_bytes().len(); // Type! + + let mut curr = val; + while curr % 10 != 0 { + match curr % 10 { + 2 => len -= "!".as_bytes().len(), // remove ! + 3 => len += "[]!".as_bytes().len(), // [Type]! + _ => {} + } + curr /= 10; + } + + len +} + +/// Checks whether the specified `subtype` [GraphQL type][0] represents a +/// [sub-type][1] of the specified `supertype`, basing on the [`WrappedType`] +/// encoding. +/// +/// To fully determine the [sub-typing][1] relation the [`Type`] should be one +/// of the [`BaseSubTypes::NAMES`]. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Types +/// [1]: https://spec.graphql.org/October2021#sel-JAHZhCHCDEJDAAAEEFDBtzC +#[must_use] +pub const fn can_be_subtype(supertype: WrappedValue, subtype: WrappedValue) -> bool { + let super_curr = supertype % 10; + let sub_curr = subtype % 10; + + if super_curr == sub_curr { + if super_curr == 1 { + true + } else { + can_be_subtype(supertype / 10, subtype / 10) + } + } else if super_curr == 2 { + can_be_subtype(supertype / 10, subtype) + } else { + false + } +} + +/// Checks whether the given `val` exists in the given `arr`. +// TODO: Remove once `slice::contains()` method is allowed in `const` context. +#[must_use] +pub const fn str_exists_in_arr(val: &str, arr: &[&str]) -> bool { + let mut i = 0; + while i < arr.len() { + if str_eq(val, arr[i]) { + return true; + } + i += 1; + } + false +} + +/// Compares strings in a `const` context. +/// +/// As there is no `const impl Trait` and `l == r` calls [`Eq`], we have to +/// provide a custom comparison function. +/// +/// [`Eq`]: std::cmp::Eq +// TODO: Remove once `Eq` trait is allowed in `const` context. +#[must_use] +pub const fn str_eq(l: &str, r: &str) -> bool { + let (l, r) = (l.as_bytes(), r.as_bytes()); + + if l.len() != r.len() { + return false; + } + + let mut i = 0; + while i < l.len() { + if l[i] != r[i] { + return false; + } + i += 1; + } + + true +} + +mod macros { + /// Asserts that `#[graphql::interface(for = ...)]` attribute has all the + /// types referencing this interface in the `impl = ...` attribute argument. + /// + /// Symmetrical to [`assert_interfaces_impls!`]. + macro_rules! assert_implemented_for { + ($behavior: ty, $implementor: ty $(, $interfaces: ty)* $(,)?) => { + const _: () = { + $({ + let is_present = $crate::reflect::str_exists_in_arr( + <$implementor as $crate::reflect::BaseType<$behavior>>::NAME, + <$interfaces as $crate::reflect::BaseSubTypes<$behavior>>::NAMES, + ); + if !is_present { + const MSG: &str = $crate::reflect::const_concat!( + "Failed to implement interface `", + <$interfaces as $crate::reflect::BaseType<$behavior>>::NAME, + "` on `", + <$implementor as $crate::reflect::BaseType<$behavior>>::NAME, + "`: missing implementer reference in interface's `for` attribute.", + ); + ::std::panic!("{}", MSG); + } + })* + }; + }; + } + + /// Asserts that `impl = ...` attribute argument has all the interfaces + /// referencing this type in `#[graphql::interface(for = ...)]` attribute. + /// + /// Symmetrical to [`assert_implemented_for!`]. + macro_rules! assert_interfaces_impls { + ($behavior: ty, $interface: ty $(, $implementers: ty)* $(,)?) => { + const _: () = { + $({ + let is_present = $crate::reflect::str_exists_in_arr( + <$interface as $crate::reflect::BaseType<$behavior>>::NAME, + <$implementers as $crate::reflect::Implements<$behavior>>::NAMES, + ); + if !is_present { + const MSG: &str = $crate::reflect::const_concat!( + "Failed to implement interface `", + <$interface as $crate::reflect::BaseType<$behavior>>::NAME, + "` on `", + <$implementers as $crate::reflect::BaseType<$behavior>>::NAME, + "`: missing interface reference in implementer's `impl` attribute.", + ); + ::std::panic!("{}", MSG); + } + })* + }; + }; + } + + /// Asserts validness of [`Field`] [`Arguments`] and its returned [`Type`]. + /// + /// This assertion is a combination of [`assert_field_type!`] and + /// [`assert_field_args!`]. + /// + /// See [spec][0] for assertion algorithm details. + /// + /// [`Arguments`]: super::Arguments + /// [`Field`]: super::Field + /// [`Type`]: super::Type + /// [0]: https://spec.graphql.org/October2021#IsValidImplementation() + macro_rules! assert_field { + ( + $base_ty: ty, + $impl_ty: ty, + $behavior: ty, + $field_name: expr $(,)? + ) => { + $crate::reflect::assert_field_type!($base_ty, $impl_ty, $behavior, $field_name); + $crate::reflect::assert_field_args!($base_ty, $impl_ty, $behavior, $field_name); + }; + } + + /// Asserts validness of a [`Field`] type. + /// + /// See [spec][0] for assertion algorithm details. + /// + /// [`Field`]: super::Field + /// [0]: https://spec.graphql.org/October2021#IsValidImplementationFieldType() + #[macro_export] + macro_rules! assert_field_type { + ( + $base_ty: ty, + $impl_ty: ty, + $behavior: ty, + $field_name: expr $(,)? + ) => { + const _: () = { + const BASE_TY: $crate::reflect::Type = + <$base_ty as $crate::reflect::BaseType<$behavior>>::NAME; + const IMPL_TY: $crate::reflect::Type = + <$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME; + const ERR_PREFIX: &str = $crate::reflect::const_concat!( + "Failed to implement interface `", + BASE_TY, + "` on `", + IMPL_TY, + "`: ", + ); + + const FIELD_NAME: $crate::reflect::Name = $field_name; + const FIELD_NAME_HASH: $crate::reflect::FieldName = + $crate::reflect::fnv1a128(FIELD_NAME); + + $crate::reflect::assert_has_field!( + FIELD_NAME, $base_ty, $behavior, ERR_PREFIX, + ); + $crate::reflect::assert_has_field!( + FIELD_NAME, $impl_ty, $behavior, ERR_PREFIX, + ); + + const BASE_RETURN_WRAPPED_VAL: $crate::reflect::WrappedValue = + <$base_ty as $crate::reflect::Field< + FIELD_NAME_HASH, $behavior, + >>::WRAPPED_VALUE; + const IMPL_RETURN_WRAPPED_VAL: $crate::reflect::WrappedValue = + <$impl_ty as $crate::reflect::Field< + FIELD_NAME_HASH, $behavior, + >>::WRAPPED_VALUE; + + const BASE_RETURN_TY: $crate::reflect::Type = + <$base_ty as $crate::reflect::Field< + FIELD_NAME_HASH, $behavior, + >>::TYPE; + const IMPL_RETURN_TY: $crate::reflect::Type = + <$impl_ty as $crate::reflect::Field< + FIELD_NAME_HASH, $behavior, + >>::TYPE; + + const BASE_RETURN_SUB_TYPES: $crate::reflect::Types = + <$base_ty as $crate::reflect::Field< + FIELD_NAME_HASH, $behavior, + >>::SUB_TYPES; + + let is_subtype = $crate::reflect::str_exists_in_arr( + IMPL_RETURN_TY, BASE_RETURN_SUB_TYPES, + ) && $crate::reflect::can_be_subtype( + BASE_RETURN_WRAPPED_VAL, IMPL_RETURN_WRAPPED_VAL, + ); + if !is_subtype { + const MSG: &str = $crate::reflect::const_concat!( + ERR_PREFIX, + "Field `", + FIELD_NAME, + "`: implementor is expected to return a subtype of interface's return object: `", + $crate::reflect::format_type!(IMPL_RETURN_TY, IMPL_RETURN_WRAPPED_VAL), + "` is not a subtype of `", + $crate::reflect::format_type!(BASE_RETURN_TY, BASE_RETURN_WRAPPED_VAL), + "`.", + ); + ::std::panic!("{}", MSG); + } + }; + }; + } + + /// Asserts validness of a [`Field`] arguments. + /// + /// See [spec][0] for assertion algorithm details. + /// + /// [`Field`]: super::Field + /// [0]: https://spec.graphql.org/October2021#sel-IAHZhCHCDEEFAAADHD8Cxob + #[macro_export] + macro_rules! assert_field_args { + ( + $base_ty: ty, + $impl_ty: ty, + $behavior: ty, + $field_name: expr $(,)? + ) => { + const _: () = { + const BASE_TY: $crate::reflect::Type = + <$base_ty as $crate::reflect::BaseType<$behavior>>::NAME; + const IMPL_TY: $crate::reflect::Type = + <$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME; + const ERR_PREFIX: &str = $crate::reflect::const_concat!( + "Failed to implement interface `", + BASE_TY, + "` on `", + IMPL_TY, + "`: ", + ); + + const FIELD_NAME: $crate::reflect::Name = $field_name; + const FIELD_NAME_HASH: $crate::reflect::FieldName = + $crate::reflect::fnv1a128(FIELD_NAME); + + $crate::reflect::assert_has_field!(FIELD_NAME, $base_ty, $behavior, ERR_PREFIX); + $crate::reflect::assert_has_field!(FIELD_NAME, $impl_ty, $behavior, ERR_PREFIX); + + const BASE_ARGS: ::juniper::reflect::Arguments = + <$base_ty as $crate::reflect::Field>::ARGUMENTS; + const IMPL_ARGS: ::juniper::reflect::Arguments = + <$impl_ty as $crate::reflect::Field>::ARGUMENTS; + + struct Error { + cause: Cause, + base: ::juniper::reflect::Argument, + implementation: ::juniper::reflect::Argument, + } + + enum Cause { + RequiredField, + AdditionalNonNullableField, + TypeMismatch, + } + + const fn unwrap_error(v: ::std::result::Result<(), Error>) -> Error { + match v { + // Unfortunately, we cannot use `unreachable!()` here, + // as this branch will be executed either way. + Ok(()) => Error { + cause: Cause::RequiredField, + base: ("unreachable", "unreachable", 1), + implementation: ("unreachable", "unreachable", 1), + }, + Err(e) => e, + } + } + + const fn check() -> Result<(), Error> { + let mut base_i = 0; + while base_i < BASE_ARGS.len() { + let (base_name, base_type, base_wrap_val) = BASE_ARGS[base_i]; + + let mut impl_i = 0; + let mut was_found = false; + while impl_i < IMPL_ARGS.len() { + let (impl_name, impl_type, impl_wrap_val) = IMPL_ARGS[impl_i]; + + if $crate::reflect::str_eq(base_name, impl_name) { + if $crate::reflect::str_eq(base_type, impl_type) + && base_wrap_val == impl_wrap_val + { + was_found = true; + break; + } else { + return Err(Error { + cause: Cause::TypeMismatch, + base: (base_name, base_type, base_wrap_val), + implementation: (impl_name, impl_type, impl_wrap_val), + }); + } + } + + impl_i += 1; + } + + if !was_found { + return Err(Error { + cause: Cause::RequiredField, + base: (base_name, base_type, base_wrap_val), + implementation: (base_name, base_type, base_wrap_val), + }); + } + + base_i += 1; + } + + let mut impl_i = 0; + while impl_i < IMPL_ARGS.len() { + let (impl_name, impl_type, impl_wrapped_val) = IMPL_ARGS[impl_i]; + impl_i += 1; + + if impl_wrapped_val % 10 == 2 { + continue; + } + + let mut base_i = 0; + let mut was_found = false; + while base_i < BASE_ARGS.len() { + let (base_name, _, _) = BASE_ARGS[base_i]; + if $crate::reflect::str_eq(base_name, impl_name) { + was_found = true; + break; + } + base_i += 1; + } + if !was_found { + return Err(Error { + cause: Cause::AdditionalNonNullableField, + base: (impl_name, impl_type, impl_wrapped_val), + implementation: (impl_name, impl_type, impl_wrapped_val), + }); + } + } + + Ok(()) + } + + const RES: ::std::result::Result<(), Error> = check(); + if RES.is_err() { + const ERROR: Error = unwrap_error(RES); + + const BASE_ARG_NAME: $crate::reflect::Name = ERROR.base.0; + const IMPL_ARG_NAME: $crate::reflect::Name = ERROR.implementation.0; + + const BASE_TYPE_FORMATTED: &str = + $crate::reflect::format_type!(ERROR.base.1, ERROR.base.2,); + const IMPL_TYPE_FORMATTED: &str = $crate::reflect::format_type!( + ERROR.implementation.1, + ERROR.implementation.2, + ); + + const MSG: &str = match ERROR.cause { + Cause::TypeMismatch => { + $crate::reflect::const_concat!( + "Argument `", + BASE_ARG_NAME, + "`: expected type `", + BASE_TYPE_FORMATTED, + "`, found: `", + IMPL_TYPE_FORMATTED, + "`.", + ) + } + Cause::RequiredField => { + $crate::reflect::const_concat!( + "Argument `", + BASE_ARG_NAME, + "` of type `", + BASE_TYPE_FORMATTED, + "` was expected, but not found.", + ) + } + Cause::AdditionalNonNullableField => { + $crate::reflect::const_concat!( + "Argument `", + IMPL_ARG_NAME, + "` of type `", + IMPL_TYPE_FORMATTED, + "` isn't present on the interface and so has to be nullable.", + ) + } + }; + const ERROR_MSG: &str = $crate::reflect::const_concat!( + ERR_PREFIX, "Field `", FIELD_NAME, "`: ", MSG, + ); + ::std::panic!("{}", ERROR_MSG); + } + }; + }; + } + + /// Ensures that the given `$impl_ty` has the specified [`Field`]. + /// + /// [`Field`]: super::Field + /// [`fnv1a128`]: super::fnv1a128 + macro_rules! assert_has_field { + ( + $field_name: expr, + $impl_ty: ty, + $behavior: ty + $(, $prefix: expr)? $(,)? + ) => {{ + let exists = $crate::reflect::str_exists_in_arr( + $field_name, + <$impl_ty as $crate::reflect::Fields<$behavior>>::NAMES, + ); + if !exists { + const MSG: &str = $crate::reflect::const_concat!( + $($prefix,)? + "Field `", + $field_name, + "` isn't implemented on `", + <$impl_ty as $crate::reflect::BaseType<$behavior>>::NAME, + "`." + ); + ::std::panic!("{}", MSG); + } + }}; + } + + /// Concatenates `const` [`str`](prim@str)s in a `const` context. + macro_rules! const_concat { + ($($s:expr),* $(,)?) => {{ + const LEN: usize = 0 $(+ $s.as_bytes().len())*; + const CNT: usize = [$($s),*].len(); + const fn concat(input: [&str; CNT]) -> [u8; LEN] { + let mut bytes = [0; LEN]; + let (mut i, mut byte) = (0, 0); + while i < CNT { + let mut b = 0; + while b < input[i].len() { + bytes[byte] = input[i].as_bytes()[b]; + byte += 1; + b += 1; + } + i += 1; + } + bytes + } + const CON: [u8; LEN] = concat([$($s),*]); + + // TODO: Use `str::from_utf8()` once it becomes `const`. + // SAFETY: This is safe, as we concatenate multiple UTF-8 strings + // one after another byte-by-byte. + #[allow(unsafe_code)] + unsafe { ::std::str::from_utf8_unchecked(&CON) } + }}; + } + + /// Formats the given [`Type`] and [`WrappedValue`] into a human-readable + /// GraphQL type name string. + /// + /// # Example + /// + /// ```rust + /// # use juniper::reflect::format_type; + /// # + /// assert_eq!(format_type!("String", 123), "[String]!"); + /// assert_eq!(format_type!("🦀", 123), "[🦀]!"); + /// ``` + /// + /// [`Type`]: super::Type + /// [`WrappedValue`]: super::WrappedValue + macro_rules! format_type { + ($ty: expr, $wrapped_value: expr $(,)?) => {{ + const TYPE: ($crate::reflect::Type, $crate::reflect::WrappedValue) = + ($ty, $wrapped_value); + const RES_LEN: usize = $crate::reflect::type_len_with_wrapped_val(TYPE.0, TYPE.1); + + const OPENING_BRACKET: &str = "["; + const CLOSING_BRACKET: &str = "]"; + const BANG: &str = "!"; + + const fn format_type_arr() -> [u8; RES_LEN] { + let (ty, wrap_val) = TYPE; + let mut type_arr: [u8; RES_LEN] = [0; RES_LEN]; + + let mut current_start = 0; + let mut current_end = RES_LEN - 1; + let mut current_wrap_val = wrap_val; + let mut is_null = false; + while current_wrap_val % 10 != 0 { + match current_wrap_val % 10 { + 2 => is_null = true, // Skips writing `BANG` later. + 3 => { + // Write `OPENING_BRACKET` at `current_start`. + let mut i = 0; + while i < OPENING_BRACKET.as_bytes().len() { + type_arr[current_start + i] = OPENING_BRACKET.as_bytes()[i]; + i += 1; + } + current_start += i; + if !is_null { + // Write `BANG` at `current_end`. + i = 0; + while i < BANG.as_bytes().len() { + type_arr[current_end - BANG.as_bytes().len() + i + 1] = + BANG.as_bytes()[i]; + i += 1; + } + current_end -= i; + } + // Write `CLOSING_BRACKET` at `current_end`. + i = 0; + while i < CLOSING_BRACKET.as_bytes().len() { + type_arr[current_end - CLOSING_BRACKET.as_bytes().len() + i + 1] = + CLOSING_BRACKET.as_bytes()[i]; + i += 1; + } + current_end -= i; + is_null = false; + } + _ => {} + } + + current_wrap_val /= 10; + } + + // Writes `Type` at `current_start`. + let mut i = 0; + while i < ty.as_bytes().len() { + type_arr[current_start + i] = ty.as_bytes()[i]; + i += 1; + } + i = 0; + if !is_null { + // Writes `BANG` at `current_end`. + while i < BANG.as_bytes().len() { + type_arr[current_end - BANG.as_bytes().len() + i + 1] = BANG.as_bytes()[i]; + i += 1; + } + } + + type_arr + } + + const TYPE_ARR: [u8; RES_LEN] = format_type_arr(); + + // TODO: Use `str::from_utf8()` once it becomes `const`. + // SAFETY: This is safe, as we concatenate multiple UTF-8 strings one + // after another byte-by-byte. + #[allow(unsafe_code)] + const TYPE_FORMATTED: &str = + unsafe { ::std::str::from_utf8_unchecked(TYPE_ARR.as_slice()) }; + TYPE_FORMATTED + }}; + } + + #[doc(inline)] + pub(super) use { + assert_field, assert_field_args, assert_field_type, assert_has_field, + assert_implemented_for, assert_interfaces_impls, checked_hash, const_concat, format_type, + }; +} diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 8e823117e..fa32f9e53 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -2,7 +2,7 @@ use crate::{ behavior, graphql, meta::MetaType, parser::{self, ParseError}, - Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, Selection, + reflect, Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, Selection, }; #[doc(inline)] @@ -100,6 +100,22 @@ pub trait Field< ) -> ExecutionResult; } +pub trait StaticField< + const N: reflect::FieldName, + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ + fn resolve_static_field( + &self, + arguments: &Arguments, + type_info: &TypeInfo, + executor: &Executor, + ) -> ExecutionResult; +} + pub trait FieldAsync< TypeInfo: ?Sized, Context: ?Sized, @@ -116,6 +132,22 @@ pub trait FieldAsync< ) -> BoxFuture<'r, ExecutionResult>; } +pub trait StaticFieldAsync< + const N: reflect::FieldName, + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ + fn resolve_static_field_async<'r>( + &'r self, + arguments: &'r Arguments, + type_info: &'r TypeInfo, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult>; +} + pub trait ToInputValue { fn to_input_value(&self) -> graphql::InputValue; } From b716a45215e5a11ed5bff5b6d7599179a3d5284e Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 7 Jun 2022 18:53:01 +0200 Subject: [PATCH 19/58] Refactor `input_value!` macro position [skip ci] --- juniper/src/graphql/mod.rs | 101 +++--- juniper/src/lib.rs | 5 +- ...{graphql_input_value.rs => input_value.rs} | 296 +++++++++--------- juniper/src/macros/mod.rs | 6 +- 4 files changed, 213 insertions(+), 195 deletions(-) rename juniper/src/macros/{graphql_input_value.rs => input_value.rs} (59%) diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 8c3d3b660..3deea79c7 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -1,67 +1,88 @@ +use crate::{behavior, resolve}; + pub use crate::{ - ast::InputValue, graphql_input_value as input_value, graphql_value as value, value::Value, + ast::InputValue, graphql_value as value, macros::input_value, + resolve::Type, value::Value, }; -pub trait Interface: - OutputType -/* - + resolve::TypeName - + resolve::ConcreteTypeName - + resolve::Value - + resolve::ValueAsync - + resolve::ConcreteValue - + resolve::ConcreteValueAsync - + resolve::Field - + resolve::FieldAsync +pub trait Interface +/*: OutputType + + resolve::TypeName + + resolve::ConcreteTypeName + + resolve::Value + + resolve::ValueAsync + + resolve::ConcreteValue + + resolve::ConcreteValueAsync + + resolve::Field + + resolve::FieldAsync - */ +*/ { fn assert_interface(); } -pub trait Object: - OutputType -/* - + resolve::TypeName - + resolve::ConcreteTypeName - + resolve::Value - + resolve::ValueAsync - + resolve::Field - + resolve::FieldAsync +pub trait Object +/*: OutputType + + resolve::TypeName + + resolve::ConcreteTypeName + + resolve::Value + + resolve::ValueAsync + + resolve::Field + + resolve::FieldAsync - */ +*/ { fn assert_object(); } -pub trait Scalar: - OutputType + /* - resolve::TypeName + resolve::Value + resolve::ValueAsync */ +pub trait Scalar< + 'inp, + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: + InputType<'inp, TypeInfo, ScalarValue, Behavior> + + OutputType + + resolve::ScalarToken { fn assert_scalar(); } -pub trait Union: - OutputType -/* - + resolve::TypeName - + resolve::ConcreteTypeName - + resolve::Value - + resolve::ValueAsync - + resolve::ConcreteValue - + resolve::ConcreteValueAsync */ +pub trait Union +/*: OutputType ++ resolve::TypeName ++ resolve::ConcreteTypeName ++ resolve::Value ++ resolve::ValueAsync ++ resolve::ConcreteValue ++ resolve::ConcreteValueAsync */ { fn assert_union(); } -pub trait InputType<'inp, Info: ?Sized, S: 'inp> /*: - crate::resolve::Type - + crate::resolve::ToInputValue - + crate::resolve::InputValue<'inp, S>*/ +pub trait InputType< + 'inp, + TypeInfo: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: + Type + + resolve::ToInputValue + + resolve::InputValue<'inp, ScalarValue, Behavior> { fn assert_input_type(); } -pub trait OutputType: /*Type*/ { +pub trait OutputType< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +>: + Type + + resolve::Value + + resolve::ValueAsync +{ fn assert_output_type(); } diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 04ac5d69c..ea55733f8 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -74,7 +74,10 @@ pub use crate::{ LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables, }, introspection::IntrospectionFormat, - macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, + macros::{ + input_value as graphql_input_value, + helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, + }, parser::{ParseError, ScalarToken, Spanning}, schema::{ meta, diff --git a/juniper/src/macros/graphql_input_value.rs b/juniper/src/macros/input_value.rs similarity index 59% rename from juniper/src/macros/graphql_input_value.rs rename to juniper/src/macros/input_value.rs index 69641e376..0edc06002 100644 --- a/juniper/src/macros/graphql_input_value.rs +++ b/juniper/src/macros/input_value.rs @@ -1,45 +1,43 @@ -//! [`graphql_input_value!`] macro implementation. -//! -//! [`graphql_input_value!`]: graphql_input_value +//! [`input_value!`] macro implementation. -/// Constructs [`InputValue`]s via JSON-like syntax. +/// Constructs [`graphql::InputValue`]s via JSON-like syntax. /// /// # Differences from [`graphql_value!`] /// /// - [`InputValue::Enum`] is constructed with `ident`, so to capture outer /// variable as [`InputValue::Scalar`] surround it with parens: `(var)`. /// ```rust -/// # use juniper::{graphql_input_value, graphql_value}; +/// # use juniper::{graphql, graphql_value}; /// # -/// # type InputValue = juniper::InputValue; -/// # type Value = juniper::Value; +/// # type InputValue = graphql::InputValue; +/// # type Value = graphql::Value; /// # /// const OUTER_VAR: i32 = 42; -/// assert_eq!(graphql_value!(OUTER_VAR), Value::scalar(42)); -/// assert_eq!(graphql_input_value!(OUTER_VAR), InputValue::enum_value("OUTER_VAR")); -/// assert_eq!(graphql_input_value!((OUTER_VAR)), InputValue::scalar(42)); +/// assert_eq!(graphql::value!(OUTER_VAR), Value::scalar(42)); +/// assert_eq!(graphql::input_value!(OUTER_VAR), InputValue::enum_value("OUTER_VAR")); +/// assert_eq!(graphql::input_value!((OUTER_VAR)), InputValue::scalar(42)); /// ``` /// /// - [`InputValue::Variable`] is constructed by prefixing `ident` with `@`. /// ```rust -/// # use juniper::graphql_input_value; +/// # use juniper::graphql; /// # -/// # type InputValue = juniper::InputValue; +/// # type InputValue = graphql::InputValue; /// # -/// assert_eq!(graphql_input_value!(@var), InputValue::variable("var")); +/// assert_eq!(graphql::input_value!(@var), InputValue::variable("var")); /// ``` /// /// - [`InputValue::Object`] key should implement [`Into`]`<`[`String`]`>`. /// ```rust /// # use std::borrow::Cow; /// # -/// # use juniper::{graphql_input_value, InputValue}; +/// # use juniper::graphql; /// # /// let code = 200; /// let features = vec!["key", "value"]; /// let key: Cow<'static, str> = "key".into(); /// -/// let value: InputValue = graphql_input_value!({ +/// let value: graphql::InputValue = graphql::input_value!({ /// "code": code, /// "success": code == 200, /// "payload": { @@ -55,128 +53,127 @@ /// # Example /// /// ```rust -/// # use juniper::{graphql_input_value, InputValue}; +/// # use juniper::graphql; /// # -/// # type V = InputValue; +/// # type V = graphql::InputValue; /// # /// # let _: V = -/// graphql_input_value!(null); +/// graphql::input_value!(null); /// # let _: V = -/// graphql_input_value!(1234); +/// graphql::input_value!(1234); /// # let _: V = -/// graphql_input_value!("test"); +/// graphql::input_value!("test"); /// # let _: V = -/// graphql_input_value!([1234, "test", true]); +/// graphql::input_value!([1234, "test", true]); /// # let _: V = -/// graphql_input_value!({"key": "value", "foo": 1234}); +/// graphql::input_value!({"key": "value", "foo": 1234}); /// # let _: V = -/// graphql_input_value!({"key": ENUM}); +/// graphql::input_value!({"key": ENUM}); /// let captured_var = 42; /// # let _: V = -/// graphql_input_value!({"key": (captured_var)}); +/// graphql::input_value!({"key": (captured_var)}); /// # let _: V = -/// graphql_input_value!({"key": @variable}); +/// graphql::input_value!({"key": @variable}); /// ``` /// -/// [`InputValue`]: crate::InputValue -/// [`InputValue::Enum`]: crate::InputValue::Enum -/// [`InputValue::List`]: crate::InputValue::List -/// [`InputValue::Object`]: crate::InputValue::Object -/// [`InputValue::Scalar`]: crate::InputValue::Scalar -/// [`InputValue::Variable`]: crate::InputValue::Variable +/// [`graphql::InputValue`]: crate::graphql::InputValue +/// [`InputValue::Enum`]: crate::graphql::InputValue::Enum +/// [`InputValue::List`]: crate::graphql::InputValue::List +/// [`InputValue::Object`]: crate::graphql::InputValue::Object +/// [`InputValue::Scalar`]: crate::graphql::InputValue::Scalar +/// [`InputValue::Variable`]: crate::graphql::InputValue::Variable /// [`Spanning::unlocated`]: crate::Spanning::unlocated -#[macro_export] -macro_rules! graphql_input_value { +macro_rules! input_value { /////////// // Array // /////////// // Done with trailing comma. (@@array [$($elems:expr,)*]) => { - $crate::InputValue::list(vec![ + $crate::graphql::InputValue::list(::std::vec![ $( $elems, )* ]) }; // Done without trailing comma. (@@array [$($elems:expr),*]) => { - $crate::InputValue::list(vec![ + $crate::graphql::InputValue::list(::std::vec![ $( $elems, )* ]) }; // Next element is `null`. (@@array [$($elems:expr,)*] null $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!(null)] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!(null)] $($rest)* ) }; // Next element is `None`. (@@array [$($elems:expr,)*] None $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!(None)] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!(None)] $($rest)* ) }; // Next element is a variable. (@@array [$($elems:expr,)*] @$var:ident $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!(@$var)] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!(@$var)] $($rest)* ) }; // Next element is an array. (@@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!([$($array)*])] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!([$($array)*])] $($rest)* ) }; // Next element is a map. (@@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!({$($map)*})] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!({$($map)*})] $($rest)* ) }; // Next element is `true`, `false` or enum ident followed by comma. (@@array [$($elems:expr,)*] $ident:ident, $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!($ident),] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!($ident),] $($rest)* ) }; // Next element is `true`, `false` or enum ident without trailing comma. (@@array [$($elems:expr,)*] $last:ident ) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!($last)] + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!($last)] ) }; // Next element is an expression followed by comma. (@@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!($next),] $($rest)* + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!($next),] $($rest)* ) }; // Last element is an expression with no trailing comma. (@@array [$($elems:expr,)*] $last:expr) => { - $crate::graphql_input_value!( - @@array [$($elems,)* $crate::graphql_input_value!($last)] + $crate::graphql::input_value!( + @@array [$($elems,)* $crate::graphql::input_value!($last)] ) }; // Comma after the most recent element. (@@array [$($elems:expr),*] , $($rest:tt)*) => { - $crate::graphql_input_value!(@@array [$($elems,)*] $($rest)*) + $crate::graphql::input_value!(@@array [$($elems,)*] $($rest)*) }; // Unexpected token after most recent element. (@@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { - $crate::graphql_input_value!(@unexpected $unexpected) + $crate::graphql::input_value!(@unexpected $unexpected) }; //////////// @@ -192,12 +189,12 @@ macro_rules! graphql_input_value { $crate::Spanning::unlocated(($($key)+).into()), $crate::Spanning::unlocated($value), )); - $crate::graphql_input_value!(@@object $object () ($($rest)*) ($($rest)*)); + $crate::graphql::input_value!(@@object $object () ($($rest)*) ($($rest)*)); }; // Current entry followed by unexpected token. (@@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { - $crate::graphql_input_value!(@unexpected $unexpected); + $crate::graphql::input_value!(@unexpected $unexpected); }; // Insert the last entry without trailing comma. @@ -210,114 +207,114 @@ macro_rules! graphql_input_value { // Next value is `null`. (@@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!(null)) $($rest)* + ($crate::graphql::input_value!(null)) $($rest)* ); }; // Next value is `None`. (@@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!(None)) $($rest)* + ($crate::graphql::input_value!(None)) $($rest)* ); }; // Next value is a variable. (@@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!(@$var)) $($rest)* + ($crate::graphql::input_value!(@$var)) $($rest)* ); }; // Next value is an array. (@@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!([$($array)*])) $($rest)* + ($crate::graphql::input_value!([$($array)*])) $($rest)* ); }; // Next value is a map. (@@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!({$($map)*})) $($rest)* + ($crate::graphql::input_value!({$($map)*})) $($rest)* ); }; // Next value is `true`, `false` or enum ident followed by comma. (@@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!($ident)) , $($rest)* + ($crate::graphql::input_value!($ident)) , $($rest)* ); }; // Next value is `true`, `false` or enum ident without trailing comma. (@@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!($last)) + ($crate::graphql::input_value!($last)) ); }; // Next value is an expression followed by comma. (@@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!($value)) , $($rest)* + ($crate::graphql::input_value!($value)) , $($rest)* ); }; // Last value is an expression with no trailing comma. (@@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object [$($key)+] - ($crate::graphql_input_value!($value)) + ($crate::graphql::input_value!($value)) ); }; // Missing value for last entry. Trigger a reasonable error message. (@@object $object:ident ($($key:tt)+) (:) $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_input_value!(); + $crate::graphql::input_value!(); }; // Missing colon and value for last entry. Trigger a reasonable error // message. (@@object $object:ident ($($key:tt)+) () $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_input_value!(); + $crate::graphql::input_value!(); }; // Misplaced colon. Trigger a reasonable error message. (@@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `:`". - $crate::graphql_input_value!(@unexpected $colon); + $crate::graphql::input_value!(@unexpected $colon); }; // Found a comma inside a key. Trigger a reasonable error message. (@@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `,`". - $crate::graphql_input_value!(@unexpected $comma); + $crate::graphql::input_value!(@unexpected $comma); }; // Key is fully parenthesized. This avoids `clippy::double_parens` false // positives because the parenthesization may be necessary here. (@@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object ($key) (: $($rest)*) (: $($rest)*) @@ -326,12 +323,12 @@ macro_rules! graphql_input_value { // Refuse to absorb colon token into key expression. (@@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { - $crate::graphql_input_value!(@@unexpected $($unexpected)+); + $crate::graphql::input_value!(@@unexpected $($unexpected)+); }; // Munch a token into the current key. (@@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { - $crate::graphql_input_value!( + $crate::graphql::input_value!( @@object $object ($($key)* $tt) ($($rest)*) ($($rest)*) @@ -349,109 +346,107 @@ macro_rules! graphql_input_value { ////////////// ([ $($arr:tt)* ]$(,)?) => { - $crate::graphql_input_value!(@@array [] $($arr)*) + $crate::graphql::input_value!(@@array [] $($arr)*) }; ({}$(,)?) => { - $crate::InputValue::parsed_object(vec![]) + $crate::graphql::InputValue::parsed_object(vec![]) }; ({ $($map:tt)+ }$(,)?) => { - $crate::InputValue::parsed_object({ + $crate::graphql::InputValue::parsed_object({ let mut object = vec![]; - $crate::graphql_input_value!(@@object object () ($($map)*) ($($map)*)); + $crate::graphql::input_value!(@@object object () ($($map)*) ($($map)*)); object }) }; - (null$(,)?) => ($crate::InputValue::null()); + (null$(,)?) => ($crate::graphql::InputValue::null()); - (None$(,)?) => ($crate::InputValue::null()); + (None$(,)?) => ($crate::graphql::InputValue::null()); - (true$(,)?) => ($crate::InputValue::from(true)); + (true$(,)?) => ($crate::graphql::InputValue::from(true)); - (false$(,)?) => ($crate::InputValue::from(false)); + (false$(,)?) => ($crate::graphql::InputValue::from(false)); - (@$var:ident$(,)?) => ($crate::InputValue::variable(stringify!($var))); + (@$var:ident$(,)?) => ($crate::graphql::InputValue::variable(stringify!($var))); - ($enum:ident$(,)?) => ($crate::InputValue::enum_value(stringify!($enum))); + ($enum:ident$(,)?) => ($crate::graphql::InputValue::enum_value(stringify!($enum))); - (($e:expr)$(,)?) => ($crate::InputValue::from($e)); + (($e:expr)$(,)?) => ($crate::graphql::InputValue::from($e)); - ($e:expr$(,)?) => ($crate::InputValue::from($e)); + ($e:expr$(,)?) => ($crate::graphql::InputValue::from($e)); } +#[doc(inline)] +pub(super) use input_value; + #[cfg(test)] mod tests { use indexmap::{indexmap, IndexMap}; - type V = crate::InputValue; + use crate::graphql; + + use super::input_value; + + type V = graphql::InputValue; #[test] fn null() { - assert_eq!(graphql_input_value!(null), V::Null); + assert_eq!(input_value!(null), V::Null); } #[test] fn scalar() { let val = 42; - assert_eq!(graphql_input_value!(1), V::scalar(1)); - assert_eq!(graphql_input_value!("val"), V::scalar("val")); - assert_eq!(graphql_input_value!(1.34), V::scalar(1.34)); - assert_eq!(graphql_input_value!(false), V::scalar(false)); - assert_eq!(graphql_input_value!(1 + 2), V::scalar(3)); - assert_eq!(graphql_input_value!((val)), V::scalar(42)); + assert_eq!(input_value!(1), V::scalar(1)); + assert_eq!(input_value!("val"), V::scalar("val")); + assert_eq!(input_value!(1.34), V::scalar(1.34)); + assert_eq!(input_value!(false), V::scalar(false)); + assert_eq!(input_value!(1 + 2), V::scalar(3)); + assert_eq!(input_value!((val)), V::scalar(42)); } #[test] fn r#enum() { - assert_eq!(graphql_input_value!(ENUM), V::enum_value("ENUM")); - assert_eq!(graphql_input_value!(lowercase), V::enum_value("lowercase")); + assert_eq!(input_value!(ENUM), V::enum_value("ENUM")); + assert_eq!(input_value!(lowercase), V::enum_value("lowercase")); } #[test] fn variable() { - assert_eq!(graphql_input_value!(@var), V::variable("var")); - assert_eq!(graphql_input_value!(@array), V::variable("array")); - assert_eq!(graphql_input_value!(@object), V::variable("object")); + assert_eq!(input_value!(@var), V::variable("var")); + assert_eq!(input_value!(@array), V::variable("array")); + assert_eq!(input_value!(@object), V::variable("object")); } #[test] fn list() { let val = 42; - assert_eq!(graphql_input_value!([]), V::list(vec![])); + assert_eq!(input_value!([]), V::list(vec![])); - assert_eq!(graphql_input_value!([null]), V::list(vec![V::Null])); + assert_eq!(input_value!([null]), V::list(vec![V::Null])); - assert_eq!(graphql_input_value!([1]), V::list(vec![V::scalar(1)])); - assert_eq!(graphql_input_value!([1 + 2]), V::list(vec![V::scalar(3)])); - assert_eq!(graphql_input_value!([(val)]), V::list(vec![V::scalar(42)])); + assert_eq!(input_value!([1]), V::list(vec![V::scalar(1)])); + assert_eq!(input_value!([1 + 2]), V::list(vec![V::scalar(3)])); + assert_eq!(input_value!([(val)]), V::list(vec![V::scalar(42)])); + assert_eq!(input_value!([ENUM]), V::list(vec![V::enum_value("ENUM")])); assert_eq!( - graphql_input_value!([ENUM]), - V::list(vec![V::enum_value("ENUM")]), - ); - assert_eq!( - graphql_input_value!([lowercase]), + input_value!([lowercase]), V::list(vec![V::enum_value("lowercase")]), ); + assert_eq!(input_value!([@var]), V::list(vec![V::variable("var")]),); + assert_eq!(input_value!([@array]), V::list(vec![V::variable("array")])); assert_eq!( - graphql_input_value!([@var]), - V::list(vec![V::variable("var")]), - ); - assert_eq!( - graphql_input_value!([@array]), - V::list(vec![V::variable("array")]), - ); - assert_eq!( - graphql_input_value!([@object]), + input_value!([@object]), V::list(vec![V::variable("object")]), ); assert_eq!( - graphql_input_value!([1, [2], 3]), + input_value!([1, [2], 3]), V::list(vec![ V::scalar(1), V::list(vec![V::scalar(2)]), @@ -459,7 +454,7 @@ mod tests { ]), ); assert_eq!( - graphql_input_value!([1, [2 + 3], 3]), + input_value!([1, [2 + 3], 3]), V::list(vec![ V::scalar(1), V::list(vec![V::scalar(5)]), @@ -467,7 +462,7 @@ mod tests { ]), ); assert_eq!( - graphql_input_value!([1, [ENUM], (val)]), + input_value!([1, [ENUM], (val)]), V::list(vec![ V::scalar(1), V::list(vec![V::enum_value("ENUM")]), @@ -475,7 +470,7 @@ mod tests { ]), ); assert_eq!( - graphql_input_value!([1 + 2, [(val)], @val]), + input_value!([1 + 2, [(val)], @val]), V::list(vec![ V::scalar(3), V::list(vec![V::scalar(42)]), @@ -483,7 +478,7 @@ mod tests { ]), ); assert_eq!( - graphql_input_value!([1, [@val], ENUM]), + input_value!([1, [@val], ENUM]), V::list(vec![ V::scalar(1), V::list(vec![V::variable("val")]), @@ -495,68 +490,65 @@ mod tests { #[test] fn object() { let val = 42; - assert_eq!( - graphql_input_value!({}), - V::object(IndexMap::::new()), - ); + assert_eq!(input_value!({}), V::object(IndexMap::::new())); assert_eq!( - graphql_input_value!({ "key": null }), + input_value!({ "key": null }), V::object(indexmap! {"key" => V::Null}), ); assert_eq!( - graphql_input_value!({"key": 123}), + input_value!({"key": 123}), V::object(indexmap! {"key" => V::scalar(123)}), ); assert_eq!( - graphql_input_value!({"key": 1 + 2}), + input_value!({"key": 1 + 2}), V::object(indexmap! {"key" => V::scalar(3)}), ); assert_eq!( - graphql_input_value!({ "key": (val) }), + input_value!({ "key": (val) }), V::object(indexmap! {"key" => V::scalar(42)}), ); assert_eq!( - graphql_input_value!({"key": []}), + input_value!({"key": []}), V::object(indexmap! {"key" => V::list(vec![])}), ); assert_eq!( - graphql_input_value!({ "key": [null] }), + input_value!({ "key": [null] }), V::object(indexmap! {"key" => V::list(vec![V::Null])}), ); assert_eq!( - graphql_input_value!({"key": [1] }), + input_value!({"key": [1] }), V::object(indexmap! {"key" => V::list(vec![V::scalar(1)])}), ); assert_eq!( - graphql_input_value!({"key": [1 + 2] }), + input_value!({"key": [1 + 2] }), V::object(indexmap! {"key" => V::list(vec![V::scalar(3)])}), ); assert_eq!( - graphql_input_value!({ "key": [(val)] }), + input_value!({ "key": [(val)] }), V::object(indexmap! {"key" => V::list(vec![V::scalar(42)])}), ); assert_eq!( - graphql_input_value!({ "key": ENUM }), + input_value!({ "key": ENUM }), V::object(indexmap! {"key" => V::enum_value("ENUM")}), ); assert_eq!( - graphql_input_value!({ "key": lowercase }), + input_value!({ "key": lowercase }), V::object(indexmap! {"key" => V::enum_value("lowercase")}), ); assert_eq!( - graphql_input_value!({"key": @val}), + input_value!({"key": @val}), V::object(indexmap! {"key" => V::variable("val")}), ); assert_eq!( - graphql_input_value!({"key": @array }), + input_value!({"key": @array }), V::object(indexmap! {"key" => V::variable("array")}), ); assert_eq!( - graphql_input_value!({ + input_value!({ "inner": { "key1": (val), "key2": "val", @@ -606,8 +598,8 @@ mod tests { fn option() { let val = Some(42); - assert_eq!(graphql_input_value!(None), V::Null); - assert_eq!(graphql_input_value!(Some(42)), V::scalar(42)); - assert_eq!(graphql_input_value!((val)), V::scalar(42)); + assert_eq!(input_value!(None), V::Null); + assert_eq!(input_value!(Some(42)), V::scalar(42)); + assert_eq!(input_value!((val)), V::scalar(42)); } } diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 4bbcfb5f6..ebde9761e 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -6,9 +6,11 @@ pub mod helper; #[macro_use] pub mod reflect; -#[macro_use] -mod graphql_input_value; +mod input_value; #[macro_use] mod graphql_value; #[macro_use] mod graphql_vars; + +#[doc(inline)] +pub use self::input_value::input_value; From b28acbd96dbbfc665bcd87c47773da3cb99a9a91 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 7 Jun 2022 19:03:39 +0200 Subject: [PATCH 20/58] Refactor `value!` macro position [skip ci] --- juniper/src/graphql/mod.rs | 6 +- juniper/src/lib.rs | 2 +- juniper/src/macros/input_value.rs | 2 +- juniper/src/macros/mod.rs | 5 +- .../src/macros/{graphql_value.rs => value.rs} | 185 +++++++++--------- 5 files changed, 103 insertions(+), 97 deletions(-) rename juniper/src/macros/{graphql_value.rs => value.rs} (62%) diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 3deea79c7..5b02afb96 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -1,8 +1,10 @@ use crate::{behavior, resolve}; pub use crate::{ - ast::InputValue, graphql_value as value, macros::input_value, - resolve::Type, value::Value, + ast::InputValue, + macros::{input_value, value}, + resolve::Type, + value::Value, }; pub trait Interface diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index ea55733f8..70c514187 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -75,8 +75,8 @@ pub use crate::{ }, introspection::IntrospectionFormat, macros::{ - input_value as graphql_input_value, helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, + input_value as graphql_input_value, value as graphql_value, }, parser::{ParseError, ScalarToken, Spanning}, schema::{ diff --git a/juniper/src/macros/input_value.rs b/juniper/src/macros/input_value.rs index 0edc06002..627b4f634 100644 --- a/juniper/src/macros/input_value.rs +++ b/juniper/src/macros/input_value.rs @@ -7,7 +7,7 @@ /// - [`InputValue::Enum`] is constructed with `ident`, so to capture outer /// variable as [`InputValue::Scalar`] surround it with parens: `(var)`. /// ```rust -/// # use juniper::{graphql, graphql_value}; +/// # use juniper::graphql; /// # /// # type InputValue = graphql::InputValue; /// # type Value = graphql::Value; diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index ebde9761e..37bd0218d 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -7,10 +7,9 @@ pub mod helper; pub mod reflect; mod input_value; -#[macro_use] -mod graphql_value; +mod value; #[macro_use] mod graphql_vars; #[doc(inline)] -pub use self::input_value::input_value; +pub use self::{input_value::input_value, value::value}; diff --git a/juniper/src/macros/graphql_value.rs b/juniper/src/macros/value.rs similarity index 62% rename from juniper/src/macros/graphql_value.rs rename to juniper/src/macros/value.rs index cc4e34d38..c689afa68 100644 --- a/juniper/src/macros/graphql_value.rs +++ b/juniper/src/macros/value.rs @@ -1,19 +1,18 @@ -//! [`graphql_value!`] macro implementation. -//! -//! [`graphql_value!`]: graphql_value +//! [`value!`] macro implementation. -/// Constructs [`Value`]s via JSON-like syntax. +/// Constructs [`graphql::Value`]s via JSON-like syntax. /// -/// [`Value`] objects are used mostly when creating custom errors from fields. +/// [`graphql::Value`] objects are used mostly when creating custom errors from +/// fields. /// /// [`Value::Object`] key should implement [`AsRef`]`<`[`str`]`>`. /// ```rust -/// # use juniper::{graphql_value, Value}; +/// # use juniper::graphql; /// # /// let code = 200; /// let features = ["key", "value"]; /// -/// let value: Value = graphql_value!({ +/// let value: graphql::Value = graphql::value!({ /// "code": code, /// "success": code == 200, /// "payload": { @@ -26,94 +25,93 @@ /// /// Resulting JSON will look just like what you passed in. /// ```rust -/// # use juniper::{graphql_value, DefaultScalarValue, Value}; +/// # use juniper::graphql; /// # -/// # type V = Value; +/// # type V = graphql::Value; /// # /// # let _: V = -/// graphql_value!(null); +/// graphql::value!(null); /// # let _: V = -/// graphql_value!(1234); +/// graphql::value!(1234); /// # let _: V = -/// graphql_value!("test"); +/// graphql::value!("test"); /// # let _: V = -/// graphql_value!([1234, "test", true]); +/// graphql::value!([1234, "test", true]); /// # let _: V = -/// graphql_value!({"key": "value", "foo": 1234}); +/// graphql::value!({"key": "value", "foo": 1234}); /// ``` /// -/// [`Value`]: crate::Value -/// [`Value::Object`]: crate::Value::Object -#[macro_export] -macro_rules! graphql_value { +/// [`graphql::Value`]: crate::graphql::Value +/// [`Value::Object`]: crate::graphql::Value::Object +macro_rules! value { /////////// // Array // /////////// // Done with trailing comma. (@array [$($elems:expr,)*]) => { - $crate::Value::list(vec![ + $crate::graphql::Value::list(::std::vec![ $( $elems, )* ]) }; // Done without trailing comma. (@array [$($elems:expr),*]) => { - $crate::Value::list(vec![ - $( $crate::graphql_value!($elems), )* + $crate::graphql::Value::list(::std::vec![ + $( $crate::graphql::value!($elems), )* ]) }; // Next element is `null`. (@array [$($elems:expr,)*] null $($rest:tt)*) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!(null)] $($rest)* + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!(null)] $($rest)* ) }; // Next element is `None`. (@array [$($elems:expr,)*] None $($rest:tt)*) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!(None)] $($rest)* + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!(None)] $($rest)* ) }; // Next element is an array. (@array [$($elems:expr,)*] [$($array:tt)*] $($rest:tt)*) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!([$($array)*])] $($rest)* + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!([$($array)*])] $($rest)* ) }; // Next element is a map. (@array [$($elems:expr,)*] {$($map:tt)*} $($rest:tt)*) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!({$($map)*})] $($rest)* + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!({$($map)*})] $($rest)* ) }; // Next element is an expression followed by comma. (@array [$($elems:expr,)*] $next:expr, $($rest:tt)*) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!($next),] $($rest)* + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!($next),] $($rest)* ) }; // Last element is an expression with no trailing comma. (@array [$($elems:expr,)*] $last:expr) => { - $crate::graphql_value!( - @array [$($elems,)* $crate::graphql_value!($last)] + $crate::graphql::value!( + @array [$($elems,)* $crate::graphql::value!($last)] ) }; // Comma after the most recent element. (@array [$($elems:expr),*] , $($rest:tt)*) => { - $crate::graphql_value!(@array [$($elems,)*] $($rest)*) + $crate::graphql::value!(@array [$($elems,)*] $($rest)*) }; // Unexpected token after most recent element. (@array [$($elems:expr),*] $unexpected:tt $($rest:tt)*) => { - $crate::graphql_value!(@unexpected $unexpected) + $crate::graphql::value!(@unexpected $unexpected) }; //////////// @@ -126,12 +124,12 @@ macro_rules! graphql_value { // Insert the current entry followed by trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { let _ = $object.add_field(($($key)+), $value); - $crate::graphql_value!(@object $object () ($($rest)*) ($($rest)*)); + $crate::graphql::value!(@object $object () ($($rest)*) ($($rest)*)); }; // Current entry followed by unexpected token. (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { - $crate::graphql_value!(@unexpected $unexpected); + $crate::graphql::value!(@unexpected $unexpected); }; // Insert the last entry without trailing comma. @@ -141,97 +139,97 @@ macro_rules! graphql_value { // Next value is `null`. (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!(null)) $($rest)* + ($crate::graphql::value!(null)) $($rest)* ); }; // Next value is `None`. (@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!(None)) $($rest)* + ($crate::graphql::value!(None)) $($rest)* ); }; // Next value is an array. (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!([$($array)*])) $($rest)* + ($crate::graphql::value!([$($array)*])) $($rest)* ); }; // Next value is a map. (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!({$($map)*})) $($rest)* + ($crate::graphql::value!({$($map)*})) $($rest)* ); }; // Next value is an expression followed by comma. (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!($value)) , $($rest)* + ($crate::graphql::value!($value)) , $($rest)* ); }; // Last value is an expression with no trailing comma. (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object [$($key)+] - ($crate::graphql_value!($value)) + ($crate::graphql::value!($value)) ); }; // Missing value for last entry. Trigger a reasonable error message. (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_value!(); + $crate::graphql::value!(); }; // Missing colon and value for last entry. Trigger a reasonable error // message. (@object $object:ident ($($key:tt)+) () $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_value!(); + $crate::graphql::value!(); }; // Misplaced colon. Trigger a reasonable error message. (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `:`". - $crate::graphql_value!(@unexpected $colon); + $crate::graphql::value!(@unexpected $colon); }; // Found a comma inside a key. Trigger a reasonable error message. (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `,`". - $crate::graphql_value!(@unexpected $comma); + $crate::graphql::value!(@unexpected $comma); }; // Key is fully parenthesized. This avoids `clippy::double_parens` false // positives because the parenthesization may be necessary here. (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!(@object $object ($key) (: $($rest)*) (: $($rest)*)); + $crate::graphql::value!(@object $object ($key) (: $($rest)*) (: $($rest)*)); }; // Refuse to absorb colon token into key expression. (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { - $crate::graphql_value!(@unexpected $($unexpected)+); + $crate::graphql::value!(@unexpected $($unexpected)+); }; // Munch a token into the current key. (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { - $crate::graphql_value!( + $crate::graphql::value!( @object $object ($($key)* $tt) ($($rest)*) ($($rest)*) @@ -249,63 +247,70 @@ macro_rules! graphql_value { ////////////// ([ $($arr:tt)* ]$(,)?) => { - $crate::graphql_value!(@array [] $($arr)*) + $crate::graphql::value!(@array [] $($arr)*) }; ({}$(,)?) => { - $crate::Value::object($crate::Object::with_capacity(0)) + $crate::graphql::Value::object($crate::Object::with_capacity(0)) }; ({ $($map:tt)+ }$(,)?) => { - $crate::Value::object({ + $crate::graphql::Value::object({ let mut object = $crate::Object::with_capacity(0); - $crate::graphql_value!(@object object () ($($map)*) ($($map)*)); + $crate::graphql::value!(@object object () ($($map)*) ($($map)*)); object }) }; - (null$(,)?) => ($crate::Value::null()); + (null$(,)?) => ($crate::graphql::Value::null()); - (None$(,)?) => ($crate::Value::null()); + (None$(,)?) => ($crate::graphql::Value::null()); - ($e:expr$(,)?) => ($crate::Value::from($e)); + ($e:expr$(,)?) => ($crate::graphql::Value::from($e)); } +#[doc(inline)] +pub(super) use value; + #[cfg(test)] mod tests { - type V = crate::Value; + use crate::graphql; + + use super::value; + + type V = graphql::Value; #[test] fn null() { - assert_eq!(graphql_value!(null), V::Null); + assert_eq!(value!(null), V::Null); } #[test] fn scalar() { let val = 42; - assert_eq!(graphql_value!(1), V::scalar(1)); - assert_eq!(graphql_value!("val"), V::scalar("val")); - assert_eq!(graphql_value!(1.34), V::scalar(1.34)); - assert_eq!(graphql_value!(false), V::scalar(false)); - assert_eq!(graphql_value!(1 + 2), V::scalar(3)); - assert_eq!(graphql_value!(val), V::scalar(42)); + assert_eq!(value!(1), V::scalar(1)); + assert_eq!(value!("val"), V::scalar("val")); + assert_eq!(value!(1.34), V::scalar(1.34)); + assert_eq!(value!(false), V::scalar(false)); + assert_eq!(value!(1 + 2), V::scalar(3)); + assert_eq!(value!(val), V::scalar(42)); } #[test] fn list() { let val = 42; - assert_eq!(graphql_value!([]), V::list(vec![])); + assert_eq!(value!([]), V::list(vec![])); - assert_eq!(graphql_value!([null]), V::list(vec![V::Null])); + assert_eq!(value!([null]), V::list(vec![V::Null])); - assert_eq!(graphql_value!([1]), V::list(vec![V::scalar(1)])); - assert_eq!(graphql_value!([1 + 2]), V::list(vec![V::scalar(3)])); - assert_eq!(graphql_value!([val]), V::list(vec![V::scalar(42)])); + assert_eq!(value!([1]), V::list(vec![V::scalar(1)])); + assert_eq!(value!([1 + 2]), V::list(vec![V::scalar(3)])); + assert_eq!(value!([val]), V::list(vec![V::scalar(42)])); assert_eq!( - graphql_value!([1, [2], 3]), + value!([1, [2], 3]), V::list(vec![ V::scalar(1), V::list(vec![V::scalar(2)]), @@ -313,7 +318,7 @@ mod tests { ]), ); assert_eq!( - graphql_value!(["string", [2 + 3], true]), + value!(["string", [2 + 3], true]), V::list(vec![ V::scalar("string"), V::list(vec![V::scalar(5)]), @@ -327,31 +332,31 @@ mod tests { let val = 42; assert_eq!( - graphql_value!({}), + value!({}), V::object(Vec::<(String, _)>::new().into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": null }), + value!({ "key": null }), V::object(vec![("key", V::Null)].into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": 123 }), + value!({ "key": 123 }), V::object(vec![("key", V::scalar(123))].into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": 1 + 2 }), + value!({ "key": 1 + 2 }), V::object(vec![("key", V::scalar(3))].into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": [] }), + value!({ "key": [] }), V::object(vec![("key", V::list(vec![]))].into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": [null] }), + value!({ "key": [null] }), V::object(vec![("key", V::list(vec![V::Null]))].into_iter().collect()), ); assert_eq!( - graphql_value!({ "key": [1] }), + value!({ "key": [1] }), V::object( vec![("key", V::list(vec![V::scalar(1)]))] .into_iter() @@ -359,7 +364,7 @@ mod tests { ), ); assert_eq!( - graphql_value!({ "key": [1 + 2] }), + value!({ "key": [1 + 2] }), V::object( vec![("key", V::list(vec![V::scalar(3)]))] .into_iter() @@ -367,7 +372,7 @@ mod tests { ), ); assert_eq!( - graphql_value!({ "key": [val] }), + value!({ "key": [val] }), V::object( vec![("key", V::list(vec![V::scalar(42)]))] .into_iter() @@ -380,8 +385,8 @@ mod tests { fn option() { let val = Some(42); - assert_eq!(graphql_value!(None), V::Null); - assert_eq!(graphql_value!(Some(42)), V::scalar(42)); - assert_eq!(graphql_value!(val), V::scalar(42)); + assert_eq!(value!(None), V::Null); + assert_eq!(value!(Some(42)), V::scalar(42)); + assert_eq!(value!(val), V::scalar(42)); } } From e347f25718aefeb2e4a10416664f45c769930081 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 8 Jun 2022 10:50:47 +0200 Subject: [PATCH 21/58] Refactor `vars!` macro position [skip ci] --- juniper/src/graphql/mod.rs | 3 +- juniper/src/lib.rs | 2 +- juniper/src/macros/mod.rs | 5 +- .../src/macros/{graphql_vars.rs => vars.rs} | 158 +++++++++--------- 4 files changed, 86 insertions(+), 82 deletions(-) rename juniper/src/macros/{graphql_vars.rs => vars.rs} (81%) diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 5b02afb96..1dfe22348 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -2,9 +2,10 @@ use crate::{behavior, resolve}; pub use crate::{ ast::InputValue, - macros::{input_value, value}, + macros::{input_value, value, vars}, resolve::Type, value::Value, + executor::Variables, }; pub trait Interface diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 70c514187..c612b27ae 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -76,7 +76,7 @@ pub use crate::{ introspection::IntrospectionFormat, macros::{ helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, - input_value as graphql_input_value, value as graphql_value, + input_value as graphql_input_value, value as graphql_value, vars as graphql_vars, }, parser::{ParseError, ScalarToken, Spanning}, schema::{ diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 37bd0218d..9e60986e6 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -8,8 +8,7 @@ pub mod reflect; mod input_value; mod value; -#[macro_use] -mod graphql_vars; +mod vars; #[doc(inline)] -pub use self::{input_value::input_value, value::value}; +pub use self::{input_value::input_value, value::value, vars::vars}; diff --git a/juniper/src/macros/graphql_vars.rs b/juniper/src/macros/vars.rs similarity index 81% rename from juniper/src/macros/graphql_vars.rs rename to juniper/src/macros/vars.rs index 56bff3b3c..d4b75e82b 100644 --- a/juniper/src/macros/graphql_vars.rs +++ b/juniper/src/macros/vars.rs @@ -1,20 +1,18 @@ -//! [`graphql_vars!`] macro implementation. -//! -//! [`graphql_vars!`]: graphql_vars +//! [`vars!`] macro implementation. -/// Constructs [`Variables`] via JSON-like syntax. +/// Constructs [`graphql::Variables`] via JSON-like syntax. /// -/// [`Variables`] key should implement [`Into`]`<`[`String`]`>`. +/// [`graphql::Variables`] key should implement [`Into`]`<`[`String`]`>`. /// ```rust /// # use std::borrow::Cow; /// # -/// # use juniper::{graphql_vars, Variables}; +/// # use juniper::graphql; /// # /// let code = 200; /// let features = vec!["key", "value"]; /// let key: Cow<'static, str> = "key".into(); /// -/// let value: Variables = graphql_vars! { +/// let value: graphql::Variables = graphql::vars! { /// "code": code, /// "success": code == 200, /// features[0]: features[1], @@ -22,12 +20,11 @@ /// }; /// ``` /// -/// See [`graphql_input_value!`] for more info on syntax of value after `:`. +/// See [`graphql::input_value!`] for more info on syntax of value after `:`. /// -/// [`graphql_input_value!`]: crate::graphql_input_value -/// [`Variables`]: crate::Variables -#[macro_export] -macro_rules! graphql_vars { +/// [`graphql::input_value!`]: crate::graphql::input_value +/// [`graphql::Variables`]: crate::graphql::Variables +macro_rules! vars { //////////// // Object // //////////// @@ -38,12 +35,12 @@ macro_rules! graphql_vars { // Insert the current entry followed by trailing comma. (@object $object:ident [$($key:tt)+] ($value:expr) , $($rest:tt)*) => { let _ = $object.insert(($($key)+).into(), $value); - $crate::graphql_vars! {@object $object () ($($rest)*) ($($rest)*)}; + $crate::graphql::vars! {@object $object () ($($rest)*) ($($rest)*)}; }; // Current entry followed by unexpected token. (@object $object:ident [$($key:tt)+] ($value:expr) $unexpected:tt $($rest:tt)*) => { - $crate::graphql_vars! {@unexpected $unexpected}; + $crate::graphql::vars! {@unexpected $unexpected}; }; // Insert the last entry without trailing comma. @@ -53,7 +50,7 @@ macro_rules! graphql_vars { // Next value is `null`. (@object $object:ident ($($key:tt)+) (: null $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!(null)) $($rest)* @@ -62,7 +59,7 @@ macro_rules! graphql_vars { // Next value is `None`. (@object $object:ident ($($key:tt)+) (: None $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!(None)) $($rest)* @@ -71,7 +68,7 @@ macro_rules! graphql_vars { // Next value is a variable. (@object $object:ident ($($key:tt)+) (: @$var:ident $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!(@$var)) $($rest)* @@ -80,7 +77,7 @@ macro_rules! graphql_vars { // Next value is an array. (@object $object:ident ($($key:tt)+) (: [$($array:tt)*] $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!([$($array)*])) $($rest)* @@ -89,7 +86,7 @@ macro_rules! graphql_vars { // Next value is a map. (@object $object:ident ($($key:tt)+) (: {$($map:tt)*} $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!({$($map)*})) $($rest)* @@ -98,7 +95,7 @@ macro_rules! graphql_vars { // Next value is `true`, `false` or enum ident followed by a comma. (@object $object:ident ($($key:tt)+) (: $ident:ident , $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!($ident)) , $($rest)* @@ -107,7 +104,7 @@ macro_rules! graphql_vars { // Next value is `true`, `false` or enum ident without trailing comma. (@object $object:ident ($($key:tt)+) (: $last:ident ) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!($last)) @@ -116,7 +113,7 @@ macro_rules! graphql_vars { // Next value is an expression followed by comma. (@object $object:ident ($($key:tt)+) (: $value:expr , $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!($value)) , $($rest)* @@ -125,7 +122,7 @@ macro_rules! graphql_vars { // Last value is an expression with no trailing comma. (@object $object:ident ($($key:tt)+) (: $value:expr) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object [$($key)+] ($crate::graphql_input_value!($value)) @@ -135,44 +132,44 @@ macro_rules! graphql_vars { // Missing value for last entry. Trigger a reasonable error message. (@object $object:ident ($($key:tt)+) (:) $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_vars! {}; + $crate::graphql::vars! {}; }; // Missing colon and value for last entry. Trigger a reasonable error // message. (@object $object:ident ($($key:tt)+) () $copy:tt) => { // "unexpected end of macro invocation" - $crate::graphql_vars! {}; + $crate::graphql::vars! {}; }; // Misplaced colon. Trigger a reasonable error message. (@object $object:ident () (: $($rest:tt)*) ($colon:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `:`". - $crate::graphql_vars! {@unexpected $colon}; + $crate::graphql::vars! {@unexpected $colon}; }; // Found a comma inside a key. Trigger a reasonable error message. (@object $object:ident ($($key:tt)*) (, $($rest:tt)*) ($comma:tt $($copy:tt)*)) => { // Takes no arguments so "no rules expected the token `,`". - $crate::graphql_vars! {@unexpected $comma}; + $crate::graphql::vars! {@unexpected $comma}; }; // Key is fully parenthesized. This avoids clippy double_parens false // positives because the parenthesization may be necessary here. (@object $object:ident () (($key:expr) : $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object ($key) (: $($rest)*) (: $($rest)*) }; }; // Refuse to absorb colon token into key expression. (@object $object:ident ($($key:tt)*) (: $($unexpected:tt)+) $copy:tt) => { - $crate::graphql_vars! {@unexpected $($unexpected)+}; + $crate::graphql::vars! {@unexpected $($unexpected)+}; }; // Munch a token into the current key. (@object $object:ident ($($key:tt)*) ($tt:tt $($rest:tt)*) $copy:tt) => { - $crate::graphql_vars! { + $crate::graphql::vars! { @object $object ($($key)* $tt) ($($rest)*) ($($rest)*) @@ -189,26 +186,33 @@ macro_rules! graphql_vars { // Defaults // ////////////// - () => {{ $crate::Variables::<_>::new() }}; + () => {{ $crate::graphql::Variables::<_>::new() }}; ( $($map:tt)+ ) => {{ - let mut object = $crate::Variables::<_>::new(); - $crate::graphql_vars! {@object object () ($($map)*) ($($map)*)}; + let mut object = $crate::graphql::Variables::<_>::new(); + $crate::graphql::vars! {@object object () ($($map)*) ($($map)*)}; object }}; } +#[doc(inline)] +pub(super) use vars; + #[cfg(test)] mod tests { use indexmap::{indexmap, IndexMap}; - type V = crate::Variables; + use crate::graphql; + + use super::vars; + + type V = graphql::Variables; - type IV = crate::InputValue; + type IV = graphql::InputValue; #[test] fn empty() { - assert_eq!(graphql_vars! {}, V::new()); + assert_eq!(vars! {}, V::new()); } #[test] @@ -216,37 +220,37 @@ mod tests { let val = 42; assert_eq!( - graphql_vars! {"key": 123}, + vars! {"key": 123}, vec![("key".to_owned(), IV::scalar(123))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": "val"}, + vars! {"key": "val"}, vec![("key".to_owned(), IV::scalar("val"))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": 1.23}, + vars! {"key": 1.23}, vec![("key".to_owned(), IV::scalar(1.23))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": 1 + 2}, + vars! {"key": 1 + 2}, vec![("key".to_owned(), IV::scalar(3))] .into_iter() .collect(), ); assert_eq!( - graphql_vars! {"key": false}, + vars! {"key": false}, vec![("key".to_owned(), IV::scalar(false))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": (val)}, + vars! {"key": (val)}, vec![("key".to_owned(), IV::scalar(42))] .into_iter() .collect::(), @@ -256,13 +260,13 @@ mod tests { #[test] fn r#enum() { assert_eq!( - graphql_vars! {"key": ENUM}, + vars! {"key": ENUM}, vec![("key".to_owned(), IV::enum_value("ENUM"))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": lowercase}, + vars! {"key": lowercase}, vec![("key".to_owned(), IV::enum_value("lowercase"))] .into_iter() .collect::(), @@ -272,19 +276,19 @@ mod tests { #[test] fn variable() { assert_eq!( - graphql_vars! {"key": @var}, + vars! {"key": @var}, vec![("key".to_owned(), IV::variable("var"))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": @array}, + vars! {"key": @array}, vec![("key".to_owned(), IV::variable("array"))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": @object}, + vars! {"key": @object}, vec![("key".to_owned(), IV::variable("object"))] .into_iter() .collect::(), @@ -296,46 +300,46 @@ mod tests { let val = 42; assert_eq!( - graphql_vars! {"key": []}, + vars! {"key": []}, vec![("key".to_owned(), IV::list(vec![]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [null]}, + vars! {"key": [null]}, vec![("key".to_owned(), IV::list(vec![IV::Null]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [1]}, + vars! {"key": [1]}, vec![("key".to_owned(), IV::list(vec![IV::scalar(1)]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [1 + 2]}, + vars! {"key": [1 + 2]}, vec![("key".to_owned(), IV::list(vec![IV::scalar(3)]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [(val)]}, + vars! {"key": [(val)]}, vec![("key".to_owned(), IV::list(vec![IV::scalar(42)]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [ENUM]}, + vars! {"key": [ENUM]}, vec![("key".to_owned(), IV::list(vec![IV::enum_value("ENUM")]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [lowercase]}, + vars! {"key": [lowercase]}, vec![( "key".to_owned(), IV::list(vec![IV::enum_value("lowercase")]) @@ -345,26 +349,26 @@ mod tests { ); assert_eq!( - graphql_vars! {"key": [@var]}, + vars! {"key": [@var]}, vec![("key".to_owned(), IV::list(vec![IV::variable("var")]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [@array]}, + vars! {"key": [@array]}, vec![("key".to_owned(), IV::list(vec![IV::variable("array")]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [@object]}, + vars! {"key": [@object]}, vec![("key".to_owned(), IV::list(vec![IV::variable("object")]))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": [1, [2], 3]}, + vars! {"key": [1, [2], 3]}, vec![( "key".to_owned(), IV::list(vec![ @@ -377,7 +381,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": [1, [2 + 3], 3]}, + vars! {"key": [1, [2 + 3], 3]}, vec![( "key".to_owned(), IV::list(vec![ @@ -390,7 +394,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": [1, [ENUM], (val)]}, + vars! {"key": [1, [ENUM], (val)]}, vec![( "key".to_owned(), IV::list(vec![ @@ -403,7 +407,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": [1 + 2, [(val)], @val]}, + vars! {"key": [1 + 2, [(val)], @val]}, vec![( "key".to_owned(), IV::list(vec![ @@ -416,7 +420,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": [1, [@val], ENUM]}, + vars! {"key": [1, [@val], ENUM]}, vec![( "key".to_owned(), IV::list(vec![ @@ -435,21 +439,21 @@ mod tests { let val = 42; assert_eq!( - graphql_vars! {"key": {}}, + vars! {"key": {}}, vec![("key".to_owned(), IV::object(IndexMap::::new()))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": null}}, + vars! {"key": {"key": null}}, vec![("key".to_owned(), IV::object(indexmap! {"key" => IV::Null}))] .into_iter() .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": 123}}, + vars! {"key": {"key": 123}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::scalar(123)}), @@ -458,7 +462,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": 1 + 2}}, + vars! {"key": {"key": 1 + 2}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::scalar(3)}), @@ -467,7 +471,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": (val)}}, + vars! {"key": {"key": (val)}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::scalar(42)}), @@ -477,7 +481,7 @@ mod tests { ); assert_eq!( - graphql_vars! {"key": {"key": []}}, + vars! {"key": {"key": []}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::list(vec![])}), @@ -486,7 +490,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": [null]}}, + vars! {"key": {"key": [null]}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::list(vec![IV::Null])}), @@ -495,7 +499,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": [1]}}, + vars! {"key": {"key": [1]}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(1)])}), @@ -504,7 +508,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": [1 + 2]}}, + vars! {"key": {"key": [1 + 2]}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(3)])}), @@ -513,7 +517,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": [(val)]}}, + vars! {"key": {"key": [(val)]}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::list(vec![IV::scalar(42)])}), @@ -522,7 +526,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": ENUM}}, + vars! {"key": {"key": ENUM}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::enum_value("ENUM")}), @@ -531,7 +535,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": lowercase}}, + vars! {"key": {"key": lowercase}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::enum_value("lowercase")}), @@ -540,7 +544,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": @val}}, + vars! {"key": {"key": @val}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::variable("val")}), @@ -549,7 +553,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! {"key": {"key": @array}}, + vars! {"key": {"key": @array}}, vec![( "key".to_owned(), IV::object(indexmap! {"key" => IV::variable("array")}), @@ -558,7 +562,7 @@ mod tests { .collect::(), ); assert_eq!( - graphql_vars! { + vars! { "inner": { "key1": (val), "key2": "val", From 5a62ddfbf224b6a0ee3711e2b42a15b887e7f5e7 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 8 Jun 2022 11:06:28 +0200 Subject: [PATCH 22/58] Restore old reflection impls --- juniper/src/macros/reflect.rs | 122 +++++++++++++++++++++- juniper/src/types/nullable.rs | 6 +- juniper/src/types/scalars.rs | 13 +++ juniper_codegen/src/graphql_scalar/mod.rs | 59 +++++++++-- 4 files changed, 187 insertions(+), 13 deletions(-) diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index b24ba4f58..e67762df0 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -7,7 +7,7 @@ use crate::{ can_be_subtype, fnv1a128, str_eq, str_exists_in_arr, type_len_with_wrapped_val, wrap, Argument, Arguments, FieldName, Name, Names, Type, Types, WrappedValue, }, - Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue, + Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue, Nullable, }; /// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`]. @@ -31,6 +31,10 @@ pub trait BaseType { const NAME: Type; } +impl<'a, S, T: BaseType + ?Sized> BaseType for &'a T { + const NAME: Type = T::NAME; +} + impl<'ctx, S, T> BaseType for (&'ctx T::Context, T) where S: ScalarValue, @@ -39,6 +43,42 @@ where const NAME: Type = T::NAME; } +impl> BaseType for Option { + const NAME: Type = T::NAME; +} + +impl> BaseType for Nullable { + const NAME: Type = T::NAME; +} + +impl, E> BaseType for Result { + const NAME: Type = T::NAME; +} + +impl> BaseType for Vec { + const NAME: Type = T::NAME; +} + +impl> BaseType for [T] { + const NAME: Type = T::NAME; +} + +impl, const N: usize> BaseType for [T; N] { + const NAME: Type = T::NAME; +} + +impl + ?Sized> BaseType for Box { + const NAME: Type = T::NAME; +} + +impl + ?Sized> BaseType for Arc { + const NAME: Type = T::NAME; +} + +impl + ?Sized> BaseType for Rc { + const NAME: Type = T::NAME; +} + /// [Sub-types][2] of a [GraphQL object][1]. /// /// This trait is transparent to [`Option`], [`Vec`] and other containers. @@ -52,6 +92,10 @@ pub trait BaseSubTypes { const NAMES: Types; } +impl<'a, S, T: BaseSubTypes + ?Sized> BaseSubTypes for &'a T { + const NAMES: Types = T::NAMES; +} + impl<'ctx, S, T> BaseSubTypes for (&'ctx T::Context, T) where S: ScalarValue, @@ -60,6 +104,42 @@ where const NAMES: Types = T::NAMES; } +impl> BaseSubTypes for Option { + const NAMES: Types = T::NAMES; +} + +impl> BaseSubTypes for Nullable { + const NAMES: Types = T::NAMES; +} + +impl, E> BaseSubTypes for Result { + const NAMES: Types = T::NAMES; +} + +impl> BaseSubTypes for Vec { + const NAMES: Types = T::NAMES; +} + +impl> BaseSubTypes for [T] { + const NAMES: Types = T::NAMES; +} + +impl, const N: usize> BaseSubTypes for [T; N] { + const NAMES: Types = T::NAMES; +} + +impl + ?Sized> BaseSubTypes for Box { + const NAMES: Types = T::NAMES; +} + +impl + ?Sized> BaseSubTypes for Arc { + const NAMES: Types = T::NAMES; +} + +impl + ?Sized> BaseSubTypes for Rc { + const NAMES: Types = T::NAMES; +} + // TODO: Just use `&str`s once they're allowed in `const` generics. /// Encoding of a composed GraphQL type in numbers. /// @@ -117,6 +197,46 @@ where const VALUE: u128 = T::VALUE; } +impl> WrappedType for Option { + const VALUE: u128 = T::VALUE * 10 + 2; +} + +impl> WrappedType for Nullable { + const VALUE: u128 = T::VALUE * 10 + 2; +} + +impl, E> WrappedType for Result { + const VALUE: u128 = T::VALUE; +} + +impl> WrappedType for Vec { + const VALUE: u128 = T::VALUE * 10 + 3; +} + +impl> WrappedType for [T] { + const VALUE: u128 = T::VALUE * 10 + 3; +} + +impl, const N: usize> WrappedType for [T; N] { + const VALUE: u128 = T::VALUE * 10 + 3; +} + +impl<'a, S, T: WrappedType + ?Sized> WrappedType for &'a T { + const VALUE: u128 = T::VALUE; +} + +impl + ?Sized> WrappedType for Box { + const VALUE: u128 = T::VALUE; +} + +impl + ?Sized> WrappedType for Arc { + const VALUE: u128 = T::VALUE; +} + +impl + ?Sized> WrappedType for Rc { + const VALUE: u128 = T::VALUE; +} + /// [GraphQL object][1] or [interface][2] [field arguments][3] [`Names`]. /// /// [1]: https://spec.graphql.org/October2021#sec-Objects diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index 04165f3bf..daec60440 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -244,7 +244,7 @@ impl Nullable<&T> { /// Maps this `Nullable<&T>` to a `Nullable` by [`Copy`]ing the contents /// of this [`Nullable`]. pub fn copied(self) -> Nullable { - self.map(|&t| t) + self.map(|t| *t) } } @@ -252,7 +252,7 @@ impl Nullable<&mut T> { /// Maps this `Nullable<&mut T>` to a `Nullable` by [`Copy`]ing the /// contents of this [`Nullable`]. pub fn copied(self) -> Nullable { - self.map(|&mut t| t) + self.map(|t| *t) } } @@ -260,7 +260,7 @@ impl Nullable<&T> { /// Maps this `Nullable<&T>` to a `Nullable` by [`Clone`]ing the contents /// of this [`Nullable`]. pub fn cloned(self) -> Nullable { - self.map(|t| t.clone()) + self.map(T::clone) } } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 0f8bb4089..66e26e74f 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -8,6 +8,7 @@ use crate::{ ast::{InputValue, Selection, ToInputValue}, executor::{ExecutionResult, Executor, Registry}, graphql_scalar, + macros::reflect, parser::{LexerError, ParseError, ScalarToken, Token}, schema::meta::MetaType, types::{ @@ -195,6 +196,18 @@ where }) } +impl reflect::WrappedType for str { + const VALUE: reflect::WrappedValue = 1; +} + +impl reflect::BaseType for str { + const NAME: reflect::Type = "String"; +} + +impl reflect::BaseSubTypes for str { + const NAMES: reflect::Types = &[>::NAME]; +} + impl GraphQLType for str where S: ScalarValue, diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 9e6e49cff..5a706851a 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -329,17 +329,19 @@ impl ToTokens for Definition { self.impl_to_input_value_tokens().to_tokens(into); self.impl_from_input_value_tokens().to_tokens(into); 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_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_graphql_input_and_output_type().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_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); } } @@ -850,6 +852,45 @@ impl Definition { } } + /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and + /// [`WrappedType`] traits for this [GraphQL scalar][1]. + /// + /// [`BaseSubTypes`]: juniper::macros::reflection::BaseSubTypes + /// [`BaseType`]: juniper::macros::reflection::BaseType + /// [`WrappedType`]: juniper::macros::reflection::WrappedType + /// [1]: https://spec.graphql.org/October2021#sec-Scalars + fn impl_reflection_traits_tokens(&self) -> TokenStream { + let scalar = &self.scalar; + let name = &self.name; + + let (ty, generics) = self.impl_self_and_generics(false); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::macros::reflect::BaseType<#scalar> for #ty + #where_clause + { + const NAME: ::juniper::macros::reflect::Type = #name; + } + + #[automatically_derived] + impl#impl_gens ::juniper::macros::reflect::BaseSubTypes<#scalar> for #ty + #where_clause + { + const NAMES: ::juniper::macros::reflect::Types = + &[>::NAME]; + } + + #[automatically_derived] + impl#impl_gens ::juniper::macros::reflect::WrappedType<#scalar> for #ty + #where_clause + { + const VALUE: ::juniper::macros::reflect::WrappedValue = 1; + } + } + } + /// Returns generated code implementing [`reflect::BaseType`], /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this /// [GraphQL scalar][0]. From 017e87c79110cb18c97f633f372b8533e1436981 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 8 Jun 2022 19:17:46 +0200 Subject: [PATCH 23/58] Reimpl renewed traits machinery for `Box` [skip ci] --- juniper/src/executor/mod.rs | 3 +- juniper/src/graphql/mod.rs | 20 +- juniper/src/lib.rs | 5 +- ...{input_value.rs => graphql_input_value.rs} | 5 +- .../src/macros/{value.rs => graphql_value.rs} | 5 +- .../src/macros/{vars.rs => graphql_vars.rs} | 5 +- juniper/src/macros/mod.rs | 8 +- juniper/src/macros/reflect.rs | 13 +- juniper/src/reflect/mod.rs | 34 ++- juniper/src/resolve/mod.rs | 16 +- juniper/src/schema/meta.rs | 5 +- juniper/src/types/arc.rs | 2 + juniper/src/types/array.rs | 2 + juniper/src/types/box.rs | 268 +++++++++--------- juniper/src/types/iter.rs | 2 + juniper/src/types/nullable.rs | 2 + juniper/src/types/option.rs | 3 +- juniper/src/types/rc.rs | 2 + juniper/src/types/ref.rs | 2 + juniper/src/types/ref_mut.rs | 2 + juniper/src/types/result.rs | 2 + juniper/src/types/slice.rs | 5 +- juniper/src/types/str.rs | 2 + juniper/src/types/vec.rs | 6 +- 24 files changed, 230 insertions(+), 189 deletions(-) rename juniper/src/macros/{input_value.rs => graphql_input_value.rs} (99%) rename juniper/src/macros/{value.rs => graphql_value.rs} (99%) rename juniper/src/macros/{vars.rs => graphql_vars.rs} (99%) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 0dc3118b1..7603acb18 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1294,6 +1294,7 @@ impl<'r, S: 'r> Registry<'r, S> { ScalarMeta::new::(Cow::Owned(name.to_string())) } + /* /// Builds a [`ScalarMeta`] information for the specified [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type @@ -1317,7 +1318,7 @@ impl<'r, S: 'r> Registry<'r, S> { { // TODO: Allow using references. ScalarMeta::new_unsized::(T::type_name(info).to_owned()) - } + }*/ /// Creates a [`ListMeta`] type. /// diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 1dfe22348..8fa9391a0 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -8,8 +8,8 @@ pub use crate::{ executor::Variables, }; -pub trait Interface -/*: OutputType +/* +pub trait Interface: OutputType + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value @@ -18,25 +18,20 @@ pub trait Interface + resolve::ConcreteValueAsync + resolve::Field + resolve::FieldAsync - -*/ { fn assert_interface(); } -pub trait Object -/*: OutputType +pub trait Object: OutputType + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value + resolve::ValueAsync + resolve::Field + resolve::FieldAsync - -*/ { fn assert_object(); -} +}*/ pub trait Scalar< 'inp, @@ -52,17 +47,18 @@ pub trait Scalar< fn assert_scalar(); } +/* pub trait Union -/*: OutputType + OutputType + resolve::TypeName + resolve::ConcreteTypeName + resolve::Value + resolve::ValueAsync + resolve::ConcreteValue -+ resolve::ConcreteValueAsync */ ++ resolve::ConcreteValueAsync { fn assert_union(); -} +}*/ pub trait InputType< 'inp, diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index c612b27ae..04ac5d69c 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -74,10 +74,7 @@ pub use crate::{ LookAheadSelection, LookAheadValue, OwnedExecutor, Registry, ValuesStream, Variables, }, introspection::IntrospectionFormat, - macros::{ - helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, - input_value as graphql_input_value, value as graphql_value, vars as graphql_vars, - }, + macros::helper::subscription::{ExtractTypeFromStream, IntoFieldResult}, parser::{ParseError, ScalarToken, Spanning}, schema::{ meta, diff --git a/juniper/src/macros/input_value.rs b/juniper/src/macros/graphql_input_value.rs similarity index 99% rename from juniper/src/macros/input_value.rs rename to juniper/src/macros/graphql_input_value.rs index 627b4f634..73233a799 100644 --- a/juniper/src/macros/input_value.rs +++ b/juniper/src/macros/graphql_input_value.rs @@ -83,7 +83,8 @@ /// [`InputValue::Scalar`]: crate::graphql::InputValue::Scalar /// [`InputValue::Variable`]: crate::graphql::InputValue::Variable /// [`Spanning::unlocated`]: crate::Spanning::unlocated -macro_rules! input_value { +#[macro_export] +macro_rules! graphql_input_value { /////////// // Array // /////////// @@ -379,7 +380,7 @@ macro_rules! input_value { } #[doc(inline)] -pub(super) use input_value; +pub use graphql_input_value as input_value; #[cfg(test)] mod tests { diff --git a/juniper/src/macros/value.rs b/juniper/src/macros/graphql_value.rs similarity index 99% rename from juniper/src/macros/value.rs rename to juniper/src/macros/graphql_value.rs index c689afa68..aa72a7fbd 100644 --- a/juniper/src/macros/value.rs +++ b/juniper/src/macros/graphql_value.rs @@ -43,7 +43,8 @@ /// /// [`graphql::Value`]: crate::graphql::Value /// [`Value::Object`]: crate::graphql::Value::Object -macro_rules! value { +#[macro_export] +macro_rules! graphql_value { /////////// // Array // /////////// @@ -270,7 +271,7 @@ macro_rules! value { } #[doc(inline)] -pub(super) use value; +pub use graphql_value as value; #[cfg(test)] mod tests { diff --git a/juniper/src/macros/vars.rs b/juniper/src/macros/graphql_vars.rs similarity index 99% rename from juniper/src/macros/vars.rs rename to juniper/src/macros/graphql_vars.rs index d4b75e82b..2034e4933 100644 --- a/juniper/src/macros/vars.rs +++ b/juniper/src/macros/graphql_vars.rs @@ -24,7 +24,8 @@ /// /// [`graphql::input_value!`]: crate::graphql::input_value /// [`graphql::Variables`]: crate::graphql::Variables -macro_rules! vars { +#[macro_export] +macro_rules! graphql_vars { //////////// // Object // //////////// @@ -196,7 +197,7 @@ macro_rules! vars { } #[doc(inline)] -pub(super) use vars; +pub use graphql_vars as vars; #[cfg(test)] mod tests { diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 9e60986e6..57f1a4c74 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -6,9 +6,9 @@ pub mod helper; #[macro_use] pub mod reflect; -mod input_value; -mod value; -mod vars; +mod graphql_input_value; +mod graphql_value; +mod graphql_vars; #[doc(inline)] -pub use self::{input_value::input_value, value::value, vars::vars}; +pub use self::{graphql_input_value::input_value, graphql_value::value, graphql_vars::vars}; diff --git a/juniper/src/macros/reflect.rs b/juniper/src/macros/reflect.rs index e67762df0..e7cb1f305 100644 --- a/juniper/src/macros/reflect.rs +++ b/juniper/src/macros/reflect.rs @@ -1,13 +1,16 @@ //! Compile-time reflection of Rust types into GraphQL types. +use std::{rc::Rc, sync::Arc}; + use futures::future::BoxFuture; use crate::{ - reflect::{ - can_be_subtype, fnv1a128, str_eq, str_exists_in_arr, type_len_with_wrapped_val, wrap, - Argument, Arguments, FieldName, Name, Names, Type, Types, WrappedValue, - }, - Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, ScalarValue, Nullable, + Arguments as FieldArguments, ExecutionResult, Executor, GraphQLValue, Nullable, ScalarValue, +}; + +pub use crate::reflect::{ + can_be_subtype, fnv1a128, str_eq, str_exists_in_arr, type_len_with_wrapped_val, Argument, + Arguments, FieldName, Name, Names, Type, Types, WrappedValue, }; /// Naming of a [GraphQL object][1], [scalar][2] or [interface][3] [`Type`]. diff --git a/juniper/src/reflect/mod.rs b/juniper/src/reflect/mod.rs index 2304e6a2b..43d738278 100644 --- a/juniper/src/reflect/mod.rs +++ b/juniper/src/reflect/mod.rs @@ -5,7 +5,7 @@ use crate::behavior; #[doc(inline)] pub use self::macros::{ assert_field, assert_field_args, assert_field_type, assert_has_field, assert_implemented_for, - assert_interfaces_impls, checked_hash, const_concat, format_type, + assert_interfaces_impls, const_concat, format_type, }; /// Name of a [GraphQL type][0] in a GraphQL schema. @@ -329,7 +329,8 @@ mod macros { /// types referencing this interface in the `impl = ...` attribute argument. /// /// Symmetrical to [`assert_interfaces_impls!`]. - macro_rules! assert_implemented_for { + #[macro_export] + macro_rules! reflect_assert_implemented_for { ($behavior: ty, $implementor: ty $(, $interfaces: ty)* $(,)?) => { const _: () = { $({ @@ -356,7 +357,8 @@ mod macros { /// referencing this type in `#[graphql::interface(for = ...)]` attribute. /// /// Symmetrical to [`assert_implemented_for!`]. - macro_rules! assert_interfaces_impls { + #[macro_export] + macro_rules! reflect_assert_interfaces_impls { ($behavior: ty, $interface: ty $(, $implementers: ty)* $(,)?) => { const _: () = { $({ @@ -390,7 +392,8 @@ mod macros { /// [`Field`]: super::Field /// [`Type`]: super::Type /// [0]: https://spec.graphql.org/October2021#IsValidImplementation() - macro_rules! assert_field { + #[macro_export] + macro_rules! reflect_assert_field { ( $base_ty: ty, $impl_ty: ty, @@ -409,7 +412,7 @@ mod macros { /// [`Field`]: super::Field /// [0]: https://spec.graphql.org/October2021#IsValidImplementationFieldType() #[macro_export] - macro_rules! assert_field_type { + macro_rules! reflect_assert_field_type { ( $base_ty: ty, $impl_ty: ty, @@ -492,7 +495,7 @@ mod macros { /// [`Field`]: super::Field /// [0]: https://spec.graphql.org/October2021#sel-IAHZhCHCDEEFAAADHD8Cxob #[macro_export] - macro_rules! assert_field_args { + macro_rules! reflect_assert_field_args { ( $base_ty: ty, $impl_ty: ty, @@ -677,7 +680,8 @@ mod macros { /// /// [`Field`]: super::Field /// [`fnv1a128`]: super::fnv1a128 - macro_rules! assert_has_field { + #[macro_export] + macro_rules! reflect_assert_has_field { ( $field_name: expr, $impl_ty: ty, @@ -703,7 +707,8 @@ mod macros { } /// Concatenates `const` [`str`](prim@str)s in a `const` context. - macro_rules! const_concat { + #[macro_export] + macro_rules! reflect_const_concat { ($($s:expr),* $(,)?) => {{ const LEN: usize = 0 $(+ $s.as_bytes().len())*; const CNT: usize = [$($s),*].len(); @@ -745,7 +750,8 @@ mod macros { /// /// [`Type`]: super::Type /// [`WrappedValue`]: super::WrappedValue - macro_rules! format_type { + #[macro_export] + macro_rules! reflect_format_type { ($ty: expr, $wrapped_value: expr $(,)?) => {{ const TYPE: ($crate::reflect::Type, $crate::reflect::WrappedValue) = ($ty, $wrapped_value); @@ -831,8 +837,12 @@ mod macros { } #[doc(inline)] - pub(super) use { - assert_field, assert_field_args, assert_field_type, assert_has_field, - assert_implemented_for, assert_interfaces_impls, checked_hash, const_concat, format_type, + pub use { + reflect_assert_field as assert_field, reflect_assert_field_args as assert_field_args, + reflect_assert_field_type as assert_field_type, + reflect_assert_has_field as assert_has_field, + reflect_assert_implemented_for as assert_implemented_for, + reflect_assert_interfaces_impls as assert_interfaces_impls, + reflect_const_concat as const_concat, reflect_format_type as format_type, }; } diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index fa32f9e53..21517fc78 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -7,8 +7,8 @@ use crate::{ #[doc(inline)] pub use crate::types::{ - arc::TryFromInputValue as InputValueAsArc, r#box::TryFromInputValue as InputValueAsBox, - r#ref::TryFromInputValue as InputValueAsRef, rc::TryFromInputValue as InputValueAsRc, + /*arc::TryFromInputValue as InputValueAsArc,*/ r#box::TryFromInputValue as InputValueAsBox, + /*r#ref::TryFromInputValue as InputValueAsRef, rc::TryFromInputValue as InputValueAsRc,*/ }; pub trait Type { @@ -24,7 +24,7 @@ pub trait TypeName { fn type_name(type_info: &TypeInfo) -> &str; } -pub trait ConcreteTypeName { +pub trait ConcreteTypeName { fn concrete_type_name<'i>(&self, type_info: &'i TypeInfo) -> &'i str; } @@ -74,7 +74,13 @@ pub trait ConcreteValue< ) -> ExecutionResult; } -pub trait ConcreteValueAsync { +pub trait ConcreteValueAsync< + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue, + Behavior: ?Sized = behavior::Standard, +> +{ fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, @@ -171,7 +177,7 @@ pub trait InputValueOwned: { } -impl InputValueOwned for T where T: for<'i> InputValue<'i, S, B> {} +impl InputValueOwned for T where T: for<'i> InputValue<'i, SV, BH> {} pub trait ScalarToken { fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index dfcc838d2..f9f50af6d 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -448,6 +448,7 @@ impl<'a, S> ScalarMeta<'a, S> { } } + /* /// Builds a new [`ScalarMeta`] information with the specified `name`. // TODO: Use `impl Into>` argument once feature // `explicit_generic_args_with_impl_trait` hits stable: @@ -483,7 +484,7 @@ impl<'a, S> ScalarMeta<'a, S> { try_parse_fn: try_parse_unsized_fn::, parse_fn: >::parse_scalar_token, } - } + }*/ /// Sets the `description` of this [`ScalarMeta`] type. /// @@ -837,6 +838,7 @@ where .map_err(T::Error::into_field_error) } +/* fn try_parse_fn_new(v: &InputValue) -> Result<(), FieldError> where T: resolve::InputValueOwned, @@ -854,3 +856,4 @@ where .map(drop) .map_err(T::Error::into_field_error) } +*/ diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 38f0f2187..3c6d12d05 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -10,6 +10,7 @@ use crate::{ IntoFieldError, Registry, Selection, }; +/* impl resolve::Type for Arc where T: resolve::Type + ?Sized, @@ -278,3 +279,4 @@ where { const VALUE: reflect::WrappedValue = T::VALUE; } +*/ diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index 77ef5f9be..f730c39da 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -11,6 +11,7 @@ use crate::{ use super::iter; +/* impl resolve::Type for [T; N] where T: resolve::Type, @@ -114,3 +115,4 @@ where { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } +*/ diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index b30936972..68edcc005 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -1,278 +1,278 @@ //! GraphQL implementation for [`Box`]. use crate::{ - graphql, + behavior, graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, - IntoFieldError, Registry, Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, + Selection, }; -impl resolve::Type for Box +impl resolve::Type for Box where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { T::meta(registry, info) } } -impl resolve::TypeName for Box +impl resolve::TypeName for Box where - T: resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn type_name(info: &Info) -> &str { + fn type_name(info: &TI) -> &str { T::type_name(info) } } -impl resolve::ConcreteTypeName for Box +impl resolve::ConcreteTypeName for Box where - T: resolve::ConcreteTypeName + ?Sized, - Info: ?Sized, + T: resolve::ConcreteTypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { (**self).concrete_type_name(info) } } -impl resolve::Value for Box +impl resolve::Value for Box where - T: resolve::Value + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_value(selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) } } -impl resolve::ValueAsync for Box +impl resolve::ValueAsync for Box where - T: resolve::ValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_value_async(selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) } } -impl resolve::ConcreteValue for Box +impl resolve::ConcreteValue for Box where - T: resolve::ConcreteValue + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValue + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_concrete_value(type_name, selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) } } -impl resolve::ConcreteValueAsync for Box +impl resolve::ConcreteValueAsync for Box where - T: resolve::ConcreteValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) } } -impl resolve::Field for Box +impl resolve::Field for Box where - T: resolve::Field + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Field + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_field(field_name, arguments, info, executor) + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) } } -impl resolve::FieldAsync for Box +impl resolve::FieldAsync for Box where - T: resolve::FieldAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::FieldAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_field_async(field_name, arguments, info, executor) + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) } } -impl resolve::ToInputValue for Box +impl resolve::ToInputValue for Box where - T: resolve::ToInputValue + ?Sized, + T: resolve::ToInputValue + ?Sized, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { (**self).to_input_value() } } -impl<'inp, T, S> resolve::InputValue<'inp, S> for Box +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Box where - T: resolve::InputValueAsBox<'inp, S> + ?Sized, - S: 'inp, + T: resolve::InputValueAsBox<'i, SV, BH> + ?Sized, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { - >::try_from_input_value(v) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + T::try_from_input_value(v) } fn try_from_implicit_null() -> Result { - >::try_from_implicit_null() + T::try_from_implicit_null() } } -pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { - type Error: IntoFieldError; +pub trait TryFromInputValue<'input, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard> { + type Error: IntoFieldError; - fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; + fn try_from_input_value( + v: &'input graphql::InputValue, + ) -> Result, Self::Error>; fn try_from_implicit_null() -> Result, Self::Error> { - Self::try_from_input_value(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::::Null) } } -impl<'inp, T, S> TryFromInputValue<'inp, S> for T +impl<'i, T, SV, BH> TryFromInputValue<'i, SV, BH> for T where - T: resolve::InputValue<'inp, S>, - S: 'inp, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Box::new) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + T::try_from_input_value(v).map(Box::new) } fn try_from_implicit_null() -> Result, Self::Error> { - >::try_from_implicit_null().map(Box::new) + T::try_from_implicit_null().map(Box::new) } } -impl resolve::ScalarToken for Box +impl resolve::ScalarToken for Box where - T: resolve::ScalarToken + ?Sized, + T: resolve::ScalarToken + ?Sized, + BH: ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { T::parse_scalar_token(token) } } -impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Box +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Box where - T: graphql::InputType<'i, Info, S> + resolve::InputValueAsBox<'i, S> + ?Sized, - Info: ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -impl graphql::OutputType for Box +impl graphql::OutputType for Box where - T: graphql::OutputType + ?Sized, + T: graphql::OutputType + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn assert_output_type() { T::assert_output_type() } } -impl graphql::Interface for Box +impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Box where - T: graphql::Interface + ?Sized, -{ - fn assert_interface() { - T::assert_interface() - } -} - -impl graphql::Object for Box -where - T: graphql::Object + ?Sized, -{ - fn assert_object() { - T::assert_object() - } -} - -impl graphql::Scalar for Box -where - T: graphql::Scalar + ?Sized, + T: graphql::Scalar<'i, TI, CX, SV, BH>, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_scalar() { T::assert_scalar() } } -impl graphql::Union for Box -where - T: graphql::Union + ?Sized, -{ - fn assert_union() { - T::assert_union() - } -} - -impl reflect::BaseType for Box +impl reflect::BaseType for Box where - T: reflect::BaseType + ?Sized, + T: reflect::BaseType + ?Sized, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Box +impl reflect::BaseSubTypes for Box where - T: reflect::BaseSubTypes + ?Sized, + T: reflect::BaseSubTypes + ?Sized, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Box +impl reflect::WrappedType for Box where - T: reflect::WrappedType + ?Sized, + T: reflect::WrappedType + ?Sized, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } diff --git a/juniper/src/types/iter.rs b/juniper/src/types/iter.rs index c3979b088..8d6aad9df 100644 --- a/juniper/src/types/iter.rs +++ b/juniper/src/types/iter.rs @@ -2,6 +2,7 @@ use crate::{graphql, resolve, ExecutionResult, Executor, Selection}; +/* pub fn resolve_list<'t, T, S, Info, Ctx, I>( iter: I, selection_set: Option<&[Selection<'_, S>]>, @@ -65,3 +66,4 @@ where } Ok(graphql::Value::list(values)) } +*/ diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index daec60440..b48064df5 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -272,6 +272,7 @@ impl Nullable<&mut T> { } } +/* impl resolve::Type for Nullable where T: resolve::Type, @@ -395,6 +396,7 @@ where const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); } + */ //////////////////////////////////////////////////////////////////////////////// impl GraphQLType for Nullable diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 0d8ebb6aa..7f3ef70d9 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -8,7 +8,7 @@ use crate::{ schema::meta::MetaType, BoxFuture, Selection, }; - +/* impl resolve::Type for Option where T: resolve::Type, @@ -127,3 +127,4 @@ where { const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); } +*/ diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index ebecc3f27..88f7089de 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -10,6 +10,7 @@ use crate::{ IntoFieldError, Registry, Selection, }; +/* impl resolve::Type for Rc where T: resolve::Type + ?Sized, @@ -278,3 +279,4 @@ where { const VALUE: reflect::WrappedValue = T::VALUE; } +*/ diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 5b84d54f6..e087482a4 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -10,6 +10,7 @@ use crate::{ IntoFieldError, Registry, Selection, }; +/* impl<'me, T, Info, S> resolve::Type for &'me T where T: resolve::Type + ?Sized, @@ -265,3 +266,4 @@ where { const VALUE: reflect::WrappedValue = T::VALUE; } +*/ diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index 77f8b602e..31a572119 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -9,6 +9,7 @@ use crate::{ reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; +/* impl<'me, T, Info, S> resolve::Type for &'me mut T where T: resolve::Type + ?Sized, @@ -225,3 +226,4 @@ where { const VALUE: reflect::WrappedValue = T::VALUE; } +*/ diff --git a/juniper/src/types/result.rs b/juniper/src/types/result.rs index 1077f979c..51ec79ad1 100644 --- a/juniper/src/types/result.rs +++ b/juniper/src/types/result.rs @@ -2,6 +2,7 @@ use crate::reflect; +/* impl reflect::BaseType for Result where T: reflect::BaseType, @@ -22,3 +23,4 @@ where { const VALUE: reflect::WrappedValue = T::VALUE; } +*/ diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index 022a335bb..0b31c7a99 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -11,6 +11,7 @@ use crate::{ use super::iter; +/* impl resolve::Type for [T] where T: resolve::Type, @@ -71,7 +72,6 @@ where } } -/* impl graphql::InputType for [T] where T: graphql::InputType, @@ -80,7 +80,7 @@ where T::assert_input_type() } } -*/ + impl graphql::OutputType for [T] where @@ -111,3 +111,4 @@ where { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } +*/ diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index c1a559a0c..a5ff4436c 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -13,6 +13,7 @@ use crate::{ reflect, resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, }; +/* impl resolve::Type for str { fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> where @@ -146,3 +147,4 @@ impl reflect::BaseSubTypes for str { impl reflect::WrappedType for str { const VALUE: reflect::WrappedValue = reflect::wrap::SINGULAR; } +*/ diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index a3022e412..ad4cd8edb 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -9,6 +9,7 @@ use crate::{ use super::iter; +/* impl resolve::Type for Vec where T: resolve::Type, @@ -69,7 +70,7 @@ where } } -/* + impl<'i, T, Info, S> graphql::InputType<'i, Info, S> for Vec where T: graphql::InputType<'i, Info, S>, @@ -79,7 +80,7 @@ where T::assert_input_type() } } - */ + impl graphql::OutputType for Vec where @@ -110,3 +111,4 @@ where { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } +*/ From 5ab398e22bb47404e90e7758c65040f7eda8f7b7 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 9 Jun 2022 15:39:14 +0200 Subject: [PATCH 24/58] Re-impl renewed traits machinery for other pointer types --- juniper/src/graphql/mod.rs | 31 +++- juniper/src/resolve/mod.rs | 32 +++- juniper/src/types/arc.rs | 276 ++++++++++++++++++----------------- juniper/src/types/box.rs | 46 +++--- juniper/src/types/mod.rs | 8 +- juniper/src/types/rc.rs | 276 ++++++++++++++++++----------------- juniper/src/types/ref.rs | 268 +++++++++++++++++----------------- juniper/src/types/ref_mut.rs | 217 +++++++++++++-------------- 8 files changed, 608 insertions(+), 546 deletions(-) diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 8fa9391a0..9e0e1af92 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -2,10 +2,10 @@ use crate::{behavior, resolve}; pub use crate::{ ast::InputValue, + executor::Variables, macros::{input_value, value, vars}, resolve::Type, value::Value, - executor::Variables, }; /* @@ -47,6 +47,21 @@ pub trait Scalar< fn assert_scalar(); } +pub trait ScalarAs< + 'inp, + Wrapper, + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: + InputTypeAs<'inp, Wrapper, TypeInfo, ScalarValue, Behavior> + + OutputType + + resolve::ScalarToken +{ + fn assert_scalar(); +} + /* pub trait Union OutputType @@ -73,6 +88,20 @@ pub trait InputType< fn assert_input_type(); } +pub trait InputTypeAs< + 'inp, + Wrapper, + TypeInfo: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: + Type + + resolve::ToInputValue + + resolve::InputValueAs<'inp, Wrapper, ScalarValue, Behavior> +{ + fn assert_input_type(); +} + pub trait OutputType< TypeInfo: ?Sized, Context: ?Sized, diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 21517fc78..206cf950c 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -5,12 +5,6 @@ use crate::{ reflect, Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, Selection, }; -#[doc(inline)] -pub use crate::types::{ - /*arc::TryFromInputValue as InputValueAsArc,*/ r#box::TryFromInputValue as InputValueAsBox, - /*r#ref::TryFromInputValue as InputValueAsRef, rc::TryFromInputValue as InputValueAsRc,*/ -}; - pub trait Type { fn meta<'r>( registry: &mut Registry<'r, ScalarValue>, @@ -179,6 +173,32 @@ pub trait InputValueOwned: impl InputValueOwned for T where T: for<'i> InputValue<'i, SV, BH> {} +pub trait InputValueAs<'input, Wrapper, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard> +{ + type Error: IntoFieldError; + + fn try_from_input_value( + v: &'input graphql::InputValue, + ) -> Result; + + fn try_from_implicit_null() -> Result { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + +pub trait InputValueAsRef { + type Error: IntoFieldError; + + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; + + fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error> + where + ScalarValue: 'a, + { + Self::try_from_input_value(&graphql::InputValue::::Null) + } +} + pub trait ScalarToken { fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result>; } diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 3c6d12d05..7000e4457 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -6,277 +6,287 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, - IntoFieldError, Registry, Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; -/* -impl resolve::Type for Arc +impl resolve::Type for Arc where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { T::meta(registry, info) } } -impl resolve::TypeName for Arc +impl resolve::TypeName for Arc where - T: resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn type_name(info: &Info) -> &str { + fn type_name(info: &TI) -> &str { T::type_name(info) } } -impl resolve::ConcreteTypeName for Arc +impl resolve::ConcreteTypeName for Arc where - T: resolve::ConcreteTypeName + ?Sized, - Info: ?Sized, + T: resolve::ConcreteTypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { (**self).concrete_type_name(info) } } -impl resolve::Value for Arc +impl resolve::Value for Arc where - T: resolve::Value + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_value(selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) } } -impl resolve::ValueAsync for Arc +impl resolve::ValueAsync for Arc where - T: resolve::ValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_value_async(selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) } } -impl resolve::ConcreteValue for Arc +impl resolve::ConcreteValue for Arc where - T: resolve::ConcreteValue + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValue + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_concrete_value(type_name, selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) } } -impl resolve::ConcreteValueAsync for Arc +impl resolve::ConcreteValueAsync for Arc where - T: resolve::ConcreteValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) } } -impl resolve::Field for Arc +impl resolve::Field for Arc where - T: resolve::Field + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Field + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_field(field_name, arguments, info, executor) + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) } } -impl resolve::FieldAsync for Arc +impl resolve::FieldAsync for Arc where - T: resolve::FieldAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::FieldAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_field_async(field_name, arguments, info, executor) + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) } } -impl resolve::ToInputValue for Arc +impl resolve::ToInputValue for Arc where - T: resolve::ToInputValue + ?Sized, + T: resolve::ToInputValue + ?Sized, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { (**self).to_input_value() } } -impl<'inp, T, S> resolve::InputValue<'inp, S> for Arc +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Arc where - T: resolve::InputValueAsArc<'inp, S> + ?Sized, - S: 'inp, + T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { - >::try_from_input_value(v) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + T::try_from_input_value(v) } fn try_from_implicit_null() -> Result { - >::try_from_implicit_null() + T::try_from_implicit_null() } } -pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { - type Error: IntoFieldError; - - fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; - - fn try_from_implicit_null() -> Result, Self::Error> { - Self::try_from_input_value(&graphql::InputValue::::Null) - } -} - -impl<'inp, T, S> TryFromInputValue<'inp, S> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc, SV, BH> for T where - T: resolve::InputValue<'inp, S>, - S: 'inp, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Arc::new) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + T::try_from_input_value(v).map(Arc::new) } fn try_from_implicit_null() -> Result, Self::Error> { - >::try_from_implicit_null().map(Arc::new) + T::try_from_implicit_null().map(Arc::new) } } -impl resolve::ScalarToken for Arc +impl resolve::ScalarToken for Arc where - T: resolve::ScalarToken + ?Sized, + T: resolve::ScalarToken + ?Sized, + BH: ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { T::parse_scalar_token(token) } } -impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Arc +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Arc where - T: graphql::InputType<'i, Info, S> + ?Sized, - Info: ?Sized, + T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -impl graphql::OutputType for Arc +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Arc, TI, SV, BH> for T where - T: graphql::OutputType + ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { - fn assert_output_type() { - T::assert_output_type() - } -} - -impl graphql::Interface for Arc -where - T: graphql::Interface + ?Sized, -{ - fn assert_interface() { - T::assert_interface() + fn assert_input_type() { + T::assert_input_type() } } -impl graphql::Object for Arc +impl graphql::OutputType for Arc where - T: graphql::Object + ?Sized, + T: graphql::OutputType + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { - fn assert_object() { - T::assert_object() + fn assert_output_type() { + T::assert_output_type() } } -impl graphql::Scalar for Arc +impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Arc where - T: graphql::Scalar + ?Sized, + T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_scalar() { T::assert_scalar() } } -impl graphql::Union for Arc +impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Arc, TI, CX, SV, BH> for T where - T: graphql::Union + ?Sized, + T: graphql::Scalar<'i, TI, CX, SV, BH>, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { - fn assert_union() { - T::assert_union() + fn assert_scalar() { + T::assert_scalar() } } -impl reflect::BaseType for Arc +impl reflect::BaseType for Arc where - T: reflect::BaseType + ?Sized, + T: reflect::BaseType + ?Sized, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Arc +impl reflect::BaseSubTypes for Arc where - T: reflect::BaseSubTypes + ?Sized, + T: reflect::BaseSubTypes + ?Sized, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Arc +impl reflect::WrappedType for Arc where - T: reflect::WrappedType + ?Sized, + T: reflect::WrappedType + ?Sized, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } -*/ diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 68edcc005..c5257b5b0 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -1,11 +1,10 @@ //! GraphQL implementation for [`Box`]. use crate::{ - behavior, graphql, + graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, IntoFieldError, Registry, - Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; impl resolve::Type for Box @@ -162,7 +161,7 @@ where impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Box where - T: resolve::InputValueAsBox<'i, SV, BH> + ?Sized, + T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized, SV: 'i, BH: ?Sized, { @@ -177,19 +176,7 @@ where } } -pub trait TryFromInputValue<'input, ScalarValue: 'input, Behavior: ?Sized = behavior::Standard> { - type Error: IntoFieldError; - - fn try_from_input_value( - v: &'input graphql::InputValue, - ) -> Result, Self::Error>; - - fn try_from_implicit_null() -> Result, Self::Error> { - Self::try_from_input_value(&graphql::InputValue::::Null) - } -} - -impl<'i, T, SV, BH> TryFromInputValue<'i, SV, BH> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box, SV, BH> for T where T: resolve::InputValue<'i, SV, BH>, SV: 'i, @@ -217,6 +204,18 @@ where } impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Box +where + T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized, + TI: ?Sized, + SV: 'i, + BH: ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Box, TI, SV, BH> for T where T: graphql::InputType<'i, TI, SV, BH>, TI: ?Sized, @@ -241,6 +240,19 @@ where } impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Box +where + T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + +impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Box, TI, CX, SV, BH> for T where T: graphql::Scalar<'i, TI, CX, SV, BH>, TI: ?Sized, diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 5e529d61a..d331959a3 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,11 +1,11 @@ -pub mod arc; +mod arc; mod array; -pub mod r#box; +mod r#box; pub mod iter; mod nullable; mod option; -pub mod rc; -pub mod r#ref; +mod rc; +mod r#ref; mod ref_mut; mod result; mod slice; diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 88f7089de..53a3248f0 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -6,277 +6,287 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, - IntoFieldError, Registry, Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; -/* -impl resolve::Type for Rc +impl resolve::Type for Rc where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { T::meta(registry, info) } } -impl resolve::TypeName for Rc +impl resolve::TypeName for Rc where - T: resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn type_name(info: &Info) -> &str { + fn type_name(info: &TI) -> &str { T::type_name(info) } } -impl resolve::ConcreteTypeName for Rc +impl resolve::ConcreteTypeName for Rc where - T: resolve::ConcreteTypeName + ?Sized, - Info: ?Sized, + T: resolve::ConcreteTypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { (**self).concrete_type_name(info) } } -impl resolve::Value for Rc +impl resolve::Value for Rc where - T: resolve::Value + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_value(selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) } } -impl resolve::ValueAsync for Rc +impl resolve::ValueAsync for Rc where - T: resolve::ValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_value_async(selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) } } -impl resolve::ConcreteValue for Rc +impl resolve::ConcreteValue for Rc where - T: resolve::ConcreteValue + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValue + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_concrete_value(type_name, selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) } } -impl resolve::ConcreteValueAsync for Rc +impl resolve::ConcreteValueAsync for Rc where - T: resolve::ConcreteValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) } } -impl resolve::Field for Rc +impl resolve::Field for Rc where - T: resolve::Field + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Field + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_field(field_name, arguments, info, executor) + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) } } -impl resolve::FieldAsync for Rc +impl resolve::FieldAsync for Rc where - T: resolve::FieldAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::FieldAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_field_async(field_name, arguments, info, executor) + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) } } -impl resolve::ToInputValue for Rc +impl resolve::ToInputValue for Rc where - T: resolve::ToInputValue + ?Sized, + T: resolve::ToInputValue + ?Sized, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { (**self).to_input_value() } } -impl<'inp, T, S> resolve::InputValue<'inp, S> for Rc +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Rc where - T: resolve::InputValueAsRc<'inp, S> + ?Sized, - S: 'inp, + T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { - >::try_from_input_value(v) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + T::try_from_input_value(v) } fn try_from_implicit_null() -> Result { - >::try_from_implicit_null() + T::try_from_implicit_null() } } -pub trait TryFromInputValue<'input, S: 'input = DefaultScalarValue> { - type Error: IntoFieldError; - - fn try_from_input_value(v: &'input graphql::InputValue) -> Result, Self::Error>; - - fn try_from_implicit_null() -> Result, Self::Error> { - Self::try_from_input_value(&graphql::InputValue::::Null) - } -} - -impl<'inp, T, S> TryFromInputValue<'inp, S> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc, SV, BH> for T where - T: resolve::InputValue<'inp, S>, - S: 'inp, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Rc::new) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + T::try_from_input_value(v).map(Rc::new) } fn try_from_implicit_null() -> Result, Self::Error> { - >::try_from_implicit_null().map(Rc::new) + T::try_from_implicit_null().map(Rc::new) } } -impl resolve::ScalarToken for Rc +impl resolve::ScalarToken for Rc where - T: resolve::ScalarToken + ?Sized, + T: resolve::ScalarToken + ?Sized, + BH: ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { T::parse_scalar_token(token) } } -impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Rc +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Rc where - T: graphql::InputType<'i, Info, S> + ?Sized, - Info: ?Sized, + T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -impl graphql::OutputType for Rc +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Rc, TI, SV, BH> for T where - T: graphql::OutputType + ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { - fn assert_output_type() { - T::assert_output_type() - } -} - -impl graphql::Interface for Rc -where - T: graphql::Interface + ?Sized, -{ - fn assert_interface() { - T::assert_interface() + fn assert_input_type() { + T::assert_input_type() } } -impl graphql::Object for Rc +impl graphql::OutputType for Rc where - T: graphql::Object + ?Sized, + T: graphql::OutputType + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { - fn assert_object() { - T::assert_object() + fn assert_output_type() { + T::assert_output_type() } } -impl graphql::Scalar for Rc +impl<'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Rc where - T: graphql::Scalar + ?Sized, + T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_scalar() { T::assert_scalar() } } -impl graphql::Union for Rc +impl<'i, T, TI, CX, SV, BH> graphql::ScalarAs<'i, Rc, TI, CX, SV, BH> for T where - T: graphql::Union + ?Sized, + T: graphql::Scalar<'i, TI, CX, SV, BH>, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { - fn assert_union() { - T::assert_union() + fn assert_scalar() { + T::assert_scalar() } } -impl reflect::BaseType for Rc +impl reflect::BaseType for Rc where - T: reflect::BaseType + ?Sized, + T: reflect::BaseType + ?Sized, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Rc +impl reflect::BaseSubTypes for Rc where - T: reflect::BaseSubTypes + ?Sized, + T: reflect::BaseSubTypes + ?Sized, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Rc +impl reflect::WrappedType for Rc where - T: reflect::WrappedType + ?Sized, + T: reflect::WrappedType + ?Sized, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } -*/ diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index e087482a4..7112c99bc 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -6,264 +6,266 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - reflect, resolve, Arguments, BoxFuture, DefaultScalarValue, ExecutionResult, Executor, - IntoFieldError, Registry, Selection, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; -/* -impl<'me, T, Info, S> resolve::Type for &'me T +impl<'me, T, TI, SV, BH> resolve::Type for &'me T where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { T::meta(registry, info) } } -impl<'me, T, Info> resolve::TypeName for &'me T +impl<'me, T, TI, BH> resolve::TypeName for &'me T where - T: resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn type_name(info: &Info) -> &str { + fn type_name(info: &TI) -> &str { T::type_name(info) } } -impl<'me, T, Info> resolve::ConcreteTypeName for &'me T +impl<'me, T, TI, BH> resolve::ConcreteTypeName for &'me T where - T: resolve::ConcreteTypeName + ?Sized, - Info: ?Sized, + T: resolve::ConcreteTypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { (**self).concrete_type_name(info) } } -impl<'me, T, Info, Ctx, S> resolve::Value for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::Value for &'me T where - T: resolve::Value + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_value(selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ValueAsync for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync for &'me T where - T: resolve::ValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_value_async(selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ConcreteValue for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue for &'me T where - T: resolve::ConcreteValue + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValue + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_concrete_value(type_name, selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ConcreteValueAsync for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync for &'me T where - T: resolve::ConcreteValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::Field for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::Field for &'me T where - T: resolve::Field + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Field + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_field(field_name, arguments, info, executor) + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::FieldAsync for &'me T +impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync for &'me T where - T: resolve::FieldAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::FieldAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_field_async(field_name, arguments, info, executor) + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) } } -impl<'me, T, S> resolve::ToInputValue for &'me T +impl<'me, T, SV, BH> resolve::ToInputValue for &'me T where - T: resolve::ToInputValue + ?Sized, + T: resolve::ToInputValue + ?Sized, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { (**self).to_input_value() } } -impl<'inp: 'me, 'me, T, S: 'inp> resolve::InputValue<'inp, S> for &'me T +impl<'me, 'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for &'me T where - T: resolve::InputValueAsRef + ?Sized, + 'i: 'me, + T: resolve::InputValueAs<'i, &'me T, SV, BH> + ?Sized, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { - >::try_from_input_value(v) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + T::try_from_input_value(v) } fn try_from_implicit_null() -> Result { - >::try_from_implicit_null() + T::try_from_implicit_null() } } -pub trait TryFromInputValue { - type Error: IntoFieldError; +impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, &'me T, SV, BH> for T +where + 'i: 'me, + T: resolve::InputValueAsRef + ?Sized, + SV: 'i, + BH: ?Sized, +{ + type Error = T::Error; - fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error>; + fn try_from_input_value(v: &'i graphql::InputValue) -> Result<&'me T, Self::Error> { + T::try_from_input_value(v) + } - fn try_from_implicit_null<'a>() -> Result<&'a Self, Self::Error> - where - S: 'a, - { - Self::try_from_input_value(&graphql::InputValue::::Null) + fn try_from_implicit_null() -> Result<&'me T, Self::Error> { + T::try_from_implicit_null() } } -impl<'me, T, S> resolve::ScalarToken for &'me T +impl<'me, T, SV, BH> resolve::ScalarToken for &'me T where - T: resolve::ScalarToken + ?Sized, + T: resolve::ScalarToken + ?Sized, + BH: ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { T::parse_scalar_token(token) } } -/* -impl<'me, 'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for &'me T +impl<'me, 'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for &'me T where - Self: resolve::Type + resolve::ToInputValue + resolve::InputValue<'i, S>, - Info: ?Sized, + 'i: 'me, + T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ?Sized + 'me, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } -}*/ +} -impl<'me, T, S> graphql::OutputType for &'me T +impl<'me, T, TI, CX, SV, BH> graphql::OutputType for &'me T where - T: graphql::OutputType + ?Sized, + T: graphql::OutputType + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn assert_output_type() { T::assert_output_type() } } -impl<'me, T, S> graphql::Interface for &'me T -where - T: graphql::Interface + ?Sized, -{ - fn assert_interface() { - T::assert_interface() - } -} - -impl<'me, T, S> graphql::Object for &'me T +impl<'me, 'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for &'me T where - T: graphql::Object + ?Sized, -{ - fn assert_object() { - T::assert_object() - } -} - -impl<'me, T, S> graphql::Scalar for &'me T -where - T: graphql::Scalar + ?Sized, + 'i: 'me, + T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_scalar() { T::assert_scalar() } } -impl<'me, T, S> graphql::Union for &'me T -where - T: graphql::Union + ?Sized, -{ - fn assert_union() { - T::assert_union() - } -} - -impl<'me, T, S> reflect::BaseType for &'me T +impl<'me, T, BH> reflect::BaseType for &'me T where - T: reflect::BaseType + ?Sized, + T: reflect::BaseType + ?Sized, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl<'me, T, S> reflect::BaseSubTypes for &'me T +impl<'me, T, BH> reflect::BaseSubTypes for &'me T where - T: reflect::BaseSubTypes + ?Sized, + T: reflect::BaseSubTypes + ?Sized, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl<'me, T, S> reflect::WrappedType for &'me T +impl<'me, T, BH> reflect::WrappedType for &'me T where - T: reflect::WrappedType + ?Sized, + T: reflect::WrappedType + ?Sized, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } -*/ diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index 31a572119..e3911abdb 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -9,221 +9,200 @@ use crate::{ reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, }; -/* -impl<'me, T, Info, S> resolve::Type for &'me mut T +impl<'me, T, TI, SV, BH> resolve::Type for &'me mut T where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { T::meta(registry, info) } } -impl<'me, T, Info> resolve::TypeName for &'me mut T +impl<'me, T, TI, BH> resolve::TypeName for &'me mut T where - T: resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn type_name(info: &Info) -> &str { + fn type_name(info: &TI) -> &str { T::type_name(info) } } -impl<'me, T, Info> resolve::ConcreteTypeName for &'me mut T +impl<'me, T, TI, BH> resolve::ConcreteTypeName for &'me mut T where - T: resolve::ConcreteTypeName + ?Sized, - Info: ?Sized, + T: resolve::ConcreteTypeName + ?Sized, + TI: ?Sized, + BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i Info) -> &'i str { + fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { (**self).concrete_type_name(info) } } -impl<'me, T, Info, Ctx, S> resolve::Value for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::Value for &'me mut T where - T: resolve::Value + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_value(selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ValueAsync for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync for &'me mut T where - T: resolve::ValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_value_async(selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ConcreteValue for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue for &'me mut T where - T: resolve::ConcreteValue + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValue + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value( &self, type_name: &str, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_concrete_value(type_name, selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::ConcreteValueAsync for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync for &'me mut T where - T: resolve::ConcreteValueAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ConcreteValueAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_concrete_value_async<'r>( &'r self, type_name: &str, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_concrete_value_async(type_name, selection_set, info, executor) + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::Field for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::Field for &'me mut T where - T: resolve::Field + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Field + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field( &self, field_name: &str, - arguments: &Arguments, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - (**self).resolve_field(field_name, arguments, info, executor) + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) } } -impl<'me, T, Info, Ctx, S> resolve::FieldAsync for &'me mut T +impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync for &'me mut T where - T: resolve::FieldAsync + ?Sized, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::FieldAsync + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_field_async<'r>( &'r self, field_name: &'r str, - arguments: &'r Arguments, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { - (**self).resolve_field_async(field_name, arguments, info, executor) + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) } } -impl<'me, T, S> resolve::ToInputValue for &'me mut T +impl<'me, T, SV, BH> resolve::ToInputValue for &'me mut T where - T: resolve::ToInputValue + ?Sized, + T: resolve::ToInputValue + ?Sized, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { (**self).to_input_value() } } -impl<'me, T, S> resolve::ScalarToken for &'me mut T +impl<'me, T, SV, BH> resolve::ScalarToken for &'me mut T where - T: resolve::ScalarToken + ?Sized, + T: resolve::ScalarToken + ?Sized, + BH: ?Sized, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { T::parse_scalar_token(token) } } -impl<'me, T, S> graphql::OutputType for &'me mut T +impl<'me, T, TI, CX, SV, BH> graphql::OutputType for &'me mut T where - T: graphql::OutputType + ?Sized, + T: graphql::OutputType + ?Sized, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn assert_output_type() { T::assert_output_type() } } -impl<'me, T, S> graphql::Interface for &'me mut T +impl<'me, T, BH> reflect::BaseType for &'me mut T where - T: graphql::Interface + ?Sized, -{ - fn assert_interface() { - T::assert_interface() - } -} - -impl<'me, T, S> graphql::Object for &'me mut T -where - T: graphql::Object + ?Sized, -{ - fn assert_object() { - T::assert_object() - } -} - -impl<'me, T, S> graphql::Scalar for &'me mut T -where - T: graphql::Scalar + ?Sized, -{ - fn assert_scalar() { - T::assert_scalar() - } -} - -impl<'me, T, S> graphql::Union for &'me mut T -where - T: graphql::Union + ?Sized, -{ - fn assert_union() { - T::assert_union() - } -} - -impl<'me, T, S> reflect::BaseType for &'me mut T -where - T: reflect::BaseType + ?Sized, + T: reflect::BaseType + ?Sized, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl<'me, T, S> reflect::BaseSubTypes for &'me mut T +impl<'me, T, BH> reflect::BaseSubTypes for &'me mut T where - T: reflect::BaseSubTypes + ?Sized, + T: reflect::BaseSubTypes + ?Sized, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl<'me, T, S> reflect::WrappedType for &'me mut T +impl<'me, T, BH> reflect::WrappedType for &'me mut T where - T: reflect::WrappedType + ?Sized, + T: reflect::WrappedType + ?Sized, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } -*/ From be010e812365ae35de545dbc77f9ef3907d907b1 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 9 Jun 2022 16:16:30 +0200 Subject: [PATCH 25/58] Re-impl renewed traits machinery for container types --- juniper/src/executor/mod.rs | 9 +-- juniper/src/resolve/mod.rs | 6 +- juniper/src/types/arc.rs | 12 ++-- juniper/src/types/box.rs | 12 ++-- juniper/src/types/nullable.rs | 113 +++++++++++++++++++--------------- juniper/src/types/option.rs | 108 ++++++++++++++++++-------------- juniper/src/types/rc.rs | 12 ++-- juniper/src/types/ref.rs | 12 ++-- juniper/src/types/ref_mut.rs | 12 ++-- 9 files changed, 164 insertions(+), 132 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 7603acb18..3c6fd9072 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1369,12 +1369,13 @@ impl<'r, S: 'r> Registry<'r, S> { /// [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type - pub fn build_nullable_type_new(&mut self, info: &Info) -> NullableMeta<'r> + pub fn build_nullable_type_reworked(&mut self, type_info: &TI) -> NullableMeta<'r> where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?Sized, + BH: ?Sized, + TI: ?Sized, { - NullableMeta::new(T::meta(self, info).as_type()) + NullableMeta::new(T::meta(self, type_info).as_type()) } /// Creates an [`ObjectMeta`] type with the given `fields`. diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 206cf950c..868ce5003 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -162,7 +162,7 @@ pub trait InputValue<'input, ScalarValue: 'input, Behavior: ?Sized = behavior::S ) -> Result; fn try_from_implicit_null() -> Result { - Self::try_from_input_value(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::Null) } } @@ -182,7 +182,7 @@ pub trait InputValueAs<'input, Wrapper, ScalarValue: 'input, Behavior: ?Sized = ) -> Result; fn try_from_implicit_null() -> Result { - Self::try_from_input_value(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::Null) } } @@ -195,7 +195,7 @@ pub trait InputValueAsRef { where ScalarValue: 'a, { - Self::try_from_input_value(&graphql::InputValue::::Null) + Self::try_from_input_value(&graphql::InputValue::Null) } } diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index 7000e4457..ef7337991 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -15,11 +15,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where SV: 'r, { - T::meta(registry, info) + T::meta(registry, type_info) } } @@ -29,8 +29,8 @@ where TI: ?Sized, BH: ?Sized, { - fn type_name(info: &TI) -> &str { - T::type_name(info) + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) } } @@ -40,8 +40,8 @@ where TI: ?Sized, BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { - (**self).concrete_type_name(info) + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) } } diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index c5257b5b0..ec0194612 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -13,11 +13,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where SV: 'r, { - T::meta(registry, info) + T::meta(registry, type_info) } } @@ -27,8 +27,8 @@ where TI: ?Sized, BH: ?Sized, { - fn type_name(info: &TI) -> &str { - T::type_name(info) + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) } } @@ -38,8 +38,8 @@ where TI: ?Sized, BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { - (**self).concrete_type_name(info) + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) } } diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index b48064df5..7728959c7 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -272,82 +272,89 @@ impl Nullable<&mut T> { } } -/* -impl resolve::Type for Nullable +impl resolve::Type for Nullable where - T: resolve::Type, - Info: ?Sized, + T: resolve::Type, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { - registry.build_nullable_type_new::(info).into_meta() + registry + .build_nullable_type_reworked::(type_info) + .into_meta() } } -impl resolve::Value for Nullable +impl resolve::Value for Nullable where - T: resolve::Value, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { match self { - Self::Some(v) => v.resolve_value(selection_set, info, executor), - Self::ExplicitNull | Self::ImplicitNull => Ok(graphql::Value::Null), + Self::Some(v) => v.resolve_value(selection_set, type_info, executor), + Self::ImplicitNull | Self::ExplicitNull => Ok(graphql::Value::Null), } } } -impl resolve::ValueAsync for Nullable +impl resolve::ValueAsync for Nullable where - T: resolve::ValueAsync, - Info: ?Sized, - Ctx: ?Sized, - S: Send, + T: resolve::ValueAsync, + TI: ?Sized, + CX: ?Sized, + SV: Send, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { match self { - Self::Some(v) => v.resolve_value_async(selection_set, info, executor), - Self::ExplicitNull | Self::ImplicitNull => Box::pin(future::ok(graphql::Value::Null)), + Self::Some(v) => v.resolve_value_async(selection_set, type_info, executor), + Self::ImplicitNull | Self::ExplicitNull => Box::pin(future::ok(graphql::Value::Null)), } } } -impl resolve::ToInputValue for Nullable +impl resolve::ToInputValue for Nullable where - T: resolve::ToInputValue, + T: resolve::ToInputValue, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { match self { Self::Some(v) => v.to_input_value(), - Self::ExplicitNull | Self::ImplicitNull => graphql::InputValue::Null, + Self::ImplicitNull | Self::ExplicitNull => graphql::InputValue::Null, } } } -impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Nullable +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Nullable where - T: resolve::InputValue<'inp, S>, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp InputValue) -> Result { + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { if v.is_null() { Ok(Self::ExplicitNull) } else { - >::try_from_input_value(v).map(Self::Some) + T::try_from_input_value(v).map(Self::Some) } } @@ -356,47 +363,55 @@ where } } -impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Nullable +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Nullable where - T: graphql::InputType<'i, Info, S>, - Info: ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -impl graphql::OutputType for Nullable +impl graphql::OutputType for Nullable where - T: graphql::OutputType, + T: graphql::OutputType, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync, { fn assert_output_type() { T::assert_output_type() } } -impl reflect::BaseType for Nullable +impl reflect::BaseType for Nullable where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Nullable +impl reflect::BaseSubTypes for Nullable where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Nullable +impl reflect::WrappedType for Nullable where - T: reflect::WrappedType, + T: reflect::WrappedType, + BH: ?Sized, { const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); } - */ //////////////////////////////////////////////////////////////////////////////// impl GraphQLType for Nullable diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 7f3ef70d9..0a83cc628 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -8,64 +8,70 @@ use crate::{ schema::meta::MetaType, BoxFuture, Selection, }; -/* -impl resolve::Type for Option + +impl resolve::Type for Option where - T: resolve::Type, - Info: ?Sized, + T: resolve::Type, + TI: ?Sized, + BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { - registry.build_nullable_type_new::(info).into_meta() + registry + .build_nullable_type_reworked::(type_info) + .into_meta() } } -impl resolve::Value for Option +impl resolve::Value for Option where - T: resolve::Value, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { match self { - Some(v) => v.resolve_value(selection_set, info, executor), + Some(v) => v.resolve_value(selection_set, type_info, executor), None => Ok(graphql::Value::Null), } } } -impl resolve::ValueAsync for Option +impl resolve::ValueAsync for Option where - T: resolve::ValueAsync, - Info: ?Sized, - Ctx: ?Sized, - S: Send, + T: resolve::ValueAsync, + TI: ?Sized, + CX: ?Sized, + SV: Send, + BH: ?Sized, { fn resolve_value_async<'r>( &'r self, - selection_set: Option<&'r [Selection<'_, S>]>, - info: &'r Info, - executor: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { match self { - Some(v) => v.resolve_value_async(selection_set, info, executor), + Some(v) => v.resolve_value_async(selection_set, type_info, executor), None => Box::pin(future::ok(graphql::Value::Null)), } } } -impl resolve::ToInputValue for Option +impl resolve::ToInputValue for Option where - T: resolve::ToInputValue, + T: resolve::ToInputValue, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { match self { Some(v) => v.to_input_value(), None => graphql::InputValue::Null, @@ -73,58 +79,68 @@ where } } -impl<'inp, T, S: 'inp> resolve::InputValue<'inp, S> for Option +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Option where - T: resolve::InputValue<'inp, S>, + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, { - type Error = >::Error; + type Error = T::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result { + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { if v.is_null() { Ok(None) } else { - >::try_from_input_value(v).map(Some) + T::try_from_input_value(v).map(Some) } } } -impl<'i, T, Info, S: 'i> graphql::InputType<'i, Info, S> for Option +impl<'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Option where - T: graphql::InputType<'i, Info, S>, - Info: ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -impl graphql::OutputType for Option +impl graphql::OutputType for Option where - T: graphql::OutputType, + T: graphql::OutputType, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync, { fn assert_output_type() { T::assert_output_type() } } -impl reflect::BaseType for Option +impl reflect::BaseType for Option where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Option +impl reflect::BaseSubTypes for Option where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Option +impl reflect::WrappedType for Option where - T: reflect::WrappedType, + T: reflect::WrappedType, + BH: ?Sized, { const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); } -*/ diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 53a3248f0..950ec0425 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -15,11 +15,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where SV: 'r, { - T::meta(registry, info) + T::meta(registry, type_info) } } @@ -29,8 +29,8 @@ where TI: ?Sized, BH: ?Sized, { - fn type_name(info: &TI) -> &str { - T::type_name(info) + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) } } @@ -40,8 +40,8 @@ where TI: ?Sized, BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { - (**self).concrete_type_name(info) + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) } } diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 7112c99bc..9b9668fe3 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -15,11 +15,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where SV: 'r, { - T::meta(registry, info) + T::meta(registry, type_info) } } @@ -29,8 +29,8 @@ where TI: ?Sized, BH: ?Sized, { - fn type_name(info: &TI) -> &str { - T::type_name(info) + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) } } @@ -40,8 +40,8 @@ where TI: ?Sized, BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { - (**self).concrete_type_name(info) + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) } } diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index e3911abdb..f24b96a5d 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -15,11 +15,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, info: &TI) -> MetaType<'r, SV> + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where SV: 'r, { - T::meta(registry, info) + T::meta(registry, type_info) } } @@ -29,8 +29,8 @@ where TI: ?Sized, BH: ?Sized, { - fn type_name(info: &TI) -> &str { - T::type_name(info) + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) } } @@ -40,8 +40,8 @@ where TI: ?Sized, BH: ?Sized, { - fn concrete_type_name<'i>(&self, info: &'i TI) -> &'i str { - (**self).concrete_type_name(info) + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) } } From 88046e3ce443803630ec27506e80b2bd613e9b99 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 9 Jun 2022 18:10:33 +0200 Subject: [PATCH 26/58] Re-implement `str`, vol.1 [skip ci] --- juniper/src/behavior.rs | 16 +++++++ juniper/src/executor/mod.rs | 44 +++++++----------- juniper/src/schema/meta.rs | 58 +++++++++++++++++------ juniper/src/types/str.rs | 91 ++++++++++++++++++++++--------------- 4 files changed, 131 insertions(+), 78 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index 3c6c84a41..d7a53f27e 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -1,5 +1,21 @@ //! Default GraphQL behaviors. +use std::{marker::PhantomData, sync::atomic::AtomicPtr}; + /// Default standard behavior of GraphQL types implementation. #[derive(Debug)] pub enum Standard {} + +pub struct Coerce(PhantomData>>, T); + +impl Coerce { + #[must_use] + pub const fn wrap(val: T) -> Self { + Self(PhantomData, val) + } +} + +#[must_use] +pub const fn coerce(val: T) -> Coerce { + Coerce::wrap(val) +} diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 3c6fd9072..295af1736 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -3,7 +3,7 @@ use std::{ borrow::Cow, cmp::Ordering, - collections::HashMap, + collections::{hash_map, HashMap}, fmt::{Debug, Display}, sync::{Arc, RwLock}, }; @@ -1190,30 +1190,19 @@ impl<'r, S: 'r> Registry<'r, S> { } } - /// Returns a [`Type`] meta information for the specified [`graphql::Type`], - /// registered in this [`Registry`]. - /// - /// If this [`Registry`] doesn't contain a [`Type`] meta information with - /// such [`TypeName`] before, it will construct the one and store it. + /// Returns an entry with a [`Type`] meta information for the specified + /// named [`graphql::Type`], registered in this [`Registry`]. /// /// [`graphql::Type`]: resolve::Type - /// [`TypeName`]: resolve::TypeName - pub fn get_type_new(&mut self, info: &Info) -> Type<'r> + pub fn entry_type( + &mut self, + type_info: &TI, + ) -> hash_map::Entry<'_, Name, MetaType<'r, S>> where - T: resolve::Type + resolve::TypeName + ?Sized, - Info: ?Sized, + T: resolve::TypeName + ?Sized, + TI: ?Sized, { - let name = T::type_name(info); - let validated_name = name.parse::().unwrap(); - if !self.types.contains_key(name) { - self.insert_placeholder( - validated_name.clone(), - Type::NonNullNamed(Cow::Owned(name.to_string())), - ); - let meta = T::meta(self, info); - self.types.insert(validated_name, meta); - } - self.types[name].as_type() + self.types.entry(T::type_name(type_info).parse().unwrap()) } /// Creates a [`Field`] with the provided `name`. @@ -1306,19 +1295,20 @@ impl<'r, S: 'r> Registry<'r, S> { // TODO: Allow using references. ScalarMeta::new_new::(T::type_name(info).to_owned()) } + */ - /// Builds a [`ScalarMeta`] information for the [`?Sized`] specified + /// Builds a [`ScalarMeta`] information for the specified [`?Sized`] /// [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type - pub fn build_scalar_type_unsized(&mut self, info: &Info) -> ScalarMeta<'r, S> + pub fn build_scalar_type_unsized(&mut self, type_info: &TI) -> ScalarMeta<'r, S> where - T: resolve::TypeName + resolve::ScalarToken + resolve::InputValueAsRef + ?Sized, - Info: ?Sized, + T: resolve::TypeName + resolve::InputValueAsRef + resolve::ScalarToken + ?Sized, + TI: ?Sized, { // TODO: Allow using references. - ScalarMeta::new_unsized::(T::type_name(info).to_owned()) - }*/ + ScalarMeta::new_unsized::(T::type_name(type_info).to_owned()) + } /// Creates a [`ListMeta`] type. /// diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index f9f50af6d..17ce8c74e 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -54,6 +54,20 @@ pub struct ScalarMeta<'a, S> { pub(crate) parse_fn: ScalarTokenParseFn, } +// Manual implementation is required here to omit redundant `S: Clone` trait +// bound, imposed by `#[derive(Clone)]`. +impl<'a, S> Clone for ScalarMeta<'a, S> { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + description: self.description.clone(), + specified_by_url: self.specified_by_url.clone(), + try_parse_fn: self.try_parse_fn, + parse_fn: self.parse_fn, + } + } +} + /// Shortcut for an [`InputValue`] parsing function. pub type InputValueParseFn = for<'b> fn(&'b InputValue) -> Result<(), FieldError>; @@ -61,7 +75,7 @@ pub type InputValueParseFn = for<'b> fn(&'b InputValue) -> Result<(), Fiel pub type ScalarTokenParseFn = for<'b> fn(ScalarToken<'b>) -> Result>; /// List type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ListMeta<'a> { #[doc(hidden)] pub of_type: Type<'a>, @@ -71,14 +85,14 @@ pub struct ListMeta<'a> { } /// Nullable type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct NullableMeta<'a> { #[doc(hidden)] pub of_type: Type<'a>, } /// Object type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct ObjectMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, @@ -101,8 +115,21 @@ pub struct EnumMeta<'a, S> { pub(crate) try_parse_fn: InputValueParseFn, } +// Manual implementation is required here to omit redundant `S: Clone` trait +// bound, imposed by `#[derive(Clone)]`. +impl<'a, S> Clone for EnumMeta<'a, S> { + fn clone(&self) -> Self { + Self { + name: self.name.clone(), + description: self.description.clone(), + values: self.values.clone(), + try_parse_fn: self.try_parse_fn, + } + } +} + /// Interface type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct InterfaceMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, @@ -113,7 +140,7 @@ pub struct InterfaceMeta<'a, S> { } /// Union type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct UnionMeta<'a> { #[doc(hidden)] pub name: Cow<'a, str>, @@ -124,6 +151,7 @@ pub struct UnionMeta<'a> { } /// Input object metadata +#[derive(Clone)] pub struct InputObjectMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, @@ -138,14 +166,14 @@ pub struct InputObjectMeta<'a, S> { /// /// After a type's `meta` method has been called but before it has returned, a placeholder type /// is inserted into a registry to indicate existence. -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct PlaceholderMeta<'a> { #[doc(hidden)] pub of_type: Type<'a>, } /// Generic type metadata -#[derive(Debug)] +#[derive(Clone, Debug)] pub enum MetaType<'a, S = DefaultScalarValue> { #[doc(hidden)] Scalar(ScalarMeta<'a, S>), @@ -168,7 +196,7 @@ pub enum MetaType<'a, S = DefaultScalarValue> { } /// Metadata for a field -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct Field<'a, S> { #[doc(hidden)] pub name: smartstring::alias::String, @@ -191,7 +219,7 @@ impl<'a, S> Field<'a, S> { } /// Metadata for an argument to a field -#[derive(Debug, Clone)] +#[derive(Clone, Debug)] pub struct Argument<'a, S> { #[doc(hidden)] pub name: String, @@ -465,7 +493,7 @@ impl<'a, S> ScalarMeta<'a, S> { try_parse_fn: try_parse_fn_new::, parse_fn: >::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. @@ -481,10 +509,10 @@ impl<'a, S> ScalarMeta<'a, S> { name: name.into(), description: None, specified_by_url: None, - try_parse_fn: try_parse_unsized_fn::, + try_parse_fn: try_parse_unsized_fn::, parse_fn: >::parse_scalar_token, } - }*/ + } /// Sets the `description` of this [`ScalarMeta`] type. /// @@ -847,13 +875,13 @@ where .map(drop) .map_err(T::Error::into_field_error) } +*/ -fn try_parse_unsized_fn(v: &InputValue) -> Result<(), FieldError> +fn try_parse_unsized_fn(v: &InputValue) -> Result<(), FieldError> where - T: resolve::InputValueAsRef + ?Sized, + T: resolve::InputValueAsRef + ?Sized, { T::try_from_input_value(v) .map(drop) .map_err(T::Error::into_field_error) } -*/ diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index a5ff4436c..6e0a39e4a 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -13,24 +13,28 @@ use crate::{ reflect, resolve, BoxFuture, ExecutionResult, Executor, Registry, ScalarValue, Selection, }; -/* -impl resolve::Type for str { - fn meta<'r>(registry: &mut Registry<'r, S>, info: &Info) -> MetaType<'r, S> +impl resolve::Type for str { + fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> where - S: 'r, + SV: 'r, { + let meta = registry + .build_scalar_type_unsized::(type_info) + .into_meta(); registry - .build_scalar_type_unsized::(info) - .into_meta() + .entry_type::(type_info) + .or_insert(meta) + .clone() } } -impl resolve::TypeName for str { - fn type_name(_: &Info) -> &'static str { - >::NAME +impl resolve::TypeName for str { + fn type_name(_: &TI) -> &'static str { + ::NAME } } +/* impl resolve::Value for str where Info: ?Sized, @@ -76,49 +80,65 @@ where } } -impl resolve::InputValueAsRef for str { + */ + +impl resolve::InputValueAsRef for str { type Error = String; - fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { + fn try_from_input_value(v: &graphql::InputValue) -> Result<&Self, Self::Error> { v.as_string_value() .ok_or_else(|| format!("Expected `String`, found: {}", v)) } } -impl<'inp, S: ScalarValue> resolve::InputValueAsBox<'inp, S> for str { - type Error = String; +impl<'i, SV> resolve::InputValueAs<'i, Box, SV> for str +where + SV: 'i, + Self: resolve::InputValueAsRef, +{ + type Error = >::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Into::into) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) } } -impl<'inp, S: ScalarValue> resolve::InputValueAsArc<'inp, S> for str { - type Error = String; +impl<'i, SV> resolve::InputValueAs<'i, Rc, SV> for str +where + SV: 'i, + Self: resolve::InputValueAsRef, +{ + type Error = >::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Into::into) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) } } -impl<'inp, S: ScalarValue> resolve::InputValueAsRc<'inp, S> for str { - type Error = String; +impl<'i, SV> resolve::InputValueAs<'i, Arc, SV> for str +where + SV: 'i, + Self: resolve::InputValueAsRef, +{ + type Error = >::Error; - fn try_from_input_value(v: &'inp graphql::InputValue) -> Result, Self::Error> { - >::try_from_input_value(v).map(Into::into) + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) } } -impl resolve::ScalarToken for str +impl resolve::ScalarToken for str +//TODO: where String: resolve::ScalarToken, where - String: resolve::ScalarToken, + String: crate::ParseScalarValue, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - >::parse_scalar_token(token) + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + // TODO: >::parse_scalar_token(token) + >::from_str(token) } } - /* + impl<'i, Info, S: 'i> graphql::InputType<'i, Info, S> for str where Self: resolve::Type + resolve::ToInputValue + resolve::InputValue<'i, S>, @@ -126,7 +146,7 @@ where { fn assert_input_type() {} } -*/ + impl graphql::OutputType for str { fn assert_output_type() {} @@ -134,17 +154,16 @@ impl graphql::OutputType for str { impl graphql::Scalar for str { fn assert_scalar() {} -} +}*/ -impl reflect::BaseType for str { - const NAME: reflect::Type = >::NAME; +impl reflect::BaseType for str { + const NAME: reflect::Type = "String"; // TODO: >::NAME; } -impl reflect::BaseSubTypes for str { - const NAMES: reflect::Types = &[>::NAME]; +impl reflect::BaseSubTypes for str { + const NAMES: reflect::Types = &[::NAME]; } -impl reflect::WrappedType for str { +impl reflect::WrappedType for str { const VALUE: reflect::WrappedValue = reflect::wrap::SINGULAR; } -*/ From 067b1e532a551b8395eabd67557bbecba3222420 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 13 Jun 2022 18:12:44 +0200 Subject: [PATCH 27/58] Re-implement `str`, vol.2 [skip ci] --- juniper/src/types/str.rs | 139 ++++++++++++++++++++++++++++++--------- 1 file changed, 107 insertions(+), 32 deletions(-) diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 6e0a39e4a..0e3f862b5 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -34,54 +34,51 @@ impl resolve::TypeName for str { } } -/* -impl resolve::Value for str +impl resolve::Value for str where - Info: ?Sized, - Ctx: ?Sized, - S: From, + TI: ?Sized, + CX: ?Sized, + SV: From, { fn resolve_value( &self, - _: Option<&[Selection<'_, S>]>, - _: &Info, - _: &Executor, - ) -> ExecutionResult { + _: Option<&[Selection<'_, SV>]>, + _: &TI, + _: &Executor, + ) -> ExecutionResult { // TODO: Remove redundant `.to_owned()` allocation by allowing // `ScalarValue` creation from reference? Ok(graphql::Value::scalar(self.to_owned())) } } -impl resolve::ValueAsync for str +impl resolve::ValueAsync for str where - Info: ?Sized, - Ctx: ?Sized, - S: From + Send, + TI: ?Sized, + CX: ?Sized, + SV: From + Send, { fn resolve_value_async<'r>( &'r self, - _: Option<&'r [Selection<'_, S>]>, - _: &'r Info, - _: &'r Executor, - ) -> BoxFuture<'r, ExecutionResult> { + _: Option<&'r [Selection<'_, SV>]>, + _: &'r TI, + _: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { // TODO: Remove redundant `.to_owned()` allocation by allowing // `ScalarValue` creation from reference? Box::pin(future::ok(graphql::Value::scalar(self.to_owned()))) } } -impl resolve::ToInputValue for str +impl resolve::ToInputValue for str where - S: From, + SV: From, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { graphql::InputValue::scalar(self.to_owned()) } } - */ - impl resolve::InputValueAsRef for str { type Error = String; @@ -91,7 +88,7 @@ impl resolve::InputValueAsRef for str { } } -impl<'i, SV> resolve::InputValueAs<'i, Box, SV> for str +impl<'i, SV> resolve::InputValueAs<'i, Box, SV> for str where SV: 'i, Self: resolve::InputValueAsRef, @@ -103,7 +100,7 @@ where } } -impl<'i, SV> resolve::InputValueAs<'i, Rc, SV> for str +impl<'i, SV> resolve::InputValueAs<'i, Rc, SV> for str where SV: 'i, Self: resolve::InputValueAsRef, @@ -115,7 +112,7 @@ where } } -impl<'i, SV> resolve::InputValueAs<'i, Arc, SV> for str +impl<'i, SV> resolve::InputValueAs<'i, Arc, SV> for str where SV: 'i, Self: resolve::InputValueAsRef, @@ -137,24 +134,102 @@ where >::from_str(token) } } -/* -impl<'i, Info, S: 'i> graphql::InputType<'i, Info, S> for str +impl<'me, 'i, TI, SV> graphql::InputTypeAs<'i, &'me Self, TI, SV> for str +where + Self: graphql::Type + + resolve::ToInputValue + + resolve::InputValueAs<'i, &'me Self, SV>, + TI: ?Sized, + SV: 'i, +{ + fn assert_input_type() {} +} + +impl<'i, TI, SV> graphql::InputTypeAs<'i, Box, TI, SV> for str +where + Self: graphql::Type + + resolve::ToInputValue + + resolve::InputValueAs<'i, Box, SV>, + TI: ?Sized, + SV: 'i, +{ + fn assert_input_type() {} +} + +impl<'i, TI, SV> graphql::InputTypeAs<'i, Rc, TI, SV> for str where - Self: resolve::Type + resolve::ToInputValue + resolve::InputValue<'i, S>, - Info: ?Sized, + Self: + graphql::Type + resolve::ToInputValue + resolve::InputValueAs<'i, Rc, SV>, + TI: ?Sized, + SV: 'i, { fn assert_input_type() {} } +impl<'i, TI, SV> graphql::InputTypeAs<'i, Arc, TI, SV> for str +where + Self: graphql::Type + + resolve::ToInputValue + + resolve::InputValueAs<'i, Arc, SV>, + TI: ?Sized, + SV: 'i, +{ + fn assert_input_type() {} +} -impl graphql::OutputType for str { +impl graphql::OutputType for str +where + Self: graphql::Type + resolve::Value + resolve::ValueAsync, + TI: ?Sized, + CX: ?Sized, +{ fn assert_output_type() {} } -impl graphql::Scalar for str { +impl<'me, 'i, TI, CX, SV> graphql::ScalarAs<'i, &'me Self, TI, CX, SV> for str +where + Self: graphql::InputTypeAs<'i, &'me Self, TI, SV> + + graphql::OutputType + + resolve::ScalarToken, + TI: ?Sized, + SV: 'i, +{ fn assert_scalar() {} -}*/ +} + +impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Box, TI, CX, SV> for str +where + Self: graphql::InputTypeAs<'i, Box, TI, SV> + + graphql::OutputType + + resolve::ScalarToken, + TI: ?Sized, + SV: 'i, +{ + fn assert_scalar() {} +} + +impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Rc, TI, CX, SV> for str +where + Self: graphql::InputTypeAs<'i, Rc, TI, SV> + + graphql::OutputType + + resolve::ScalarToken, + TI: ?Sized, + SV: 'i, +{ + fn assert_scalar() {} +} + +impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Arc, TI, CX, SV> for str +where + Self: graphql::InputTypeAs<'i, Arc, TI, SV> + + graphql::OutputType + + resolve::ScalarToken, + TI: ?Sized, + SV: 'i, +{ + fn assert_scalar() {} +} impl reflect::BaseType for str { const NAME: reflect::Type = "String"; // TODO: >::NAME; From 25404eb4a717d1e09d1e4f38a74551cda5db0011 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 17 Jun 2022 16:04:48 +0200 Subject: [PATCH 28/58] Rework meta creation [skip ci] --- juniper/src/behavior.rs | 26 +++++++++++--- juniper/src/executor/mod.rs | 68 ++++++++++++++++++++++++++--------- juniper/src/resolve/mod.rs | 4 +-- juniper/src/schema/meta.rs | 23 ++++++++++++ juniper/src/types/arc.rs | 2 +- juniper/src/types/box.rs | 2 +- juniper/src/types/nullable.rs | 7 ++-- juniper/src/types/option.rs | 7 ++-- juniper/src/types/rc.rs | 2 +- juniper/src/types/ref.rs | 2 +- juniper/src/types/ref_mut.rs | 2 +- juniper/src/types/str.rs | 12 +++---- 12 files changed, 114 insertions(+), 43 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index d7a53f27e..c68cae6cd 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -2,20 +2,38 @@ use std::{marker::PhantomData, sync::atomic::AtomicPtr}; +use crate::{meta::MetaType, resolve, Registry}; + /// Default standard behavior of GraphQL types implementation. #[derive(Debug)] pub enum Standard {} +/// Coercion of behavior types and type parameters. pub struct Coerce(PhantomData>>, T); impl Coerce { #[must_use] - pub const fn wrap(val: T) -> Self { - Self(PhantomData, val) + pub const fn wrap(value: T) -> Self { + Self(PhantomData, value) } } #[must_use] -pub const fn coerce(val: T) -> Coerce { - Coerce::wrap(val) +pub const fn coerce(value: T) -> Coerce { + Coerce::wrap(value) +} + +impl resolve::Type for Coerce +where + T: resolve::Type + ?Sized, + TI: ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> + where + SV: 'r, + { + T::meta(registry, type_info) + } } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 295af1736..7c0bd8d29 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -84,12 +84,6 @@ where field_path: Arc>, } -impl<'r, 'a, Ctx: ?Sized, S> Executor<'r, 'a, Ctx, S> { - pub(crate) fn current_type_new(&self) -> &TypeType<'a, S> { - &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 @@ -1297,17 +1291,58 @@ impl<'r, S: 'r> Registry<'r, S> { } */ - /// Builds a [`ScalarMeta`] information for the specified [`?Sized`] - /// [`graphql::Type`]. + /// Builds a [`ScalarMeta`] information for the specified non-[`Sized`] + /// [`graphql::Type`], 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_unsized(&mut self, type_info: &TI) -> ScalarMeta<'r, S> + /// [`TypeName`]: resolve::TypeName + pub fn register_scalar_unsized<'ti, T, TI>(&mut self, type_info: &'ti TI) -> MetaType<'r, S> where T: resolve::TypeName + resolve::InputValueAsRef + resolve::ScalarToken + ?Sized, TI: ?Sized, + 'ti: 'r, + S: Clone, { - // TODO: Allow using references. - ScalarMeta::new_unsized::(T::type_name(type_info).to_owned()) + // TODO: Use `drop` instead of `|_| {}` once Rust's inference becomes + // better for HRTB closures. + self.register_scalar_unsized_with::(type_info, |_| {}) + } + + /// Builds a [`ScalarMeta`] information for the specified non-[`Sized`] + /// [`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 + /// [`TypeName`]: resolve::TypeName + pub fn register_scalar_unsized_with<'ti, T, TI, F>( + &mut self, + type_info: &'ti TI, + customize: F, + ) -> MetaType<'r, S> + where + T: resolve::TypeName + resolve::InputValueAsRef + resolve::ScalarToken + ?Sized, + TI: ?Sized, + 'ti: 'r, + F: FnOnce(&mut ScalarMeta<'r, S>), + S: Clone, + { + self.entry_type::(type_info) + .or_insert_with(move || { + let mut scalar = ScalarMeta::new_unsized::(T::type_name(type_info)); + customize(&mut scalar); + scalar.into_meta() + }) + .clone() } /// Creates a [`ListMeta`] type. @@ -1342,7 +1377,8 @@ impl<'r, S: 'r> Registry<'r, S> { T: resolve::Type + ?Sized, Info: ?Sized, { - ListMeta::new(T::meta(self, info).as_type(), expected_size) + todo!() + //ListMeta::new(T::meta(self, info).as_type(), expected_size) } /// Creates a [`NullableMeta`] type. @@ -1359,13 +1395,13 @@ impl<'r, S: 'r> Registry<'r, S> { /// [`graphql::Type`]. /// /// [`graphql::Type`]: resolve::Type - pub fn build_nullable_type_reworked(&mut self, type_info: &TI) -> NullableMeta<'r> + pub fn wrap_nullable<'ti, T, TI>(&mut self, type_info: &'ti TI) -> MetaType<'r, S> where - T: resolve::Type + ?Sized, - BH: ?Sized, + T: resolve::Type + ?Sized, TI: ?Sized, + 'ti: 'r, { - NullableMeta::new(T::meta(self, type_info).as_type()) + NullableMeta::new(T::meta(self, type_info).into()).into_meta() } /// Creates an [`ObjectMeta`] type with the given `fields`. diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 868ce5003..353095fd1 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -6,9 +6,9 @@ use crate::{ }; pub trait Type { - fn meta<'r>( + fn meta<'r, 'ti: 'r>( registry: &mut Registry<'r, ScalarValue>, - type_info: &TypeInfo, + type_info: &'ti TypeInfo, ) -> MetaType<'r, ScalarValue> where ScalarValue: 'r; // TODO: remove? diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 17ce8c74e..73d0620d4 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -460,6 +460,29 @@ impl<'a, S> MetaType<'a, S> { } } +impl<'a, S> From> for Type<'a> { + fn from(meta: MetaType<'a, S>) -> Self { + match meta { + MetaType::Scalar(ScalarMeta { name, .. }) + | MetaType::Object(ObjectMeta { name, .. }) + | MetaType::Enum(EnumMeta { name, .. }) + | MetaType::Interface(InterfaceMeta { name, .. }) + | MetaType::Union(UnionMeta { name, .. }) + | MetaType::InputObject(InputObjectMeta { name, .. }) => Type::NonNullNamed(name), + MetaType::List(ListMeta { + of_type, + expected_size, + }) => Type::NonNullList(Box::new(of_type), expected_size), + MetaType::Nullable(NullableMeta { of_type }) => match of_type { + Type::NonNullNamed(inner) => Type::Named(inner), + Type::NonNullList(inner, expected_size) => Type::List(inner, expected_size), + t => t, + }, + MetaType::Placeholder(PlaceholderMeta { of_type }) => of_type, + } + } +} + impl<'a, S> ScalarMeta<'a, S> { /// Builds a new [`ScalarMeta`] type with the specified `name`. pub fn new(name: Cow<'a, str>) -> Self diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index ef7337991..db4c6cba7 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -15,7 +15,7 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index ec0194612..845343d22 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -13,7 +13,7 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { diff --git a/juniper/src/types/nullable.rs b/juniper/src/types/nullable.rs index 7728959c7..ee4a861aa 100644 --- a/juniper/src/types/nullable.rs +++ b/juniper/src/types/nullable.rs @@ -6,6 +6,7 @@ use futures::future; use crate::{ ast::{FromInputValue, InputValue, ToInputValue}, + behavior, executor::{ExecutionResult, Executor, Registry}, graphql, reflect, resolve, schema::meta::MetaType, @@ -278,13 +279,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { - registry - .build_nullable_type_reworked::(type_info) - .into_meta() + registry.wrap_nullable::, _>(type_info) } } diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index 0a83cc628..cdc919416 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -3,6 +3,7 @@ use futures::future; use crate::{ + behavior, executor::{ExecutionResult, Executor, Registry}, graphql, reflect, resolve, schema::meta::MetaType, @@ -15,13 +16,11 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { - registry - .build_nullable_type_reworked::(type_info) - .into_meta() + registry.wrap_nullable::, _>(type_info) } } diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index 950ec0425..f886e910c 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -15,7 +15,7 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 9b9668fe3..867f4f481 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -15,7 +15,7 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { diff --git a/juniper/src/types/ref_mut.rs b/juniper/src/types/ref_mut.rs index f24b96a5d..c0c479c68 100644 --- a/juniper/src/types/ref_mut.rs +++ b/juniper/src/types/ref_mut.rs @@ -15,7 +15,7 @@ where TI: ?Sized, BH: ?Sized, { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index 0e3f862b5..aeeac1edb 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -14,17 +14,11 @@ use crate::{ }; impl resolve::Type for str { - fn meta<'r>(registry: &mut Registry<'r, SV>, type_info: &TI) -> MetaType<'r, SV> + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> where SV: 'r, { - let meta = registry - .build_scalar_type_unsized::(type_info) - .into_meta(); - registry - .entry_type::(type_info) - .or_insert(meta) - .clone() + registry.register_scalar_unsized::(type_info) } } @@ -75,6 +69,8 @@ where SV: From, { fn to_input_value(&self) -> graphql::InputValue { + // TODO: Remove redundant `.to_owned()` allocation by allowing + // `ScalarValue` creation from reference? graphql::InputValue::scalar(self.to_owned()) } } From f2718cc01a9e0eed184e1bfa1c5e70a218d0edb6 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 17 Jun 2022 18:24:02 +0200 Subject: [PATCH 29/58] Impl macro for scalars, vol.1 [skip ci] --- juniper/src/behavior.rs | 60 ++++- juniper/src/executor/mod.rs | 34 ++- juniper/src/schema/meta.rs | 15 +- juniper/src/types/str.rs | 8 +- juniper_codegen/src/common/behavior.rs | 57 +++++ juniper_codegen/src/common/mod.rs | 1 + juniper_codegen/src/graphql_scalar/attr.rs | 2 + juniper_codegen/src/graphql_scalar/derive.rs | 13 +- juniper_codegen/src/graphql_scalar/mod.rs | 217 ++++++++++++------- 9 files changed, 303 insertions(+), 104 deletions(-) create mode 100644 juniper_codegen/src/common/behavior.rs diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index c68cae6cd..6ad3fee4b 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -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(PhantomData>>, T); +#[repr(transparent)] +pub struct Coerce(PhantomData>>, T); -impl Coerce { +impl Coerce { #[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(value: T) -> Coerce { +pub const fn coerce(value: T) -> Coerce { Coerce::wrap(value) } @@ -37,3 +48,44 @@ where T::meta(registry, type_info) } } + +impl resolve::TypeName for Coerce +where + T: resolve::TypeName + ?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 +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) -> Result { + T::try_from_input_value(v).map(Self::wrap) + } + + fn try_from_implicit_null() -> Result { + T::try_from_implicit_null().map(Self::wrap) + } +} + +impl resolve::ScalarToken for Coerce +where + T: resolve::ScalarToken + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 7c0bd8d29..6cf83769f 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1277,19 +1277,37 @@ impl<'r, S: 'r> Registry<'r, S> { ScalarMeta::new::(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(&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 + resolve::ScalarToken + resolve::InputValueOwned, - Info: ?Sized, + T: resolve::TypeName + resolve::InputValueOwned + resolve::ScalarToken, + TI: ?Sized, + 'ti: 'r, + F: FnOnce(&mut ScalarMeta<'r, S>), + S: Clone, { - // TODO: Allow using references. - ScalarMeta::new_new::(T::type_name(info).to_owned()) + self.entry_type::(type_info) + .or_insert_with(move || { + let mut scalar = ScalarMeta::new_reworked::(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`]. diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 73d0620d4..1113cffe6 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -499,12 +499,11 @@ impl<'a, S> ScalarMeta<'a, S> { } } - /* /// Builds a new [`ScalarMeta`] information with the specified `name`. // TODO: Use `impl Into>` argument once feature // `explicit_generic_args_with_impl_trait` hits stable: // https://github.com/rust-lang/rust/issues/83701 - pub fn new_new(name: N) -> Self + pub fn new_reworked(name: N) -> Self where T: resolve::InputValueOwned + resolve::ScalarToken, Cow<'a, str>: From, @@ -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::, + try_parse_fn: try_parse_fn_reworked::, parse_fn: >::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>` 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(v: &InputValue) -> Result<(), FieldError> +fn try_parse_fn_reworked(v: &InputValue) -> Result<(), FieldError> where - T: resolve::InputValueOwned, + T: resolve::InputValueOwned, { T::try_from_input_value(v) .map(drop) .map_err(T::Error::into_field_error) } -*/ fn try_parse_unsized_fn(v: &InputValue) -> Result<(), FieldError> where diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index aeeac1edb..a51299335 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -121,13 +121,11 @@ where } impl resolve::ScalarToken for str -//TODO: where String: resolve::ScalarToken, where - String: crate::ParseScalarValue, + String: resolve::ScalarToken, { fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - // TODO: >::parse_scalar_token(token) - >::from_str(token) + >::parse_scalar_token(token) } } @@ -228,7 +226,7 @@ where } impl reflect::BaseType for str { - const NAME: reflect::Type = "String"; // TODO: >::NAME; + const NAME: reflect::Type = ::NAME; } impl reflect::BaseSubTypes for str { diff --git a/juniper_codegen/src/common/behavior.rs b/juniper_codegen/src/common/behavior.rs new file mode 100644 index 000000000..d20d7c167 --- /dev/null +++ b/juniper_codegen/src/common/behavior.rs @@ -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 { + input.parse::().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(), + } + } +} diff --git a/juniper_codegen/src/common/mod.rs b/juniper_codegen/src/common/mod.rs index fd16d954d..ae2e92575 100644 --- a/juniper_codegen/src/common/mod.rs +++ b/juniper_codegen/src/common/mod.rs @@ -4,3 +4,4 @@ pub(crate) mod field; pub(crate) mod gen; pub(crate) mod parse; pub(crate) mod scalar; +pub(crate) mod behavior; diff --git a/juniper_codegen/src/graphql_scalar/attr.rs b/juniper_codegen/src/graphql_scalar/attr.rs index b9ff5b587..0c99f041c 100644 --- a/juniper_codegen/src/graphql_scalar/attr.rs +++ b/juniper_codegen/src/graphql_scalar/attr.rs @@ -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! { diff --git a/juniper_codegen/src/graphql_scalar/derive.rs b/juniper_codegen/src/graphql_scalar/derive.rs index e16bcd7d4..885bc0764 100644 --- a/juniper_codegen/src/graphql_scalar/derive.rs +++ b/juniper_codegen/src/graphql_scalar/derive.rs @@ -33,6 +33,7 @@ pub fn expand(input: TokenStream) -> syn::Result { 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(), diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 5a706851a..35b168658 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -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>, + /// 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>, + /// 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>>, - /// 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::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "to_output_with" => { input.parse::()?; let scl = input.parse::()?; @@ -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 { - >::NAME + >::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::(info) - #description - #specified_by_url - .into_meta() + registry.register_scalar_with::< + ::juniper::behavior::Coerce, _, _, + >(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 = - &[>::NAME]; + &[>::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, @@ -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 { + fn bound_parse_scalar_token( + &self, + sv: &scalar::Type, + bh: &behavior::Type, + ) -> Vec { 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 { + fn bound_parse_scalar_token( + &self, + sv: &scalar::Type, + bh: &behavior::Type, + ) -> Vec { 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, + + /// Indicator whether this [`Field`] is named. + is_named: bool, - /// Unnamed [`Field`]. - Unnamed(syn::Field), + /// [`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 } } } } From a05a0091f6a2ac970de408c8bbdc56c60f5e7438 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 17 Jun 2022 21:30:30 +0200 Subject: [PATCH 30/58] Impl macro for scalars, vol.2 [skip ci] --- juniper/src/executor/mod.rs | 17 ++++++----------- juniper_codegen/src/graphql_scalar/mod.rs | 9 +++++---- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 6cf83769f..2026f97b6 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -4,6 +4,7 @@ use std::{ borrow::Cow, cmp::Ordering, collections::{hash_map, HashMap}, + convert, fmt::{Debug, Display}, sync::{Arc, RwLock}, }; @@ -1297,14 +1298,12 @@ impl<'r, S: 'r> Registry<'r, S> { T: resolve::TypeName + resolve::InputValueOwned + resolve::ScalarToken, TI: ?Sized, 'ti: 'r, - F: FnOnce(&mut ScalarMeta<'r, S>), + F: FnOnce(ScalarMeta<'r, S>) -> ScalarMeta<'r, S>, S: Clone, { self.entry_type::(type_info) .or_insert_with(move || { - let mut scalar = ScalarMeta::new_reworked::(T::type_name(type_info)); - customize(&mut scalar); - scalar.into_meta() + customize(ScalarMeta::new_reworked::(T::type_name(type_info))).into_meta() }) .clone() } @@ -1326,9 +1325,7 @@ impl<'r, S: 'r> Registry<'r, S> { 'ti: 'r, S: Clone, { - // TODO: Use `drop` instead of `|_| {}` once Rust's inference becomes - // better for HRTB closures. - self.register_scalar_unsized_with::(type_info, |_| {}) + self.register_scalar_unsized_with::(type_info, convert::identity) } /// Builds a [`ScalarMeta`] information for the specified non-[`Sized`] @@ -1351,14 +1348,12 @@ impl<'r, S: 'r> Registry<'r, S> { T: resolve::TypeName + resolve::InputValueAsRef + resolve::ScalarToken + ?Sized, TI: ?Sized, 'ti: 'r, - F: FnOnce(&mut ScalarMeta<'r, S>), + F: FnOnce(ScalarMeta<'r, S>) -> ScalarMeta<'r, S>, S: Clone, { self.entry_type::(type_info) .or_insert_with(move || { - let mut scalar = ScalarMeta::new_unsized::(T::type_name(type_info)); - customize(&mut scalar); - scalar.into_meta() + customize(ScalarMeta::new_unsized::(T::type_name(type_info))).into_meta() }) .clone() } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 35b168658..e7a9e1a1f 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -529,9 +529,10 @@ impl Definition { 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> + ::juniper::behavior::Coerce: + ::juniper::resolve::TypeName<#inf, #bh> + + ::juniper::resolve::ScalarToken<#sv, #bh> + + ::juniper::resolve::InputValueOwned<#sv, #bh> }); let (impl_gens, _, where_clause) = generics.split_for_impl(); @@ -561,7 +562,7 @@ impl Definition { ::juniper::behavior::Coerce, _, _, >(type_info, |meta| { meta#description - #specified_by_url; + #specified_by_url }) } } From 679c1c1162842b870ee96b53eb82fa4aea7c24a8 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 17 Jun 2022 23:53:18 +0200 Subject: [PATCH 31/58] Impl macro for scalars, vol.3 [skip ci] --- juniper/src/ast.rs | 38 +++++- juniper_codegen/src/graphql_scalar/attr.rs | 2 + juniper_codegen/src/graphql_scalar/derive.rs | 1 + juniper_codegen/src/graphql_scalar/mod.rs | 129 +++++++++++++------ 4 files changed, 130 insertions(+), 40 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index b535f9215..c5f9897f8 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, fmt, hash::Hash, slice, vec}; +use std::{any::TypeId, borrow::Cow, convert::Into, fmt, hash::Hash, mem, slice, vec}; use indexmap::IndexMap; @@ -461,6 +461,42 @@ impl InputValue { _ => false, } } + + /// Maps the [`ScalarValue`] type of this [`InputValue`] into the specified + /// one. + pub fn map_scalar_value(self) -> InputValue + where + To: From + 'static, + S: 'static, + { + if TypeId::of::() == TypeId::of::() { + // SAFETY: This is safe, because we're transmuting the value into + // itself, so no invariants may change and we're just + // satisfying the type checker. + // As `mem::transmute_copy` creates a copy of data, we need + // `mem::ManuallyDrop` here to omit double-free when + // `S: Drop`. + let val = mem::ManuallyDrop::new(self); + unsafe { mem::transmute_copy(&*val) } + } else { + match self { + Self::Null => InputValue::Null, + Self::Scalar(s) => InputValue::Scalar(s.into()), + Self::Enum(v) => InputValue::Enum(v), + Self::Variable(n) => InputValue::Variable(n), + Self::List(l) => InputValue::List( + l.into_iter() + .map(|i| i.map(InputValue::map_scalar_value)) + .collect(), + ), + Self::Object(o) => InputValue::Object( + o.into_iter() + .map(|(k, v)| (k, v.map(InputValue::map_scalar_value))) + .collect(), + ), + } + } + } } impl fmt::Display for InputValue { diff --git a/juniper_codegen/src/graphql_scalar/attr.rs b/juniper_codegen/src/graphql_scalar/attr.rs index 0c99f041c..e9754e970 100644 --- a/juniper_codegen/src/graphql_scalar/attr.rs +++ b/juniper_codegen/src/graphql_scalar/attr.rs @@ -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, + scalar_value: attr.scalar.as_deref().into(), behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), }; @@ -97,6 +98,7 @@ fn expand_on_derive_input( description: attr.description.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, + scalar_value: attr.scalar.as_deref().into(), behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), }; diff --git a/juniper_codegen/src/graphql_scalar/derive.rs b/juniper_codegen/src/graphql_scalar/derive.rs index 885bc0764..835ea37b2 100644 --- a/juniper_codegen/src/graphql_scalar/derive.rs +++ b/juniper_codegen/src/graphql_scalar/derive.rs @@ -33,6 +33,7 @@ pub fn expand(input: TokenStream) -> syn::Result { description: attr.description.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, + scalar_value: attr.scalar.as_deref().into(), behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), } .to_token_stream()) diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index e7a9e1a1f..15c44703d 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -339,6 +339,13 @@ struct Definition { /// [1]: https://spec.graphql.org/October2021#sec-Scalars scalar: scalar::Type, + /// [`ScalarValue`] parametrization to generate code with for this + /// [GraphQL scalar][0]. + /// + /// [`ScalarValue`]: juniper::ScalarValue + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + scalar_value: ScalarValue, + /// [`Behavior`] parametrization to generate code with for this /// [GraphQL scalar][0]. /// @@ -804,7 +811,7 @@ impl Definition { generics .make_where_clause() .predicates - .push(self.methods.bound_try_from_input_value(<, sv, bh)); + .extend(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, bh); @@ -866,10 +873,10 @@ impl Definition { generics .make_where_clause() .predicates - .extend(self.methods.bound_parse_scalar_token(sv, bh)); + .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, bh); + let body = self.methods.expand_parse_scalar_token(&sv, bh); quote! { #[automatically_derived] @@ -1075,9 +1082,7 @@ impl Definition { #[must_use] 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) } @@ -1086,32 +1091,23 @@ impl Definition { #[must_use] 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 its [`scalar::Type`]. + /// [`syn::Generics`] and returns it. /// /// [`ScalarValue`] trait bound is not made here, because some trait - /// implementations may not require it depending on the generated code or + /// implementations may not require it, depending on the generated code or /// even at all. /// /// [`ScalarValue`]: juniper::ScalarValue #[must_use] - fn mix_scalar_value(&self, mut generics: syn::Generics) -> (&scalar::Type, syn::Generics) { - let scalar = &self.scalar; - - if scalar.is_implicit_generic() { - generics.params.push(parse_quote! { #scalar }); - } - if let Some(bound) = scalar.bounds() { - generics.make_where_clause().predicates.push(bound); - } - - (scalar, generics) + fn mix_scalar_value(&self, mut generics: syn::Generics) -> (&ScalarValue, syn::Generics) { + let sv = &self.scalar_value; + generics.params.push(parse_quote! { #sv }); + (sv, generics) } } @@ -1198,7 +1194,7 @@ impl Methods { &self, inf: &syn::Ident, cx: &syn::Ident, - sv: &scalar::Type, + sv: &ScalarValue, ) -> TokenStream { match self { Self::Custom { to_output, .. } @@ -1234,7 +1230,7 @@ impl Methods { &self, inf: &syn::Ident, cx: &syn::Ident, - sv: &scalar::Type, + sv: &ScalarValue, ) -> syn::WherePredicate { match self { Self::Custom { .. } @@ -1282,7 +1278,7 @@ 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 { + fn expand_to_input_value(&self, sv: &ScalarValue) -> TokenStream { match self { Self::Custom { to_output, .. } | Self::Delegated { @@ -1312,7 +1308,7 @@ impl Methods { /// /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue /// [0]: juniper::resolve::ToInputValue::to_input_value - fn bound_to_input_value(&self, sv: &scalar::Type) -> syn::WherePredicate { + fn bound_to_input_value(&self, sv: &ScalarValue) -> syn::WherePredicate { match self { Self::Custom { .. } | Self::Delegated { @@ -1360,14 +1356,21 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::InputValue::try_from_input_value - fn expand_try_from_input_value(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream { + fn expand_try_from_input_value(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { match self { Self::Custom { from_input, .. } | Self::Delegated { from_input: Some(from_input), .. } => { - quote! { #from_input(input) } + let map_sv = sv.custom.as_ref().map(|custom_ty| { + quote! { + .map_scalar_value() + } + }); + quote! { + #from_input(input#map_sv) + } } Self::Delegated { field, .. } => { @@ -1395,17 +1398,23 @@ impl Methods { fn bound_try_from_input_value( &self, lt: &syn::GenericParam, - sv: &scalar::Type, + sv: &ScalarValue, bh: &behavior::Type, - ) -> syn::WherePredicate { + ) -> Vec { match self { Self::Custom { .. } | Self::Delegated { from_input: Some(_), .. } => { - parse_quote! { - #sv: ::juniper::ScalarValue + if let Some(custom_sv) = &sv.custom { + vec![ + parse_quote! { #custom_sv: ::std::convert::From<#sv> }, + parse_quote! { #custom_sv: 'static }, + parse_quote! { #sv: 'static }, + ] + } else { + vec![parse_quote! { #sv: ::juniper::ScalarValue }] } } @@ -1413,10 +1422,10 @@ impl Methods { let field_ty = field.ty(); let field_bh = &field.behavior; - parse_quote! { + vec![parse_quote! { ::juniper::behavior::Coerce<#field_ty, #bh>: ::juniper::resolve::InputValue<#lt, #sv, #field_bh> - } + }] } } } @@ -1447,7 +1456,7 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { match self { Self::Custom { parse_token, .. } | Self::Delegated { @@ -1476,7 +1485,7 @@ impl Methods { /// [0]: juniper::resolve::ScalarToken::parse_scalar_token fn bound_parse_scalar_token( &self, - sv: &scalar::Type, + sv: &ScalarValue, bh: &behavior::Type, ) -> Vec { match self { @@ -1545,10 +1554,17 @@ impl ParseToken { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, sv: &scalar::Type, bh: &behavior::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { match self { Self::Custom(parse_token) => { - quote! { #parse_token(token) } + let into = sv.custom.as_ref().map(|custom_ty| { + quote! { + .map(<#sv as ::std::convert::From<#custom_ty>>::from) + } + }); + quote! { + #parse_token(token)#into + } } Self::Delegated(delegated) => delegated @@ -1585,13 +1601,19 @@ impl ParseToken { /// [0]: juniper::resolve::ScalarToken::parse_scalar_token fn bound_parse_scalar_token( &self, - sv: &scalar::Type, + sv: &ScalarValue, bh: &behavior::Type, ) -> Vec { match self { Self::Custom(_) => { - vec![parse_quote! { - #sv: ::juniper::ScalarValue + vec![if let Some(custom_sv) = &sv.custom { + parse_quote! { + #sv: ::std::convert::From<#custom_sv> + } + } else { + parse_quote! { + #sv: ::juniper::ScalarValue + } }] } @@ -1655,3 +1677,32 @@ impl Field { } } } + +/// [`ScalarValue`] parametrization of a [GraphQL scalar][0] implementation. +/// +/// [`ScalarValue`]: juniper::ScalarValue +/// [0]: https://spec.graphql.org/October2021#sec-Scalars +struct ScalarValue { + /// Concrete custom Rust type used in user-provided [`Methods`] as + /// [`ScalarValue`]. + /// + /// [`ScalarValue`]: juniper::ScalarValue + custom: Option, +} + +impl ToTokens for ScalarValue { + fn to_tokens(&self, tokens: &mut TokenStream) { + (quote! { __ScalarValue }).to_tokens(tokens) + } +} + +impl<'a> From> for ScalarValue { + fn from(attr: Option<&'a scalar::AttrValue>) -> Self { + Self { + custom: match attr { + Some(scalar::AttrValue::Concrete(ty)) => Some(ty.clone()), + Some(scalar::AttrValue::Generic(_)) | None => None, + }, + } + } +} From 1aafd3da5de5c6ea371b662d20b1f8363ef63524 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jun 2022 22:18:50 +0200 Subject: [PATCH 32/58] Impl macro for scalars, vol.4 [skip ci] --- juniper/src/ast.rs | 10 +- juniper/src/graphql/mod.rs | 1 + juniper_codegen/src/graphql_scalar/derive.rs | 16 +- juniper_codegen/src/graphql_scalar/mod.rs | 193 ++++++++++++++----- 4 files changed, 157 insertions(+), 63 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index c5f9897f8..40cceee4a 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -464,12 +464,12 @@ impl InputValue { /// Maps the [`ScalarValue`] type of this [`InputValue`] into the specified /// one. - pub fn map_scalar_value(self) -> InputValue + pub fn map_scalar_value(self) -> InputValue where - To: From + 'static, - S: 'static, + S: ScalarValue, + Into: ScalarValue, { - if TypeId::of::() == TypeId::of::() { + if TypeId::of::() == TypeId::of::() { // SAFETY: This is safe, because we're transmuting the value into // itself, so no invariants may change and we're just // satisfying the type checker. @@ -481,7 +481,7 @@ impl InputValue { } else { match self { Self::Null => InputValue::Null, - Self::Scalar(s) => InputValue::Scalar(s.into()), + Self::Scalar(s) => InputValue::Scalar(s.into_another()), Self::Enum(v) => InputValue::Enum(v), Self::Variable(n) => InputValue::Variable(n), Self::List(l) => InputValue::List( diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 9e0e1af92..a88b154dc 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -6,6 +6,7 @@ pub use crate::{ macros::{input_value, value, vars}, resolve::Type, value::Value, + GraphQLScalar as Scalar, }; /* diff --git a/juniper_codegen/src/graphql_scalar/derive.rs b/juniper_codegen/src/graphql_scalar/derive.rs index 835ea37b2..b4503796f 100644 --- a/juniper_codegen/src/graphql_scalar/derive.rs +++ b/juniper_codegen/src/graphql_scalar/derive.rs @@ -1,5 +1,7 @@ //! Code generation for `#[derive(GraphQLScalar)]` macro. +use std::convert::TryFrom; + use proc_macro2::TokenStream; use quote::ToTokens; use syn::{parse_quote, spanned::Spanned}; @@ -84,11 +86,8 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn: .first() .filter(|_| fields.unnamed.len() == 1) .cloned() - .map(|f| Field { - itself: f, - is_named: false, - behavior: None.unwrap_or_default(), // TODO: Parse attribute! - }) + .map(Field::try_from) + .transpose()? .ok_or_else(|| { ERR.custom_error( ast.span(), @@ -101,11 +100,8 @@ pub(super) fn parse_derived_methods(ast: &syn::DeriveInput, attr: &Attr) -> syn: .first() .filter(|_| fields.named.len() == 1) .cloned() - .map(|f| Field { - itself: f, - is_named: true, - behavior: None.unwrap_or_default(), // TODO: Parse attribute! - }) + .map(Field::try_from) + .transpose()? .ok_or_else(|| { ERR.custom_error( ast.span(), diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 15c44703d..0434ecfff 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -2,6 +2,8 @@ //! //! [1]: https://spec.graphql.org/October2021#sec-Scalars +use std::convert::TryFrom; + use proc_macro2::{Literal, TokenStream}; use quote::{format_ident, quote, ToTokens, TokenStreamExt}; use syn::{ @@ -536,10 +538,10 @@ impl Definition { let predicates = &mut generics.make_where_clause().predicates; predicates.push(parse_quote! { #sv: Clone }); predicates.push(parse_quote! { - ::juniper::behavior::Coerce: - ::juniper::resolve::TypeName<#inf, #bh> - + ::juniper::resolve::ScalarToken<#sv, #bh> - + ::juniper::resolve::InputValueOwned<#sv, #bh> + ::juniper::behavior::Coerce: + ::juniper::resolve::TypeName<#inf> + + ::juniper::resolve::ScalarToken<#sv> + + ::juniper::resolve::InputValueOwned<#sv> }); let (impl_gens, _, where_clause) = generics.split_for_impl(); @@ -566,7 +568,7 @@ impl Definition { #sv: '__r, { registry.register_scalar_with::< - ::juniper::behavior::Coerce, _, _, + ::juniper::behavior::Coerce, _, _, >(type_info, |meta| { meta#description #specified_by_url @@ -808,27 +810,25 @@ impl Definition { let (sv, mut generics) = self.mix_scalar_value(generics); let lt: syn::GenericParam = parse_quote! { '__inp }; generics.params.push(lt.clone()); - generics - .make_where_clause() - .predicates - .extend(self.methods.bound_try_from_input_value(<, sv, bh)); + let predicates = &mut generics.make_where_clause().predicates; + predicates.push(parse_quote! { #sv: #lt }); + predicates.extend(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, bh); + let error_ty = self.methods.expand_try_from_input_value_error(<, sv, bh); + let body = self.methods.expand_try_from_input_value(sv, bh); quote! { #[automatically_derived] impl#impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> for #ty #where_clause { - type Error = ::juniper::FieldError<#sv>; + type Error = #error_ty; fn try_from_input_value( input: &#lt ::juniper::graphql::InputValue<#sv>, ) -> ::std::result::Result { - #conversion.map_err( - ::juniper::IntoFieldError::<#sv>::into_field_error, - ) + #body } } } @@ -1363,13 +1363,14 @@ impl Methods { from_input: Some(from_input), .. } => { - let map_sv = sv.custom.as_ref().map(|custom_ty| { - quote! { - .map_scalar_value() - } + let map_sv = sv.custom.is_some().then(|| { + quote! { .map_scalar_value() } }); quote! { #from_input(input#map_sv) + .map_err( + ::juniper::IntoFieldError::<#sv>::into_field_error, + ) } } @@ -1382,13 +1383,45 @@ impl Methods { <::juniper::behavior::Coerce<#field_ty, #bh> as ::juniper::resolve::InputValue<'_, #sv, #field_bh>> ::try_from_input_value(input) - .into_inner() + .map(::juniper::behavior::Coerce::into_inner) .map(#self_constructor) } } } } + /// Expands error type of [`resolve::InputValue`] trait. + /// + /// [`resolve::InputValue`]: juniper::resolve::InputValue + fn expand_try_from_input_value_error( + &self, + lt: &syn::GenericParam, + sv: &ScalarValue, + bh: &behavior::Type, + ) -> syn::Type { + match self { + Self::Custom { .. } + | Self::Delegated { + from_input: Some(_), + .. + } => { + parse_quote! { + ::juniper::FieldError<#sv> + } + } + + Self::Delegated { field, .. } => { + let field_ty = field.ty(); + let field_bh = &field.behavior; + + parse_quote! { + <::juniper::behavior::Coerce<#field_ty, #bh> as + ::juniper::resolve::InputValue<#lt, #sv, #field_bh>>::Error + } + } + } + } + /// Generates additional trait bounds for [`resolve::InputValue`] /// implementation allowing to execute /// [`resolve::InputValue::try_from_input_value()`][0] method. @@ -1407,15 +1440,15 @@ impl Methods { from_input: Some(_), .. } => { + let mut bounds = vec![parse_quote! { + #sv: ::juniper::ScalarValue + }]; if let Some(custom_sv) = &sv.custom { - vec![ - parse_quote! { #custom_sv: ::std::convert::From<#sv> }, - parse_quote! { #custom_sv: 'static }, - parse_quote! { #sv: 'static }, - ] - } else { - vec![parse_quote! { #sv: ::juniper::ScalarValue }] + bounds.push(parse_quote! { + #custom_sv: ::juniper::ScalarValue + }); } + bounds } Self::Delegated { field, .. } => { @@ -1557,10 +1590,8 @@ impl ParseToken { fn expand_parse_scalar_token(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { match self { Self::Custom(parse_token) => { - let into = sv.custom.as_ref().map(|custom_ty| { - quote! { - .map(<#sv as ::std::convert::From<#custom_ty>>::from) - } + let into = sv.custom.is_some().then(|| { + quote! { .map(::juniper::ScalarValue::into_another) } }); quote! { #parse_token(token)#into @@ -1606,15 +1637,15 @@ impl ParseToken { ) -> Vec { match self { Self::Custom(_) => { - vec![if let Some(custom_sv) = &sv.custom { - parse_quote! { - #sv: ::std::convert::From<#custom_sv> - } - } else { - parse_quote! { - #sv: ::juniper::ScalarValue - } - }] + let mut bounds = vec![parse_quote! { + #sv: ::juniper::ScalarValue + }]; + if let Some(custom_sv) = &sv.custom { + bounds.push(parse_quote! { + #custom_sv: ::juniper::ScalarValue + }); + } + bounds } Self::Delegated(delegated) => delegated @@ -1630,6 +1661,65 @@ impl ParseToken { } } +/// Available arguments behind `#[graphql]` attribute on a [`Field`] when +/// generating code for a [GraphQL scalar][0] implementation. +/// +/// [0]: https://spec.graphql.org/October2021#sec-Scalars +#[derive(Debug, Default)] +struct FieldAttr { + /// Explicitly specified type of the custom [`Behavior`] used for + /// [GraphQL scalar][0] implementation by the [`Field`]. + /// + /// 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>, +} + +impl Parse for FieldAttr { + fn parse(input: ParseStream<'_>) -> syn::Result { + let mut out = Self::default(); + while !input.is_empty() { + let ident = input.parse_any_ident()?; + match ident.to_string().as_str() { + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } + name => { + return Err(err::unknown_arg(&ident, name)); + } + } + input.try_parse::()?; + } + Ok(out) + } +} + +impl FieldAttr { + /// Tries to merge two [`FieldAttr`]s into a single one, reporting about + /// duplicates, if any. + fn try_merge(self, mut another: Self) -> syn::Result { + Ok(Self { + behavior: try_merge_opt!(behavior: self, another), + }) + } + + /// Parses [`FieldAttr`] from the given multiple `name`d [`syn::Attribute`]s + /// placed on a field definition. + fn from_attrs(name: &str, attrs: &[syn::Attribute]) -> syn::Result { + filter_attrs(name, attrs) + .map(|attr| attr.parse_args()) + .try_fold(Self::default(), |prev, curr| prev.try_merge(curr?)) + } +} + /// Inner field of a type implementing [GraphQL scalar][0], that the /// implementation delegates calls to. /// @@ -1638,19 +1728,28 @@ struct Field { /// This [`Field`] itself. itself: syn::Field, - /// Indicator whether this [`Field`] is named. - is_named: bool, - /// [`Behavior`] parametrization of this [`Field`]. /// /// [`Behavior`]: juniper::behavior behavior: behavior::Type, } +impl TryFrom for Field { + type Error = syn::Error; + + fn try_from(field: syn::Field) -> syn::Result { + let attr = FieldAttr::from_attrs("graphql", &field.attrs)?; + Ok(Self { + itself: field, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + }) + } +} + impl ToTokens for Field { fn to_tokens(&self, tokens: &mut TokenStream) { - if self.is_named { - self.itself.ident.to_tokens(tokens) + if let Some(name) = &self.itself.ident { + name.to_tokens(tokens) } else { tokens.append(Literal::u8_unsuffixed(0)) } @@ -1668,10 +1767,8 @@ impl Field { /// /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn closure_constructor(&self) -> TokenStream { - if self.is_named { - let ident = &self.itself.ident; - - quote! { |v| Self { #ident: v } } + if let Some(name) = &self.itself.ident { + quote! { |v| Self { #name: v } } } else { quote! { Self } } From 4d7770d226b2085f2d087d73a7b6bcd067ae2942 Mon Sep 17 00:00:00 2001 From: tyranron Date: Sun, 19 Jun 2022 23:06:59 +0200 Subject: [PATCH 33/58] Impl macro for scalars, vol.5 [skip ci] --- juniper/src/behavior.rs | 7 +- juniper_codegen/src/graphql_scalar/attr.rs | 6 +- juniper_codegen/src/graphql_scalar/derive.rs | 18 +-- juniper_codegen/src/graphql_scalar/mod.rs | 138 ++++++++----------- 4 files changed, 76 insertions(+), 93 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index 6ad3fee4b..c98c959f3 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -1,4 +1,4 @@ -//! Default GraphQL behaviors. +//! GraphQL types behavior machinery. use std::{marker::PhantomData, sync::atomic::AtomicPtr}; @@ -13,22 +13,25 @@ use crate::{ #[derive(Debug)] pub enum Standard {} -/// Coercion of behavior types and type parameters. +/// Transparent wrapper allowing coercion of behavior types and type parameters. #[repr(transparent)] pub struct Coerce(PhantomData>>, T); impl Coerce { + /// Wraps the provided `value` into a [`Coerce`] wrapper. #[must_use] pub const fn wrap(value: T) -> Self { Self(PhantomData, value) } + /// Unwraps into the inner value. #[must_use] pub fn into_inner(self) -> T { self.1 } } +/// Wraps the provided `value` into a [`Coerce`] wrapper. #[must_use] pub const fn coerce(value: T) -> Coerce { Coerce::wrap(value) diff --git a/juniper_codegen/src/graphql_scalar/attr.rs b/juniper_codegen/src/graphql_scalar/attr.rs index e9754e970..01b34dd43 100644 --- a/juniper_codegen/src/graphql_scalar/attr.rs +++ b/juniper_codegen/src/graphql_scalar/attr.rs @@ -6,7 +6,6 @@ use syn::{parse_quote, spanned::Spanned}; use crate::{ common::{parse, scalar}, - graphql_scalar::TypeOrIdent, GraphQLScope, }; @@ -50,7 +49,7 @@ fn expand_on_type_alias( let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics); let def = Definition { - ty: TypeOrIdent::Type(ast.ty.clone()), + ident: ast.ident.clone(), where_clause: attr .where_clause .map_or_else(Vec::new, |cl| cl.into_inner()), @@ -80,11 +79,12 @@ fn expand_on_derive_input( ast: syn::DeriveInput, ) -> syn::Result { let attr = Attr::from_attrs("graphql_scalar", &attrs)?; + let methods = parse_derived_methods(&ast, &attr)?; let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics); let def = Definition { - ty: TypeOrIdent::Ident(ast.ident.clone()), + ident: ast.ident.clone(), where_clause: attr .where_clause .map_or_else(Vec::new, |cl| cl.into_inner()), diff --git a/juniper_codegen/src/graphql_scalar/derive.rs b/juniper_codegen/src/graphql_scalar/derive.rs index b4503796f..33c0db850 100644 --- a/juniper_codegen/src/graphql_scalar/derive.rs +++ b/juniper_codegen/src/graphql_scalar/derive.rs @@ -8,7 +8,7 @@ use syn::{parse_quote, spanned::Spanned}; use crate::{common::scalar, result::GraphQLScope}; -use super::{Attr, Definition, Field, Methods, ParseToken, TypeOrIdent}; +use super::{Attr, Definition, Field, Methods, ParseToken}; /// [`GraphQLScope`] of errors for `#[derive(GraphQLScalar)]` macro. const ERR: GraphQLScope = GraphQLScope::ScalarDerive; @@ -17,21 +17,23 @@ const ERR: GraphQLScope = GraphQLScope::ScalarDerive; pub fn expand(input: TokenStream) -> syn::Result { let ast = syn::parse2::(input)?; let attr = Attr::from_attrs("graphql", &ast.attrs)?; + let methods = parse_derived_methods(&ast, &attr)?; let scalar = scalar::Type::parse(attr.scalar.as_deref(), &ast.generics); + let name = attr + .name + .as_deref() + .cloned() + .unwrap_or_else(|| ast.ident.to_string()); Ok(Definition { - ty: TypeOrIdent::Ident(ast.ident.clone()), + ident: ast.ident, where_clause: attr .where_clause .map_or_else(Vec::new, |cl| cl.into_inner()), - generics: ast.generics.clone(), + generics: ast.generics, methods, - name: attr - .name - .as_deref() - .cloned() - .unwrap_or_else(|| ast.ident.to_string()), + name, description: attr.description.as_deref().cloned(), specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 0434ecfff..aca8dce99 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -284,17 +284,6 @@ impl Attr { } } -/// [`syn::Type`] in case of `#[graphql_scalar]` or [`syn::Ident`] in case of -/// `#[derive(GraphQLScalar)]`. -#[derive(Clone)] -enum TypeOrIdent { - /// [`syn::Type`]. - Type(Box), - - /// [`syn::Ident`]. - Ident(syn::Ident), -} - /// Definition of [GraphQL scalar][1] for code generation. /// /// [1]: https://spec.graphql.org/October2021#sec-Scalars @@ -304,10 +293,10 @@ struct Definition { /// [1]: https://spec.graphql.org/October2021#sec-Scalars name: String, - /// [`TypeOrIdent`] of this [GraphQL scalar][1] in GraphQL schema. + /// [`syn::Ident`] of the Rust type implementing this [GraphQL scalar][0]. /// - /// [1]: https://spec.graphql.org/October2021#sec-Scalars - ty: TypeOrIdent, + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + ident: syn::Ident, /// Additional [`Self::generics`] [`syn::WhereClause`] predicates. where_clause: Vec, @@ -371,7 +360,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_to_input_value().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); @@ -749,19 +738,20 @@ impl Definition { /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_to_input_value(&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 - .push(self.methods.bound_to_input_value(sv)); + .extend(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 + impl#impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> for #ty #where_clause { fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { @@ -812,11 +802,11 @@ impl Definition { generics.params.push(lt.clone()); let predicates = &mut generics.make_where_clause().predicates; predicates.push(parse_quote! { #sv: #lt }); - predicates.extend(self.methods.bound_try_from_input_value(<, sv, bh)); + predicates.extend(self.methods.bound_try_from_input_value(<, sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let error_ty = self.methods.expand_try_from_input_value_error(<, sv, bh); - let body = self.methods.expand_try_from_input_value(sv, bh); + let error_ty = self.methods.expand_try_from_input_value_error(<, sv); + let body = self.methods.expand_try_from_input_value(sv); quote! { #[automatically_derived] @@ -873,10 +863,10 @@ impl Definition { generics .make_where_clause() .predicates - .extend(self.methods.bound_parse_scalar_token(&sv, bh)); + .extend(self.methods.bound_parse_scalar_token(sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); - let body = self.methods.expand_parse_scalar_token(&sv, bh); + let body = self.methods.expand_parse_scalar_token(sv); quote! { #[automatically_derived] @@ -987,12 +977,10 @@ impl Definition { fn impl_self_and_generics(&self, for_async: bool) -> (TokenStream, syn::Generics) { let mut generics = self.generics.clone(); - let ty = match &self.ty { - TypeOrIdent::Type(ty) => ty.into_token_stream(), - TypeOrIdent::Ident(ident) => { - let (_, ty_gen, _) = self.generics.split_for_impl(); - quote! { #ident#ty_gen } - } + let ty = { + let ident = &self.ident; + let (_, ty_gen, _) = self.generics.split_for_impl(); + quote! { #ident#ty_gen } }; if !self.where_clause.is_empty() { @@ -1022,15 +1010,10 @@ impl Definition { ModifyLifetimes.visit_generics_mut(&mut generics); let lifetimes = generics.lifetimes().map(|lt| <.lifetime); - let ty = match self.ty.clone() { - TypeOrIdent::Type(mut ty) => { - ModifyLifetimes.visit_type_mut(&mut ty); - ty.into_token_stream() - } - TypeOrIdent::Ident(ident) => { - let (_, ty_gens, _) = generics.split_for_impl(); - quote! { #ident#ty_gens } - } + let ty = { + let ident = &self.ident; + let (_, ty_gen, _) = generics.split_for_impl(); + quote! { #ident#ty_gen } }; quote! { for<#( #lifetimes ),*> #ty } @@ -1059,12 +1042,10 @@ impl Definition { fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let mut generics = self.generics.clone(); - let ty = match &self.ty { - TypeOrIdent::Type(ty) => (**ty).clone(), - TypeOrIdent::Ident(ident) => { - let (_, ty_gen, _) = self.generics.split_for_impl(); - parse_quote! { #ident#ty_gen } - } + let ty = { + let ident = &self.ident; + let (_, ty_gen, _) = self.generics.split_for_impl(); + parse_quote! { #ident#ty_gen } }; if !self.where_clause.is_empty() { @@ -1285,17 +1266,22 @@ impl Methods { to_output: Some(to_output), .. } => { + let into = sv.custom.is_some().then(|| { + quote! { .map_scalar_value() } + }); quote! { - let v = #to_output(self); + let v = #to_output(self)#into; ::juniper::resolve::ToInputValue::<#sv>::to_input_value(&v) } } Self::Delegated { field, .. } => { let field_ty = field.ty(); + let field_bh = &field.behavior; quote! { - <#field_ty as ::juniper::resolve::ToInputValue<#sv>> + <#field_ty as + ::juniper::resolve::ToInputValue<#sv, #field_bh>> ::to_input_value(&self.#field) } } @@ -1308,23 +1294,30 @@ impl Methods { /// /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue /// [0]: juniper::resolve::ToInputValue::to_input_value - fn bound_to_input_value(&self, sv: &ScalarValue) -> syn::WherePredicate { + fn bound_to_input_value(&self, sv: &ScalarValue) -> Vec { match self { Self::Custom { .. } | Self::Delegated { to_output: Some(_), .. } => { - parse_quote! { + let mut bounds = vec![parse_quote! { #sv: ::juniper::ScalarValue + }]; + if let Some(custom_sv) = &sv.custom { + bounds.push(parse_quote! { + #custom_sv: ::juniper::ScalarValue + }); } + bounds } Self::Delegated { field, .. } => { let field_ty = field.ty(); + let field_bh = &field.behavior; - parse_quote! { - #field_ty: ::juniper::resolve::ToInputValue<#sv>> - } + vec![parse_quote! { + #field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh> + }] } } } @@ -1356,7 +1349,7 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::InputValue::try_from_input_value - fn expand_try_from_input_value(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { + fn expand_try_from_input_value(&self, sv: &ScalarValue) -> TokenStream { match self { Self::Custom { from_input, .. } | Self::Delegated { @@ -1380,10 +1373,9 @@ impl Methods { let self_constructor = field.closure_constructor(); quote! { - <::juniper::behavior::Coerce<#field_ty, #bh> as + <#field_ty as ::juniper::resolve::InputValue<'_, #sv, #field_bh>> ::try_from_input_value(input) - .map(::juniper::behavior::Coerce::into_inner) .map(#self_constructor) } } @@ -1397,7 +1389,6 @@ impl Methods { &self, lt: &syn::GenericParam, sv: &ScalarValue, - bh: &behavior::Type, ) -> syn::Type { match self { Self::Custom { .. } @@ -1415,7 +1406,7 @@ impl Methods { let field_bh = &field.behavior; parse_quote! { - <::juniper::behavior::Coerce<#field_ty, #bh> as + <#field_ty as ::juniper::resolve::InputValue<#lt, #sv, #field_bh>>::Error } } @@ -1432,7 +1423,6 @@ impl Methods { &self, lt: &syn::GenericParam, sv: &ScalarValue, - bh: &behavior::Type, ) -> Vec { match self { Self::Custom { .. } @@ -1456,7 +1446,7 @@ impl Methods { let field_bh = &field.behavior; vec![parse_quote! { - ::juniper::behavior::Coerce<#field_ty, #bh>: + #field_ty: ::juniper::resolve::InputValue<#lt, #sv, #field_bh> }] } @@ -1489,20 +1479,20 @@ impl Methods { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &ScalarValue) -> TokenStream { match self { Self::Custom { parse_token, .. } | Self::Delegated { parse_token: Some(parse_token), .. - } => parse_token.expand_parse_scalar_token(sv, bh), + } => parse_token.expand_parse_scalar_token(sv), Self::Delegated { field, .. } => { let field_ty = field.ty(); let field_bh = &field.behavior; quote! { - <::juniper::behavior::Coerce<#field_ty, #bh> as + <#field_ty as ::juniper::resolve::ScalarToken<#sv, #field_bh>> ::parse_scalar_token(token) } @@ -1516,25 +1506,20 @@ impl Methods { /// /// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn bound_parse_scalar_token( - &self, - sv: &ScalarValue, - bh: &behavior::Type, - ) -> Vec { + fn bound_parse_scalar_token(&self, sv: &ScalarValue) -> Vec { match self { Self::Custom { parse_token, .. } | Self::Delegated { parse_token: Some(parse_token), .. - } => parse_token.bound_parse_scalar_token(sv, bh), + } => parse_token.bound_parse_scalar_token(sv), Self::Delegated { field, .. } => { let field_ty = field.ty(); let field_bh = &field.behavior; vec![parse_quote! { - ::juniper::behavior::Coerce<#field_ty, #bh>: - ::juniper::resolve::ScalarToken<#sv, #field_bh> + #field_ty: ::juniper::resolve::ScalarToken<#sv, #field_bh> }] } } @@ -1587,7 +1572,7 @@ impl ParseToken { /// method. /// /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn expand_parse_scalar_token(&self, sv: &ScalarValue, bh: &behavior::Type) -> TokenStream { + fn expand_parse_scalar_token(&self, sv: &ScalarValue) -> TokenStream { match self { Self::Custom(parse_token) => { let into = sv.custom.is_some().then(|| { @@ -1604,16 +1589,14 @@ impl ParseToken { acc.map_or_else( || { Some(quote! { - <::juniper::behavior::Coerce<#ty, #bh> as - ::juniper::resolve::ScalarToken<#sv>> + <#ty as ::juniper::resolve::ScalarToken<#sv>> ::parse_scalar_token(token) }) }, |prev| { Some(quote! { #prev.or_else(|_| { - <::juniper::behavior::Coerce<#ty, #bh> as - ::juniper::resolve::ScalarToken<#sv>> + <#ty as ::juniper::resolve::ScalarToken<#sv>> ::parse_scalar_token(token) }) }) @@ -1630,11 +1613,7 @@ impl ParseToken { /// /// [`resolve::ScalarToken`]: juniper::resolve::ScalarToken /// [0]: juniper::resolve::ScalarToken::parse_scalar_token - fn bound_parse_scalar_token( - &self, - sv: &ScalarValue, - bh: &behavior::Type, - ) -> Vec { + fn bound_parse_scalar_token(&self, sv: &ScalarValue) -> Vec { match self { Self::Custom(_) => { let mut bounds = vec![parse_quote! { @@ -1652,8 +1631,7 @@ impl ParseToken { .iter() .map(|ty| { parse_quote! { - ::juniper::behavior::Coerce<#ty, #bh>: - ::juniper::resolve::ScalarToken<#sv> + #ty: ::juniper::resolve::ScalarToken<#sv> } }) .collect(), From 66c11ec7825b49a617f75b4671a7a9143e305c54 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 20 Jun 2022 17:41:45 +0200 Subject: [PATCH 34/58] Impl macro for scalars, vol.6 [skip ci] --- juniper_codegen/src/graphql_scalar/mod.rs | 167 ++++++++++++++++------ 1 file changed, 121 insertions(+), 46 deletions(-) diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index aca8dce99..876ec9fa0 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -358,14 +358,14 @@ impl ToTokens for Definition { //////////////////////////////////////////////////////////////////////// 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_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_graphql_output_type().to_tokens(into); - //self.impl_graphql_output_type().to_tokens(into); - //self.impl_graphql_scalar().to_tokens(into); + self.impl_graphql_input_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); } } @@ -395,29 +395,58 @@ impl Definition { } } - /// Returns generated code implementing [`graphql::InputType`] and - /// [`graphql::OutputType`] traits for this [GraphQL scalar][0]. + /// Returns generated code implementing [`graphql::InputType`] trait for + /// this [GraphQL scalar][0]. /// /// [`graphql::InputType`]: juniper::graphql::InputType - /// [`graphql::OutputType`]: juniper::graphql::OutputType /// [0]: https://spec.graphql.org/October2021#sec-Scalars #[must_use] - fn impl_graphql_input_and_output_type(&self) -> TokenStream { + fn impl_graphql_input_type(&self) -> TokenStream { + let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::resolve::Type<#inf, #sv, #bh> + + ::juniper::resolve::ToInputValue<#sv, #bh> + + ::juniper::resolve::InputValue<#lt, #sv, #bh> + }); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::graphql::InputType<#sv> for #ty - #where_clause + impl#impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + for #ty #where_clause { fn assert_input_type() {} } + } + } + /// Returns generated code implementing [`graphql::OutputType`] trait for + /// this [GraphQL scalar][0]. + /// + /// [`graphql::OutputType`]: juniper::graphql::OutputType + /// [0]: https://spec.graphql.org/October2021#sec-Scalars + #[must_use] + fn impl_graphql_output_type(&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, mut generics) = self.mix_scalar_value(generics); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::resolve::Type<#inf, #sv, #bh> + + ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + + ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { #[automatically_derived] - impl#impl_gens ::juniper::graphql::OutputType<#sv> for #ty - #where_clause + impl#impl_gens ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> + for #ty #where_clause { fn assert_output_type() {} } @@ -431,14 +460,23 @@ impl Definition { /// [0]: https://spec.graphql.org/October2021#sec-Scalars #[must_use] fn impl_graphql_scalar(&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); + let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + + ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> + + ::juniper::resolve::ScalarToken<#sv, #bh> + }); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { #[automatically_derived] - impl#impl_gens ::juniper::graphql::Scalar<#sv> for #ty - #where_clause + impl#impl_gens ::juniper::graphql::Scalar<#lt, #inf, #cx, #sv, #bh> + for #ty #where_clause { fn assert_scalar() {} } @@ -524,9 +562,9 @@ impl Definition { let (ty, generics) = self.ty_and_generics(); let (inf, generics) = self.mix_type_info(generics); let (sv, mut generics) = self.mix_scalar_value(generics); - let predicates = &mut generics.make_where_clause().predicates; - predicates.push(parse_quote! { #sv: Clone }); - predicates.push(parse_quote! { + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { #sv: Clone }); + preds.push(parse_quote! { ::juniper::behavior::Coerce: ::juniper::resolve::TypeName<#inf> + ::juniper::resolve::ScalarToken<#sv> @@ -610,6 +648,7 @@ impl Definition { /// [`resolve::Value`]: juniper::resolve::Value /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_value(&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); @@ -617,20 +656,20 @@ impl Definition { generics .make_where_clause() .predicates - .push(self.methods.bound_resolve_value(&inf, &cx, sv)); + .extend(self.methods.bound_resolve_value(&inf, &cx, sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); let body = self.methods.expand_resolve_value(&inf, &cx, sv); quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #sv> for #ty - #where_clause + impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause { fn resolve_value( &self, - selection: Option<&[::juniper::Selection<'_, #sv>]>, - info: &#inf, + selection_set: Option<&[::juniper::Selection<'_, #sv>]>, + type_info: &#inf, executor: &::juniper::Executor<'_, '_, #cx, #sv>, ) -> ::juniper::ExecutionResult<#sv> { #body @@ -675,13 +714,14 @@ impl Definition { /// [`resolve::ValueAsync`]: juniper::resolve::ValueAsync /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_resolve_value_async(&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, mut generics) = self.mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { - Self: ::juniper::resolve::Value<#inf, #cx, #sv> + Self: ::juniper::resolve::Value<#inf, #cx, #sv, #bh> }); preds.push(parse_quote! { #sv: Send @@ -690,17 +730,20 @@ impl Definition { quote! { #[automatically_derived] - impl#impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv> for #ty - #where_clause + impl#impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> + for #ty #where_clause { fn resolve_value_async<'__r>( &'__r self, - selection: Option<&'__r [::juniper::Selection<'_, #sv>]>, - info: &'__r #inf, + sel_set: Option<&'__r [::juniper::Selection<'_, #sv>]>, + type_info: &'__r #inf, executor: &'__r ::juniper::Executor<'_, '_, #cx, #sv>, - ) -> ::juniper::BoxFuture<'__r, ::juniper::ExecutionResult<#sv>> { - let v = > - ::resolve_value(self, selection, info, executor); + ) -> ::juniper::BoxFuture< + '__r, ::juniper::ExecutionResult<#sv>, + > { + let v = + > + ::resolve_value(self, sel_set, type_info, executor); ::std::boxed::Box::pin(::juniper::futures::future::ready(v)) } } @@ -797,12 +840,12 @@ impl Definition { 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 }; - generics.params.push(lt.clone()); - let predicates = &mut generics.make_where_clause().predicates; - predicates.push(parse_quote! { #sv: #lt }); - predicates.extend(self.methods.bound_try_from_input_value(<, sv)); + let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + generics + .make_where_clause() + .predicates + .extend(self.methods.bound_try_from_input_value(<, sv)); let (impl_gens, _, where_clause) = generics.split_for_impl(); let error_ty = self.methods.expand_try_from_input_value_error(<, sv); @@ -1090,6 +1133,25 @@ impl Definition { generics.params.push(parse_quote! { #sv }); (sv, generics) } + + /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the + /// provided [`syn::Generics`] and returns it. + /// + /// [`InputValue`]: juniper::resolve::InputValue + #[must_use] + fn mix_input_lifetime( + &self, + mut generics: syn::Generics, + sv: &ScalarValue, + ) -> (syn::GenericParam, syn::Generics) { + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #sv: #lt }); + (lt, generics) + } } /// Adds `__fa__` prefix to all lifetimes to avoid "lifetime name `'a` shadows a @@ -1183,18 +1245,23 @@ impl Methods { to_output: Some(to_output), .. } => { - quote! { Ok(#to_output(self)) } + let into = sv.custom.is_some().then(|| { + quote! { .map_scalar_value() } + }); + quote! { Ok(#to_output(self)#into) } } Self::Delegated { field, .. } => { let field_ty = field.ty(); + let field_bh = &field.behavior; quote! { - <#field_ty as ::juniper::resolve::Value<#inf, #cx, #sv>> + <#field_ty as + ::juniper::resolve::Value<#inf, #cx, #sv, #field_bh>> ::resolve_value( &self.#field, - info, - selection, + selection_set, + type_info, executor, ) } @@ -1212,23 +1279,31 @@ impl Methods { inf: &syn::Ident, cx: &syn::Ident, sv: &ScalarValue, - ) -> syn::WherePredicate { + ) -> Vec { match self { Self::Custom { .. } | Self::Delegated { to_output: Some(_), .. } => { - parse_quote! { + let mut bounds = vec![parse_quote! { #sv: ::juniper::ScalarValue + }]; + if let Some(custom_sv) = &sv.custom { + bounds.push(parse_quote! { + #custom_sv: ::juniper::ScalarValue + }); } + bounds } Self::Delegated { field, .. } => { let field_ty = field.ty(); + let field_bh = &field.behavior; - parse_quote! { - #field_ty: ::juniper::resolve::Value<#inf, #cx, #sv> - } + vec![parse_quote! { + #field_ty: + ::juniper::resolve::Value<#inf, #cx, #sv, #field_bh> + }] } } } From 2d89de3403e4e22d2df49d0e88bffa0391ac0486 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 20 Jun 2022 19:12:58 +0200 Subject: [PATCH 35/58] Impl new machinery for `Vec` [skip ci] --- juniper/src/executor/mod.rs | 20 ++-- juniper/src/schema/meta.rs | 4 +- juniper/src/types/iter.rs | 47 +++++----- juniper/src/types/mod.rs | 2 +- juniper/src/types/vec.rs | 147 +++++++++++++++++++++--------- juniper_codegen/src/common/mod.rs | 2 +- 6 files changed, 146 insertions(+), 76 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 2026f97b6..73d8b6a48 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -85,6 +85,12 @@ where field_path: Arc>, } +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( + pub fn wrap_list<'ti, T, TI>( &mut self, - info: &Info, + type_info: &'ti TI, expected_size: Option, - ) -> ListMeta<'r> + ) -> MetaType<'r, S> where - T: resolve::Type + ?Sized, - Info: ?Sized, + T: resolve::Type + ?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 1113cffe6..47e7f705a 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 8d6aad9df..f0f599a04 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, -) -> ExecutionResult + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, +) -> ExecutionResult where I: Iterator + ExactSizeIterator, - T: resolve::Value + ?Sized + 't, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value + ?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 + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor<'r, '_, CX, SV>, +) -> ExecutionResult where I: Iterator + ExactSizeIterator, - T: resolve::ValueAsync + ?Sized + 't, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::ValueAsync + ?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::>(); 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 d331959a3..44635ccd1 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 ad4cd8edb..df2d87a68 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 resolve::Type for Vec +impl resolve::Type for Vec where - T: resolve::Type, - Info: ?Sized, + T: resolve::Type, + 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::(info, None).into_meta() + registry.wrap_list::, _>(type_info, None) } } -impl resolve::Value for Vec +impl resolve::Value for Vec where - T: resolve::Value, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - iter::resolve_list(self.iter(), selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, type_info, executor) } } -impl resolve::ValueAsync for Vec +impl resolve::ValueAsync for Vec where - T: resolve::ValueAsync + Sync, - Info: Sync + ?Sized, - Ctx: Sync + ?Sized, - S: Send + Sync, + T: resolve::ValueAsync + 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, - ) -> BoxFuture<'r, ExecutionResult> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { Box::pin(iter::resolve_list_async( self.iter(), selection_set, - info, + type_info, executor, )) } } -impl resolve::ToInputValue for Vec +impl resolve::ToInputValue for Vec where - T: resolve::ToInputValue, + T: resolve::ToInputValue, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { graphql::InputValue::list(self.iter().map(T::to_input_value)) } } +impl<'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Vec +where + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = TryFromInputValueError; -impl<'i, T, Info, S> graphql::InputType<'i, Info, S> for Vec + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + 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 where - T: graphql::InputType<'i, Info, S>, - Info: ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } - -impl graphql::OutputType for Vec +impl graphql::OutputType for Vec where - T: graphql::OutputType, + T: graphql::OutputType, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync, { fn assert_output_type() { T::assert_output_type() } } -impl reflect::BaseType for Vec +impl reflect::BaseType for Vec where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Vec +impl reflect::BaseSubTypes for Vec where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Vec +impl reflect::WrappedType for Vec where - T: reflect::WrappedType, + T: reflect::WrappedType, + 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 { + /// [`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 IntoFieldError for TryFromInputValueError +where + E: IntoFieldError, +{ + fn into_field_error(self) -> FieldError { + 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 ae2e92575..638042dd5 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; From d648181b26d4734614ac1a22e53321e7708e7fa4 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 21 Jun 2022 14:50:16 +0200 Subject: [PATCH 36/58] Fix `reflect` impls for `Result` --- juniper/src/types/result.rs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/juniper/src/types/result.rs b/juniper/src/types/result.rs index 51ec79ad1..62d28d108 100644 --- a/juniper/src/types/result.rs +++ b/juniper/src/types/result.rs @@ -2,25 +2,26 @@ use crate::reflect; -/* -impl reflect::BaseType for Result +impl reflect::BaseType for Result where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for Result +impl reflect::BaseSubTypes for Result where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for Result +impl reflect::WrappedType for Result where - T: reflect::WrappedType, + T: reflect::WrappedType, + BH: ?Sized, { const VALUE: reflect::WrappedValue = T::VALUE; } -*/ From 17af7c5ac526cd2d6f09b2618d69cc5a4b0a12f8 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 21 Jun 2022 15:25:06 +0200 Subject: [PATCH 37/58] Impl new machinery for array --- juniper/src/types/array.rs | 277 ++++++++++++++++++++++++++++++------- juniper/src/types/mod.rs | 2 +- 2 files changed, 231 insertions(+), 48 deletions(-) diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index f730c39da..5e7e48efa 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -1,118 +1,301 @@ //! GraphQL implementation for [array]. //! -//! [array]: primitive@std::array +//! [array]: prim@array + +use std::{ + mem::{self, MaybeUninit}, + ptr, +}; use crate::{ + behavior, executor::{ExecutionResult, Executor, Registry}, graphql, reflect, resolve, schema::meta::MetaType, - BoxFuture, Selection, + BoxFuture, FieldError, IntoFieldError, Selection, }; use super::iter; -/* -impl resolve::Type for [T; N] +impl resolve::Type for [T; N] where - T: resolve::Type, - Info: ?Sized, + T: resolve::Type, + 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::(info, Some(N)) - .into_meta() + registry.wrap_list::, _>(type_info, None) } } -impl resolve::Value for [T; N] +impl resolve::Value for [T; N] where - T: resolve::Value, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - iter::resolve_list(self.iter(), selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, type_info, executor) } } -impl resolve::ValueAsync for [T; N] +impl resolve::ValueAsync for [T; N] where - T: resolve::ValueAsync + Sync, - Info: Sync + ?Sized, - Ctx: Sync + ?Sized, - S: Send + Sync, + T: resolve::ValueAsync + 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, - ) -> BoxFuture<'r, ExecutionResult> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { Box::pin(iter::resolve_list_async( self.iter(), selection_set, - info, + type_info, executor, )) } } -impl resolve::ToInputValue for [T; N] +impl resolve::ToInputValue for [T; N] where - T: resolve::ToInputValue, + T: resolve::ToInputValue, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { graphql::InputValue::list(self.iter().map(T::to_input_value)) } } -/* -impl<'i, T, Info, S, const N: usize> graphql::InputType<'i, Info, S> for [T; N] +impl<'i, T, SV, BH, const N: usize> resolve::InputValue<'i, SV, BH> for [T; N] +where + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = TryFromInputValueError; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + struct PartiallyInitializedArray { + arr: [MaybeUninit; N], + init_len: usize, + no_drop: bool, + } + + impl Drop for PartiallyInitializedArray { + fn drop(&mut self) { + if self.no_drop { + return; + } + // Dropping a `MaybeUninit` does nothing, thus we need to drop + // the initialized elements manually, otherwise we may introduce + // a memory/resource leak if `T: Drop`. + for elem in &mut self.arr[0..self.init_len] { + // SAFETY: This is safe, because `self.init_len` represents + // the number of the initialized elements exactly. + unsafe { + ptr::drop_in_place(elem.as_mut_ptr()); + } + } + } + } + + match v { + graphql::InputValue::List(ls) => { + if ls.len() != N { + return Err(TryFromInputValueError::WrongCount { + actual: ls.len(), + expected: N, + }); + } + if N == 0 { + // TODO: Use `mem::transmute` instead of + // `mem::transmute_copy` below, once it's allowed + // for const generics: + // https://github.com/rust-lang/rust/issues/61956 + // SAFETY: `mem::transmute_copy` is safe here, because we + // check `N` to be `0`. It's no-op, actually. + return Ok(unsafe { mem::transmute_copy::<[T; 0], Self>(&[]) }); + } + + // SAFETY: The reason we're using a wrapper struct implementing + // `Drop` here is to be panic safe: + // `T: resolve::InputValue` implementation is not + // controlled by us, so calling + // `T::try_from_input_value(&i.item)` below may cause a + // panic when our array is initialized only partially. + // In such situation we need to drop already initialized + // values to avoid possible memory/resource leaks if + // `T: Drop`. + let mut out = PartiallyInitializedArray:: { + // SAFETY: The `.assume_init()` here is safe, because the + // type we are claiming to have initialized here is + // a bunch of `MaybeUninit`s, which do not require + // any initialization. + arr: unsafe { MaybeUninit::uninit().assume_init() }, + init_len: 0, + no_drop: false, + }; + + let mut items = ls.iter().map(|i| T::try_from_input_value(&i.item)); + for elem in &mut out.arr[..] { + if let Some(i) = items + .next() + .transpose() + .map_err(TryFromInputValueError::Item)? + { + *elem = MaybeUninit::new(i); + out.init_len += 1; + } + } + + // Do not drop collected `items`, because we're going to return + // them. + out.no_drop = true; + + // TODO: Use `mem::transmute` instead of `mem::transmute_copy` + // below, once it's allowed for const generics: + // https://github.com/rust-lang/rust/issues/61956 + // SAFETY: `mem::transmute_copy` is safe here, because we have + // exactly `N` initialized `items`. + // Also, despite `mem::transmute_copy` copies the value, + // we won't have a double-free when `T: Drop` here, + // because original array elements are `MaybeUninit`, so + // do nothing on `Drop`. + Ok(unsafe { mem::transmute_copy::<_, Self>(&out.arr) }) + } + // 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_err(TryFromInputValueError::Item) + .and_then(|e: T| { + // TODO: Use `mem::transmute` instead of + // `mem::transmute_copy` below, once it's allowed + // for const generics: + // https://github.com/rust-lang/rust/issues/61956 + if N == 1 { + // SAFETY: `mem::transmute_copy` is safe here, because + // we check `N` to be `1`. Also, despite + // `mem::transmute_copy` copies the value, we + // won't have a double-free when `T: Drop` here, + // because original `e: T` value is wrapped into + // `mem::ManuallyDrop`, so does nothing on + // `Drop`. + Ok(unsafe { mem::transmute_copy::<_, Self>(&[mem::ManuallyDrop::new(e)]) }) + } else { + Err(TryFromInputValueError::WrongCount { + actual: 1, + expected: N, + }) + } + }), + } + } +} + +impl<'i, T, TI, SV, BH, const N: usize> graphql::InputType<'i, TI, SV, BH> for [T; N] where - T: graphql::InputType<'i, Info, S>, - Info: ?Sized, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } -*/ -impl graphql::OutputType for [T; N] +impl graphql::OutputType for [T; N] where - T: graphql::OutputType, + T: graphql::OutputType, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync, { fn assert_output_type() { T::assert_output_type() } } -impl reflect::BaseType for [T; N] +impl reflect::BaseType for [T; N] where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for [T; N] +impl reflect::BaseSubTypes for [T; N] where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for [T; N] +impl reflect::WrappedType for [T; N] where - T: reflect::WrappedType, + T: reflect::WrappedType, + BH: ?Sized, { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } -*/ + +/// Possible errors of converting a [`graphql::InputValue`] into an exact-size +/// [array](prim@array). +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TryFromInputValueError { + /// [`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, + + /// Wrong count of items. + WrongCount { + /// Actual count of items. + actual: usize, + + /// Expected count of items ([array](prim@array) size). + expected: usize, + }, + + /// Error of converting a [`graphql::InputValue::List`]'s item. + Item(E), +} + +impl IntoFieldError for TryFromInputValueError +where + E: IntoFieldError, +{ + fn into_field_error(self) -> FieldError { + const ERROR_PREFIX: &str = "Failed to convert into exact-size array"; + match self { + Self::IsNull => format!("{}: Value cannot be `null`", ERROR_PREFIX).into(), + Self::WrongCount { actual, expected } => format!( + "{}: wrong elements count: {} instead of {}", + ERROR_PREFIX, actual, expected, + ) + .into(), + Self::Item(s) => s.into_field_error(), + } + } +} diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 44635ccd1..1fe0dfe24 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,5 +1,5 @@ mod arc; -mod array; +pub mod array; mod r#box; pub mod iter; mod nullable; From 4a6b3abfc69cd35e083822fd8ed7dc0eb3e51f84 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 21 Jun 2022 16:15:21 +0200 Subject: [PATCH 38/58] Impl new machinery for slice [skip ci] --- juniper/src/types/slice.rs | 190 +++++++++++++++++++++++++++++-------- 1 file changed, 148 insertions(+), 42 deletions(-) diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index 0b31c7a99..6eabb1fc9 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -1,114 +1,220 @@ //! GraphQL implementation for [slice]. //! -//! [slice]: primitive@std::slice +//! [slice]: prim@slice + +use std::{rc::Rc, sync::Arc}; use crate::{ + behavior, executor::{ExecutionResult, Executor, Registry}, graphql, reflect, resolve, schema::meta::MetaType, BoxFuture, Selection, }; -use super::iter; +use super::{iter, vec::TryFromInputValueError}; -/* -impl resolve::Type for [T] +impl resolve::Type for [T] where - T: resolve::Type, - Info: ?Sized, + T: resolve::Type, + 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::(info, None).into_meta() + registry.wrap_list::, _>(type_info, None) } } -impl resolve::Value for [T] +impl resolve::Value for [T] where - T: resolve::Value, - Info: ?Sized, - Ctx: ?Sized, + T: resolve::Value, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, { fn resolve_value( &self, - selection_set: Option<&[Selection<'_, S>]>, - info: &Info, - executor: &Executor, - ) -> ExecutionResult { - iter::resolve_list(self.iter(), selection_set, info, executor) + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + iter::resolve_list(self.iter(), selection_set, type_info, executor) } } -impl resolve::ValueAsync for [T] +impl resolve::ValueAsync for [T] where - T: resolve::ValueAsync + Sync, - Info: Sync + ?Sized, - Ctx: Sync + ?Sized, - S: Send + Sync, + T: resolve::ValueAsync + 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, - ) -> BoxFuture<'r, ExecutionResult> { + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { Box::pin(iter::resolve_list_async( self.iter(), selection_set, - info, + type_info, executor, )) } } -impl resolve::ToInputValue for [T] +impl resolve::ToInputValue for [T] where - T: resolve::ToInputValue, + T: resolve::ToInputValue, + BH: ?Sized, { - fn to_input_value(&self) -> graphql::InputValue { + fn to_input_value(&self) -> graphql::InputValue { graphql::InputValue::list(self.iter().map(T::to_input_value)) } } -impl graphql::InputType for [T] +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box, SV, BH> for [T] +where + Vec: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = as resolve::InputValue<'i, SV, BH>>::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + as resolve::InputValue<'i, SV, BH>>::try_from_input_value(v) + .map(Vec::into_boxed_slice) + } +} + +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc, SV, BH> for [T] +where + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = TryFromInputValueError; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + // We don't want to reuse `Vec` implementation in the same way we do + // for `Box<[T]>`, because `impl From> for Rc<[T]>` reallocates. + 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), + // TODO: Use `.into_iter()` after upgrade to 2021 Rust edition. + other => T::try_from_input_value(other) + .map(|e| std::iter::once(e).collect()) + .map_err(TryFromInputValueError::Item), + } + } +} + +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc, SV, BH> for [T] +where + T: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, +{ + type Error = TryFromInputValueError; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + // We don't want to reuse `Vec` implementation in the same way we do + // for `Box<[T]>`, because `impl From> for Arc<[T]>` reallocates. + 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), + // TODO: Use `.into_iter()` after upgrade to 2021 Rust edition. + other => T::try_from_input_value(other) + .map(|e| std::iter::once(e).collect()) + .map_err(TryFromInputValueError::Item), + } + } +} + +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Box, TI, SV, BH> for [T] +where + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Rc, TI, SV, BH> for [T] where - T: graphql::InputType, + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, { fn assert_input_type() { T::assert_input_type() } } +impl<'i, T, TI, SV, BH> graphql::InputTypeAs<'i, Arc, TI, SV, BH> for [T] +where + T: graphql::InputType<'i, TI, SV, BH>, + TI: ?Sized, + SV: 'i, + BH: ?Sized, +{ + fn assert_input_type() { + T::assert_input_type() + } +} -impl graphql::OutputType for [T] +impl graphql::OutputType for [T] where - T: graphql::OutputType, + T: graphql::OutputType, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: resolve::ValueAsync, { fn assert_output_type() { T::assert_output_type() } } -impl reflect::BaseType for [T] +impl reflect::BaseType for [T] where - T: reflect::BaseType, + T: reflect::BaseType, + BH: ?Sized, { const NAME: reflect::Type = T::NAME; } -impl reflect::BaseSubTypes for [T] +impl reflect::BaseSubTypes for [T] where - T: reflect::BaseSubTypes, + T: reflect::BaseSubTypes, + BH: ?Sized, { const NAMES: reflect::Types = T::NAMES; } -impl reflect::WrappedType for [T] +impl reflect::WrappedType for [T] where - T: reflect::WrappedType, + T: reflect::WrappedType, + BH: ?Sized, { const VALUE: reflect::WrappedValue = reflect::wrap::list(T::VALUE); } -*/ From 3cfc4eb0e4a4d2b905f31f4b78870fb1cc391211 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 21 Jun 2022 17:49:35 +0200 Subject: [PATCH 39/58] Impl new machinery for `Cow` [skip ci] --- juniper/src/types/arc.rs | 2 +- juniper/src/types/box.rs | 2 +- juniper/src/types/cow.rs | 291 +++++++++++++++++++++++++++++++++++++ juniper/src/types/mod.rs | 1 + juniper/src/types/rc.rs | 2 +- juniper/src/types/ref.rs | 8 +- juniper/src/types/slice.rs | 17 ++- juniper/src/types/str.rs | 37 ++++- 8 files changed, 351 insertions(+), 9 deletions(-) create mode 100644 juniper/src/types/cow.rs diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs index db4c6cba7..c49d9a452 100644 --- a/juniper/src/types/arc.rs +++ b/juniper/src/types/arc.rs @@ -178,7 +178,7 @@ where } } -impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc, SV, BH> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Arc, SV, BH> for T where T: resolve::InputValue<'i, SV, BH>, SV: 'i, diff --git a/juniper/src/types/box.rs b/juniper/src/types/box.rs index 845343d22..9724d307f 100644 --- a/juniper/src/types/box.rs +++ b/juniper/src/types/box.rs @@ -176,7 +176,7 @@ where } } -impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box, SV, BH> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box, SV, BH> for T where T: resolve::InputValue<'i, SV, BH>, SV: 'i, diff --git a/juniper/src/types/cow.rs b/juniper/src/types/cow.rs new file mode 100644 index 000000000..9626a8703 --- /dev/null +++ b/juniper/src/types/cow.rs @@ -0,0 +1,291 @@ +//! GraphQL implementation for [`Cow`]. + +use std::{borrow::Cow, ops::Deref}; + +use crate::{ + graphql, + meta::MetaType, + parser::{ParseError, ScalarToken}, + reflect, resolve, Arguments, BoxFuture, ExecutionResult, Executor, Registry, Selection, +}; + +impl<'me, T, TI, SV, BH> resolve::Type for Cow<'me, T> +where + T: resolve::Type + ToOwned + ?Sized + 'me, + TI: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn meta<'r, 'ti: 'r>(registry: &mut Registry<'r, SV>, type_info: &'ti TI) -> MetaType<'r, SV> + where + SV: 'r, + { + T::meta(registry, type_info) + } +} + +impl<'me, T, TI, BH> resolve::TypeName for Cow<'me, T> +where + T: resolve::TypeName + ToOwned + ?Sized + 'me, + TI: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn type_name(type_info: &TI) -> &str { + T::type_name(type_info) + } +} + +impl<'me, T, TI, BH> resolve::ConcreteTypeName for Cow<'me, T> +where + T: resolve::ConcreteTypeName + ToOwned + ?Sized + 'me, + TI: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn concrete_type_name<'i>(&self, type_info: &'i TI) -> &'i str { + (**self).concrete_type_name(type_info) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::Value for Cow<'me, T> +where + T: resolve::Value + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_value( + &self, + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_value(selection_set, type_info, executor) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::ValueAsync for Cow<'me, T> +where + T: resolve::ValueAsync + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_value_async<'r>( + &'r self, + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_value_async(selection_set, type_info, executor) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValue for Cow<'me, T> +where + T: resolve::ConcreteValue + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_concrete_value( + &self, + type_name: &str, + selection_set: Option<&[Selection<'_, SV>]>, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_concrete_value(type_name, selection_set, type_info, executor) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::ConcreteValueAsync for Cow<'me, T> +where + T: resolve::ConcreteValueAsync + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_concrete_value_async<'r>( + &'r self, + type_name: &str, + selection_set: Option<&'r [Selection<'_, SV>]>, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_concrete_value_async(type_name, selection_set, type_info, executor) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::Field for Cow<'me, T> +where + T: resolve::Field + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_field( + &self, + field_name: &str, + arguments: &Arguments, + type_info: &TI, + executor: &Executor, + ) -> ExecutionResult { + (**self).resolve_field(field_name, arguments, type_info, executor) + } +} + +impl<'me, T, TI, CX, SV, BH> resolve::FieldAsync for Cow<'me, T> +where + T: resolve::FieldAsync + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn resolve_field_async<'r>( + &'r self, + field_name: &'r str, + arguments: &'r Arguments, + type_info: &'r TI, + executor: &'r Executor, + ) -> BoxFuture<'r, ExecutionResult> { + (**self).resolve_field_async(field_name, arguments, type_info, executor) + } +} + +impl<'me, T, SV, BH> resolve::ToInputValue for Cow<'me, T> +where + T: resolve::ToInputValue + ToOwned + ?Sized + 'me, + BH: ?Sized, + Self: Deref, +{ + fn to_input_value(&self) -> graphql::InputValue { + (**self).to_input_value() + } +} + +impl<'me, 'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for Cow<'me, T> +where + 'i: 'me, + T: resolve::InputValueAs<'i, Self, SV, BH> + ToOwned + ?Sized + 'me, + SV: 'i, + BH: ?Sized, + Self: Deref, +{ + type Error = T::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result { + T::try_from_input_value(v) + } + + fn try_from_implicit_null() -> Result { + T::try_from_implicit_null() + } +} + +impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, Cow<'me, Self>, SV, BH> for T +where + 'i: 'me, + T: resolve::InputValueAsRef + ToOwned + 'me, + SV: 'i, + BH: ?Sized, + Cow<'me, Self>: Deref, +{ + type Error = T::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + T::try_from_input_value(v).map(Cow::Borrowed) + } + + fn try_from_implicit_null() -> Result, Self::Error> { + T::try_from_implicit_null().map(Cow::Borrowed) + } +} + +impl<'me, T, SV, BH> resolve::ScalarToken for Cow<'me, T> +where + T: resolve::ScalarToken + ToOwned + ?Sized + 'me, + BH: ?Sized, + Self: Deref, +{ + fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { + T::parse_scalar_token(token) + } +} + +impl<'me, 'i, T, TI, SV, BH> graphql::InputType<'i, TI, SV, BH> for Cow<'me, T> +where + 'i: 'me, + T: graphql::InputTypeAs<'i, Self, TI, SV, BH> + ToOwned + ?Sized + 'me, + TI: ?Sized, + SV: 'i, + BH: ?Sized, + Self: Deref, +{ + fn assert_input_type() { + T::assert_input_type() + } +} + +impl<'me, T, TI, CX, SV, BH> graphql::OutputType for Cow<'me, T> +where + T: graphql::OutputType + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + BH: ?Sized, + Self: Deref, +{ + fn assert_output_type() { + T::assert_output_type() + } +} + +impl<'me, 'i, T, TI, CX, SV, BH> graphql::Scalar<'i, TI, CX, SV, BH> for Cow<'me, T> +where + 'i: 'me, + T: graphql::ScalarAs<'i, Self, TI, CX, SV, BH> + ToOwned + ?Sized + 'me, + TI: ?Sized, + CX: ?Sized, + SV: 'i, + BH: ?Sized, + + Self: Deref, +{ + fn assert_scalar() { + T::assert_scalar() + } +} + +impl<'me, T, BH> reflect::BaseType for Cow<'me, T> +where + T: reflect::BaseType + ToOwned + ?Sized + 'me, + BH: ?Sized, + Self: Deref, +{ + const NAME: reflect::Type = T::NAME; +} + +impl<'me, T, BH> reflect::BaseSubTypes for Cow<'me, T> +where + T: reflect::BaseSubTypes + ToOwned + ?Sized + 'me, + BH: ?Sized, + Self: Deref, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl<'me, T, BH> reflect::WrappedType for Cow<'me, T> +where + T: reflect::WrappedType + ToOwned + ?Sized + 'me, + BH: ?Sized, + Self: Deref, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} diff --git a/juniper/src/types/mod.rs b/juniper/src/types/mod.rs index 1fe0dfe24..fd5e0abbf 100644 --- a/juniper/src/types/mod.rs +++ b/juniper/src/types/mod.rs @@ -1,6 +1,7 @@ mod arc; pub mod array; mod r#box; +mod cow; pub mod iter; mod nullable; mod option; diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs index f886e910c..8e00993d0 100644 --- a/juniper/src/types/rc.rs +++ b/juniper/src/types/rc.rs @@ -178,7 +178,7 @@ where } } -impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc, SV, BH> for T +impl<'i, T, SV, BH> resolve::InputValueAs<'i, Rc, SV, BH> for T where T: resolve::InputValue<'i, SV, BH>, SV: 'i, diff --git a/juniper/src/types/ref.rs b/juniper/src/types/ref.rs index 867f4f481..805b14720 100644 --- a/juniper/src/types/ref.rs +++ b/juniper/src/types/ref.rs @@ -164,7 +164,7 @@ where impl<'me, 'i, T, SV, BH> resolve::InputValue<'i, SV, BH> for &'me T where 'i: 'me, - T: resolve::InputValueAs<'i, &'me T, SV, BH> + ?Sized, + T: resolve::InputValueAs<'i, Self, SV, BH> + ?Sized, SV: 'i, BH: ?Sized, { @@ -179,7 +179,7 @@ where } } -impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, &'me T, SV, BH> for T +impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, &'me Self, SV, BH> for T where 'i: 'me, T: resolve::InputValueAsRef + ?Sized, @@ -188,11 +188,11 @@ where { type Error = T::Error; - fn try_from_input_value(v: &'i graphql::InputValue) -> Result<&'me T, Self::Error> { + fn try_from_input_value(v: &'i graphql::InputValue) -> Result<&'me Self, Self::Error> { T::try_from_input_value(v) } - fn try_from_implicit_null() -> Result<&'me T, Self::Error> { + fn try_from_implicit_null() -> Result<&'me Self, Self::Error> { T::try_from_implicit_null() } } diff --git a/juniper/src/types/slice.rs b/juniper/src/types/slice.rs index 6eabb1fc9..6a3f0124b 100644 --- a/juniper/src/types/slice.rs +++ b/juniper/src/types/slice.rs @@ -2,7 +2,7 @@ //! //! [slice]: prim@slice -use std::{rc::Rc, sync::Arc}; +use std::{borrow::Cow, rc::Rc, sync::Arc}; use crate::{ behavior, @@ -78,6 +78,21 @@ where } } +impl<'me, 'i, T, SV, BH> resolve::InputValueAs<'i, Cow<'me, Self>, SV, BH> for [T] +where + Vec: resolve::InputValue<'i, SV, BH>, + SV: 'i, + BH: ?Sized, + Self: ToOwned, +{ + type Error = as resolve::InputValue<'i, SV, BH>>::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + as resolve::InputValue<'i, SV, BH>>::try_from_input_value(v) + .map(|v| Cow::Owned(v.to_owned())) + } +} + impl<'i, T, SV, BH> resolve::InputValueAs<'i, Box, SV, BH> for [T] where Vec: resolve::InputValue<'i, SV, BH>, diff --git a/juniper/src/types/str.rs b/juniper/src/types/str.rs index a51299335..5b874073b 100644 --- a/juniper/src/types/str.rs +++ b/juniper/src/types/str.rs @@ -2,7 +2,7 @@ //! //! [`str`]: primitive@std::str -use std::{rc::Rc, sync::Arc}; +use std::{borrow::Cow, rc::Rc, sync::Arc}; use futures::future; @@ -84,6 +84,19 @@ impl resolve::InputValueAsRef for str { } } +impl<'me, 'i, SV> resolve::InputValueAs<'i, Cow<'me, Self>, SV> for str +where + 'i: 'me, + SV: 'i, + Self: resolve::InputValueAsRef, +{ + type Error = >::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Cow::Borrowed) + } +} + impl<'i, SV> resolve::InputValueAs<'i, Box, SV> for str where SV: 'i, @@ -140,6 +153,17 @@ where fn assert_input_type() {} } +impl<'me, 'i, TI, SV> graphql::InputTypeAs<'i, Cow<'me, Self>, TI, SV> for str +where + Self: graphql::Type + + resolve::ToInputValue + + resolve::InputValueAs<'i, Cow<'me, Self>, SV>, + TI: ?Sized, + SV: 'i, +{ + fn assert_input_type() {} +} + impl<'i, TI, SV> graphql::InputTypeAs<'i, Box, TI, SV> for str where Self: graphql::Type @@ -192,6 +216,17 @@ where fn assert_scalar() {} } +impl<'me, 'i, TI, CX, SV> graphql::ScalarAs<'i, Cow<'me, Self>, TI, CX, SV> for str +where + Self: graphql::InputTypeAs<'i, Cow<'me, Self>, TI, SV> + + graphql::OutputType + + resolve::ScalarToken, + TI: ?Sized, + SV: 'i, +{ + fn assert_scalar() {} +} + impl<'i, TI, CX, SV> graphql::ScalarAs<'i, Box, TI, CX, SV> for str where Self: graphql::InputTypeAs<'i, Box, TI, SV> From 07ed785a51b21f1513197885469f769f832a1081 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 21 Jun 2022 18:10:33 +0200 Subject: [PATCH 40/58] Move tests for containers [skip ci] --- juniper/src/types/array.rs | 137 ++++++++++++++++++++++++++++++++++++ juniper/src/types/option.rs | 19 +++++ juniper/src/types/vec.rs | 127 +++++++++++++++++++++++++++++++++ 3 files changed, 283 insertions(+) diff --git a/juniper/src/types/array.rs b/juniper/src/types/array.rs index 5e7e48efa..3565294eb 100644 --- a/juniper/src/types/array.rs +++ b/juniper/src/types/array.rs @@ -299,3 +299,140 @@ where } } } + +// See "Input Coercion" examples on List types: +// https://spec.graphql.org/October2021#sec-List.Input-Coercion +#[cfg(test)] +mod coercion { + use crate::{graphql, resolve::InputValue as _, IntoFieldError as _}; + + use super::TryFromInputValueError; + + type V = graphql::InputValue; + + #[test] + fn from_null() { + let v: V = graphql::input_value!(null); + assert_eq!( + <[i32; 0]>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + <[i32; 1]>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + <[Option; 0]>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + <[Option; 1]>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!(>::try_from_input_value(&v), Ok(None)); + assert_eq!(>::try_from_input_value(&v), Ok(None)); + assert_eq!( + ; 0]>>::try_from_input_value(&v), + Ok(None), + ); + assert_eq!( + ; 1]>>::try_from_input_value(&v), + Ok(None), + ); + assert_eq!( + <[[i32; 1]; 1]>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + ; 1]>; 1]>>::try_from_input_value(&v), + Ok(None), + ); + } + + #[test] + fn from_value() { + let v: V = graphql::input_value!(1); + assert_eq!(<[i32; 1]>::try_from_input_value(&v), Ok([1])); + assert_eq!( + <[i32; 0]>::try_from_input_value(&v), + Err(TryFromInputValueError::WrongCount { + expected: 0, + actual: 1, + }), + ); + assert_eq!(<[Option; 1]>::try_from_input_value(&v), Ok([Some(1)])); + assert_eq!(>::try_from_input_value(&v), Ok(Some([1]))); + assert_eq!( + ; 1]>>::try_from_input_value(&v), + Ok(Some([Some(1)])), + ); + assert_eq!(<[[i32; 1]; 1]>::try_from_input_value(&v), Ok([[1]])); + assert_eq!( + ; 1]>; 1]>>::try_from_input_value(&v), + Ok(Some([Some([Some(1)])])), + ); + } + + #[test] + fn from_list() { + let v: V = graphql::input_value!([1, 2, 3]); + assert_eq!(<[i32; 3]>::try_from_input_value(&v), Ok([1, 2, 3])); + assert_eq!( + >::try_from_input_value(&v), + Ok(Some([1, 2, 3])), + ); + assert_eq!( + <[Option; 3]>::try_from_input_value(&v), + Ok([Some(1), Some(2), Some(3)]), + ); + assert_eq!( + ; 3]>>::try_from_input_value(&v), + Ok(Some([Some(1), Some(2), Some(3)])), + ); + assert_eq!( + <[[i32; 1]; 3]>::try_from_input_value(&v), + Ok([[1], [2], [3]]), + ); + // Looks like the spec ambiguity. + // See: https://github.com/graphql/graphql-spec/pull/515 + assert_eq!( + ; 1]>; 3]>>::try_from_input_value(&v), + Ok(Some([Some([Some(1)]), Some([Some(2)]), Some([Some(3)])])), + ); + } + + #[test] + fn from_list_with_null() { + let v: V = graphql::input_value!([1, 2, null]); + assert_eq!( + <[i32; 3]>::try_from_input_value(&v), + Err(TryFromInputValueError::Item( + "Expected `Int`, found: null".into_field_error(), + )), + ); + assert_eq!( + >::try_from_input_value(&v), + Err(TryFromInputValueError::Item( + "Expected `Int`, found: null".into_field_error(), + )), + ); + assert_eq!( + <[Option; 3]>::try_from_input_value(&v), + Ok([Some(1), Some(2), None]), + ); + assert_eq!( + ; 3]>>::try_from_input_value(&v), + Ok(Some([Some(1), Some(2), None])), + ); + assert_eq!( + <[[i32; 1]; 3]>::try_from_input_value(&v), + Err(TryFromInputValueError::Item(TryFromInputValueError::IsNull)), + ); + // Looks like the spec ambiguity. + // See: https://github.com/graphql/graphql-spec/pull/515 + assert_eq!( + ; 1]>; 3]>>::try_from_input_value(&v), + Ok(Some([Some([Some(1)]), Some([Some(2)]), None])), + ); + } +} diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs index cdc919416..3f348de94 100644 --- a/juniper/src/types/option.rs +++ b/juniper/src/types/option.rs @@ -143,3 +143,22 @@ where { const VALUE: reflect::WrappedValue = reflect::wrap::nullable(T::VALUE); } + +#[cfg(test)] +mod coercion { + use crate::{graphql, resolve::InputValue as _}; + + type V = graphql::InputValue; + + #[test] + fn from_null() { + let v: V = graphql::input_value!(null); + assert_eq!(>::try_from_input_value(&v), Ok(None)); + } + + #[test] + fn from_value() { + let v: V = graphql::input_value!(1); + assert_eq!(>::try_from_input_value(&v), Ok(Some(1))); + } +} diff --git a/juniper/src/types/vec.rs b/juniper/src/types/vec.rs index df2d87a68..0882cd51a 100644 --- a/juniper/src/types/vec.rs +++ b/juniper/src/types/vec.rs @@ -173,3 +173,130 @@ where } } } + +// See "Input Coercion" examples on List types: +// https://spec.graphql.org/October2021#sec-List.Input-Coercion +#[cfg(test)] +mod coercion { + use crate::{graphql, resolve::InputValue as _, IntoFieldError as _}; + + use super::TryFromInputValueError; + + type V = graphql::InputValue; + + #[test] + fn from_null() { + let v: V = graphql::input_value!(null); + assert_eq!( + >::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + >>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!(>>::try_from_input_value(&v), Ok(None)); + assert_eq!( + >>>::try_from_input_value(&v), + Ok(None), + ); + assert_eq!( + >>::try_from_input_value(&v), + Err(TryFromInputValueError::IsNull), + ); + assert_eq!( + >>>>>::try_from_input_value(&v), + Ok(None), + ); + } + + #[test] + fn from_value() { + let v: V = graphql::input_value!(1); + assert_eq!(>::try_from_input_value(&v), Ok(vec![1])); + assert_eq!( + >>::try_from_input_value(&v), + Ok(vec![Some(1)]), + ); + assert_eq!( + >>::try_from_input_value(&v), + Ok(Some(vec![1])), + ); + assert_eq!( + >>>::try_from_input_value(&v), + Ok(Some(vec![Some(1)])), + ); + assert_eq!(>>::try_from_input_value(&v), Ok(vec![vec![1]])); + assert_eq!( + >>>>>::try_from_input_value(&v), + Ok(Some(vec![Some(vec![Some(1)])])), + ); + } + + #[test] + fn from_list() { + let v: V = graphql::input_value!([1, 2, 3]); + assert_eq!(>::try_from_input_value(&v), Ok(vec![1, 2, 3])); + assert_eq!( + >>::try_from_input_value(&v), + Ok(Some(vec![1, 2, 3])), + ); + assert_eq!( + >>::try_from_input_value(&v), + Ok(vec![Some(1), Some(2), Some(3)]), + ); + assert_eq!( + >>>::try_from_input_value(&v), + Ok(Some(vec![Some(1), Some(2), Some(3)])), + ); + assert_eq!( + >>::try_from_input_value(&v), + Ok(vec![vec![1], vec![2], vec![3]]), + ); + // Looks like the spec ambiguity. + // See: https://github.com/graphql/graphql-spec/pull/515 + assert_eq!( + >>>>>::try_from_input_value(&v), + Ok(Some(vec![ + Some(vec![Some(1)]), + Some(vec![Some(2)]), + Some(vec![Some(3)]), + ])), + ); + } + + #[test] + fn from_list_with_null() { + let v: V = graphql::input_value!([1, 2, null]); + assert_eq!( + >::try_from_input_value(&v), + Err(TryFromInputValueError::Item( + "Expected `Int`, found: null".into_field_error(), + )), + ); + assert_eq!( + >>::try_from_input_value(&v), + Err(TryFromInputValueError::Item( + "Expected `Int`, found: null".into_field_error(), + )), + ); + assert_eq!( + >>::try_from_input_value(&v), + Ok(vec![Some(1), Some(2), None]), + ); + assert_eq!( + >>>::try_from_input_value(&v), + Ok(Some(vec![Some(1), Some(2), None])), + ); + assert_eq!( + >>::try_from_input_value(&v), + Err(TryFromInputValueError::Item(TryFromInputValueError::IsNull)), + ); + // Looks like the spec ambiguity. + // See: https://github.com/graphql/graphql-spec/pull/515 + assert_eq!( + >>>>>::try_from_input_value(&v), + Ok(Some(vec![Some(vec![Some(1)]), Some(vec![Some(2)]), None])), + ); + } +} From 42e278014272beb0b9a57a559cdf5f1a6ec840e0 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 23 Jun 2022 16:41:16 +0200 Subject: [PATCH 41/58] Fix tests --- juniper/src/executor_tests/interfaces_unions.rs | 4 ++-- juniper/src/tests/introspection_tests.rs | 2 +- juniper/src/tests/schema_introspection.rs | 6 +++--- juniper_codegen/src/util/mod.rs | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs index 1044baafd..c25512154 100644 --- a/juniper/src/executor_tests/interfaces_unions.rs +++ b/juniper/src/executor_tests/interfaces_unions.rs @@ -1,6 +1,6 @@ mod interface { use crate::{ - graphql_interface, graphql_object, + graphql_interface, graphql_object, graphql_value, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, GraphQLObject, @@ -96,7 +96,7 @@ mod interface { mod union { use crate::{ - graphql_object, graphql_union, + graphql_object, graphql_union, graphql_value, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, }; diff --git a/juniper/src/tests/introspection_tests.rs b/juniper/src/tests/introspection_tests.rs index f4f064914..a887907ce 100644 --- a/juniper/src/tests/introspection_tests.rs +++ b/juniper/src/tests/introspection_tests.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use crate::{ - graphql_vars, + graphql_value, graphql_vars, introspection::IntrospectionFormat, schema::model::RootNode, tests::fixtures::starwars::schema::{Database, Query}, diff --git a/juniper/src/tests/schema_introspection.rs b/juniper/src/tests/schema_introspection.rs index 1e1b03e0a..291c85f16 100644 --- a/juniper/src/tests/schema_introspection.rs +++ b/juniper/src/tests/schema_introspection.rs @@ -1,4 +1,4 @@ -use crate::value::Value; +use crate::graphql::{self, Value}; // Sort a nested schema Value. // In particular, lists are sorted by the "name" key of children, if present. @@ -34,7 +34,7 @@ pub(super) fn sort_schema_value(value: &mut Value) { } pub(crate) fn schema_introspection_result() -> Value { - let mut v = graphql_value!({ + let mut v = graphql::value!({ "__schema": { "description": null, "queryType": { @@ -1451,7 +1451,7 @@ pub(crate) fn schema_introspection_result() -> Value { } pub(crate) fn schema_introspection_result_without_descriptions() -> Value { - let mut v = graphql_value!({ + let mut v = graphql::value!({ "__schema": { "queryType": { "name": "Query" diff --git a/juniper_codegen/src/util/mod.rs b/juniper_codegen/src/util/mod.rs index 510c3aeb2..d83cc000f 100644 --- a/juniper_codegen/src/util/mod.rs +++ b/juniper_codegen/src/util/mod.rs @@ -1144,9 +1144,9 @@ impl GraphQLTypeDefiniton { #where_clause { fn to_input_value(&self) -> ::juniper::InputValue<#scalar> { - ::juniper::InputValue::object(vec![ + ::juniper::InputValue::object([ #( #to_inputs )* - ].into_iter().collect()) + ]) } } From 23e01956b348cf2820c1366dcda330fb98412ec2 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 23 Jun 2022 17:42:55 +0200 Subject: [PATCH 42/58] Impl `reflect` traits for objects and interfaces [skip ci] --- juniper/src/behavior.rs | 38 ++++++- juniper_codegen/src/graphql_interface/attr.rs | 2 + .../src/graphql_interface/derive.rs | 1 + juniper_codegen/src/graphql_interface/mod.rs | 97 ++++++++++++++++- juniper_codegen/src/graphql_object/attr.rs | 1 + juniper_codegen/src/graphql_object/derive.rs | 1 + juniper_codegen/src/graphql_object/mod.rs | 100 +++++++++++++++++- juniper_codegen/src/graphql_scalar/mod.rs | 8 +- 8 files changed, 241 insertions(+), 7 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index c98c959f3..506cc3841 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -6,7 +6,7 @@ use crate::{ graphql, meta::MetaType, parser::{ParseError, ScalarToken}, - resolve, Registry, + reflect, resolve, Registry, }; /// Default standard behavior of GraphQL types implementation. @@ -92,3 +92,39 @@ where T::parse_scalar_token(token) } } + +impl reflect::BaseType for Coerce +where + T: reflect::BaseType + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + const NAME: reflect::Type = T::NAME; +} + +impl reflect::BaseSubTypes for Coerce +where + T: reflect::BaseSubTypes + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} + +impl reflect::WrappedType for Coerce +where + T: reflect::WrappedType + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + const VALUE: reflect::WrappedValue = T::VALUE; +} + +impl reflect::Implements for Coerce +where + T: reflect::Implements + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + const NAMES: reflect::Types = T::NAMES; +} diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs index 746b7af76..ba7a456d1 100644 --- a/juniper_codegen/src/graphql_interface/attr.rs +++ b/juniper_codegen/src/graphql_interface/attr.rs @@ -123,6 +123,7 @@ fn expand_on_trait( description: attr.description.as_deref().cloned(), context, scalar, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), fields, implemented_for: attr .implemented_for @@ -304,6 +305,7 @@ fn expand_on_derive_input( description: attr.description.as_deref().cloned(), context, scalar, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), fields, implemented_for: attr .implemented_for diff --git a/juniper_codegen/src/graphql_interface/derive.rs b/juniper_codegen/src/graphql_interface/derive.rs index e113bdced..bf60e5cff 100644 --- a/juniper_codegen/src/graphql_interface/derive.rs +++ b/juniper_codegen/src/graphql_interface/derive.rs @@ -96,6 +96,7 @@ pub fn expand(input: TokenStream) -> syn::Result { description: attr.description.as_deref().cloned(), context, scalar, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), fields, implemented_for: attr .implemented_for diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index 2114e2cf5..fae351574 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -21,7 +21,7 @@ use syn::{ use crate::{ common::{ - field, gen, + behavior, field, gen, parse::{ attr::{err, OptionExt as _}, GenericsExt as _, ParseBufferExt as _, @@ -111,6 +111,17 @@ struct Attr { /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces scalar: Option>, + /// Explicitly specified type of the custom [`Behavior`] to parametrize this + /// [GraphQL interface][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-Interfaces + behavior: Option>, + /// Explicitly specified marker indicating that the Rust trait should be /// transformed into [`async_trait`]. /// @@ -173,6 +184,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::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "for" | "implementers" => { input.parse::()?; for impler in input.parse_maybe_wrapped_and_punctuated::< @@ -231,6 +249,7 @@ impl Attr { description: try_merge_opt!(description: self, another), context: try_merge_opt!(context: self, another), scalar: try_merge_opt!(scalar: self, another), + behavior: try_merge_opt!(behavior: self, another), implemented_for: try_merge_hashset!(implemented_for: self, another => span_joined), r#enum: try_merge_opt!(r#enum: self, another), asyncness: try_merge_opt!(asyncness: self, another), @@ -309,6 +328,13 @@ struct Definition { /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces scalar: scalar::Type, + /// [`Behavior`] parametrization to generate code with for this + /// [GraphQL interface][0]. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Interfaces + behavior: behavior::Type, + /// Defined [GraphQL fields][2] of this [GraphQL interface][1]. /// /// [1]: https://spec.graphql.org/June2018/#sec-Interfaces @@ -347,6 +373,8 @@ impl ToTokens for Definition { self.impl_field_meta_tokens().to_tokens(into); self.impl_field_tokens().to_tokens(into); self.impl_async_field_tokens().to_tokens(into); + //////////////////////////////////////////////////////////////////////// + self.impl_reflect().to_tokens(into); } } @@ -847,6 +875,59 @@ impl Definition { } } + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`], [`reflect::WrappedType`] and + /// [`reflect::Fields`] traits for this [GraphQL interface][0]. + /// + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::Fields`]: juniper::reflect::Fields + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Interfaces + fn impl_reflect(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let name = &self.name; + let implers = &self.implemented_for; + let fields = self.fields.iter().map(|f| &f.name); + + quote! { + #[automatically_derived] + 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<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Types = &[ + >::NAME, + #( <#implers as ::juniper::reflect::BaseType<#bh>>::NAME ),* + ]; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty + #where_clause + { + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::Fields<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Names = &[#( #fields ),*]; + } + } + } + /// Returns generated code implementing [`FieldMeta`] for each field of this /// [GraphQL interface][1]. /// @@ -1267,6 +1348,20 @@ impl Definition { generics } + /// 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.enum_alias_ident; + let (_, ty_gen, _) = generics.split_for_impl(); + parse_quote! { #ident#ty_gen } + }; + + (ty, generics) + } + /// Indicates whether this enum has non-exhaustive phantom variant to hold /// type parameters. #[must_use] diff --git a/juniper_codegen/src/graphql_object/attr.rs b/juniper_codegen/src/graphql_object/attr.rs index 34a70c211..5461bf848 100644 --- a/juniper_codegen/src/graphql_object/attr.rs +++ b/juniper_codegen/src/graphql_object/attr.rs @@ -121,6 +121,7 @@ where description: attr.description.map(SpanContainer::into_inner), context, scalar, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), fields, interfaces: attr .interfaces diff --git a/juniper_codegen/src/graphql_object/derive.rs b/juniper_codegen/src/graphql_object/derive.rs index 7cbe961b8..e20f533cb 100644 --- a/juniper_codegen/src/graphql_object/derive.rs +++ b/juniper_codegen/src/graphql_object/derive.rs @@ -98,6 +98,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result> { .map(SpanContainer::into_inner) .unwrap_or_else(|| parse_quote! { () }), scalar, + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), fields, interfaces: attr .interfaces diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 0f3b05acb..c51cab094 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -18,7 +18,7 @@ use syn::{ use crate::{ common::{ - field, gen, + behavior, field, gen, parse::{ attr::{err, OptionExt as _}, GenericsExt as _, ParseBufferExt as _, TypeExt, @@ -73,6 +73,17 @@ pub(crate) struct Attr { /// [1]: https://spec.graphql.org/June2018/#sec-Objects pub(crate) scalar: Option>, + /// Explicitly specified type of the custom [`Behavior`] to parametrize this + /// [GraphQL object][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-Objects + pub(crate) behavior: Option>, + /// Explicitly specified [GraphQL interfaces][2] this [GraphQL object][1] /// type implements. /// @@ -135,6 +146,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::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "impl" | "implements" | "interfaces" => { input.parse::()?; for iface in input.parse_maybe_wrapped_and_punctuated::< @@ -180,6 +198,7 @@ impl Attr { description: try_merge_opt!(description: self, another), context: try_merge_opt!(context: self, another), scalar: try_merge_opt!(scalar: self, another), + behavior: try_merge_opt!(behavior: self, another), interfaces: try_merge_hashset!(interfaces: self, another => span_joined), rename_fields: try_merge_opt!(rename_fields: self, another), is_internal: self.is_internal || another.is_internal, @@ -245,6 +264,13 @@ pub(crate) struct Definition { /// [1]: https://spec.graphql.org/June2018/#sec-Objects pub(crate) scalar: scalar::Type, + /// [`Behavior`] parametrization to generate code with for this + /// [GraphQL object][0]. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Objects + pub(crate) behavior: behavior::Type, + /// Defined [GraphQL fields][2] of this [GraphQL object][1]. /// /// [1]: https://spec.graphql.org/June2018/#sec-Objects @@ -329,6 +355,13 @@ impl Definition { (quote! { #impl_generics }, where_clause.cloned()) } + /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait + /// implementation. + #[must_use] + fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) { + (&self.ty, self.generics.clone()) + } + /// Returns generated code implementing [`marker::IsOutputType`] trait for /// this [GraphQL object][1]. /// @@ -423,6 +456,69 @@ impl Definition { } } + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`], [`reflect::Implements`], + /// [`reflect::WrappedType`] and [`reflect::Fields`] traits for this + /// [GraphQL object][0]. + /// + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::Fields`]: juniper::reflect::Fields + /// [`reflect::Implements`]: juniper::reflect::Implements + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Objects + #[must_use] + pub(crate) fn impl_reflect(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let name = &self.name; + let interfaces = self.interfaces.iter(); + let fields = self.fields.iter().map(|f| &f.name); + + quote! { + #[automatically_derived] + 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<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Types = + &[>::NAME]; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::Implements<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Types = &[#( + <#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME + ),*]; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty + #where_clause + { + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::Fields<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Names = &[#( #fields ),*]; + } + } + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL object][1]. /// @@ -504,6 +600,8 @@ impl ToTokens for Definition { self.impl_field_meta_tokens().to_tokens(into); self.impl_field_tokens().to_tokens(into); self.impl_async_field_tokens().to_tokens(into); + //////////////////////////////////////////////////////////////////////// + self.impl_reflect().to_tokens(into); } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 876ec9fa0..643707968 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -971,9 +971,9 @@ impl Definition { /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this /// [GraphQL scalar][0]. /// - /// [`reflect::BaseSubTypes`]: juniper::reflection::BaseSubTypes - /// [`reflect::BaseType`]: juniper::reflection::BaseType - /// [`reflect::WrappedType`]: juniper::reflection::WrappedType + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType /// [0]: https://spec.graphql.org/October2021#sec-Scalars fn impl_reflect(&self) -> TokenStream { let bh = &self.behavior; @@ -1087,7 +1087,7 @@ impl Definition { let ty = { let ident = &self.ident; - let (_, ty_gen, _) = self.generics.split_for_impl(); + let (_, ty_gen, _) = generics.split_for_impl(); parse_quote! { #ident#ty_gen } }; From 1819ab6e12f4b686be61a5b81587278a8c4f5721 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 23 Jun 2022 18:37:14 +0200 Subject: [PATCH 43/58] Impl `reflect` traits for objects and interfaces fields, vol.1 [skip ci] --- juniper_codegen/src/common/field/arg.rs | 31 +++++++++ juniper_codegen/src/common/field/mod.rs | 30 +++++++++ juniper_codegen/src/graphql_interface/attr.rs | 2 + .../src/graphql_interface/derive.rs | 1 + juniper_codegen/src/graphql_object/attr.rs | 1 + juniper_codegen/src/graphql_object/derive.rs | 1 + juniper_codegen/src/graphql_object/mod.rs | 66 +++++++++++++++++++ 7 files changed, 132 insertions(+) diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs index 9eeb72cf6..a65b5e701 100644 --- a/juniper_codegen/src/common/field/arg.rs +++ b/juniper_codegen/src/common/field/arg.rs @@ -16,6 +16,7 @@ use syn::{ use crate::{ common::{ + behavior, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, TypeExt as _, @@ -58,6 +59,19 @@ pub(crate) struct Attr { /// [2]: https://spec.graphql.org/June2018/#sec-Required-Arguments pub(crate) default: Option>>, + /// Explicitly specified type of the custom [`Behavior`] this + /// [GraphQL argument][0] 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-Language.Arguments + /// [coerce]: juniper::behavior::Coerce + pub(crate) behavior: Option>, + /// Explicitly specified marker indicating that this method argument doesn't /// represent a [GraphQL argument][1], but is a [`Context`] being injected /// into a [GraphQL field][2] resolving function. @@ -121,6 +135,13 @@ impl Parse for Attr { )) .none_or_else(|_| err::dup_arg(&ident))? } + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "ctx" | "context" | "Context" => { let span = ident.span(); out.context @@ -151,6 +172,7 @@ impl Attr { name: try_merge_opt!(name: self, another), description: try_merge_opt!(description: self, another), default: try_merge_opt!(default: self, another), + behavior: try_merge_opt!(behavior: self, another), context: try_merge_opt!(context: self, another), executor: try_merge_opt!(executor: self, another), }) @@ -253,6 +275,14 @@ pub(crate) struct OnField { /// [1]: https://spec.graphql.org/June2018/#sec-Language.Arguments /// [2]: https://spec.graphql.org/June2018/#sec-Required-Arguments pub(crate) default: Option>, + + /// [`Behavior`] parametrization of this [GraphQL argument][0] + /// implementation to [coerce] from in the generated code. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Language.Arguments + /// [coerce]: juniper::behavior::Coerce + pub(crate) behavior: behavior::Type, } /// Possible kinds of Rust method arguments for code generation. @@ -464,6 +494,7 @@ impl OnMethod { ty: argument.ty.as_ref().clone(), description: attr.description.as_ref().map(|d| d.as_ref().value()), default: attr.default.as_ref().map(|v| v.as_ref().clone()), + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), }))) } } diff --git a/juniper_codegen/src/common/field/mod.rs b/juniper_codegen/src/common/field/mod.rs index 2efe969f6..3ea86e406 100644 --- a/juniper_codegen/src/common/field/mod.rs +++ b/juniper_codegen/src/common/field/mod.rs @@ -16,6 +16,7 @@ use syn::{ use crate::{ common::{ + behavior, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -58,6 +59,19 @@ pub(crate) struct Attr { /// [2]: https://spec.graphql.org/June2018/#sec-Deprecation pub(crate) deprecated: Option>>, + /// Explicitly specified type of the custom [`Behavior`] this + /// [GraphQL field][0] 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-Language.Fields + /// [coerce]: juniper::behavior::Coerce + pub(crate) behavior: Option>, + /// Explicitly specified marker indicating that this method (or struct /// field) should be omitted by code generation and not considered as the /// [GraphQL field][1] definition. @@ -100,6 +114,13 @@ impl Parse for Attr { )) .none_or_else(|_| err::dup_arg(&ident))? } + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "ignore" | "skip" => out .ignore .replace(SpanContainer::new(ident.span(), None, ident.clone())) @@ -122,6 +143,7 @@ impl Attr { name: try_merge_opt!(name: self, another), description: try_merge_opt!(description: self, another), deprecated: try_merge_opt!(deprecated: self, another), + behavior: try_merge_opt!(behavior: self, another), ignore: try_merge_opt!(ignore: self, another), }) } @@ -199,6 +221,14 @@ pub(crate) struct Definition { /// [1]: https://spec.graphql.org/June2018/#sec-Language.Fields pub(crate) ident: syn::Ident, + /// [`Behavior`] parametrization of this [GraphQL field][0] implementation + /// to [coerce] from in the generated code. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Language.Fields + /// [coerce]: juniper::behavior::Coerce + pub(crate) behavior: behavior::Type, + /// Rust [`MethodArgument`]s required to call the method representing this /// [GraphQL field][1]. /// diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs index ba7a456d1..505e02db6 100644 --- a/juniper_codegen/src/graphql_interface/attr.rs +++ b/juniper_codegen/src/graphql_interface/attr.rs @@ -212,6 +212,7 @@ fn parse_trait_method( description, deprecated, ident: method_ident.clone(), + behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), arguments: Some(arguments), has_receiver: method.sig.receiver().is_some(), is_async: method.sig.asyncness.is_some(), @@ -375,6 +376,7 @@ fn parse_struct_field(field: &mut syn::Field, renaming: &RenameRule) -> Option Option Option Definition { } } + /// Returns generated code implementing [`reflect::Field`] trait for each + /// [field][1] of this [GraphQL object][0]. + /// + /// [`reflect::Field`]: juniper::reflect::Field + /// [0]: https://spec.graphql.org/October2021#sec-Objects + /// [1]: https://spec.graphql.org/October2021#sec-Language.Fields + #[must_use] + pub(crate) fn impl_reflect_field(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + self.fields + .iter() + .map(|field| { + let (f_name, f_ty, f_bh) = (&field.name, &field.ty, &field.behavior); + + let arguments = field + .arguments + .as_ref() + .iter() + .flat_map(|vec| vec.iter().filter_map(field::MethodArgument::as_regular)) + .map(|arg| { + let (a_name, a_ty, a_bh) = (&arg.name, &arg.ty, &arg.behavior); + + quote! {( + #a_name, + <#a_ty as ::juniper::reflect::BaseType<#a_bh>> + ::NAME, + <#a_ty as ::juniper::reflect::WrappedType<#a_bh>> + ::VALUE, + )} + }) + .collect::>(); + + quote! { + #[allow(deprecated, non_snake_case)] + #[automatically_derived] + impl#impl_gens ::juniper::reflect::Field< + { ::juniper::reflect::fnv1a128(#f_name) }, #bh, + > for #ty #where_clause { + const TYPE: ::juniper::reflect::Type = + <#f_ty as ::juniper::reflect::BaseType<#f_bh>> + ::NAME; + + const SUB_TYPES: ::juniper::reflect::Types = + <#f_ty as ::juniper::reflect::BaseSubTypes<#f_bh>> + ::NAMES; + + const WRAPPED_VALUE: juniper::reflect::WrappedValue = + <#f_ty as ::juniper::reflect::WrappedType<#f_bh>> + ::VALUE; + + const ARGUMENTS: &'static [( + ::juniper::reflect::Name, + ::juniper::reflect::Type, + ::juniper::reflect::WrappedValue, + )] = &[#( #arguments ),*]; + } + } + }) + .collect() + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL object][1]. /// @@ -602,6 +666,8 @@ impl ToTokens for Definition { self.impl_async_field_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// self.impl_reflect().to_tokens(into); + self.impl_reflect_field().to_tokens(into); + //self.impl_resolve_field_static().to_tokens(into); } } From 505e25cea2e43e642d56fdd2b31606a6ff0317a5 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 14:53:49 +0200 Subject: [PATCH 44/58] Temporary disable new reflection for fields as it requires all leaf types implement --- juniper_codegen/src/graphql_object/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 10925b238..ba4d72ad6 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -666,7 +666,7 @@ impl ToTokens for Definition { self.impl_async_field_tokens().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); } } From 48631e9b25d089a422f1bb7d2595ac14aa837464 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 15:14:05 +0200 Subject: [PATCH 45/58] Impl `reflect` traits for enums --- juniper_codegen/src/common/behavior.rs | 8 ++ juniper_codegen/src/common/field/arg.rs | 2 +- juniper_codegen/src/graphql_enum/derive.rs | 1 + juniper_codegen/src/graphql_enum/mod.rs | 85 +++++++++++++++++++ juniper_codegen/src/graphql_interface/attr.rs | 8 +- .../src/graphql_interface/derive.rs | 4 +- juniper_codegen/src/graphql_object/attr.rs | 4 +- juniper_codegen/src/graphql_object/derive.rs | 4 +- juniper_codegen/src/graphql_object/mod.rs | 2 +- juniper_codegen/src/graphql_scalar/attr.rs | 4 +- juniper_codegen/src/graphql_scalar/derive.rs | 2 +- juniper_codegen/src/graphql_scalar/mod.rs | 4 +- 12 files changed, 111 insertions(+), 17 deletions(-) diff --git a/juniper_codegen/src/common/behavior.rs b/juniper_codegen/src/common/behavior.rs index d20d7c167..5976c9d07 100644 --- a/juniper_codegen/src/common/behavior.rs +++ b/juniper_codegen/src/common/behavior.rs @@ -10,6 +10,8 @@ use syn::{ parse_quote, }; +use crate::util::span_container::SpanContainer; + /// [`Behaviour`] parametrization of the code generation. /// /// [`Behaviour`]: juniper::behavior @@ -55,3 +57,9 @@ impl Type { } } } + +impl From>> for Type { + fn from(attr: Option>) -> Self { + attr.map(SpanContainer::into_inner).unwrap_or_default() + } +} diff --git a/juniper_codegen/src/common/field/arg.rs b/juniper_codegen/src/common/field/arg.rs index a65b5e701..3bd1f9f37 100644 --- a/juniper_codegen/src/common/field/arg.rs +++ b/juniper_codegen/src/common/field/arg.rs @@ -494,7 +494,7 @@ impl OnMethod { ty: argument.ty.as_ref().clone(), description: attr.description.as_ref().map(|d| d.as_ref().value()), default: attr.default.as_ref().map(|v| v.as_ref().clone()), - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), }))) } } diff --git a/juniper_codegen/src/graphql_enum/derive.rs b/juniper_codegen/src/graphql_enum/derive.rs index 7600226e8..c386827b5 100644 --- a/juniper_codegen/src/graphql_enum/derive.rs +++ b/juniper_codegen/src/graphql_enum/derive.rs @@ -92,6 +92,7 @@ pub(crate) fn expand(input: TokenStream) -> syn::Result { description, context, scalar, + behavior: attr.behavior.into(), values, has_ignored_variants, }; diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 8556d8b6e..bebe05c2e 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -18,6 +18,7 @@ use syn::{ use crate::{ common::{ + behavior, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -71,6 +72,17 @@ struct ContainerAttr { /// [0]: https://spec.graphql.org/October2021#sec-Enums scalar: Option>, + /// Explicitly specified type of the custom [`Behavior`] to parametrize this + /// [GraphQL enum][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-Enums + behavior: Option>, + /// Explicitly specified [`RenameRule`] for all [values][1] of this /// [GraphQL enum][0]. /// @@ -128,6 +140,13 @@ impl Parse for ContainerAttr { .replace(SpanContainer::new(ident.span(), Some(scl.span()), scl)) .none_or_else(|_| err::dup_arg(&ident))? } + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "rename_all" => { input.parse::()?; let val = input.parse::()?; @@ -161,6 +180,7 @@ impl ContainerAttr { description: try_merge_opt!(description: self, another), context: try_merge_opt!(context: self, another), scalar: try_merge_opt!(scalar: self, another), + behavior: try_merge_opt!(behavior: self, another), rename_values: try_merge_opt!(rename_values: self, another), is_internal: self.is_internal || another.is_internal, }) @@ -389,6 +409,13 @@ struct Definition { /// [0]: https://spec.graphql.org/October2021#sec-Enums scalar: scalar::Type, + /// [`Behavior`] parametrization to generate code with for this + /// [GraphQL enum][0]. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Enums + behavior: behavior::Type, + /// [Values][1] of this [GraphQL enum][0]. /// /// [0]: https://spec.graphql.org/October2021#sec-Enums @@ -411,6 +438,8 @@ impl ToTokens for Definition { self.impl_from_input_value_tokens().to_tokens(into); self.impl_to_input_value_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); + //////////////////////////////////////////////////////////////////////// + self.impl_reflect().to_tokens(into); } } @@ -729,6 +758,47 @@ impl Definition { } } + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this + /// [GraphQL enum][0]. + /// + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_reflect(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let name = &self.name; + + quote! { + #[automatically_derived] + 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<#bh> for #ty + #where_clause + { + const NAMES: ::juniper::reflect::Types = + &[>::NAME]; + } + + #[automatically_derived] + impl#impl_gens ::juniper::reflect::WrappedType<#bh> for #ty + #where_clause + { + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; + } + } + } + /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and /// similar) implementation of this enum. /// @@ -787,4 +857,19 @@ impl Definition { generics } + + /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait + /// implementation. + #[must_use] + fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { + let generics = self.generics.clone(); + + let ty = { + let ident = &self.ident; + let (_, ty_gen, _) = generics.split_for_impl(); + parse_quote! { #ident#ty_gen } + }; + + (ty, generics) + } } diff --git a/juniper_codegen/src/graphql_interface/attr.rs b/juniper_codegen/src/graphql_interface/attr.rs index dc4a52ff3..6d5464119 100644 --- a/juniper_codegen/src/graphql_interface/attr.rs +++ b/juniper_codegen/src/graphql_interface/attr.rs @@ -124,7 +124,7 @@ fn expand_on_trait( description: attr.description.map(|d| d.into_inner().into_boxed_str()), context, scalar, - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), fields, implemented_for: attr .implemented_for @@ -218,7 +218,7 @@ fn parse_trait_method( description, deprecated, ident: method_ident.clone(), - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), arguments: Some(arguments), has_receiver: method.sig.receiver().is_some(), is_async: method.sig.asyncness.is_some(), @@ -313,7 +313,7 @@ fn expand_on_derive_input( description: attr.description.map(|d| d.into_inner().into_boxed_str()), context, scalar, - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), fields, implemented_for: attr .implemented_for @@ -388,7 +388,7 @@ fn parse_struct_field(field: &mut syn::Field, renaming: &RenameRule) -> Option syn::Result { description: attr.description.map(|d| d.into_inner().into_boxed_str()), context, scalar, - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), fields, implemented_for: attr .implemented_for @@ -160,7 +160,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option syn::Result> { .map(SpanContainer::into_inner) .unwrap_or_else(|| parse_quote! { () }), scalar, - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), fields, interfaces: attr .interfaces @@ -154,7 +154,7 @@ fn parse_field(field: &syn::Field, renaming: &RenameRule) -> Option>, /// Explicitly specified type of the custom [`Behavior`] to parametrize this - /// [GraphQL object][0] type implementation with. + /// [GraphQL object][0] implementation with. /// /// If [`None`], then [`behavior::Standard`] will be used for the generated /// code. diff --git a/juniper_codegen/src/graphql_scalar/attr.rs b/juniper_codegen/src/graphql_scalar/attr.rs index 01b34dd43..3139da74b 100644 --- a/juniper_codegen/src/graphql_scalar/attr.rs +++ b/juniper_codegen/src/graphql_scalar/attr.rs @@ -64,7 +64,7 @@ fn expand_on_type_alias( specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, scalar_value: attr.scalar.as_deref().into(), - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), }; Ok(quote! { @@ -99,7 +99,7 @@ fn expand_on_derive_input( specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, scalar_value: attr.scalar.as_deref().into(), - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), }; Ok(quote! { diff --git a/juniper_codegen/src/graphql_scalar/derive.rs b/juniper_codegen/src/graphql_scalar/derive.rs index 33c0db850..552c28ba7 100644 --- a/juniper_codegen/src/graphql_scalar/derive.rs +++ b/juniper_codegen/src/graphql_scalar/derive.rs @@ -38,7 +38,7 @@ pub fn expand(input: TokenStream) -> syn::Result { specified_by_url: attr.specified_by_url.as_deref().cloned(), scalar, scalar_value: attr.scalar.as_deref().into(), - behavior: attr.behavior.map(|bh| bh.into_inner()).unwrap_or_default(), + behavior: attr.behavior.into(), } .to_token_stream()) } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 643707968..e4ebe8067 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -67,7 +67,7 @@ struct Attr { scalar: Option>, /// Explicitly specified type of the custom [`Behavior`] to parametrize this - /// [GraphQL scalar][0] type implementation with. + /// [GraphQL scalar][0] implementation with. /// /// If [`None`], then [`behavior::Standard`] will be used for the generated /// code. @@ -1721,7 +1721,7 @@ impl ParseToken { #[derive(Debug, Default)] struct FieldAttr { /// Explicitly specified type of the custom [`Behavior`] used for - /// [GraphQL scalar][0] implementation by the [`Field`]. + /// [GraphQL scalar][0] implementation by this [`Field`]. /// /// If [`None`], then [`behavior::Standard`] will be used for the generated /// code. From 7364187437740042365c0840fb57f1f6fa1d5154 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 15:58:20 +0200 Subject: [PATCH 46/58] Impl `resolve::Value` and `resolve::ToInputValue` traits for enums [skip ci] --- juniper_codegen/src/graphql_enum/mod.rs | 202 +++++++++++++++++++++- juniper_codegen/src/graphql_scalar/mod.rs | 3 +- 2 files changed, 201 insertions(+), 4 deletions(-) diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index bebe05c2e..2964b8738 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -439,6 +439,16 @@ impl ToTokens for Definition { self.impl_to_input_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_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_graphql_input_type().to_tokens(into); + //self.impl_graphql_output_type().to_tokens(into); + //self.impl_graphql_enum().to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -602,6 +612,64 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL enum][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_value(&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, mut generics) = self.mix_scalar_value(generics); + generics.make_where_clause().predicates.push(parse_quote! { + #sv: From + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let variant_arms = self.values.iter().map(|v| { + let v_ident = &v.ident; + let v_name = &v.name; + + quote! { + Self::#v_ident => ::std::result::Result::Ok( + ::juniper::Value::<#sv>::scalar( + ::std::string::String::from(#v_name), + ), + ), + } + }); + let ignored_arm = self.has_ignored_variants.then(|| { + let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident); + + quote! { + _ => ::std::result::Result::Err( + ::juniper::FieldError::<#sv>::from(#err_msg), + ), + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value( + &self, + _: Option<&[::juniper::Selection<'_, #sv>]>, + _: &#inf, + _: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { + match self { + #( #variant_arms )* + #ignored_arm + } + } + } + } + } + /// Returns generated code implementing [`GraphQLValueAsync`] trait for this /// [GraphQL enum][0]. /// @@ -633,6 +701,48 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ValueAsync`] trait for + /// this [GraphQL enum][0]. + /// + /// [`resolve::ValueAsync`]: juniper::resolve::ValueAsync + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_value_async(&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, mut generics) = self.mix_scalar_value(generics); + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { + Self: ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + }); + preds.push(parse_quote! { + #sv: Send + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value_async<'__r>( + &'__r self, + sel_set: Option<&'__r [::juniper::Selection<'_, #sv>]>, + type_info: &'__r #inf, + executor: &'__r ::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::BoxFuture< + '__r, ::juniper::ExecutionResult<#sv>, + > { + let v = + > + ::resolve_value(self, sel_set, type_info, executor); + ::std::boxed::Box::pin(::juniper::futures::future::ready(v)) + } + } + } + } + /// Returns generated code implementing [`FromInputValue`] trait for this /// [GraphQL enum][0]. /// @@ -717,6 +827,53 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::ToInputValue`] trait for + /// this [GraphQL enum][0]. + /// + /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue + /// [0]: https://spec.graphql.org/October2021#sec-Enums + fn impl_resolve_to_input_value(&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.push(parse_quote! { + #sv: From + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let variant_arms = self.values.iter().map(|v| { + let v_ident = &v.ident; + let v_name = &v.name; + + quote! { + Self::#v_ident => ::juniper::graphql::InputValue::<#sv>::scalar( + ::std::string::String::from(#v_name), + ), + } + }); + let ignored_arm = self.has_ignored_variants.then(|| { + let err_msg = format!("Cannot resolve ignored variant of `{}` enum", self.ident); + + quote! { + _ => ::std::panic!(#err_msg), + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> for #ty + #where_clause + { + fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { + match self { + #( #variant_arms )* + #ignored_arm + } + } + } + } + } + /// Returns generated code implementing [`BaseType`], [`BaseSubTypes`] and /// [`WrappedType`] traits for this [GraphQL enum][0]. /// @@ -860,16 +1017,57 @@ impl Definition { /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait /// implementation. - #[must_use] fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let generics = self.generics.clone(); - let ty = { let ident = &self.ident; let (_, ty_gen, _) = generics.split_for_impl(); parse_quote! { #ident#ty_gen } }; + (ty, generics) + } + /// 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) + } + + /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the + /// provided [`syn::Generics`] and returns it. + /// + /// [`InputValue`]: juniper::resolve::InputValue + fn mix_input_lifetime( + &self, + mut generics: syn::Generics, + sv: &syn::Ident, + ) -> (syn::GenericParam, syn::Generics) { + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #sv: #lt }); + (lt, generics) + } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index e4ebe8067..c29924c9f 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -1084,7 +1084,6 @@ impl Definition { #[must_use] fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { let mut generics = self.generics.clone(); - let ty = { let ident = &self.ident; let (_, ty_gen, _) = generics.split_for_impl(); @@ -1248,7 +1247,7 @@ impl Methods { let into = sv.custom.is_some().then(|| { quote! { .map_scalar_value() } }); - quote! { Ok(#to_output(self)#into) } + quote! { ::std::result::Result::Ok(#to_output(self)#into) } } Self::Delegated { field, .. } => { From 98b282d06d821751fbaf21e387526176a2aeb8fd Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 16:17:50 +0200 Subject: [PATCH 47/58] Impl `resolve::InputValue` trait for enums --- juniper_codegen/src/graphql_enum/mod.rs | 46 +++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 2964b8738..51aba54e7 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -444,8 +444,7 @@ impl ToTokens for Definition { 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_graphql_input_type().to_tokens(into); //self.impl_graphql_output_type().to_tokens(into); //self.impl_graphql_enum().to_tokens(into); @@ -782,6 +781,49 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::InputValue`] trait for + /// this [GraphQL scalar][0]. + /// + /// [`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, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + generics.make_where_clause().predicates.push(parse_quote! { + #sv: ::juniper::ScalarValue + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let variant_arms = self.values.iter().map(|v| { + let v_ident = &v.ident; + let v_name = &v.name; + + quote! { + Some(#v_name) => Ok(Self::#v_ident), + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> for #ty + #where_clause + { + type Error = ::std::string::String; + + fn try_from_input_value( + input: &#lt ::juniper::graphql::InputValue<#sv>, + ) -> ::std::result::Result { + match v.as_enum_value().or_else(|| v.as_string_value()) { + #( #variant_arms )* + _ => Err(::std::format!("Unknown enum value: {}", v)), + } + } + } + } + } + /// Returns generated code implementing [`ToInputValue`] trait for this /// [GraphQL enum][0]. /// From 4257e72c751e724d5b14d57124f13fed2fb02796 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 16:57:54 +0200 Subject: [PATCH 48/58] Impl `resolve::Type` trait for enums --- juniper/src/executor/mod.rs | 35 +++++++ juniper/src/schema/meta.rs | 18 ++++ juniper_codegen/src/graphql_enum/mod.rs | 114 ++++++++++++++++++++-- juniper_codegen/src/graphql_scalar/mod.rs | 2 +- 4 files changed, 160 insertions(+), 9 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 73d8b6a48..b0b7b08c1 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1456,6 +1456,41 @@ impl<'r, S: 'r> Registry<'r, S> { EnumMeta::new::(Cow::Owned(name.to_string()), values) } + /// Builds an [`EnumMeta`] 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 + /// [`TypeName`]: resolve::TypeName + pub fn register_enum_with<'ti, T, TI, F>( + &mut self, + values: &[EnumValue], + type_info: &'ti TI, + customize: F, + ) -> MetaType<'r, S> + where + T: resolve::TypeName + resolve::InputValueOwned, + TI: ?Sized, + 'ti: 'r, + F: FnOnce(EnumMeta<'r, S>) -> EnumMeta<'r, S>, + S: Clone, + { + self.entry_type::(type_info) + .or_insert_with(move || { + customize(EnumMeta::new_reworked::( + T::type_name(type_info), + values, + )) + .into_meta() + }) + .clone() + } + /// Creates an [`InterfaceMeta`] type with the given `fields`. pub fn build_interface_type( &mut self, diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index d686a09ca..ce5b0874f 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -651,6 +651,24 @@ impl<'a, S> EnumMeta<'a, S> { } } + /// Builds a new [`EnumMeta`] information with the specified `name` and + /// possible `values`. + // TODO: Use `impl Into>` argument once feature + // `explicit_generic_args_with_impl_trait` hits stable: + // https://github.com/rust-lang/rust/issues/83701 + pub fn new_reworked(name: N, values: &[EnumValue]) -> Self + where + T: resolve::InputValueOwned, + Cow<'a, str>: From, + { + Self { + name: name.into(), + description: None, + values: values.to_owned(), + try_parse_fn: try_parse_fn_reworked::, + } + } + /// Sets the `description` of this [`EnumMeta`] type. /// /// Overwrites any previously set description. diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 51aba54e7..7cb9a558a 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -439,8 +439,8 @@ impl ToTokens for Definition { self.impl_to_input_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); @@ -553,6 +553,98 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Type`] trait for this + /// [GraphQL enum][0]. + /// + /// [`resolve::Type`]: juniper::resolve::Type + /// [0]: https://spec.graphql.org/October2021#sec-Enums + 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); + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { #sv: Clone }); + preds.push(parse_quote! { + ::juniper::behavior::Coerce: + ::juniper::resolve::TypeName<#inf> + + ::juniper::resolve::InputValueOwned<#sv> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let description = self + .description + .as_ref() + .map(|text| quote! { .description(#text) }); + + let values_meta = self.values.iter().map(|v| { + let v_name = &v.name; + let v_description = v + .description + .as_ref() + .map(|text| quote! { .description(#text) }); + let v_deprecation = v.deprecated.as_ref().map(|depr| { + let reason = depr.as_ref().map_or_else( + || quote! { ::std::option::Option::None }, + |text| quote! { ::std::option::Option::Some(#text) }, + ); + quote! { .deprecated(#reason) } + }); + + quote! { + ::juniper::meta::EnumValue::new(#v_name) + #v_description + #v_deprecation + } + }); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> for #ty + #where_clause + { + fn meta<'__r, '__ti: '__r>( + registry: &mut ::juniper::Registry<'__r, #sv>, + type_info: &'__ti #inf, + ) -> ::juniper::meta::MetaType<'__r, #sv> + where + #sv: '__r, + { + let values = [#( #values_meta ),*]; + + registry.register_enum_with::< + ::juniper::behavior::Coerce, _, _, + >(&values, type_info, |meta| { + meta#description + }) + } + } + } + } + + /// Returns generated code implementing [`resolve::TypeName`] trait for this + /// [GraphQL enum][0]. + /// + /// [`resolve::TypeName`]: juniper::resolve::TypeName + /// [0]: https://spec.graphql.org/October2021#sec-Enums + 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, #bh> for #ty + #where_clause + { + fn type_name(_: &#inf) -> &'static str { + >::NAME + } + } + } + } + /// Returns generated code implementing [`GraphQLValue`] trait for this /// [GraphQL enum][0]. /// @@ -782,15 +874,15 @@ impl Definition { } /// Returns generated code implementing [`resolve::InputValue`] trait for - /// this [GraphQL scalar][0]. + /// this [GraphQL enum][0]. /// /// [`resolve::InputValue`]: juniper::resolve::InputValue - /// [0]: https://spec.graphql.org/October2021#sec-Scalars + /// [0]: https://spec.graphql.org/October2021#sec-Enums fn impl_resolve_input_value(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { #sv: ::juniper::ScalarValue }); @@ -801,7 +893,8 @@ impl Definition { let v_name = &v.name; quote! { - Some(#v_name) => Ok(Self::#v_ident), + ::std::option::Option::Some(#v_name) => + ::std::result::Result::Ok(Self::#v_ident), } }); @@ -815,9 +908,14 @@ impl Definition { fn try_from_input_value( input: &#lt ::juniper::graphql::InputValue<#sv>, ) -> ::std::result::Result { - match v.as_enum_value().or_else(|| v.as_string_value()) { + match input + .as_enum_value() + .or_else(|| input.as_string_value()) + { #( #variant_arms )* - _ => Err(::std::format!("Unknown enum value: {}", v)), + _ => ::std::result::Result::Err( + ::std::format!("Unknown enum value: {}", input), + ), } } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index c29924c9f..21ad3b32b 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -575,7 +575,7 @@ impl Definition { let description = self .description .as_ref() - .map(|val| quote! { .description(#val) }); + .map(|text| quote! { .description(#text) }); let specified_by_url = self.specified_by_url.as_ref().map(|url| { let url_lit = url.as_str(); From fa03c7fa4d1884a700e17c13aa55cada2b0ad20e Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 27 Jun 2022 17:05:01 +0200 Subject: [PATCH 49/58] Impl `graphql::Enum` trait for enums [skip ci] --- juniper/src/graphql/mod.rs | 15 +++- juniper_codegen/src/graphql_enum/mod.rs | 93 ++++++++++++++++++++++++- 2 files changed, 104 insertions(+), 4 deletions(-) diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index a88b154dc..097e775d5 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -6,9 +6,22 @@ pub use crate::{ macros::{input_value, value, vars}, resolve::Type, value::Value, - GraphQLScalar as Scalar, + GraphQLEnum as Enum, GraphQLScalar as Scalar, }; +pub trait Enum< + 'inp, + TypeInfo: ?Sized, + Context: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: + InputType<'inp, TypeInfo, ScalarValue, Behavior> + + OutputType +{ + fn assert_enum(); +} + /* pub trait Interface: OutputType + resolve::TypeName diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 7cb9a558a..ae1b23228 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -445,9 +445,9 @@ impl ToTokens for Definition { 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_graphql_input_type().to_tokens(into); - //self.impl_graphql_output_type().to_tokens(into); - //self.impl_graphql_enum().to_tokens(into); + self.impl_graphql_input_type().to_tokens(into); + self.impl_graphql_output_type().to_tokens(into); + self.impl_graphql_enum().to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -479,6 +479,93 @@ impl Definition { } } + /// Returns generated code implementing [`graphql::InputType`] trait for + /// this [GraphQL enum][0]. + /// + /// [`graphql::InputType`]: juniper::graphql::InputType + /// [0]: https://spec.graphql.org/October2021#sec-Enums + #[must_use] + fn impl_graphql_input_type(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::resolve::Type<#inf, #sv, #bh> + + ::juniper::resolve::ToInputValue<#sv, #bh> + + ::juniper::resolve::InputValue<#lt, #sv, #bh> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + for #ty #where_clause + { + fn assert_input_type() {} + } + } + } + + /// Returns generated code implementing [`graphql::OutputType`] trait for + /// this [GraphQL enum][0]. + /// + /// [`graphql::OutputType`]: juniper::graphql::OutputType + /// [0]: https://spec.graphql.org/October2021#sec-Enums + #[must_use] + fn impl_graphql_output_type(&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, mut generics) = self.mix_scalar_value(generics); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::resolve::Type<#inf, #sv, #bh> + + ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + + ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn assert_output_type() {} + } + } + } + + /// Returns generated code implementing [`graphql::Enum`] trait for this + /// [GraphQL enum][0]. + /// + /// [`graphql::Enum`]: juniper::graphql::Enum + /// [0]: https://spec.graphql.org/October2021#sec-Enums + #[must_use] + fn impl_graphql_enum(&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); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + + ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl#impl_gens ::juniper::graphql::Enum<#lt, #inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn assert_enum() {} + } + } + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL enum][0]. /// From 464ff10c14494bff238a57e1b070ae8ee36f7d82 Mon Sep 17 00:00:00 2001 From: tyranron Date: Wed, 10 Aug 2022 19:10:38 +0300 Subject: [PATCH 50/58] Impl input objects, vol.1 [skip ci] --- juniper/src/behavior.rs | 11 + .../src/graphql_input_object/derive.rs | 12 +- .../src/graphql_input_object/mod.rs | 232 ++++++++++++++++-- 3 files changed, 233 insertions(+), 22 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index cb1976cd4..d8f8b4d21 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -64,6 +64,17 @@ where } } +impl resolve::ToInputValue for Coerce +where + T: resolve::ToInputValue + ?Sized, + B1: ?Sized, + B2: ?Sized, +{ + fn to_input_value(&self) -> graphql::InputValue { + self.1.to_input_value() + } +} + impl<'i, T, SV, B1, B2> resolve::InputValue<'i, SV, B1> for Coerce where T: resolve::InputValue<'i, SV, B2>, diff --git a/juniper_codegen/src/graphql_input_object/derive.rs b/juniper_codegen/src/graphql_input_object/derive.rs index 370fc2002..562343ea2 100644 --- a/juniper_codegen/src/graphql_input_object/derive.rs +++ b/juniper_codegen/src/graphql_input_object/derive.rs @@ -80,6 +80,7 @@ pub fn expand(input: TokenStream) -> syn::Result { description: attr.description.map(SpanContainer::into_inner), context, scalar, + behavior: attr.behavior.into(), fields, }; @@ -94,13 +95,13 @@ fn parse_field( renaming: rename::Policy, is_internal: bool, ) -> Option { - let field_attr = FieldAttr::from_attrs("graphql", &f.attrs) + let attr = FieldAttr::from_attrs("graphql", &f.attrs) .map_err(|e| proc_macro_error::emit_error!(e)) .ok()?; let ident = f.ident.as_ref().or_else(|| err_unnamed_field(f))?; - let name = field_attr + let name = attr .name .map_or_else( || renaming.apply(&ident.unraw().to_string()), @@ -114,10 +115,11 @@ fn parse_field( Some(FieldDefinition { ident: ident.clone(), ty: f.ty.clone(), - default: field_attr.default.map(SpanContainer::into_inner), + default: attr.default.map(SpanContainer::into_inner), + behavior: attr.behavior.into(), name, - description: field_attr.description.map(SpanContainer::into_inner), - ignored: field_attr.ignore.is_some(), + description: attr.description.map(SpanContainer::into_inner), + ignored: attr.ignore.is_some(), }) } diff --git a/juniper_codegen/src/graphql_input_object/mod.rs b/juniper_codegen/src/graphql_input_object/mod.rs index 93c21d6f8..9d37269f2 100644 --- a/juniper_codegen/src/graphql_input_object/mod.rs +++ b/juniper_codegen/src/graphql_input_object/mod.rs @@ -15,7 +15,7 @@ use syn::{ }; use crate::common::{ - default, filter_attrs, + behavior, default, filter_attrs, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -66,6 +66,17 @@ struct ContainerAttr { /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects scalar: Option>, + /// Explicitly specified type of the custom [`Behavior`] to parametrize this + /// [GraphQL input object][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-Input-Objects + behavior: Option>, + /// Explicitly specified [`rename::Policy`] for all fields of this /// [GraphQL input object][0]. /// @@ -118,6 +129,13 @@ impl Parse for ContainerAttr { .replace(SpanContainer::new(ident.span(), Some(scl.span()), scl)) .none_or_else(|_| err::dup_arg(&ident))? } + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "rename_all" => { input.parse::()?; let val = input.parse::()?; @@ -151,6 +169,7 @@ impl ContainerAttr { description: try_merge_opt!(description: self, another), context: try_merge_opt!(context: self, another), scalar: try_merge_opt!(scalar: self, another), + behavior: try_merge_opt!(behavior: self, another), rename_fields: try_merge_opt!(rename_fields: self, another), is_internal: self.is_internal || another.is_internal, }) @@ -185,6 +204,16 @@ struct FieldAttr { /// [1]: https://spec.graphql.org/October2021#InputValueDefinition name: Option>, + /// Explicitly specified [description][2] of this + /// [GraphQL input object field][1]. + /// + /// If [`None`], then Rust doc comment will be used as the [description][2], + /// if any. + /// + /// [1]: https://spec.graphql.org/October2021#InputValueDefinition + /// [2]: https://spec.graphql.org/October2021#sec-Descriptions + description: Option>, + /// Explicitly specified [default value][2] of this /// [GraphQL input object field][1] to be used used in case a field value is /// not provided. @@ -195,15 +224,18 @@ struct FieldAttr { /// [2]: https://spec.graphql.org/October2021#DefaultValue default: Option>, - /// Explicitly specified [description][2] of this - /// [GraphQL input object field][1]. + /// Explicitly specified type of the custom [`Behavior`] this + /// [GraphQL input object field][1] implementation is parametrized with, to + /// [coerce] in the generated code from. /// - /// If [`None`], then Rust doc comment will be used as the [description][2], - /// if any. + /// If [`None`], then [`behavior::Standard`] will be used for the generated + /// code. /// + /// [`Behavior`]: juniper::behavior + /// [`behavior::Standard`]: juniper::behavior::Standard /// [1]: https://spec.graphql.org/October2021#InputValueDefinition - /// [2]: https://spec.graphql.org/October2021#sec-Descriptions - description: Option>, + /// [coerce]: juniper::behavior::Coerce + behavior: Option>, /// Explicitly specified marker for the Rust struct field to be ignored and /// not included into the code generated for a [GraphQL input object][0] @@ -234,17 +266,24 @@ impl Parse for FieldAttr { )) .none_or_else(|_| err::dup_arg(&ident))? } + "desc" | "description" => { + input.parse::()?; + let desc = input.parse::()?; + out.description + .replace(SpanContainer::new(ident.span(), Some(desc.span()), desc)) + .none_or_else(|_| err::dup_arg(&ident))? + } "default" => { let val = input.parse::()?; out.default .replace(SpanContainer::new(ident.span(), Some(val.span()), val)) .none_or_else(|_| err::dup_arg(&ident))? } - "desc" | "description" => { + "behave" | "behavior" => { input.parse::()?; - let desc = input.parse::()?; - out.description - .replace(SpanContainer::new(ident.span(), Some(desc.span()), desc)) + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) .none_or_else(|_| err::dup_arg(&ident))? } "ignore" | "skip" => out @@ -267,8 +306,9 @@ impl FieldAttr { fn try_merge(self, mut another: Self) -> syn::Result { Ok(Self { name: try_merge_opt!(name: self, another), - default: try_merge_opt!(default: self, another), description: try_merge_opt!(description: self, another), + default: try_merge_opt!(default: self, another), + behavior: try_merge_opt!(behavior: self, another), ignore: try_merge_opt!(ignore: self, another), }) } @@ -314,6 +354,14 @@ struct FieldDefinition { /// [2]: https://spec.graphql.org/October2021#DefaultValue default: Option, + /// [`Behavior`] parametrization of this [GraphQL input object field][1] + /// implementation to [coerce] from in the generated code. + /// + /// [`Behavior`]: juniper::behavior + /// [1]: https://spec.graphql.org/October2021#InputValueDefinition + /// [coerce]: juniper::behavior::Coerce + behavior: behavior::Type, + /// Name of this [GraphQL input object field][1] in GraphQL schema. /// /// [1]: https://spec.graphql.org/October2021#InputValueDefinition @@ -383,6 +431,13 @@ struct Definition { /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects scalar: scalar::Type, + /// [`Behavior`] parametrization to generate code with for this + /// [GraphQL input object][0]. + /// + /// [`Behavior`]: juniper::behavior + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + behavior: behavior::Type, + /// [Fields][1] of this [GraphQL input object][0]. /// /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects @@ -399,6 +454,14 @@ impl ToTokens for Definition { self.impl_from_input_value_tokens().to_tokens(into); self.impl_to_input_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_to_input_value().to_tokens(into); + //self.impl_resolve_input_value().to_tokens(into); + //self.impl_graphql_input_type().to_tokens(into); + //self.impl_graphql_input_object().to_tokens(into); + self.impl_reflect().to_tokens(into); } } @@ -500,6 +563,29 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::TypeName`] trait for this + /// [GraphQL input object][0]. + /// + /// [`resolve::TypeName`]: juniper::resolve::TypeName + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + 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, #bh> + for #ty #where_clause + { + fn type_name(_: &#inf) -> &'static str { + >::NAME + } + } + } + } + /// Returns generated code implementing [`GraphQLValue`] trait for this /// [GraphQL input object][0]. /// @@ -663,11 +749,52 @@ impl Definition { #where_clause { fn to_input_value(&self) -> ::juniper::InputValue<#scalar> { - ::juniper::InputValue::object( - #[allow(deprecated)] - ::std::array::IntoIter::new([#( #fields ),*]) - .collect() - ) + ::juniper::InputValue::object([#( #fields ),*]) + } + } + } + } + + /// Returns generated code implementing [`resolve::ToInputValue`] trait for + /// this [GraphQL input object][0]. + /// + /// [`resolve::ToInputValue`]: juniper::resolve::ToInputValue + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + fn impl_resolve_to_input_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (sv, mut generics) = self.mix_scalar_value(generics); + for f in &self.fields { + let field_ty = &f.ty; + let field_bh = &f.behavior; + generics.make_where_clause().predicates.push(parse_quote! { + #field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh> + }); + } + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let fields = self.fields.iter().filter_map(|f| { + let field = &f.ident; + let field_ty = &f.ty; + let field_bh = &f.behavior; + let name = &f.name; + + (!f.ignored).then(|| { + quote! { + (#name, <#field_ty as + ::juniper::resolve::ToInputValue<#sv, #field_bh>> + ::to_input_value(&self.#field)) + } + }) + }); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> + for #ty #where_clause + { + fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { + ::juniper::InputValue::object([#( #fields ),*]) } } } @@ -716,6 +843,47 @@ impl Definition { } } + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this + /// [GraphQL input object][0]. + /// + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + fn impl_reflect(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let name = &self.name; + + quote! { + #[automatically_derived] + 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<#bh> + for #ty #where_clause + { + const NAMES: ::juniper::reflect::Types = + &[>::NAME]; + } + + #[automatically_derived] + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause + { + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; + } + } + } + /// Returns prepared [`syn::Generics`] for [`GraphQLType`] trait (and /// similar) implementation of this struct. /// @@ -775,4 +943,34 @@ impl Definition { generics } + + /// 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.ident; + let (_, ty_gen, _) = generics.split_for_impl(); + parse_quote! { #ident #ty_gen } + }; + (ty, generics) + } + + /// 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 [`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) + } } From fea722b1961e0aa2d91aa764e5ab9294a12b4be4 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 11 Aug 2022 18:06:43 +0300 Subject: [PATCH 51/58] Impl input objects, vol.2 [skip ci] --- juniper/src/behavior.rs | 11 - juniper/src/executor/mod.rs | 45 +++ juniper/src/graphql/mod.rs | 10 + juniper/src/schema/meta.rs | 19 ++ juniper_codegen/src/graphql_enum/mod.rs | 9 +- .../src/graphql_input_object/mod.rs | 308 +++++++++++++++++- juniper_codegen/src/graphql_scalar/mod.rs | 9 +- 7 files changed, 386 insertions(+), 25 deletions(-) diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index d8f8b4d21..cb1976cd4 100644 --- a/juniper/src/behavior.rs +++ b/juniper/src/behavior.rs @@ -64,17 +64,6 @@ where } } -impl resolve::ToInputValue for Coerce -where - T: resolve::ToInputValue + ?Sized, - B1: ?Sized, - B2: ?Sized, -{ - fn to_input_value(&self) -> graphql::InputValue { - self.1.to_input_value() - } -} - impl<'i, T, SV, B1, B2> resolve::InputValue<'i, SV, B1> for Coerce where T: resolve::InputValue<'i, SV, B2>, diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 4cd8bffa7..558d58508 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1263,6 +1263,16 @@ impl<'r, S: 'r> Registry<'r, S> { Argument::new(name, self.get_type::(info)).default_value(value.to_input_value()) } + /// Creates an [`Argument`] with the provided `name`. + pub fn arg_reworked<'ti, T, TI>(&mut self, name: &str, type_info: &'ti TI) -> Argument<'r, S> + where + T: resolve::Type + resolve::InputValueOwned, + TI: ?Sized, + 'ti: 'r, + { + Argument::new(name, T::meta(self, type_info).as_type()) + } + fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) { self.types .entry(name) @@ -1531,4 +1541,39 @@ impl<'r, S: 'r> Registry<'r, S> { InputObjectMeta::new::(Cow::Owned(name.into()), args) } + + /// Builds an [`InputObjectMeta`] 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 + /// [`TypeName`]: resolve::TypeName + pub fn register_input_object_with<'ti, T, TI, F>( + &mut self, + fields: &[Argument<'r, S>], + type_info: &'ti TI, + customize: F, + ) -> MetaType<'r, S> + where + T: resolve::TypeName + resolve::InputValueOwned, + TI: ?Sized, + 'ti: 'r, + F: FnOnce(InputObjectMeta<'r, S>) -> InputObjectMeta<'r, S>, + S: Clone, + { + self.entry_type::(type_info) + .or_insert_with(move || { + customize(InputObjectMeta::new_reworked::( + T::type_name(type_info), + fields, + )) + .into_meta() + }) + .clone() + } } diff --git a/juniper/src/graphql/mod.rs b/juniper/src/graphql/mod.rs index 097e775d5..387690d16 100644 --- a/juniper/src/graphql/mod.rs +++ b/juniper/src/graphql/mod.rs @@ -47,6 +47,16 @@ pub trait Object: OutputType fn assert_object(); }*/ +pub trait InputObject< + 'inp, + TypeInfo: ?Sized, + ScalarValue: 'inp, + Behavior: ?Sized = behavior::Standard, +>: InputType<'inp, TypeInfo, ScalarValue, Behavior> +{ + fn assert_input_object(); +} + pub trait Scalar< 'inp, TypeInfo: ?Sized, diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 717a23834..13d526ebb 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -769,6 +769,25 @@ impl<'a, S> InputObjectMeta<'a, S> { } } + /// Builds a new [`InputObjectMeta`] information with the specified `name` + /// and its `fields`. + // TODO: Use `impl Into>` argument once feature + // `explicit_generic_args_with_impl_trait` hits stable: + // https://github.com/rust-lang/rust/issues/83701 + pub fn new_reworked(name: N, fields: &[Argument<'a, S>]) -> Self + where + T: resolve::InputValueOwned, + Cow<'a, str>: From, + S: Clone, + { + Self { + name: name.into(), + description: None, + input_fields: fields.to_vec(), + try_parse_fn: try_parse_fn_reworked::, + } + } + /// Set the `description` of this [`InputObjectMeta`] type. /// /// Overwrites any previously set description. diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 85e22be03..3f26625f0 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -533,9 +533,14 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::Enum<#lt, #inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { - fn assert_enum() {} + fn assert_enum() { + > + ::assert_input_type(); + > + ::assert_output_type(); + } } } } diff --git a/juniper_codegen/src/graphql_input_object/mod.rs b/juniper_codegen/src/graphql_input_object/mod.rs index 9d37269f2..ec6ea64c5 100644 --- a/juniper_codegen/src/graphql_input_object/mod.rs +++ b/juniper_codegen/src/graphql_input_object/mod.rs @@ -386,6 +386,14 @@ struct FieldDefinition { ignored: bool, } +impl FieldDefinition { + /// Indicates whether this [`FieldDefinition`] uses [`Default::default()`] + /// ans its [`FieldDefinition::default`] value. + fn needs_default_trait_bound(&self) -> bool { + matches!(self.default, Some(default::Value::Default)) + } +} + /// Representation of [GraphQL input object][0] for code generation. /// /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects @@ -455,12 +463,12 @@ impl ToTokens for Definition { self.impl_to_input_value_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// - //self.impl_resolve_type().to_tokens(into); + self.impl_resolve_type().to_tokens(into); self.impl_resolve_type_name().to_tokens(into); self.impl_resolve_to_input_value().to_tokens(into); - //self.impl_resolve_input_value().to_tokens(into); - //self.impl_graphql_input_type().to_tokens(into); - //self.impl_graphql_input_object().to_tokens(into); + self.impl_resolve_input_value().to_tokens(into); + self.impl_graphql_input_type().to_tokens(into); + self.impl_graphql_input_object().to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -503,6 +511,88 @@ impl Definition { } } + /// Returns generated code implementing [`graphql::InputType`] trait for + /// [GraphQL input object][0]. + /// + /// [`graphql::InputType`]: juniper::graphql::InputType + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + #[must_use] + fn impl_graphql_input_type(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::resolve::Type<#inf, #sv, #bh> + + ::juniper::resolve::ToInputValue<#sv, #bh> + + ::juniper::resolve::InputValue<#lt, #sv, #bh> + }); + for f in self.fields.iter().filter(|f| !f.ignored) { + let field_ty = &f.ty; + let field_bh = &f.behavior; + generics.make_where_clause().predicates.push(parse_quote! { + #field_ty: + ::juniper::graphql::InputType<#lt, #inf, #sv, #field_bh> + }); + } + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let fields_assertions = self.fields.iter().filter_map(|f| { + (!f.ignored).then(|| { + let field_ty = &f.ty; + let field_bh = &f.behavior; + + quote! { + <#field_ty as + ::juniper::graphql::InputType<#lt, #inf, #sv, #field_bh>> + ::assert_input_type(); + } + }) + }); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + for #ty #where_clause + { + fn assert_input_type() { + #( #fields_assertions )* + } + } + } + } + + /// Returns generated code implementing [`graphql::InputObject`] trait for + /// this [GraphQL input object][0]. + /// + /// [`graphql::InputObject`]: juniper::graphql::InputObject + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + #[must_use] + fn impl_graphql_input_object(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = self.mix_type_info(generics); + let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + generics.make_where_clause().predicates.push(parse_quote! { + Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + }); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::graphql::InputObject<#lt, #inf, #sv, #bh> + for #ty #where_clause + { + fn assert_input_object() { + > + ::assert_input_type(); + } + } + } + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL input object][0]. /// @@ -563,6 +653,96 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Type`] trait for this + /// [GraphQL input object][0]. + /// + /// [`resolve::Type`]: juniper::resolve::Type + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + 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); + let preds = &mut generics.make_where_clause().predicates; + preds.push(parse_quote! { #sv: Clone }); + preds.push(parse_quote! { + ::juniper::behavior::Coerce: + ::juniper::resolve::TypeName<#inf> + + ::juniper::resolve::InputValueOwned<#sv> + }); + for f in self.fields.iter().filter(|f| !f.ignored) { + let field_ty = &f.ty; + let field_bh = &f.behavior; + preds.push(parse_quote! { + ::juniper::behavior::Coerce<#field_ty>: + ::juniper::resolve::Type<#inf, #sv> + + ::juniper::resolve::InputValueOwned<#sv> + }); + if f.default.is_some() { + preds.push(parse_quote! { + #field_ty: ::juniper::resolve::ToInputValue<#sv, #field_bh> + }); + } + if f.needs_default_trait_bound() { + preds.push(parse_quote! { + #field_ty: ::std::default::Default + }); + } + } + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let description = &self.description; + + let fields_meta = self.fields.iter().filter_map(|f| { + (!f.ignored).then(|| { + let f_ty = &f.ty; + let f_bh = &f.behavior; + let f_name = &f.name; + let f_description = &f.description; + let f_default = f.default.as_ref().map(|expr| { + quote! { + .default_value( + <#f_ty as + ::juniper::resolve::ToInputValue<#sv, #f_bh>> + ::to_input_value(&{ #expr }), + ) + } + }); + + quote! { + registry.arg_reworked::< + ::juniper::behavior::Coerce<#f_ty>, _, + >(#f_name, type_info) + #f_description + #f_default + } + }) + }); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> + for #ty #where_clause + { + fn meta<'__r, '__ti: '__r>( + registry: &mut ::juniper::Registry<'__r, #sv>, + type_info: &'__ti #inf, + ) -> ::juniper::meta::MetaType<'__r, #sv> + where + #sv: '__r, + { + let fields = [#( #fields_meta ),*]; + + registry.register_input_object_with::< + ::juniper::behavior::Coerce, _, _, + >(&fields, type_info, |meta| { + meta #description + }) + } + } + } + } + /// Returns generated code implementing [`resolve::TypeName`] trait for this /// [GraphQL input object][0]. /// @@ -717,6 +897,96 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::InputValue`] trait for + /// this [GraphQL input object][0]. + /// + /// [`resolve::InputValue`]: juniper::resolve::InputValue + /// [0]: https://spec.graphql.org/October2021#sec-Input-Objects + fn impl_resolve_input_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (sv, generics) = self.mix_scalar_value(generics); + let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + generics.make_where_clause().predicates.push(parse_quote! { + #sv: ::juniper::ScalarValue + }); + for f in self.fields.iter().filter(|f| !f.ignored) { + let field_ty = &f.ty; + let field_bh = &f.behavior; + generics.make_where_clause().predicates.push(parse_quote! { + #field_ty: ::juniper::resolve::InputValue<#lt, #sv, #field_bh> + }); + } + for f in self.fields.iter().filter(|f| f.needs_default_trait_bound()) { + let field_ty = &f.ty; + generics.make_where_clause().predicates.push(parse_quote! { + #field_ty: ::std::default::Default, + }); + } + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let fields = self.fields.iter().map(|f| { + let field = &f.ident; + let field_ty = &f.ty; + let field_bh = &f.behavior; + + let constructor = if f.ignored { + let expr = f.default.clone().unwrap_or_default(); + + quote! { #expr } + } else { + let name = &f.name; + + let fallback = f.default.as_ref().map_or_else( + || { + quote! { + <#field_ty as ::juniper::resolve::InputValue<#lt, #sv, #field_bh>> + ::try_from_implicit_null() + .map_err(::juniper::IntoFieldError::<#sv>::into_field_error)? + } + }, + |expr| quote! { #expr }, + ); + + quote! { + match obj.get(#name) { + ::std::option::Option::Some(v) => { + <#field_ty as ::juniper::resolve::InputValue<#lt, #sv, #field_bh>> + ::try_from_input_value(v) + .map_err(::juniper::IntoFieldError::<#sv>::into_field_error)? + } + ::std::option::Option::None => { #fallback } + } + } + }; + + quote! { #field: { #constructor }, } + }); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> + for #ty #where_clause + { + type Error = ::juniper::FieldError<#sv>; + + fn try_from_input_value( + input: &#lt ::juniper::graphql::InputValue<#sv>, + ) -> ::std::result::Result { + let obj = input + .to_object_value() + .ok_or_else(|| ::std::format!( + "Expected input object, found: {}", input, + ))?; + + ::std::result::Result::Ok(Self { + #( #fields )* + }) + } + } + } + } + /// Returns generated code implementing [`ToInputValue`] trait for this /// [GraphQL input object][0]. /// @@ -764,7 +1034,7 @@ impl Definition { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); let (sv, mut generics) = self.mix_scalar_value(generics); - for f in &self.fields { + for f in self.fields.iter().filter(|f| !f.ignored) { let field_ty = &f.ty; let field_bh = &f.behavior; generics.make_where_clause().predicates.push(parse_quote! { @@ -774,12 +1044,12 @@ impl Definition { let (impl_gens, _, where_clause) = generics.split_for_impl(); let fields = self.fields.iter().filter_map(|f| { - let field = &f.ident; - let field_ty = &f.ty; - let field_bh = &f.behavior; - let name = &f.name; - (!f.ignored).then(|| { + let field = &f.ident; + let field_ty = &f.ty; + let field_bh = &f.behavior; + let name = &f.name; + quote! { (#name, <#field_ty as ::juniper::resolve::ToInputValue<#sv, #field_bh>> @@ -973,4 +1243,22 @@ impl Definition { generics.params.push(parse_quote! { #sv }); (sv, generics) } + + /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the + /// provided [`syn::Generics`] and returns it. + /// + /// [`InputValue`]: juniper::resolve::InputValue + fn mix_input_lifetime( + &self, + mut generics: syn::Generics, + sv: &syn::Ident, + ) -> (syn::GenericParam, syn::Generics) { + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #sv: #lt }); + (lt, generics) + } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 316d79eda..2752c4eb2 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -467,9 +467,14 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::Scalar<#lt, #inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { - fn assert_scalar() {} + fn assert_scalar() { + > + ::assert_input_type(); + > + ::assert_output_type(); + } } } } From 37257934b71c64873c5115e576837f2eeba12f91 Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 11 Aug 2022 18:23:46 +0300 Subject: [PATCH 52/58] Some code style corrections [skip ci] --- juniper_codegen/src/graphql_enum/mod.rs | 34 ++++++++++---------- juniper_codegen/src/graphql_scalar/mod.rs | 38 +++++++++++------------ 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 3f26625f0..9f973edc7 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -475,7 +475,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn assert_input_type() {} } @@ -504,7 +504,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn assert_output_type() {} } @@ -634,8 +634,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> + for #ty #where_clause { fn meta<'__r, '__ti: '__r>( registry: &mut ::juniper::Registry<'__r, #sv>, @@ -669,8 +669,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh> + for #ty #where_clause { fn type_name(_: &#inf) -> &'static str { >::NAME @@ -778,7 +778,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn resolve_value( &self, @@ -849,7 +849,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn resolve_value_async<'__r>( &'__r self, @@ -935,7 +935,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { type Error = ::std::string::String; @@ -1035,8 +1035,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> + for #ty #where_clause { fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { match self { @@ -1106,23 +1106,23 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::reflect::BaseType<#bh> for #ty - #where_clause + 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<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[>::NAME]; } #[automatically_derived] - impl #impl_gens ::juniper::reflect::WrappedType<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause { const VALUE: ::juniper::reflect::WrappedValue = ::juniper::reflect::wrap::SINGULAR; diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 2752c4eb2..568d1dd22 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -408,7 +408,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn assert_input_type() {} } @@ -437,7 +437,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn assert_output_type() {} } @@ -535,8 +535,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::TypeName<#inf, #bh> + for #ty #where_clause { fn type_name(_: &#inf) -> &'static str { >::NAME @@ -573,8 +573,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::Type<#inf, #sv, #bh> + for #ty #where_clause { fn meta<'__r, '__ti: '__r>( registry: &mut ::juniper::Registry<'__r, #sv>, @@ -652,7 +652,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn resolve_value( &self, @@ -719,7 +719,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::ValueAsync<#inf, #cx, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { fn resolve_value_async<'__r>( &'__r self, @@ -782,8 +782,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::ToInputValue<#sv, #bh> + for #ty #where_clause { fn to_input_value(&self) -> ::juniper::graphql::InputValue<#sv> { #body @@ -842,7 +842,7 @@ impl Definition { quote! { #[automatically_derived] impl #impl_gens ::juniper::resolve::InputValue<#lt, #sv, #bh> - for #ty #where_clause + for #ty #where_clause { type Error = #error_ty; @@ -901,8 +901,8 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::resolve::ScalarToken<#sv, #bh> for #ty - #where_clause + impl #impl_gens ::juniper::resolve::ScalarToken<#sv, #bh> + for #ty #where_clause { fn parse_scalar_token( token: ::juniper::parser::ScalarToken<'_>, @@ -969,23 +969,23 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::reflect::BaseType<#bh> for #ty - #where_clause + 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<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[>::NAME]; } #[automatically_derived] - impl #impl_gens ::juniper::reflect::WrappedType<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause { const VALUE: ::juniper::reflect::WrappedValue = ::juniper::reflect::wrap::SINGULAR; From b3659b88a4a5aef2b4b6a02b2d568709c57e9f6c Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 11 Aug 2022 19:06:10 +0300 Subject: [PATCH 53/58] Impl objects, vol.1 [skip ci] --- juniper_codegen/src/graphql_interface/mod.rs | 22 +++++----- juniper_codegen/src/graphql_object/mod.rs | 33 +++++++------- juniper_codegen/src/graphql_union/mod.rs | 45 +++++++++++++++++++- 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index 832531c2f..6893e9db7 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -395,7 +395,7 @@ impl ToTokens for Definition { self.impl_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); } } @@ -1002,15 +1002,15 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::reflect::BaseType<#bh> for #ty - #where_clause + 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<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[ >::NAME, @@ -1019,8 +1019,8 @@ impl Definition { } #[automatically_derived] - impl #impl_gens ::juniper::reflect::Implements<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::Implements<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[#( <#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME @@ -1028,16 +1028,16 @@ impl Definition { } #[automatically_derived] - impl #impl_gens ::juniper::reflect::WrappedType<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause { const VALUE: ::juniper::reflect::WrappedValue = ::juniper::reflect::wrap::SINGULAR; } #[automatically_derived] - impl #impl_gens ::juniper::reflect::Fields<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::Fields<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Names = &[#( #fields ),*]; } diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index de132f899..6a25090a8 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -474,23 +474,23 @@ impl Definition { quote! { #[automatically_derived] - impl #impl_gens ::juniper::reflect::BaseType<#bh> for #ty - #where_clause + 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<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[>::NAME]; } #[automatically_derived] - impl #impl_gens ::juniper::reflect::Implements<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::Implements<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Types = &[#( <#interfaces as ::juniper::reflect::BaseType<#bh>>::NAME @@ -498,16 +498,16 @@ impl Definition { } #[automatically_derived] - impl #impl_gens ::juniper::reflect::WrappedType<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause { const VALUE: ::juniper::reflect::WrappedValue = ::juniper::reflect::wrap::SINGULAR; } #[automatically_derived] - impl #impl_gens ::juniper::reflect::Fields<#bh> for #ty - #where_clause + impl #impl_gens ::juniper::reflect::Fields<#bh> + for #ty #where_clause { const NAMES: ::juniper::reflect::Names = &[#( #fields ),*]; } @@ -531,9 +531,8 @@ impl Definition { .map(|field| { let (f_name, f_ty, f_bh) = (&field.name, &field.ty, &field.behavior); - let arguments = field - .arguments - .as_ref() + let arguments = field.arguments.as_ref(); + let arguments = arguments .iter() .flat_map(|vec| vec.iter().filter_map(field::MethodArgument::as_regular)) .map(|arg| { @@ -546,11 +545,9 @@ impl Definition { <#a_ty as ::juniper::reflect::WrappedType<#a_bh>> ::VALUE, )} - }) - .collect::>(); + }); quote! { - #[allow(deprecated, non_snake_case)] #[automatically_derived] impl #impl_gens ::juniper::reflect::Field< { ::juniper::reflect::fnv1a128(#f_name) }, #bh, @@ -657,8 +654,8 @@ impl ToTokens for Definition { self.impl_field_tokens().to_tokens(into); self.impl_async_field_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// - //self.impl_reflect().to_tokens(into); - //self.impl_reflect_field().to_tokens(into); + self.impl_reflect().to_tokens(into); + self.impl_reflect_field().to_tokens(into); //self.impl_resolve_field_static().to_tokens(into); } } diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index fa585b004..14355511a 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -315,6 +315,8 @@ impl ToTokens for Definition { self.impl_graphql_value_tokens().to_tokens(into); self.impl_graphql_value_async_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); + //////////////////////////////////////////////////////////////////////// + self.impl_reflect().to_tokens(into); } } @@ -610,7 +612,7 @@ impl Definition { /// [`WrappedType`]: juniper::macros::reflect::WrappedType /// [1]: https://spec.graphql.org/October2021#sec-Unions #[must_use] - pub(crate) fn impl_reflection_traits_tokens(&self) -> TokenStream { + pub(crate)fn impl_reflection_traits_tokens(&self) -> TokenStream { let scalar = &self.scalar; let name = &self.name; let variants = self.variants.iter().map(|var| &var.ty); @@ -645,6 +647,47 @@ impl Definition { } } } + + /// Returns generated code implementing [`reflect::BaseType`], + /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this + /// [GraphQL union][0]. + /// + /// [`reflect::BaseSubTypes`]: juniper::reflect::BaseSubTypes + /// [`reflect::BaseType`]: juniper::reflect::BaseType + /// [`reflect::WrappedType`]: juniper::reflect::WrappedType + /// [0]: https://spec.graphql.org/October2021#sec-Unions + fn impl_reflect(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + let name = &self.name; + + quote! { + #[automatically_derived] + 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<#bh> + for #ty #where_clause + { + const NAMES: ::juniper::reflect::Types = + &[>::NAME]; + } + + #[automatically_derived] + impl #impl_gens ::juniper::reflect::WrappedType<#bh> + for #ty #where_clause + { + const VALUE: ::juniper::reflect::WrappedValue = + ::juniper::reflect::wrap::SINGULAR; + } + } + } } /// Definition of [GraphQL union][1] variant for code generation. From c75d33ae0aea4011d760d8fe994c5bb2f7577e1f Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 29 Aug 2022 12:55:44 +0300 Subject: [PATCH 54/58] Impl field resolving, vol.1 --- juniper/src/executor/mod.rs | 37 +++-- juniper/src/executor_tests/executor.rs | 4 +- .../src/executor_tests/interfaces_unions.rs | 36 ++--- juniper/src/extract.rs | 9 ++ juniper/src/lib.rs | 2 + juniper/src/resolve/mod.rs | 80 ++++++++++- juniper/src/types/base.rs | 35 +++++ juniper_codegen/src/graphql_object/mod.rs | 132 +++++++++++++++++- juniper_codegen/src/graphql_union/attr.rs | 2 + juniper_codegen/src/graphql_union/derive.rs | 3 + juniper_codegen/src/graphql_union/mod.rs | 91 +++++++++++- 11 files changed, 384 insertions(+), 47 deletions(-) create mode 100644 juniper/src/extract.rs diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 36076455e..cd550d7bb 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -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> { &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(&self, value: &Type, type_info: &TI) -> ExecutionResult + where + Type: resolve::Value + ?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 @@ -635,14 +664,6 @@ where 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 pub fn schema(&self) -> &'a SchemaType { self.schema diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index 06d93fc0e..088974187 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -369,6 +369,8 @@ mod threads_context_correctly { } } +// TODO: Remove as should be unnecessary with generic context. +/* mod dynamic_context_switching { use indexmap::IndexMap; @@ -672,7 +674,7 @@ mod dynamic_context_switching { assert_eq!(result, graphql_value!({"first": {"value": "First value"}})); } } - +*/ mod propagates_errors_to_nullable_fields { use crate::{ executor::{ExecutionError, FieldError, FieldResult, IntoFieldError}, diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs index 3e9fccf43..9b8ac7469 100644 --- a/juniper/src/executor_tests/interfaces_unions.rs +++ b/juniper/src/executor_tests/interfaces_unions.rs @@ -96,19 +96,15 @@ mod interface { mod union { use crate::{ - graphql_object, graphql_union, graphql_value, + graphql_object, GraphQLUnion, graphql_value, schema::model::RootNode, types::scalars::{EmptyMutation, EmptySubscription}, }; - #[graphql_union] - trait Pet { - fn as_dog(&self) -> Option<&Dog> { - None - } - fn as_cat(&self) -> Option<&Cat> { - None - } + #[derive(GraphQLUnion)] + enum Pet { + Dog(Dog), + Cat(Cat), } struct Dog { @@ -116,12 +112,6 @@ mod union { woofs: bool, } - impl Pet for Dog { - fn as_dog(&self) -> Option<&Dog> { - Some(self) - } - } - #[graphql_object] impl Dog { fn name(&self) -> &str { @@ -137,12 +127,6 @@ mod union { meows: bool, } - impl Pet for Cat { - fn as_cat(&self) -> Option<&Cat> { - Some(self) - } - } - #[graphql_object] impl Cat { fn name(&self) -> &str { @@ -154,13 +138,13 @@ mod union { } struct Schema { - pets: Vec>, + pets: Vec, } #[graphql_object] impl Schema { - fn pets(&self) -> Vec<&(dyn Pet + Send + Sync)> { - self.pets.iter().map(|p| p.as_ref()).collect() + fn pets(&self) -> &[Pet] { + &self.pets } } @@ -169,11 +153,11 @@ mod union { let schema = RootNode::new( Schema { pets: vec![ - Box::new(Dog { + Pet::Dog(Dog { name: "Odie".into(), woofs: true, }), - Box::new(Cat { + Pet::Cat(Cat { name: "Garfield".into(), meows: false, }), diff --git a/juniper/src/extract.rs b/juniper/src/extract.rs new file mode 100644 index 000000000..3b02845b5 --- /dev/null +++ b/juniper/src/extract.rs @@ -0,0 +1,9 @@ +pub trait Extract { + fn extract(&self) -> &T; +} + +impl Extract for T { + fn extract(&self) -> &Self { + self + } +} diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index a40e1bf81..9271f8d47 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -33,6 +33,7 @@ pub mod macros; mod ast; pub mod behavior; pub mod executor; +pub mod extract; pub mod graphql; pub mod http; pub mod integrations; @@ -94,6 +95,7 @@ pub use crate::{ }, validation::RuleError, value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value}, + extract::Extract, }; /// An error that prevented query execution diff --git a/juniper/src/resolve/mod.rs b/juniper/src/resolve/mod.rs index 7c7e5c191..cdab181c3 100644 --- a/juniper/src/resolve/mod.rs +++ b/juniper/src/resolve/mod.rs @@ -2,8 +2,10 @@ use crate::{ behavior, graphql, meta::MetaType, 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 { fn meta<'r, 'ti: 'r>( @@ -110,9 +112,9 @@ pub trait StaticField< { fn resolve_static_field( &self, - arguments: &Arguments, + arguments: &Arguments<'_, ScalarValue>, type_info: &TypeInfo, - executor: &Executor, + executor: &Executor<'_, '_, Context, ScalarValue>, ) -> ExecutionResult; } @@ -202,3 +204,75 @@ pub trait InputValueAsRef { pub trait ScalarToken { fn parse_scalar_token(token: parser::ScalarToken<'_>) -> Result; } + +/* +pub trait IntoResolvable< + ScalarValue, +> +{ + type Type; + + fn into_resolvable(self) -> FieldResult; +} + +impl IntoResolvable for T +where + T: crate::GraphQLValue, + SV: crate::ScalarValue, +{ + type Type = Self; + + fn into_resolvable(self) -> FieldResult { + Ok(self) + } +} + +impl IntoResolvable for Result +where + T: crate::GraphQLValue, + SV: crate::ScalarValue, + E: IntoFieldError, +{ + type Type = T; + + fn into_resolvable(self) -> FieldResult { + self.map_err(IntoFieldError::into_field_error) + } +} +*/ + +#[doc(hidden)] +pub trait IntoResolvable +where + T: crate::GraphQLValue, + S: crate::ScalarValue, +{ + type Type; + + #[doc(hidden)] + fn into_resolvable(self) -> FieldResult; +} + +impl<'a, S, T> IntoResolvable for T +where + T: crate::GraphQLValue, + S: crate::ScalarValue, +{ + type Type = T; + + fn into_resolvable(self) -> FieldResult { + Ok(self) + } +} + +impl<'a, S, T, E: IntoFieldError> IntoResolvable for Result +where + S: crate::ScalarValue, + T: crate::GraphQLValue, +{ + type Type = T; + + fn into_resolvable(self) -> FieldResult { + self.map_err(IntoFieldError::into_field_error) + } +} diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index 9d54fee08..aab0c6df4 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -4,6 +4,7 @@ use crate::{ ast::{Directive, FromInputValue, InputValue, Selection}, executor::{ExecutionResult, Executor, Registry, Variables}, parser::Spanning, + resolve, schema::meta::{Argument, MetaType}, value::{DefaultScalarValue, Object, ScalarValue, Value}, FieldResult, GraphQLEnum, IntoFieldError, @@ -98,6 +99,8 @@ impl<'a, S> Arguments<'a, S> { Self { args } } + /// TODO: DEPRECATED! + /// /// Gets an argument by the given `name` and converts it into the desired /// type. /// @@ -121,6 +124,38 @@ impl<'a, S> Arguments<'a, S> { .transpose() .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 + where + T: resolve::InputValue<'s, S, BH>, + BH: ?Sized, + { + self.args + .as_ref() + .and_then(|args| args.get(name)) + .map(>::try_from_input_value) + .transpose() + .map_err(IntoFieldError::into_field_error)? + .map_or_else( + || { + >::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. diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 125bc5e24..5e01d322c 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -352,11 +352,36 @@ impl Definition { /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait /// implementation. - #[must_use] fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) { (&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 /// this [GraphQL object][1]. /// @@ -574,7 +599,108 @@ impl Definition { }) .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, + >::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 /// [GraphQL object][1]. /// @@ -655,8 +781,8 @@ impl ToTokens for Definition { self.impl_async_field_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// self.impl_reflect().to_tokens(into); - //self.impl_reflect_field().to_tokens(into); - //self.impl_resolve_field_static().to_tokens(into); + self.impl_reflect_field().to_tokens(into); + //self.impl_resolve_static_field().to_tokens(into); } } diff --git a/juniper_codegen/src/graphql_union/attr.rs b/juniper_codegen/src/graphql_union/attr.rs index 006ad2263..a07ac78ee 100644 --- a/juniper_codegen/src/graphql_union/attr.rs +++ b/juniper_codegen/src/graphql_union/attr.rs @@ -93,6 +93,7 @@ fn expand_on_trait( description: attr.description.map(SpanContainer::into_inner), context, scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics), + behavior: attr.behavior.into(), generics: ast.generics.clone(), variants, }; @@ -210,6 +211,7 @@ fn parse_variant_from_trait_method( ty, resolver_code, resolver_check, + behavior: attr.behavior.into(), context: method_context_ty, }) } diff --git a/juniper_codegen/src/graphql_union/derive.rs b/juniper_codegen/src/graphql_union/derive.rs index a10be60cc..0a2818c2b 100644 --- a/juniper_codegen/src/graphql_union/derive.rs +++ b/juniper_codegen/src/graphql_union/derive.rs @@ -84,6 +84,7 @@ fn expand_enum(ast: syn::DeriveInput) -> syn::Result { .map(SpanContainer::into_inner) .unwrap_or_else(|| parse_quote! { () }), scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics), + behavior: attr.behavior.into(), generics: ast.generics, variants, }) @@ -163,6 +164,7 @@ fn parse_variant_from_enum_variant( ty, resolver_code, resolver_check, + behavior: attr.behavior.into(), context: None, }) } @@ -214,6 +216,7 @@ fn expand_struct(ast: syn::DeriveInput) -> syn::Result { .map(SpanContainer::into_inner) .unwrap_or_else(|| parse_quote! { () }), scalar: scalar::Type::parse(attr.scalar.as_deref(), &ast.generics), + behavior: attr.behavior.into(), generics: ast.generics, variants, }) diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index df162fdd0..ff21e3c6d 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -18,7 +18,7 @@ use syn::{ }; use crate::common::{ - filter_attrs, gen, + filter_attrs, gen, behavior, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -74,6 +74,17 @@ struct Attr { /// [1]: https://spec.graphql.org/October2021#sec-Unions scalar: Option>, + /// 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>, + /// Explicitly specified external resolver functions for [GraphQL union][1] /// variants. /// @@ -128,6 +139,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::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "on" => { let ty = input.parse::()?; input.parse::()?; @@ -160,6 +178,7 @@ impl Attr { description: try_merge_opt!(description: self, another), context: try_merge_opt!(context: self, another), scalar: try_merge_opt!(scalar: self, another), + behavior: try_merge_opt!(behavior: self, another), external_resolvers: try_merge_hashmap!( external_resolvers: self, another => span_joined ), @@ -188,6 +207,19 @@ impl Attr { /// [1]: https://spec.graphql.org/October2021#sec-Unions #[derive(Debug, Default)] 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>, + /// Explicitly specified marker for the variant/field being ignored and not /// included into [GraphQL union][1]. /// @@ -210,6 +242,13 @@ impl Parse for VariantAttr { while !input.is_empty() { let ident = input.parse::()?; match ident.to_string().as_str() { + "behave" | "behavior" => { + input.parse::()?; + let bh = input.parse::()?; + out.behavior + .replace(SpanContainer::new(ident.span(), Some(bh.span()), bh)) + .none_or_else(|_| err::dup_arg(&ident))? + } "ignore" | "skip" => out .ignore .replace(SpanContainer::new(ident.span(), None, ident.clone())) @@ -236,6 +275,7 @@ impl VariantAttr { /// duplicates, if any. fn try_merge(self, mut another: Self) -> syn::Result { Ok(Self { + behavior: try_merge_opt!(behavior: self, another), ignore: try_merge_opt!(ignore: 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 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]. /// /// [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_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`], /// [`reflect::BaseSubTypes`] and [`reflect::WrappedType`] traits for this /// [GraphQL union][0]. @@ -663,6 +710,15 @@ impl Definition { 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! { #[automatically_derived] impl #impl_gens ::juniper::reflect::BaseType<#bh> @@ -675,8 +731,10 @@ impl Definition { impl #impl_gens ::juniper::reflect::BaseSubTypes<#bh> for #ty #where_clause { - const NAMES: ::juniper::reflect::Types = - &[>::NAME]; + const NAMES: ::juniper::reflect::Types = &[ + >::NAME, + #( #member_names ),* + ]; } #[automatically_derived] @@ -687,7 +745,19 @@ impl Definition { ::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. @@ -710,6 +780,14 @@ struct VariantDefinition { /// [1]: https://spec.graphql.org/October2021#sec-Unions 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 /// for resolution. /// @@ -826,6 +904,7 @@ fn emerge_union_variants_from_attr( ty, resolver_code, resolver_check, + behavior: behavior::Type::default(), // TODO: remove at all context: None, }) } From 129ff559d19bb05be903f002b7f884d0655727b3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Fri, 9 Sep 2022 14:39:32 +0300 Subject: [PATCH 55/58] Impl fields resolving, vol.3 [skip ci] --- juniper_codegen/src/common/gen.rs | 76 +++++- juniper_codegen/src/graphql_enum/mod.rs | 117 ++------- .../src/graphql_input_object/mod.rs | 62 +---- juniper_codegen/src/graphql_interface/mod.rs | 32 +++ juniper_codegen/src/graphql_object/mod.rs | 243 ++++++++++-------- juniper_codegen/src/graphql_scalar/mod.rs | 100 ++----- juniper_codegen/src/graphql_union/mod.rs | 32 +++ 7 files changed, 325 insertions(+), 337 deletions(-) diff --git a/juniper_codegen/src/common/gen.rs b/juniper_codegen/src/common/gen.rs index a0b5e7315..57bf3f993 100644 --- a/juniper_codegen/src/common/gen.rs +++ b/juniper_codegen/src/common/gen.rs @@ -1,7 +1,81 @@ //! Common code generated parts, used by this crate. use proc_macro2::TokenStream; -use quote::quote; +use quote::{quote, ToTokens}; +use syn::parse_quote; + +use crate::common::{behavior}; + +/// Returns generated code implementing [`resolve::Resolvable`] trait for the +/// provided [`syn::Type`] with its [`syn::Generics`]. +/// +/// [`resolve::Resolvable`]: juniper::resolve::Resolvable +/// [0]: https://spec.graphql.org/October2021#sec-Interfaces +pub(crate) fn impl_resolvable( + bh: &behavior::Type, + (ty, generics): (syn::Type, syn::Generics), +) -> TokenStream { + let (sv, generics) = mix_scalar_value(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::Resolvable<#sv, #bh> + for #ty #where_clause + { + type Value = Self; + + fn into_value(self) -> ::juniper::FieldResult { + ::juniper::FieldResult::Ok(self) + } + } + } +} + +/// Mixes a type info [`syn::GenericParam`] into the provided [`syn::Generics`] +/// and returns its [`syn::Ident`]. +#[must_use] +pub(crate) fn mix_type_info(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`]. +pub(crate) fn mix_context(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 +pub(crate) fn mix_scalar_value(mut generics: syn::Generics) -> (syn::Ident, syn::Generics) { + let sv = parse_quote! { __ScalarValue }; + generics.params.push(parse_quote! { #sv }); + (sv, generics) +} + +/// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the provided +/// [`syn::Generics`] and returns it. +/// +/// [`InputValue`]: juniper::resolve::InputValue +#[must_use] +pub(crate) fn mix_input_lifetime( + mut generics: syn::Generics, + sv: impl ToTokens, +) -> (syn::GenericParam, syn::Generics) { + let lt: syn::GenericParam = parse_quote! { '__inp }; + generics.params.push(lt.clone()); + generics + .make_where_clause() + .predicates + .push(parse_quote! { #sv: #lt }); + (lt, generics) +} /// Generate the code resolving some [GraphQL type][1] in a synchronous manner. /// diff --git a/juniper_codegen/src/graphql_enum/mod.rs b/juniper_codegen/src/graphql_enum/mod.rs index 2fe21f7cd..4718cd87d 100644 --- a/juniper_codegen/src/graphql_enum/mod.rs +++ b/juniper_codegen/src/graphql_enum/mod.rs @@ -15,7 +15,7 @@ use syn::{ }; use crate::common::{ - behavior, deprecation, filter_attrs, + behavior, deprecation, filter_attrs, gen, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -417,7 +417,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_resolvable().to_tokens(into); + gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into); self.impl_resolve_to_input_value().to_tokens(into); self.impl_resolve_input_value().to_tokens(into); self.impl_graphql_input_type().to_tokens(into); @@ -463,9 +463,9 @@ impl Definition { fn impl_graphql_input_type(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_type_info(generics); - let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (inf, generics) = gen::mix_type_info(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::resolve::Type<#inf, #sv, #bh> + ::juniper::resolve::ToInputValue<#sv, #bh> @@ -492,9 +492,9 @@ impl Definition { fn impl_graphql_output_type(&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, mut generics) = self.mix_scalar_value(generics); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, mut generics) = gen::mix_scalar_value(generics); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::resolve::Type<#inf, #sv, #bh> + ::juniper::resolve::Value<#inf, #cx, #sv, #bh> @@ -521,10 +521,10 @@ impl Definition { fn impl_graphql_enum(&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); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> @@ -608,8 +608,8 @@ impl Definition { 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); + let (inf, generics) = gen::mix_type_info(generics); + let (sv, mut generics) = gen::mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { #sv: Clone }); preds.push(parse_quote! { @@ -665,7 +665,7 @@ impl Definition { 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 (inf, generics) = gen::mix_type_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { @@ -746,9 +746,9 @@ impl Definition { fn impl_resolve_value(&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, mut generics) = self.mix_scalar_value(generics); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, mut generics) = gen::mix_scalar_value(generics); generics.make_where_clause().predicates.push(parse_quote! { #sv: From }); @@ -835,9 +835,9 @@ impl Definition { fn impl_resolve_value_async(&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, mut generics) = self.mix_scalar_value(generics); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, mut generics) = gen::mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { Self: ::juniper::resolve::Value<#inf, #cx, #sv, #bh> @@ -869,31 +869,6 @@ impl Definition { } } - /// Returns generated code implementing [`resolve::Resolvable`] trait for - /// this [GraphQL enum][0]. - /// - /// [`resolve::Resolvable`]: juniper::resolve::Resolvable - /// [0]: https://spec.graphql.org/October2021#sec-Enums - fn impl_resolvable(&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(); - - quote! { - #[automatically_derived] - impl #impl_gens ::juniper::resolve::Resolvable<#sv, #bh> - for #ty #where_clause - { - type Value = Self; - - fn into_value(self) -> ::juniper::FieldResult { - ::juniper::FieldResult::Ok(self) - } - } - } - } - /// Returns generated code implementing [`FromInputValue`] trait for this /// [GraphQL enum][0]. /// @@ -941,8 +916,8 @@ impl Definition { fn impl_resolve_input_value(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { #sv: ::juniper::ScalarValue }); @@ -1035,7 +1010,7 @@ impl Definition { fn impl_resolve_to_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 (sv, mut generics) = gen::mix_scalar_value(generics); generics.make_where_clause().predicates.push(parse_quote! { #sv: From }); @@ -1226,48 +1201,4 @@ impl Definition { }; (ty, generics) } - - /// 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) - } - - /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the - /// provided [`syn::Generics`] and returns it. - /// - /// [`InputValue`]: juniper::resolve::InputValue - fn mix_input_lifetime( - &self, - mut generics: syn::Generics, - sv: &syn::Ident, - ) -> (syn::GenericParam, syn::Generics) { - let lt: syn::GenericParam = parse_quote! { '__inp }; - generics.params.push(lt.clone()); - generics - .make_where_clause() - .predicates - .push(parse_quote! { #sv: #lt }); - (lt, generics) - } } diff --git a/juniper_codegen/src/graphql_input_object/mod.rs b/juniper_codegen/src/graphql_input_object/mod.rs index 0d72a0ddd..c339b4346 100644 --- a/juniper_codegen/src/graphql_input_object/mod.rs +++ b/juniper_codegen/src/graphql_input_object/mod.rs @@ -15,7 +15,7 @@ use syn::{ }; use crate::common::{ - behavior, default, filter_attrs, + behavior, default, filter_attrs, gen, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -520,9 +520,9 @@ impl Definition { fn impl_graphql_input_type(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_type_info(generics); - let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (inf, generics) = gen::mix_type_info(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::resolve::Type<#inf, #sv, #bh> + ::juniper::resolve::ToInputValue<#sv, #bh> @@ -572,9 +572,9 @@ impl Definition { fn impl_graphql_input_object(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_type_info(generics); - let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (inf, generics) = gen::mix_type_info(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> }); @@ -661,8 +661,8 @@ impl Definition { 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); + let (inf, generics) = gen::mix_type_info(generics); + let (sv, mut generics) = gen::mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { #sv: Clone }); preds.push(parse_quote! { @@ -751,7 +751,7 @@ impl Definition { 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 (inf, generics) = gen::mix_type_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { @@ -905,8 +905,8 @@ impl Definition { fn impl_resolve_input_value(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, &sv); + let (sv, generics) = gen::mix_scalar_value(generics); + let (lt, mut generics) = gen::mix_input_lifetime(generics, &sv); generics.make_where_clause().predicates.push(parse_quote! { #sv: ::juniper::ScalarValue }); @@ -1033,7 +1033,7 @@ impl Definition { fn impl_resolve_to_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 (sv, mut generics) = gen::mix_scalar_value(generics); for f in self.fields.iter().filter(|f| !f.ignored) { let field_ty = &f.ty; let field_bh = &f.behavior; @@ -1225,40 +1225,4 @@ impl Definition { }; (ty, generics) } - - /// 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 [`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) - } - - /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the - /// provided [`syn::Generics`] and returns it. - /// - /// [`InputValue`]: juniper::resolve::InputValue - fn mix_input_lifetime( - &self, - mut generics: syn::Generics, - sv: &syn::Ident, - ) -> (syn::GenericParam, syn::Generics) { - let lt: syn::GenericParam = parse_quote! { '__inp }; - generics.params.push(lt.clone()); - generics - .make_where_clause() - .predicates - .push(parse_quote! { #sv: #lt }); - (lt, generics) - } } diff --git a/juniper_codegen/src/graphql_interface/mod.rs b/juniper_codegen/src/graphql_interface/mod.rs index 6893e9db7..0c5fcf0ee 100644 --- a/juniper_codegen/src/graphql_interface/mod.rs +++ b/juniper_codegen/src/graphql_interface/mod.rs @@ -395,6 +395,8 @@ impl ToTokens for Definition { self.impl_field_tokens().to_tokens(into); self.impl_async_field_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// + self.impl_resolve_value().to_tokens(into); + gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -850,6 +852,36 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL interface][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Interfaces + fn impl_resolve_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value( + &self, + _: Option<&[::juniper::Selection<'_, #sv>]>, + _: &#inf, + _: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { + todo!() + } + } + } + } + /// Returns generated code implementing [`GraphQLValueAsync`] trait for this /// [GraphQL interface][1]. /// diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 0017a0e69..df3f195e4 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -352,34 +352,8 @@ impl Definition { /// Returns prepared self [`syn::Type`] and [`syn::Generics`] for a trait /// implementation. - fn ty_and_generics(&self) -> (&syn::Type, syn::Generics) { - (&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) + fn ty_and_generics(&self) -> (syn::Type, syn::Generics) { + (self.ty.clone(), self.generics.clone()) } /// Returns generated code implementing [`marker::IsOutputType`] trait for @@ -599,108 +573,149 @@ impl Definition { }) .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)? - } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL object][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Objects + pub(crate) fn impl_resolve_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value( + &self, + _: Option<&[::juniper::Selection<'_, #sv>]>, + _: &#inf, + _: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { + todo!() + } + } + } + } + + /// 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) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, generics) = gen::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::Resolvable<#sv, #f_bh> + }); + generics.make_where_clause().predicates.push(parse_quote! { + <#f_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>>::Value: + ::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_name = &arg.name; + 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>(#a_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::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 - } + } + field::MethodArgument::Executor => { + quote! { + executor } } }); - let rcv = field.has_receiver.then(|| { - quote! { self, } - }); - - quote! { Self::#ident(#rcv #( #args ),*) } - } else { - quote! { - &self.#f_ident - } - }; + let rcv = field.has_receiver.then(|| { + quote! { self, } + }); quote! { - executor.resolve_value::<#f_bh, _, _>(#res, type_info) + <#f_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>> + ::into_value(Self::#f_ident(#rcv #( #args ),*))? } } else { quote! { - ::std::panic!( - "Tried to resolve async field `{}` on type `{}` with a sync resolver", - #f_name, - >::NAME, - ); + &self.#f_ident } }; - 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 - } + 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, + >::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() - } - */ + } + }) + .collect() + } + /// Returns generated code implementing [`GraphQLType`] trait for this /// [GraphQL object][1]. /// @@ -780,9 +795,11 @@ impl ToTokens for Definition { self.impl_field_tokens().to_tokens(into); self.impl_async_field_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// + self.impl_resolve_value().to_tokens(into); + gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into); + self.impl_resolve_static_field().to_tokens(into); self.impl_reflect().to_tokens(into); self.impl_reflect_field().to_tokens(into); - //self.impl_resolve_static_field().to_tokens(into); } } diff --git a/juniper_codegen/src/graphql_scalar/mod.rs b/juniper_codegen/src/graphql_scalar/mod.rs index 80ebefaa0..eba30018c 100644 --- a/juniper_codegen/src/graphql_scalar/mod.rs +++ b/juniper_codegen/src/graphql_scalar/mod.rs @@ -2,6 +2,9 @@ //! //! [1]: https://spec.graphql.org/October2021#sec-Scalars +pub mod attr; +pub mod derive; + use proc_macro2::{Literal, TokenStream}; use quote::{format_ident, quote, ToTokens, TokenStreamExt}; use syn::{ @@ -15,7 +18,7 @@ use syn::{ use url::Url; use crate::common::{ - behavior, filter_attrs, + behavior, filter_attrs, gen, parse::{ attr::{err, OptionExt as _}, ParseBufferExt as _, @@ -23,9 +26,6 @@ use crate::common::{ scalar, Description, SpanContainer, }; -pub mod attr; -pub mod derive; - /// Available arguments behind `#[graphql]`/`#[graphql_scalar]` attributes when /// generating code for [GraphQL scalar][1]. /// @@ -351,7 +351,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_resolvable().to_tokens(into); + gen::impl_resolvable(&self.behavior, self.ty_and_generics()).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); @@ -396,9 +396,9 @@ impl Definition { fn impl_graphql_input_type(&self) -> TokenStream { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); - let (inf, generics) = self.mix_type_info(generics); + let (inf, generics) = gen::mix_type_info(generics); let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + let (lt, mut generics) = gen::mix_input_lifetime(generics, sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::resolve::Type<#inf, #sv, #bh> + ::juniper::resolve::ToInputValue<#sv, #bh> @@ -425,8 +425,8 @@ impl Definition { fn impl_graphql_output_type(&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 (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); let (sv, mut generics) = self.mix_scalar_value(generics); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::resolve::Type<#inf, #sv, #bh> @@ -454,10 +454,10 @@ impl Definition { fn impl_graphql_scalar(&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 (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + let (lt, mut generics) = gen::mix_input_lifetime(generics, sv); generics.make_where_clause().predicates.push(parse_quote! { Self: ::juniper::graphql::InputType<#lt, #inf, #sv, #bh> + ::juniper::graphql::OutputType<#inf, #cx, #sv, #bh> @@ -531,7 +531,7 @@ impl Definition { 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 (inf, generics) = gen::mix_type_info(generics); let (impl_gens, _, where_clause) = generics.split_for_impl(); quote! { @@ -554,7 +554,7 @@ impl Definition { 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 (inf, generics) = gen::mix_type_info(generics); let (sv, mut generics) = self.mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { #sv: Clone }); @@ -639,8 +639,8 @@ impl Definition { fn impl_resolve_value(&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 (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); let (sv, mut generics) = self.mix_scalar_value(generics); generics .make_where_clause() @@ -705,8 +705,8 @@ impl Definition { fn impl_resolve_value_async(&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 (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); let (sv, mut generics) = self.mix_scalar_value(generics); let preds = &mut generics.make_where_clause().predicates; preds.push(parse_quote! { @@ -739,31 +739,6 @@ impl Definition { } } - /// Returns generated code implementing [`resolve::Resolvable`] trait for - /// this [GraphQL scalar][0]. - /// - /// [`resolve::Resolvable`]: juniper::resolve::Resolvable - /// [0]: https://spec.graphql.org/October2021#sec-Scalars - fn impl_resolvable(&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(); - - quote! { - #[automatically_derived] - impl #impl_gens ::juniper::resolve::Resolvable<#sv, #bh> - for #ty #where_clause - { - type Value = Self; - - fn into_value(self) -> ::juniper::FieldResult { - ::juniper::FieldResult::Ok(self) - } - } - } - } - /// Returns generated code implementing [`InputValue`] trait for this /// [GraphQL scalar][1]. /// @@ -855,7 +830,7 @@ impl Definition { let bh = &self.behavior; let (ty, generics) = self.ty_and_generics(); let (sv, generics) = self.mix_scalar_value(generics); - let (lt, mut generics) = self.mix_input_lifetime(generics, sv); + let (lt, mut generics) = gen::mix_input_lifetime(generics, sv); generics .make_where_clause() .predicates @@ -1111,24 +1086,6 @@ impl Definition { (ty, generics) } - /// Mixes a type info [`syn::GenericParam`] into the provided - /// [`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! { __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`]. - #[must_use] - 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. /// @@ -1143,25 +1100,6 @@ impl Definition { generics.params.push(parse_quote! { #sv }); (sv, generics) } - - /// Mixes an [`InputValue`]'s lifetime [`syn::GenericParam`] into the - /// provided [`syn::Generics`] and returns it. - /// - /// [`InputValue`]: juniper::resolve::InputValue - #[must_use] - fn mix_input_lifetime( - &self, - mut generics: syn::Generics, - sv: &ScalarValue, - ) -> (syn::GenericParam, syn::Generics) { - let lt: syn::GenericParam = parse_quote! { '__inp }; - generics.params.push(lt.clone()); - generics - .make_where_clause() - .predicates - .push(parse_quote! { #sv: #lt }); - (lt, generics) - } } /// Adds `__fa__` prefix to all lifetimes to avoid "lifetime name `'a` shadows a diff --git a/juniper_codegen/src/graphql_union/mod.rs b/juniper_codegen/src/graphql_union/mod.rs index 5b86cbe25..7931fccee 100644 --- a/juniper_codegen/src/graphql_union/mod.rs +++ b/juniper_codegen/src/graphql_union/mod.rs @@ -363,6 +363,8 @@ impl ToTokens for Definition { self.impl_graphql_value_async_tokens().to_tokens(into); self.impl_reflection_traits_tokens().to_tokens(into); //////////////////////////////////////////////////////////////////////// + self.impl_resolve_value().to_tokens(into); + gen::impl_resolvable(&self.behavior, self.ty_and_generics()).to_tokens(into); self.impl_reflect().to_tokens(into); } } @@ -609,6 +611,36 @@ impl Definition { } } + /// Returns generated code implementing [`resolve::Value`] trait for this + /// [GraphQL union][0]. + /// + /// [`resolve::Value`]: juniper::resolve::Value + /// [0]: https://spec.graphql.org/October2021#sec-Unions + fn impl_resolve_value(&self) -> TokenStream { + let bh = &self.behavior; + let (ty, generics) = self.ty_and_generics(); + let (inf, generics) = gen::mix_type_info(generics); + let (cx, generics) = gen::mix_context(generics); + let (sv, generics) = gen::mix_scalar_value(generics); + let (impl_gens, _, where_clause) = generics.split_for_impl(); + + quote! { + #[automatically_derived] + impl #impl_gens ::juniper::resolve::Value<#inf, #cx, #sv, #bh> + for #ty #where_clause + { + fn resolve_value( + &self, + _: Option<&[::juniper::Selection<'_, #sv>]>, + _: &#inf, + _: &::juniper::Executor<'_, '_, #cx, #sv>, + ) -> ::juniper::ExecutionResult<#sv> { + todo!() + } + } + } + } + /// Returns generated code implementing [`GraphQLValueAsync`] trait for this /// [GraphQL union][1]. /// From bf219867b6b55f8ac07188d9ffa891a3d48ba8d5 Mon Sep 17 00:00:00 2001 From: tyranron Date: Mon, 12 Sep 2022 17:54:57 +0300 Subject: [PATCH 56/58] Impl fields resolving, vol.4 [skip ci] --- juniper_codegen/src/common/parse/mod.rs | 49 ++++++++++++++++++++--- juniper_codegen/src/graphql_object/mod.rs | 2 +- 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/juniper_codegen/src/common/parse/mod.rs b/juniper_codegen/src/common/parse/mod.rs index 53781675e..40eae7bd4 100644 --- a/juniper_codegen/src/common/parse/mod.rs +++ b/juniper_codegen/src/common/parse/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod downcaster; use std::{any::TypeId, iter, mem}; use proc_macro2::Span; -use quote::quote; +use quote::{quote, format_ident}; use syn::{ ext::IdentExt as _, parse::{Parse, ParseBuffer}, @@ -105,15 +105,29 @@ pub(crate) trait TypeExt { #[must_use] fn unreferenced(&self) -> &Self; - /// Iterates mutably over all the lifetime parameters of this [`syn::Type`] - /// with the given `func`tion. - fn lifetimes_iter_mut(&mut self, func: &mut F); + /// Iterates mutably over all the lifetime parameters (even implicit) of + /// this [`syn::Type`] with the given `func`tion. + fn lifetimes_iter_mut)>(&mut self, func: &mut F); + + /// Iterates mutably over all the named lifetime parameters (explicit only) + /// of this [`syn::Type`] with the given `func`tion. + fn named_lifetimes_iter_mut(&mut self, func: &mut F) { + self.lifetimes_iter_mut(&mut |lt| { + if let Some(lt) = lt { + func(lt); + } + }) + } /// Anonymizes all the lifetime parameters of this [`syn::Type`] (except /// the `'static` ones), making it suitable for using in contexts with /// inferring. fn lifetimes_anonymized(&mut self); + /// Lifts all the lifetimes of this [`syn::Type`] (except `'static`) to a + /// `for<>` quantifier, preserving the type speciality as much as possible. + fn to_hrtb_lifetimes(&self) -> (Option, syn::Type); + /// Returns the topmost [`syn::Ident`] of this [`syn::TypePath`], if any. #[must_use] fn topmost_ident(&self) -> Option<&syn::Ident>; @@ -135,7 +149,7 @@ impl TypeExt for syn::Type { } } - fn lifetimes_iter_mut(&mut self, func: &mut F) { + fn lifetimes_iter_mut)>(&mut self, func: &mut F) { use syn::{GenericArgument as GA, Type as T}; fn iter_path(path: &mut syn::Path, func: &mut F) { @@ -213,13 +227,36 @@ impl TypeExt for syn::Type { } fn lifetimes_anonymized(&mut self) { - self.lifetimes_iter_mut(&mut |lt| { + self.named_lifetimes_iter_mut(&mut |lt| { if lt.ident != "_" && lt.ident != "static" { lt.ident = syn::Ident::new("_", Span::call_site()); } }); } + fn to_hrtb_lifetimes(&self) -> (Option, syn::Type) { + let mut ty = self.clone(); + let mut lts = vec![]; + + let anon_ident = &syn::Ident::new("_", Span::call_site()); + + ty.lifetimes_iter_mut(&mut |lt| { + let ident = lt.map(|l| l.ident).unwrap_or(anon_ident); + if ident != "static" { + let ident = format_ident!("__fa_f_{ident}"); + if !lts.contains(&ident) { + lts.push(ident); + } + *lt = Some(parse_quote! { '#ident }) + } + }); + + let for_ = (!lts.is_empty()).then(|| parse_quote! { + for< #( #lts ),* >} + ); + (for_, ty) + } + fn topmost_ident(&self) -> Option<&syn::Ident> { match self.unparenthesized() { syn::Type::Path(p) => Some(&p.path), diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index df3f195e4..496294b35 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -323,7 +323,7 @@ impl Definition { // Modify lifetime names to omit "lifetime name `'a` shadows a // lifetime name that is already in scope" error. let mut ty = self.ty.clone(); - ty.lifetimes_iter_mut(&mut |lt| { + ty.named_lifetimes_iter_mut(&mut |lt| { let ident = lt.ident.unraw(); lt.ident = format_ident!("__fa__{ident}"); lifetimes.push(lt.clone()); From 1ddf0bd611256775ce5d702956dea3144d2e96c3 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 20 Sep 2022 15:45:26 +0300 Subject: [PATCH 57/58] Impl hrtbifying --- juniper_codegen/Cargo.toml | 2 +- juniper_codegen/src/common/gen.rs | 2 +- juniper_codegen/src/common/parse/mod.rs | 125 +++++++++++++++++++----- 3 files changed, 103 insertions(+), 26 deletions(-) diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml index b22916581..9d696a7a1 100644 --- a/juniper_codegen/Cargo.toml +++ b/juniper_codegen/Cargo.toml @@ -31,5 +31,5 @@ url = "2.0" [dev-dependencies] derive_more = "0.99.7" futures = "0.3.22" -juniper = { path = "../juniper" } +#juniper = { path = "../juniper" } serde = "1.0" diff --git a/juniper_codegen/src/common/gen.rs b/juniper_codegen/src/common/gen.rs index 57bf3f993..846ed25e1 100644 --- a/juniper_codegen/src/common/gen.rs +++ b/juniper_codegen/src/common/gen.rs @@ -4,7 +4,7 @@ use proc_macro2::TokenStream; use quote::{quote, ToTokens}; use syn::parse_quote; -use crate::common::{behavior}; +use crate::common::behavior; /// Returns generated code implementing [`resolve::Resolvable`] trait for the /// provided [`syn::Type`] with its [`syn::Generics`]. diff --git a/juniper_codegen/src/common/parse/mod.rs b/juniper_codegen/src/common/parse/mod.rs index 40eae7bd4..5789f6e38 100644 --- a/juniper_codegen/src/common/parse/mod.rs +++ b/juniper_codegen/src/common/parse/mod.rs @@ -7,7 +7,7 @@ pub(crate) mod downcaster; use std::{any::TypeId, iter, mem}; use proc_macro2::Span; -use quote::{quote, format_ident}; +use quote::quote; use syn::{ ext::IdentExt as _, parse::{Parse, ParseBuffer}, @@ -105,15 +105,15 @@ pub(crate) trait TypeExt { #[must_use] fn unreferenced(&self) -> &Self; - /// Iterates mutably over all the lifetime parameters (even implicit) of - /// this [`syn::Type`] with the given `func`tion. - fn lifetimes_iter_mut)>(&mut self, func: &mut F); + /// Iterates mutably over all the lifetime parameters (even elided) of this + /// [`syn::Type`] with the given `func`tion. + fn lifetimes_iter_mut)>(&mut self, func: &mut F); - /// Iterates mutably over all the named lifetime parameters (explicit only) - /// of this [`syn::Type`] with the given `func`tion. + /// Iterates mutably over all the named lifetime parameters (no elided) of + /// this [`syn::Type`] with the given `func`tion. fn named_lifetimes_iter_mut(&mut self, func: &mut F) { self.lifetimes_iter_mut(&mut |lt| { - if let Some(lt) = lt { + if let MaybeElidedLifetimeMut::Named(lt) = lt { func(lt); } }) @@ -124,8 +124,9 @@ pub(crate) trait TypeExt { /// inferring. fn lifetimes_anonymized(&mut self); - /// Lifts all the lifetimes of this [`syn::Type`] (except `'static`) to a - /// `for<>` quantifier, preserving the type speciality as much as possible. + /// Lifts all the lifetimes of this [`syn::Type`] (even elided, but except + /// `'static`) to a `for<>` quantifier, preserving the type speciality as + /// much as possible. fn to_hrtb_lifetimes(&self) -> (Option, syn::Type); /// Returns the topmost [`syn::Ident`] of this [`syn::TypePath`], if any. @@ -149,16 +150,16 @@ impl TypeExt for syn::Type { } } - fn lifetimes_iter_mut)>(&mut self, func: &mut F) { + fn lifetimes_iter_mut)>(&mut self, func: &mut F) { use syn::{GenericArgument as GA, Type as T}; - fn iter_path(path: &mut syn::Path, func: &mut F) { + fn iter_path)>(path: &mut syn::Path, func: &mut F) { for seg in path.segments.iter_mut() { match &mut seg.arguments { syn::PathArguments::AngleBracketed(angle) => { for arg in angle.args.iter_mut() { match arg { - GA::Lifetime(lt) => func(lt), + GA::Lifetime(lt) => func(lt.into()), GA::Type(ty) => ty.lifetimes_iter_mut(func), GA::Binding(b) => b.ty.lifetimes_iter_mut(func), GA::Constraint(_) | GA::Const(_) => {} @@ -195,7 +196,7 @@ impl TypeExt for syn::Type { | T::TraitObject(syn::TypeTraitObject { bounds, .. }) => { for bound in bounds.iter_mut() { match bound { - syn::TypeParamBound::Lifetime(lt) => func(lt), + syn::TypeParamBound::Lifetime(lt) => func(lt.into()), syn::TypeParamBound::Trait(bound) => { if bound.lifetimes.is_some() { todo!("Iterating over HRTB lifetimes in trait is not yet supported") @@ -207,9 +208,7 @@ impl TypeExt for syn::Type { } T::Reference(ref_ty) => { - if let Some(lt) = ref_ty.lifetime.as_mut() { - func(lt) - } + func((&mut ref_ty.lifetime).into()); (*ref_ty.elem).lifetimes_iter_mut(func) } @@ -240,20 +239,26 @@ impl TypeExt for syn::Type { let anon_ident = &syn::Ident::new("_", Span::call_site()); - ty.lifetimes_iter_mut(&mut |lt| { - let ident = lt.map(|l| l.ident).unwrap_or(anon_ident); + ty.lifetimes_iter_mut(&mut |mut lt_mut| { + let ident = match <_mut { + MaybeElidedLifetimeMut::Elided(v) => { + v.as_ref().map(|l| &l.ident).unwrap_or(anon_ident) + } + MaybeElidedLifetimeMut::Named(l) => &l.ident, + }; if ident != "static" { - let ident = format_ident!("__fa_f_{ident}"); - if !lts.contains(&ident) { - lts.push(ident); + let new_lt = syn::Lifetime::new(&format!("'__fa_f_{ident}"), Span::call_site()); + if !lts.contains(&new_lt) { + lts.push(new_lt.clone()); } - *lt = Some(parse_quote! { '#ident }) + lt_mut.set(new_lt); } }); - let for_ = (!lts.is_empty()).then(|| parse_quote! { + let for_ = (!lts.is_empty()).then(|| { + parse_quote! { for< #( #lts ),* >} - ); + }); (for_, ty) } @@ -276,6 +281,38 @@ impl TypeExt for syn::Type { } } +/// Mutable reference to a place that may containing a [`syn::Lifetime`]. +pub(crate) enum MaybeElidedLifetimeMut<'a> { + /// [`syn::Lifetime`] may be elided. + Elided(&'a mut Option), + + /// [`syn::Lifetime`] is always present. + Named(&'a mut syn::Lifetime), +} + +impl<'a> MaybeElidedLifetimeMut<'a> { + /// Assigns the provided [`syn::Lifetime`] to the place pointed by this + /// [`MaybeElidedLifetimeMut`] reference. + fn set(&mut self, lt: syn::Lifetime) { + match self { + Self::Elided(v) => **v = Some(lt), + Self::Named(l) => **l = lt, + } + } +} + +impl<'a> From<&'a mut Option> for MaybeElidedLifetimeMut<'a> { + fn from(lt: &'a mut Option) -> Self { + Self::Elided(lt) + } +} + +impl<'a> From<&'a mut syn::Lifetime> for MaybeElidedLifetimeMut<'a> { + fn from(lt: &'a mut syn::Lifetime) -> Self { + Self::Named(lt) + } +} + /// Extension of [`syn::Generics`] providing common function widely used by this crate for parsing. pub(crate) trait GenericsExt { /// Removes all default types out of type parameters and const parameters in these @@ -391,3 +428,43 @@ impl<'a> VisitMut for ReplaceWithDefaults<'a> { } } } + +#[cfg(test)] +mod test_type_ext_to_hrtb_lifetimes { + use quote::quote; + use syn::parse_quote; + + use super::TypeExt as _; + + #[test] + fn test() { + for (input, expected) in [ + (parse_quote! { &'static T }, quote! { &'static T }), + (parse_quote! { &T }, quote! { for<'__fa_f__>: &'__fa_f__ T }), + ( + parse_quote! { &'a mut T }, + quote! { for<'__fa_f_a>: &'__fa_f_a mut T }, + ), + ( + parse_quote! { &str }, + quote! { for<'__fa_f__>: &'__fa_f__ str }, + ), + ( + parse_quote! { &Cow<'static, str> }, + quote! { for<'__fa_f__>: &'__fa_f__ Cow<'static, str> }, + ), + ( + parse_quote! { &Cow<'a, str> }, + quote! { for<'__fa_f__, '__fa_f_a>: &'__fa_f__ Cow<'__fa_f_a, str> }, + ), + ] { + let (actual_for, actual_ty) = syn::Type::to_hrtb_lifetimes(&input); + let actual_for = actual_for.map(|for_| quote! { #for_: }); + + assert_eq!( + quote! { #actual_for #actual_ty }.to_string(), + expected.to_string(), + ); + } + } +} From 2d01486ee9e1e03de2bd93b953cf9d5a35099133 Mon Sep 17 00:00:00 2001 From: tyranron Date: Tue, 20 Sep 2022 18:06:07 +0300 Subject: [PATCH 58/58] Yay! Static fields, finally, minimally resolving [skip ci] --- juniper/src/schema/schema.rs | 20 ++++++++++---------- juniper_codegen/src/common/parse/mod.rs | 15 ++++++++++++++- juniper_codegen/src/graphql_object/mod.rs | 18 ++++++++++-------- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 618227462..311bcab26 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -141,7 +141,7 @@ impl<'a, S: ScalarValue + 'a> SchemaType<'a, S> { self.description.as_deref() } - fn types(&self) -> Vec> { + fn types(&self) -> Vec> { self.type_list() .into_iter() .filter(|t| { @@ -156,21 +156,21 @@ impl<'a, S: ScalarValue + 'a> SchemaType<'a, S> { } #[graphql(name = "queryType")] - fn query_type_(&self) -> TypeType { + fn query_type_(&self) -> TypeType<'_, S> { self.query_type() } #[graphql(name = "mutationType")] - fn mutation_type_(&self) -> Option> { + fn mutation_type_(&self) -> Option> { self.mutation_type() } #[graphql(name = "subscriptionType")] - fn subscription_type_(&self) -> Option> { + fn subscription_type_(&self) -> Option> { self.subscription_type() } - fn directives(&self) -> Vec<&DirectiveType> { + fn directives(&self) -> Vec<&DirectiveType<'_, S>> { self.directive_list() } } @@ -214,7 +214,7 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> { fn fields( &self, #[graphql(default = false)] include_deprecated: Option, - ) -> Option>> { + ) -> Option>> { match self { TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) | TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some( @@ -231,14 +231,14 @@ impl<'a, S: ScalarValue + 'a> TypeType<'a, S> { } } - fn of_type(&self) -> Option<&TypeType> { + fn of_type(&self) -> Option<&TypeType<'_, S>> { match self { TypeType::Concrete(_) => None, TypeType::List(l, _) | TypeType::NonNull(l) => Some(&**l), } } - fn input_fields(&self) -> Option<&[Argument]> { + fn input_fields(&self) -> Option<&[Argument<'_, S>]> { match self { TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, @@ -343,7 +343,7 @@ impl<'a, S: ScalarValue + 'a> Field<'a, S> { self.description.as_deref() } - fn args(&self) -> Vec<&Argument> { + fn args(&self) -> Vec<&Argument<'_, S>> { self.arguments .as_ref() .map_or_else(Vec::new, |v| v.iter().collect()) @@ -434,7 +434,7 @@ impl<'a, S: ScalarValue + 'a> DirectiveType<'a, S> { self.is_repeatable } - fn args(&self) -> &[Argument] { + fn args(&self) -> &[Argument<'_, S>] { &self.arguments } diff --git a/juniper_codegen/src/common/parse/mod.rs b/juniper_codegen/src/common/parse/mod.rs index 5789f6e38..ba4e87181 100644 --- a/juniper_codegen/src/common/parse/mod.rs +++ b/juniper_codegen/src/common/parse/mod.rs @@ -124,9 +124,16 @@ pub(crate) trait TypeExt { /// inferring. fn lifetimes_anonymized(&mut self); + /// Anonymizes all the lifetime parameters of this [`syn::Type`] (except + /// the `'static` ones), making it suitable for using in contexts with + /// inferring, and returns it as a new type, leaving the original one + /// unchanged. + fn to_anonymized_lifetimes(&self) -> syn::Type; + /// Lifts all the lifetimes of this [`syn::Type`] (even elided, but except /// `'static`) to a `for<>` quantifier, preserving the type speciality as - /// much as possible. + /// much as possible, and returns it as a new type, leaving the original one + /// unchanged. fn to_hrtb_lifetimes(&self) -> (Option, syn::Type); /// Returns the topmost [`syn::Ident`] of this [`syn::TypePath`], if any. @@ -233,6 +240,12 @@ impl TypeExt for syn::Type { }); } + fn to_anonymized_lifetimes(&self) -> syn::Type { + let mut ty = self.clone(); + ty.lifetimes_anonymized(); + ty + } + fn to_hrtb_lifetimes(&self) -> (Option, syn::Type) { let mut ty = self.clone(); let mut lts = vec![]; diff --git a/juniper_codegen/src/graphql_object/mod.rs b/juniper_codegen/src/graphql_object/mod.rs index 496294b35..a0cef4cb2 100644 --- a/juniper_codegen/src/graphql_object/mod.rs +++ b/juniper_codegen/src/graphql_object/mod.rs @@ -21,7 +21,7 @@ use crate::common::{ behavior, field, filter_attrs, gen, parse::{ attr::{err, OptionExt as _}, - GenericsExt as _, ParseBufferExt as _, TypeExt, + GenericsExt as _, ParseBufferExt as _, TypeExt as _, }, rename, scalar, Description, SpanContainer, }; @@ -626,15 +626,18 @@ impl Definition { let (f_ident, f_ty) = (&field.ident, &field.ty); let body = if !field.is_async { + let (f_for_ty, f_hrtb_ty) = f_ty.to_hrtb_lifetimes(); generics.make_where_clause().predicates.push(parse_quote! { - #f_ty: ::juniper::resolve::Resolvable<#sv, #f_bh> + #f_for_ty #f_hrtb_ty: + ::juniper::resolve::Resolvable<#sv, #f_bh> }); generics.make_where_clause().predicates.push(parse_quote! { - <#f_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>>::Value: + #f_for_ty <#f_hrtb_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>>::Value: ::juniper::resolve::Value<#inf, #cx, #sv, #f_bh> }); - let res = if field.is_method() { + let val = if field.is_method() { + let f_anon_ty = f_ty.to_anonymized_lifetimes(); let args = field .arguments .as_ref() @@ -666,23 +669,22 @@ impl Definition { } } }); - let rcv = field.has_receiver.then(|| { quote! { self, } }); quote! { - <#f_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>> + <#f_anon_ty as ::juniper::resolve::Resolvable<#sv, #f_bh>> ::into_value(Self::#f_ident(#rcv #( #args ),*))? } } else { quote! { - &self.#f_ident + self.#f_ident } }; quote! { - executor.resolve_value::<#f_bh, _, _>(#res, type_info) + executor.resolve_value::<#f_bh, _, _>(&#val, type_info) } } else { quote! {