diff --git a/benches/bench.rs b/benches/bench.rs new file mode 100644 index 00000000..5d707527 --- /dev/null +++ b/benches/bench.rs @@ -0,0 +1,127 @@ +#[macro_use] extern crate bencher; +extern crate juniper; + +use bencher::Bencher; + +use juniper::{execute, RootNode, EmptyMutation, Variables}; +use juniper::tests::model::Database; + +fn query_type_name(b: &mut Bencher) { + let database = Database::new(); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); + + let doc = r#" + query IntrospectionQueryTypeQuery { + __schema { + queryType { + name + } + } + }"#; + + b.iter(|| execute(doc, None, &schema, &Variables::new(), &database)); +} + +fn introspection_query(b: &mut Bencher) { + let database = Database::new(); + let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); + + let doc = r#" + query IntrospectionQuery { + __schema { + queryType { name } + mutationType { name } + subscriptionType { name } + types { + ...FullType + } + directives { + name + description + locations + args { + ...InputValue + } + } + } + } + + fragment FullType on __Type { + kind + name + description + fields(includeDeprecated: true) { + name + description + args { + ...InputValue + } + type { + ...TypeRef + } + isDeprecated + deprecationReason + } + inputFields { + ...InputValue + } + interfaces { + ...TypeRef + } + enumValues(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + possibleTypes { + ...TypeRef + } + } + + fragment InputValue on __InputValue { + name + description + type { ...TypeRef } + defaultValue + } + + fragment TypeRef on __Type { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + ofType { + kind + name + } + } + } + } + } + } + } + } +"#; + + b.iter(|| execute(doc, None, &schema, &Variables::new(), &database)); +} + +benchmark_group!(queries, query_type_name, introspection_query); +benchmark_main!(queries); diff --git a/examples/warp_async/src/main.rs b/examples/warp_async/src/main.rs index a7142d4e..9bbe3884 100644 --- a/examples/warp_async/src/main.rs +++ b/examples/warp_async/src/main.rs @@ -43,7 +43,7 @@ impl User { } } -struct Query; +struct Query; #[juniper::object(Context = Context)] impl Query { diff --git a/juniper/CHANGELOG.md b/juniper/CHANGELOG.md index 3af51898..dc6ad7ed 100644 --- a/juniper/CHANGELOG.md +++ b/juniper/CHANGELOG.md @@ -2,6 +2,11 @@ - No changes yet +# [[0.14.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.1) + +- Fix panic when an invalid scalar is used by a client [#434](https://github.com/graphql-rust/juniper/pull/434) +- `EmptyMutation` now implements `Send` [#443](https://github.com/graphql-rust/juniper/pull/443) + # [[0.14.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper-0.14.0) - Require `url` 2.x if `url` feature is enabled. diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index f981af04..a0eb926a 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper" -version = "0.14.0" +version = "0.14.1" authors = [ "Magnus Hallin <mhallin@fastmail.com>", "Christoph Herzog <chris@theduke.at>", @@ -33,20 +33,19 @@ default = [ ] [dependencies] -juniper_codegen = { version = "0.14.0", path = "../juniper_codegen" } +juniper_codegen = { version = "0.14.1", path = "../juniper_codegen" } +async-trait = "0.1.16" +chrono = { version = "0.4.0", optional = true } fnv = "1.0.3" +futures-preview = { version = "=0.3.0-alpha.19", optional = true } indexmap = { version = "1.0.0", features = ["serde-1"] } serde = { version = "1.0.8" } serde_derive = { version = "1.0.2" } - -chrono = { version = "0.4.0", optional = true } serde_json = { version="1.0.2", optional = true } url = { version = "2", optional = true } uuid = { version = "0.7", optional = true } -futures-preview = { version = "=0.3.0-alpha.19", optional = true } - [dev-dependencies] bencher = "0.1.2" serde_json = { version = "1.0.2" } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 2fdcd4fd..c080f9d4 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -210,20 +210,6 @@ impl<S> FieldError<S> { /// The result of resolving the value of a field of type `T` pub type FieldResult<T, S = DefaultScalarValue> = Result<T, FieldError<S>>; -/* -pub enum ResolvedValue<'a, S = DefaultScalarValue> { - Value(Value<S>), - Future(crate::BoxFuture<'a, Value<S>>), -} - -impl<'a, S> From<Value<S>> for ResolvedValue<'a, S> { - #[inline] - fn from(value: Value<S>) -> Self { - ResolvedValue::Value(value) - } -} -*/ - /// The result of resolving an unspecified field pub type ExecutionResult<S = DefaultScalarValue> = Result<Value<S>, FieldError<S>>; diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 18bba937..dd1cfffa 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -88,7 +88,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected. [chrono]: https://crates.io/crates/chrono */ -#![doc(html_root_url = "https://docs.rs/juniper/0.14.0")] +#![doc(html_root_url = "https://docs.rs/juniper/0.14.1")] #![warn(missing_docs)] #[doc(hidden)] diff --git a/juniper/src/macros/common.rs b/juniper/src/macros/common.rs index 9a6ddef3..88320cd6 100644 --- a/juniper/src/macros/common.rs +++ b/juniper/src/macros/common.rs @@ -96,25 +96,6 @@ macro_rules! __juniper_insert_generic { }; } -// TODO: remove me. -#[doc(hidden)] -#[macro_export] -macro_rules! __juniper_extract_generic { - (<$name:ident>) => { - $name - }; - ( - <$generic:tt $(: $bound: tt)*> - ) => { - $generic - }; - ( - $scalar: ty - ) => { - $scalar - }; -} - #[doc(hidden)] #[macro_export] macro_rules! __juniper_parse_object_header { diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index 4f3ae84e..32c8b988 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -423,12 +423,16 @@ macro_rules! graphql_scalar { ) { - fn resolve_async<'a>( + fn resolve_async<'a, 'async_trait>( &'a self, info: &'a Self::TypeInfo, - selection_set: Option<&'a [$crate::Selection<$crate::__juniper_insert_generic!($($scalar)+)>]>, - executor: &'a $crate::Executor<Self::Context, $crate::__juniper_insert_generic!($($scalar)+)>, - ) -> futures::future::BoxFuture<'a, $crate::Value<$crate::__juniper_insert_generic!($($scalar)+)>> { + selection_set: Option<&'a [$crate::Selection<'a, $crate::__juniper_insert_generic!($($scalar)+)>]>, + executor: &'a $crate::Executor<'a, Self::Context, $crate::__juniper_insert_generic!($($scalar)+)>, + ) -> futures::future::BoxFuture<'async_trait, $crate::Value<$crate::__juniper_insert_generic!($($scalar)+)>> + where + 'a: 'async_trait, + Self: 'async_trait, + { use $crate::GraphQLType; use futures::future; let v = self.resolve(info, selection_set, executor); diff --git a/juniper/src/macros/tests/args.rs b/juniper/src/macros/tests/args.rs index 99915094..95b8dacb 100644 --- a/juniper/src/macros/tests/args.rs +++ b/juniper/src/macros/tests/args.rs @@ -73,13 +73,17 @@ impl Root { 0 } - // TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented - // fn attr_arg_descr(#[doc = "The arg"] arg: i32) -> i32 { 0 } - // fn attr_arg_descr_collapse( - // #[doc = "The arg"] - // #[doc = "and more details"] - // arg: i32, - // ) -> i32 { 0 } +// TODO: enable once [parameter attributes are supported by proc macros] +// (https://github.com/graphql-rust/juniper/pull/441) +// fn attr_arg_descr( +// #[graphql(description = "The arg")] +// arg: i32) -> i32 +// { 0 } +// fn attr_arg_descr_collapse( +// #[graphql(description = "The first arg")] +// #[graphql(description = "and more details")] +// arg: i32, +// ) -> i32 { 0 } #[graphql(arguments(arg(default = 123,),))] fn arg_with_default(arg: i32) -> i32 { @@ -559,7 +563,8 @@ fn introspect_field_multi_args_descr_trailing_comma() { }); } -// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented +// TODO: enable once [parameter attributes are supported by proc macros] +// (https://github.com/graphql-rust/juniper/pull/441) // #[test] // fn introspect_field_attr_arg_descr() { // run_args_info_query("attrArgDescr", |args| { @@ -593,7 +598,8 @@ fn introspect_field_multi_args_descr_trailing_comma() { // }); // } -// TODO: enable once [RFC 2565](https://github.com/rust-lang/rust/issues/60406) is implemented +// TODO: enable once [parameter attributes are supported by proc macros] +// (https://github.com/graphql-rust/juniper/pull/441) // #[test] // fn introspect_field_attr_arg_descr_collapse() { // run_args_info_query("attrArgDescrCollapse", |args| { diff --git a/juniper/src/parser/parser.rs b/juniper/src/parser/parser.rs index e010180d..095fc1e7 100644 --- a/juniper/src/parser/parser.rs +++ b/juniper/src/parser/parser.rs @@ -13,6 +13,9 @@ pub enum ParseError<'a> { /// An error during tokenization occurred LexerError(LexerError), + + /// A scalar of unexpected type occurred in the source + ExpectedScalarError(&'static str), } #[doc(hidden)] @@ -196,6 +199,7 @@ impl<'a> fmt::Display for ParseError<'a> { ParseError::UnexpectedToken(ref token) => write!(f, "Unexpected \"{}\"", token), ParseError::UnexpectedEndOfFile => write!(f, "Unexpected end of input"), ParseError::LexerError(ref err) => err.fmt(f), + ParseError::ExpectedScalarError(err) => err.fmt(f), } } } diff --git a/juniper/src/parser/tests/document.rs b/juniper/src/parser/tests/document.rs index 97b6c31d..cfe18d64 100644 --- a/juniper/src/parser/tests/document.rs +++ b/juniper/src/parser/tests/document.rs @@ -4,6 +4,7 @@ use crate::{ }, parser::{document::parse_document_source, ParseError, SourcePosition, Spanning, Token}, schema::model::SchemaType, + types::scalars::EmptyMutation, validation::test_harness::{MutationRoot, QueryRoot}, value::{DefaultScalarValue, ScalarRefValue, ScalarValue}, }; @@ -145,3 +146,23 @@ fn errors() { ) ); } + +#[test] +fn issue_427_panic_is_not_expected() { + struct QueryWithoutFloat; + + #[crate::object_internal] + impl QueryWithoutFloat { + fn echo(value: String) -> String { + value + } + } + + let schema = SchemaType::new::<QueryWithoutFloat, EmptyMutation<()>>(&(), &()); + let parse_result = parse_document_source(r##"{ echo(value: 123.0) }"##, &schema); + + assert_eq!( + parse_result.unwrap_err().item, + ParseError::ExpectedScalarError("There needs to be a Float type") + ); +} diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs index 74dde9f9..260f9ec0 100644 --- a/juniper/src/parser/value.rs +++ b/juniper/src/parser/value.rs @@ -210,33 +210,36 @@ fn parse_scalar_literal_by_infered_type<'a, 'b, S>( where S: ScalarValue, { - match token { + let result = match token { ScalarToken::String(_) => { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("String") { - (s.parse_fn)(token) - .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s))) - .map_err(|e| Spanning::start_end(start, end, e)) + (s.parse_fn)(token).map(InputValue::Scalar) } else { - panic!("There needs to be a String type") + Err(ParseError::ExpectedScalarError( + "There needs to be a String type", + )) } } ScalarToken::Int(_) => { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Int") { - (s.parse_fn)(token) - .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s))) - .map_err(|e| Spanning::start_end(start, end, e)) + (s.parse_fn)(token).map(InputValue::Scalar) } else { - panic!("There needs to be a Int type") + Err(ParseError::ExpectedScalarError( + "There needs to be an Int type", + )) } } ScalarToken::Float(_) => { if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Float") { - (s.parse_fn)(token) - .map(|s| Spanning::start_end(start, end, InputValue::Scalar(s))) - .map_err(|e| Spanning::start_end(start, end, e)) + (s.parse_fn)(token).map(InputValue::Scalar) } else { - panic!("There needs to be a Float type") + Err(ParseError::ExpectedScalarError( + "There needs to be a Float type", + )) } } - } + }; + result + .map(|s| Spanning::start_end(start, end, s)) + .map_err(|e| Spanning::start_end(start, end, e)) } diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 14c3f2c2..5192dc20 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -77,6 +77,7 @@ where } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<'a, CtxT, S, QueryT, MutationT> crate::GraphQLTypeAsync<S> for RootNode<'a, QueryT, MutationT, S> where @@ -85,16 +86,16 @@ where QueryT::TypeInfo: Send + Sync, MutationT: crate::GraphQLTypeAsync<S, Context = CtxT>, MutationT::TypeInfo: Send + Sync, - CtxT: Send + Sync, - for<'b> &'b S: ScalarRefValue<'b>, + CtxT: Send + Sync + 'a, + for<'c> &'c S: ScalarRefValue<'c>, { - fn resolve_field_async<'b>( + async fn resolve_field_async<'b>( &'b self, - info: &'b Self::TypeInfo, + info: &'b <Self as crate::GraphQLType<S>>::TypeInfo, field_name: &'b str, - arguments: &'b Arguments<S>, - executor: &'b Executor<Self::Context, S>, - ) -> crate::BoxFuture<'b, ExecutionResult<S>> { + arguments: &'b Arguments<'b, S>, + executor: &'b Executor<'b, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> ExecutionResult<S> { use futures::future::{ready, FutureExt}; match field_name { "__schema" | "__type" => { diff --git a/juniper/src/types/async_await.rs b/juniper/src/types/async_await.rs index ab7b95d4..bf06e190 100644 --- a/juniper/src/types/async_await.rs +++ b/juniper/src/types/async_await.rs @@ -12,6 +12,7 @@ use crate::BoxFuture; use super::base::{is_excluded, merge_key_into, Arguments, GraphQLType}; +#[async_trait::async_trait] pub trait GraphQLTypeAsync<S>: GraphQLType<S> + Send + Sync where Self::Context: Send + Sync, @@ -19,28 +20,42 @@ where S: ScalarValue + Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { - fn resolve_field_async<'a>( + async fn resolve_field_async<'a>( &'a self, info: &'a Self::TypeInfo, field_name: &'a str, - arguments: &'a Arguments<S>, - executor: &'a Executor<Self::Context, S>, - ) -> BoxFuture<'a, ExecutionResult<S>> { + arguments: &'a Arguments<'a, S>, + executor: &'a Executor<'a, Self::Context, S>, + ) -> ExecutionResult<S> { panic!("resolve_field must be implemented by object types"); } - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> BoxFuture<'a, Value<S>> { + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, Self::Context, S>, + ) -> Value<S> { if let Some(selection_set) = selection_set { - resolve_selection_set_into_async(self, info, selection_set, executor) + resolve_selection_set_into_async(self, info, selection_set, executor).await } else { panic!("resolve() must be implemented by non-object output types"); } } + + async fn resolve_into_type_async<'a>( + &'a self, + info: &'a Self::TypeInfo, + type_name: &str, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, Self::Context, S>, + ) -> ExecutionResult<S> { + if Self::name(info).unwrap() == type_name { + Ok(self.resolve_async(info, selection_set, executor).await) + } else { + panic!("resolve_into_type_async must be implemented by unions and interfaces"); + } + } } // Wrapper function around resolve_selection_set_into_async_recursive. @@ -160,7 +175,7 @@ where let response_name = response_name.to_string(); let field_future = async move { // TODO: implement custom future type instead of - // two-level boxing. + // two-level boxing. let res = instance .resolve_field_async(info, f.name.item, &args, &sub_exec) .await; @@ -223,14 +238,14 @@ where ); if let Some(ref type_condition) = fragment.type_condition { - // FIXME: implement async version. - - let sub_result = instance.resolve_into_type( - info, - type_condition.item, - Some(&fragment.selection_set[..]), - &sub_exec, - ); + let sub_result = instance + .resolve_into_type_async( + info, + type_condition.item, + Some(&fragment.selection_set[..]), + &sub_exec, + ) + .await; if let Ok(Value::Object(obj)) = sub_result { for (k, v) in obj { diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index 4a2e2315..b3705630 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -343,7 +343,7 @@ where } } -pub fn resolve_selection_set_into<T, CtxT, S>( +pub(crate) fn resolve_selection_set_into<T, CtxT, S>( instance: &T, info: &T::TypeInfo, selection_set: &[Selection<S>], diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index 06634072..7f6d1374 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -257,6 +257,7 @@ where } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Vec<T> where T: crate::GraphQLTypeAsync<S, Context = CtxT>, @@ -265,18 +266,18 @@ where CtxT: Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, - info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> crate::BoxFuture<'a, Value<S>> { - let f = resolve_into_list_async(executor, info, self.iter()); - Box::pin(f) + info: &'a <Self as crate::GraphQLType<S>>::TypeInfo, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> Value<S> { + resolve_into_list_async(executor, info, self.iter()).await } } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for &[T] where T: crate::GraphQLTypeAsync<S, Context = CtxT>, @@ -285,18 +286,18 @@ where CtxT: Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, - info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> crate::BoxFuture<'a, Value<S>> { - let f = resolve_into_list_async(executor, info, self.iter()); - Box::pin(f) + info: &'a <Self as crate::GraphQLType<S>>::TypeInfo, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> Value<S> { + resolve_into_list_async(executor, info, self.iter()).await } } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<S, T, CtxT> crate::GraphQLTypeAsync<S> for Option<T> where T: crate::GraphQLTypeAsync<S, Context = CtxT>, @@ -305,18 +306,15 @@ where CtxT: Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, - info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> crate::BoxFuture<'a, Value<S>> { - let f = async move { - match *self { - Some(ref obj) => executor.resolve_into_value_async(info, obj).await, - None => Value::null(), - } - }; - Box::pin(f) + info: &'a <Self as crate::GraphQLType<S>>::TypeInfo, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> Value<S> { + match *self { + Some(ref obj) => executor.resolve_into_value_async(info, obj).await, + None => Value::null(), + } } } diff --git a/juniper/src/types/pointers.rs b/juniper/src/types/pointers.rs index 1e8478a1..ed1ecd02 100644 --- a/juniper/src/types/pointers.rs +++ b/juniper/src/types/pointers.rs @@ -137,31 +137,33 @@ where } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<'e, S, T> crate::GraphQLTypeAsync<S> for &'e T where S: ScalarValue + Send + Sync, T: crate::GraphQLTypeAsync<S>, T::TypeInfo: Send + Sync, T::Context: Send + Sync, - for<'b> &'b S: ScalarRefValue<'b>, + for<'c> &'c S: ScalarRefValue<'c>, { - fn resolve_field_async<'b>( + async fn resolve_field_async<'b>( &'b self, - info: &'b Self::TypeInfo, + info: &'b <Self as crate::GraphQLType<S>>::TypeInfo, field_name: &'b str, - arguments: &'b Arguments<S>, - executor: &'b Executor<Self::Context, S>, - ) -> crate::BoxFuture<'b, ExecutionResult<S>> { + arguments: &'b Arguments<'b, S>, + executor: &'b Executor<'b, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> ExecutionResult<S> { crate::GraphQLTypeAsync::resolve_field_async(&**self, info, field_name, arguments, executor) + .await } - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, - info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> crate::BoxFuture<'a, Value<S>> { - crate::GraphQLTypeAsync::resolve_async(&**self, info, selection_set, executor) + info: &'a <Self as crate::GraphQLType<S>>::TypeInfo, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> Value<S> { + crate::GraphQLTypeAsync::resolve_async(&**self, info, selection_set, executor).await } } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index dba13834..53876003 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -197,19 +197,19 @@ where } #[cfg(feature = "async")] +#[async_trait::async_trait] impl<'e, S> crate::GraphQLTypeAsync<S> for &'e str where S: ScalarValue + Send + Sync, for<'b> &'b S: ScalarRefValue<'b>, { - fn resolve_async<'a>( + async fn resolve_async<'a>( &'a self, - info: &'a Self::TypeInfo, - selection_set: Option<&'a [Selection<S>]>, - executor: &'a Executor<Self::Context, S>, - ) -> crate::BoxFuture<'a, crate::Value<S>> { - use futures::future; - future::FutureExt::boxed(future::ready(self.resolve(info, selection_set, executor))) + info: &'a <Self as crate::GraphQLType<S>>::TypeInfo, + selection_set: Option<&'a [Selection<'a, S>]>, + executor: &'a Executor<'a, <Self as crate::GraphQLType<S>>::Context, S>, + ) -> crate::Value<S> { + self.resolve(info, selection_set, executor) } } @@ -308,6 +308,9 @@ impl<T> EmptyMutation<T> { } } +// This is safe due to never using `T`. +unsafe impl<T> Send for EmptyMutation<T> {} + impl<S, T> GraphQLType<S> for EmptyMutation<T> where S: ScalarValue, @@ -343,7 +346,7 @@ where #[cfg(test)] mod tests { - use super::ID; + use super::{EmptyMutation, ID}; use crate::{ parser::ScalarToken, value::{DefaultScalarValue, ParseScalarValue}, @@ -390,4 +393,10 @@ mod tests { "unicode \u{1234}\u{5678}\u{90ab}\u{cdef}", ); } + + #[test] + fn empty_mutation_is_send() { + fn check_if_send<T: Send>() {} + check_if_send::<EmptyMutation<()>>(); + } } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index d6a384ce..78042591 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -260,8 +260,6 @@ pub enum DefaultScalarValue { Boolean(bool), } -trait S: Send + Sync {} - impl ScalarValue for DefaultScalarValue { type Visitor = DefaultScalarValueVisitor; diff --git a/juniper_codegen/Cargo.toml b/juniper_codegen/Cargo.toml index f8ea341c..e22c7600 100644 --- a/juniper_codegen/Cargo.toml +++ b/juniper_codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper_codegen" -version = "0.14.0" +version = "0.14.1" authors = [ "Magnus Hallin <mhallin@fastmail.com>", "Christoph Herzog <chris@theduke.at>", @@ -24,7 +24,7 @@ quote = "1.0.2" proc-macro-error = "0.3.4" [dev-dependencies] -juniper = { version = "0.14.0", path = "../juniper" } +juniper = { version = "0.14.1", path = "../juniper" } [badges] travis-ci = { repository = "graphql-rust/juniper" } diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index d1262e7d..607d2d1f 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -213,12 +213,16 @@ pub fn impl_enum(ast: &syn::DeriveInput, is_internal: bool) -> TokenStream { __S: #juniper_path::ScalarValue + Send + Sync, for<'__b> &'__b __S: #juniper_path::ScalarRefValue<'__b> { - fn resolve_async<'a>( + fn resolve_async<'a, 'async_trait>( &'a self, info: &'a Self::TypeInfo, selection_set: Option<&'a [#juniper_path::Selection<__S>]>, executor: &'a #juniper_path::Executor<Self::Context, __S>, - ) -> futures::future::BoxFuture<'a, #juniper_path::Value<__S>> { + ) -> futures::future::BoxFuture<'async_trait, #juniper_path::Value<__S>> + where + 'a: 'async_trait, + Self: 'async_trait + { use #juniper_path::GraphQLType; use futures::future; let v = self.resolve(info, selection_set, executor); diff --git a/juniper_codegen/src/impl_object.rs b/juniper_codegen/src/impl_object.rs index db7ad2e1..308938cb 100644 --- a/juniper_codegen/src/impl_object.rs +++ b/juniper_codegen/src/impl_object.rs @@ -42,8 +42,7 @@ pub fn build_object(args: TokenStream, body: TokenStream, is_internal: bool) -> } - let name = - if let Some(name) = impl_attrs.name.as_ref(){ + let name = if let Some(name) = impl_attrs.name.as_ref(){ name.to_string() } else { diff --git a/juniper_codegen/src/impl_union.rs b/juniper_codegen/src/impl_union.rs index 58edade1..fa3927c1 100644 --- a/juniper_codegen/src/impl_union.rs +++ b/juniper_codegen/src/impl_union.rs @@ -1,7 +1,7 @@ use proc_macro::TokenStream; use proc_macro_error::MacroError; -use quote::{quote}; +use quote::quote; use syn::spanned::Spanned; use crate::util; @@ -39,7 +39,7 @@ impl syn::parse::Parse for ResolveBody { body.parse::<syn::token::Match>()?; body.parse::<syn::token::SelfValue>()?; - let match_body; + let match_body; syn::braced!( match_body in body ); let mut variants = Vec::new(); @@ -154,7 +154,7 @@ pub fn impl_union( let context = attrs.context.map(|c| quote!{ #c } ).unwrap_or_else(|| quote!{ () }); let output = quote! { - impl #impl_generics #juniper::GraphQLType<#scalar> for #ty #where_clause + impl #impl_generics #juniper::GraphQLType<#scalar> for #ty #where_clause { type Context = #context; type TypeInfo = (); diff --git a/juniper_codegen/src/util.rs b/juniper_codegen/src/util.rs index d6a71d59..40607de0 100644 --- a/juniper_codegen/src/util.rs +++ b/juniper_codegen/src/util.rs @@ -731,15 +731,31 @@ impl GraphQLTypeDefiniton { } }); + let scalar = self + .scalar + .as_ref() + .map(|s| quote!( #s )) + .unwrap_or_else(|| { + if self.generic_scalar { + // If generic_scalar is true, we always insert a generic scalar. + // See more comments below. + quote!(__S) + } else { + quote!(#juniper_crate_name::DefaultScalarValue) + } + }); + let resolve_matches = self.fields.iter().map(|field| { let name = &field.name; let code = &field.resolver_code; if field.is_async { - // TODO: better error message with field/type name. quote!( #name => { - panic!("Tried to resolve async field with a sync resolver"); + panic!("Tried to resolve async field {} on type {:?} with a sync resolver", + #name, + <Self as #juniper_crate_name::GraphQLType<#scalar>>::name(_info) + ); }, ) } else { @@ -780,20 +796,6 @@ impl GraphQLTypeDefiniton { ) }); - let scalar = self - .scalar - .as_ref() - .map(|s| quote!( #s )) - .unwrap_or_else(|| { - if self.generic_scalar { - // If generic_scalar is true, we always insert a generic scalar. - // See more comments below. - quote!(__S) - } else { - quote!(#juniper_crate_name::DefaultScalarValue) - } - }); - // Preserve the original type_generics before modification, // since alteration makes them invalid if self.generic_scalar // is specified. @@ -925,21 +927,27 @@ impl GraphQLTypeDefiniton { impl#impl_generics #juniper_crate_name::GraphQLTypeAsync<#scalar> for #ty #type_generics_tokens #where_async { - fn resolve_field_async<'b>( + fn resolve_field_async<'b, 'async_trait>( &'b self, info: &'b Self::TypeInfo, field: &'b str, args: &'b #juniper_crate_name::Arguments<#scalar>, executor: &'b #juniper_crate_name::Executor<Self::Context, #scalar>, - ) -> futures::future::BoxFuture<'b, #juniper_crate_name::ExecutionResult<#scalar>> - where #scalar: Send + Sync, + ) -> futures::future::BoxFuture<'async_trait, #juniper_crate_name::ExecutionResult<#scalar>> + where + #scalar: Send + Sync, + 'b: 'async_trait, + Self: 'async_trait, { use futures::future; use #juniper_crate_name::GraphQLType; match field { #( #resolve_matches_async )* _ => { - panic!("Field {} not found on type {}", field, "Mutation"); + panic!("Field {} not found on type {:?}", + field, + <Self as #juniper_crate_name::GraphQLType<#scalar>>::name(info) + ); } } } @@ -989,7 +997,10 @@ impl GraphQLTypeDefiniton { match field { #( #resolve_matches )* _ => { - panic!("Field {} not found on type {}", field, "Mutation"); + panic!("Field {} not found on type {:?}", + field, + <Self as #juniper_crate_name::GraphQLType<#scalar>>::name(_info) + ); } } } diff --git a/juniper_hyper/CHANGELOG.md b/juniper_hyper/CHANGELOG.md index d51be2c4..3735fcd6 100644 --- a/juniper_hyper/CHANGELOG.md +++ b/juniper_hyper/CHANGELOG.md @@ -2,6 +2,10 @@ - Compatibility with the latest `juniper`. +# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.1) + +- Compatibility with the latest `juniper`. + # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_hyper-0.5.0) - Compatibility with the latest `juniper`. diff --git a/juniper_hyper/Cargo.toml b/juniper_hyper/Cargo.toml index 28525061..2ac41d24 100644 --- a/juniper_hyper/Cargo.toml +++ b/juniper_hyper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper_hyper" -version = "0.5.0" +version = "0.5.1" authors = ["Damir Vandic <info@dvic.io>"] description = "Juniper GraphQL integration with Hyper" license = "BSD-2-Clause" @@ -13,7 +13,7 @@ serde = "1.0" serde_json = "1.0" serde_derive = "1.0" url = "2" -juniper = { version = "0.14.0", default-features = false, path = "../juniper"} +juniper = { version = "0.14.1", default-features = false, path = "../juniper"} futures = "0.1" tokio = "0.1.8" @@ -25,6 +25,6 @@ pretty_env_logger = "0.2" reqwest = "0.9" [dev-dependencies.juniper] -version = "0.14.0" +version = "0.14.1" features = ["expose-test-schema", "serde_json"] path = "../juniper" diff --git a/juniper_iron/CHANGELOG.md b/juniper_iron/CHANGELOG.md index 60c8be81..00de0024 100644 --- a/juniper_iron/CHANGELOG.md +++ b/juniper_iron/CHANGELOG.md @@ -2,6 +2,10 @@ - Compatibility with the latest `juniper`. +# [[0.6.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.1) + +- Compatibility with the latest `juniper`. + # [[0.6.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_iron-0.6.0) - Compatibility with the latest `juniper`. diff --git a/juniper_iron/Cargo.toml b/juniper_iron/Cargo.toml index 81ad40c2..1a53bc49 100644 --- a/juniper_iron/Cargo.toml +++ b/juniper_iron/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper_iron" -version = "0.6.0" +version = "0.6.1" authors = [ "Magnus Hallin <mhallin@fastmail.com>", "Christoph Herzog <chris@theduke.at>", @@ -15,7 +15,7 @@ edition = "2018" serde = { version = "1.0.2" } serde_json = { version = "1.0.2" } serde_derive = { version = "1.0.2" } -juniper = { version = "0.14.0", path = "../juniper" } +juniper = { version = "0.14.1", path = "../juniper" } urlencoded = { version = ">= 0.5, < 0.7" } iron = ">= 0.5, < 0.7" @@ -29,6 +29,6 @@ url = "2" percent-encoding = "2" [dev-dependencies.juniper] -version = "0.14.0" +version = "0.14.1" features = ["expose-test-schema", "serde_json"] path = "../juniper" diff --git a/juniper_rocket/CHANGELOG.md b/juniper_rocket/CHANGELOG.md index fd9e102e..01432d4b 100644 --- a/juniper_rocket/CHANGELOG.md +++ b/juniper_rocket/CHANGELOG.md @@ -2,6 +2,10 @@ - Compatibility with the latest `juniper`. +# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.1) + +- Compatibility with the latest `juniper`. + # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_rocket-0.5.0) - Compatibility with the latest `juniper`. diff --git a/juniper_rocket/Cargo.toml b/juniper_rocket/Cargo.toml index ad4cc5bc..8129904c 100644 --- a/juniper_rocket/Cargo.toml +++ b/juniper_rocket/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper_rocket" -version = "0.5.0" +version = "0.5.1" authors = [ "Magnus Hallin <mhallin@fastmail.com>", "Christoph Herzog <chris@theduke.at>", @@ -18,13 +18,13 @@ async = [ "juniper/async" ] serde = { version = "1.0.2" } serde_json = { version = "1.0.2" } serde_derive = { version = "1.0.2" } -juniper = { version = "0.14.0", default-features = false, path = "../juniper"} +juniper = { version = "0.14.1", default-features = false, path = "../juniper"} futures03 = { version = "=0.3.0-alpha.19", package = "futures-preview", features = ["compat"] } rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "async" } tokio = "=0.2.0-alpha.6" [dev-dependencies.juniper] -version = "0.14.0" +version = "0.14.1" features = ["expose-test-schema", "serde_json"] path = "../juniper" diff --git a/juniper_warp/CHANGELOG.md b/juniper_warp/CHANGELOG.md index eee06a3e..be3a836a 100644 --- a/juniper_warp/CHANGELOG.md +++ b/juniper_warp/CHANGELOG.md @@ -2,6 +2,10 @@ - Compatibility with the latest `juniper`. +# [[0.5.1] 2019-10-24](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.1) + +- Compatibility with the latest `juniper`. + # [[0.5.0] 2019-09-29](https://github.com/graphql-rust/juniper/releases/tag/juniper_warp-0.5.0) - Compatibility with the latest `juniper`. diff --git a/juniper_warp/Cargo.toml b/juniper_warp/Cargo.toml index a1c1907c..a39392e3 100644 --- a/juniper_warp/Cargo.toml +++ b/juniper_warp/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "juniper_warp" -version = "0.5.0" +version = "0.5.1" authors = ["Tom Houlé <tom@tomhoule.com>"] description = "Juniper GraphQL integration with Warp" license = "BSD-2-Clause" @@ -13,7 +13,7 @@ async = [ "juniper/async", "futures03" ] [dependencies] warp = "0.1.8" -juniper = { version = "0.14.0", path = "../juniper", default-features = false } +juniper = { version = "0.14.1", path = "../juniper", default-features = false } serde_json = "1.0.24" serde_derive = "1.0.75" failure = "0.1.2" @@ -24,7 +24,7 @@ tokio-threadpool = "0.1.7" futures03 = { version = "=0.3.0-alpha.19", optional = true, package = "futures-preview", features = ["compat"] } [dev-dependencies] -juniper = { version = "0.14.0", path = "../juniper", features = ["expose-test-schema", "serde_json"] } +juniper = { version = "0.14.1", path = "../juniper", features = ["expose-test-schema", "serde_json"] } env_logger = "0.5.11" log = "0.4.3" percent-encoding = "1.0"