From 25404eb4a717d1e09d1e4f38a74551cda5db0011 Mon Sep 17 00:00:00 2001
From: tyranron <tyranron@gmail.com>
Date: Fri, 17 Jun 2022 16:04:48 +0200
Subject: [PATCH] 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 d7a53f27..c68cae6c 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<T: ?Sized, From: ?Sized = Standard>(PhantomData<AtomicPtr<Box<From>>>, T);
 
 impl<T, From: ?Sized> Coerce<T, From> {
     #[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<T, From: ?Sized>(val: T) -> Coerce<T, From> {
-    Coerce::wrap(val)
+pub const fn coerce<T, From: ?Sized>(value: T) -> Coerce<T, From> {
+    Coerce::wrap(value)
+}
+
+impl<T, TI, SV, B1, B2> resolve::Type<TI, SV, B1> for Coerce<T, B2>
+where
+    T: resolve::Type<TI, SV, B2> + ?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 295af173..7c0bd8d2 100644
--- a/juniper/src/executor/mod.rs
+++ b/juniper/src/executor/mod.rs
@@ -84,12 +84,6 @@ where
     field_path: Arc<FieldPath<'a>>,
 }
 
-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<T, TI>(&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<TI> + resolve::InputValueAsRef<S> + resolve::ScalarToken<S> + ?Sized,
         TI: ?Sized,
+        'ti: 'r,
+        S: Clone,
     {
-        // TODO: Allow using references.
-        ScalarMeta::new_unsized::<T, _>(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::<T, TI, _>(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<TI> + resolve::InputValueAsRef<S> + resolve::ScalarToken<S> + ?Sized,
+        TI: ?Sized,
+        'ti: 'r,
+        F: FnOnce(&mut ScalarMeta<'r, S>),
+        S: Clone,
+    {
+        self.entry_type::<T, _>(type_info)
+            .or_insert_with(move || {
+                let mut scalar = ScalarMeta::new_unsized::<T, _>(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<Info, S> + ?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<T, BH, TI>(&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<TI, S, BH> + ?Sized,
-        BH: ?Sized,
+        T: resolve::Type<TI, S> + ?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 868ce500..353095fd 100644
--- a/juniper/src/resolve/mod.rs
+++ b/juniper/src/resolve/mod.rs
@@ -6,9 +6,9 @@ use crate::{
 };
 
 pub trait Type<TypeInfo: ?Sized, ScalarValue, Behavior: ?Sized = behavior::Standard> {
-    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 17ce8c74..73d0620d 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<MetaType<'a, S>> 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<T>(name: Cow<'a, str>) -> Self
diff --git a/juniper/src/types/arc.rs b/juniper/src/types/arc.rs
index ef733799..db4c6cba 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 ec019461..845343d2 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 7728959c..ee4a861a 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::<T, BH, _>(type_info)
-            .into_meta()
+        registry.wrap_nullable::<behavior::Coerce<T, BH>, _>(type_info)
     }
 }
 
diff --git a/juniper/src/types/option.rs b/juniper/src/types/option.rs
index 0a83cc62..cdc91941 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::<T, BH, _>(type_info)
-            .into_meta()
+        registry.wrap_nullable::<behavior::Coerce<T, BH>, _>(type_info)
     }
 }
 
diff --git a/juniper/src/types/rc.rs b/juniper/src/types/rc.rs
index 950ec042..f886e910 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 9b9668fe..867f4f48 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 f24b96a5..c0c479c6 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 0e3f862b..aeeac1ed 100644
--- a/juniper/src/types/str.rs
+++ b/juniper/src/types/str.rs
@@ -14,17 +14,11 @@ use crate::{
 };
 
 impl<TI: ?Sized, SV: ScalarValue> resolve::Type<TI, SV> 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::<Self, _>(type_info)
-            .into_meta();
-        registry
-            .entry_type::<Self, _>(type_info)
-            .or_insert(meta)
-            .clone()
+        registry.register_scalar_unsized::<Self, _>(type_info)
     }
 }
 
@@ -75,6 +69,8 @@ where
     SV: From<String>,
 {
     fn to_input_value(&self) -> graphql::InputValue<SV> {
+        // TODO: Remove redundant `.to_owned()` allocation by allowing
+        //       `ScalarValue` creation from reference?
         graphql::InputValue::scalar(self.to_owned())
     }
 }