diff --git a/juniper/src/behavior.rs b/juniper/src/behavior.rs index 3c6c84a4..d7a53f27 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 3c6fd907..295af173 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 f9f50af6..17ce8c74 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 a5ff4436..6e0a39e4 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; - - 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 resolve::ScalarToken for str +impl<'i, SV> resolve::InputValueAs<'i, Box, SV> for str where - String: resolve::ScalarToken, + SV: 'i, + Self: resolve::InputValueAsRef, { - fn parse_scalar_token(token: ScalarToken<'_>) -> Result> { - >::parse_scalar_token(token) + type Error = >::Error; + + fn try_from_input_value(v: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) } } +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: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) + } +} + +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: &'i graphql::InputValue) -> Result, Self::Error> { + >::try_from_input_value(v).map(Into::into) + } +} + +impl resolve::ScalarToken for str +//TODO: where String: resolve::ScalarToken, +where + String: crate::ParseScalarValue, +{ + 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 = "String"; // TODO: >::NAME; } -impl reflect::BaseType for str { - const NAME: reflect::Type = >::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; } -*/