From 64cf7adb4f5370f8edc0d59ec8f6f55565ebdeac Mon Sep 17 00:00:00 2001 From: tyranron Date: Thu, 5 May 2022 18:35:42 +0300 Subject: [PATCH] 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 23073222..d749fc4a 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 4d87480d..fb1ac95f 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 e027d0cf..dd23fd4d 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 2ba1faa4..f80afce3 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 467951c8..309022d5 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 c8140f3e..91f2434b 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 5fceeb2b..5823a8a5 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 3cc1f735..430790c9 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 36b27e4d..42204d89 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 eee1ae3b..a9418a0f 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 00000000..1b62c3c2 --- /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 {}