Default to generic ScalarValue in #[graphql_object] macro (#779)

* Change codegen ScalarValue defaults for #[graphql_object] macro

* Fix integration tests

* Fix codegen failure tests

* Fix 'juniper' crate tests

* Fix integration crates tests

* Fix 'juniper_benchmarks' crate

* Fix examples

* Fix Book

* Fix

* Add CHANGELOG entry

* Some Book corrections

* Fix

* Bootstrap coercion machinery

* Reimpl coercion

* Correct tests, vol.1

* Correct tests, vol.2

* Correct tests, vol.3

* Correct tests, vol.4

* Correct tests, vol.5

* Fix coercion for subscriptions

* README fixes

Co-authored-by: Christian Legnitto <christian@legnitto.com>
Co-authored-by: Christian Legnitto <LegNeato@users.noreply.github.com>
This commit is contained in:
Kai Ren 2020-11-07 03:15:18 +01:00 committed by GitHub
parent 4c40826eff
commit a4871887bb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 817 additions and 646 deletions

View file

@ -33,11 +33,14 @@ result can then be converted to JSON for use with tools and libraries such as
# #![allow(unused_variables)] # #![allow(unused_variables)]
# extern crate juniper; # extern crate juniper;
# extern crate serde_json; # extern crate serde_json;
use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat}; use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
GraphQLObject, IntrospectionFormat,
};
// Define our schema. // Define our schema.
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
struct Example { struct Example {
id: String, id: String,
} }
@ -47,9 +50,7 @@ impl juniper::Context for Context {}
struct Query; struct Query;
#[juniper::graphql_object( #[graphql_object(context = Context)]
Context = Context,
)]
impl Query { impl Query {
fn example(id: String) -> FieldResult<Example> { fn example(id: String) -> FieldResult<Example> {
unimplemented!() unimplemented!()

View file

@ -10,9 +10,10 @@ errors from a mutation:
```rust ```rust
# extern crate juniper; # extern crate juniper;
# use juniper::{graphql_object, GraphQLObject};
# #[derive(juniper::GraphQLObject)] struct User { name: String } # #[derive(juniper::GraphQLObject)] struct User { name: String }
#
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
struct ValidationError { struct ValidationError {
field: String, field: String,
message: String, message: String,
@ -24,7 +25,7 @@ enum SignUpResult {
Error(Vec<ValidationError>), Error(Vec<ValidationError>),
} }
#[juniper::graphql_object] #[graphql_object]
impl SignUpResult { impl SignUpResult {
fn user(&self) -> Option<&User> { fn user(&self) -> Option<&User> {
match *self { match *self {
@ -40,7 +41,7 @@ impl SignUpResult {
} }
} }
} }
#
# fn main() {} # fn main() {}
``` ```

View file

@ -29,7 +29,7 @@ sequentially:
# extern crate juniper; # extern crate juniper;
# extern crate juniper_subscriptions; # extern crate juniper_subscriptions;
# extern crate tokio; # extern crate tokio;
# use juniper::FieldError; # use juniper::{graphql_object, graphql_subscription, FieldError};
# use futures::Stream; # use futures::Stream;
# use std::pin::Pin; # use std::pin::Pin;
# #
@ -38,7 +38,7 @@ sequentially:
# impl juniper::Context for Database {} # impl juniper::Context for Database {}
# pub struct Query; # pub struct Query;
# #[juniper::graphql_object(Context = Database)] # #[graphql_object(context = Database)]
# impl Query { # impl Query {
# fn hello_world() -> &str { # fn hello_world() -> &str {
# "Hello World!" # "Hello World!"
@ -48,7 +48,7 @@ pub struct Subscription;
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>; type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
#[juniper::graphql_subscription(Context = Database)] #[graphql_subscription(context = Database)]
impl Subscription { impl Subscription {
async fn hello_world() -> StringStream { async fn hello_world() -> StringStream {
let stream = tokio::stream::iter(vec![ let stream = tokio::stream::iter(vec![
@ -58,6 +58,7 @@ impl Subscription {
Box::pin(stream) Box::pin(stream)
} }
} }
#
# fn main () {} # fn main () {}
``` ```
@ -84,8 +85,12 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati
# extern crate juniper_subscriptions; # extern crate juniper_subscriptions;
# extern crate serde_json; # extern crate serde_json;
# extern crate tokio; # extern crate tokio;
# use juniper::http::GraphQLRequest; # use juniper::{
# use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode, SubscriptionCoordinator}; # http::GraphQLRequest,
# graphql_object, graphql_subscription,
# DefaultScalarValue, EmptyMutation, FieldError,
# RootNode, SubscriptionCoordinator,
# };
# use juniper_subscriptions::Coordinator; # use juniper_subscriptions::Coordinator;
# use futures::{Stream, StreamExt}; # use futures::{Stream, StreamExt};
# use std::pin::Pin; # use std::pin::Pin;
@ -103,7 +108,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati
# #
# pub struct Query; # pub struct Query;
# #
# #[juniper::graphql_object(Context = Database)] # #[graphql_object(context = Database)]
# impl Query { # impl Query {
# fn hello_world() -> &str { # fn hello_world() -> &str {
# "Hello World!" # "Hello World!"
@ -114,7 +119,7 @@ where [`Connection`][Connection] is a `Stream` of values returned by the operati
# #
# type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>; # type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
# #
# #[juniper::graphql_subscription(Context = Database)] # #[graphql_subscription(context = Database)]
# impl Subscription { # impl Subscription {
# async fn hello_world() -> StringStream { # async fn hello_world() -> StringStream {
# let stream = # let stream =
@ -132,11 +137,9 @@ async fn run_subscription() {
let schema = schema(); let schema = schema();
let coordinator = Coordinator::new(schema); let coordinator = Coordinator::new(schema);
let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str( let req: GraphQLRequest<DefaultScalarValue> = serde_json::from_str(
r#" r#"{
{
"query": "subscription { helloWorld }" "query": "subscription { helloWorld }"
} }"#,
"#,
) )
.unwrap(); .unwrap();
let ctx = Database::new(); let ctx = Database::new();
@ -145,7 +148,7 @@ async fn run_subscription() {
println!("{}", serde_json::to_string(&result).unwrap()); println!("{}", serde_json::to_string(&result).unwrap());
} }
} }
#
# fn main() { } # fn main() { }
``` ```

View file

@ -28,8 +28,12 @@ resolvers, which you will use for the `Query` and `Mutation` roots.
```rust ```rust
# #![allow(unused_variables)] # #![allow(unused_variables)]
# extern crate juniper; # extern crate juniper;
use juniper::{FieldResult, EmptySubscription}; # use std::fmt::Display;
use juniper::{
graphql_object, EmptySubscription, FieldResult, GraphQLEnum,
GraphQLInputObject, GraphQLObject, ScalarValue,
};
#
# struct DatabasePool; # struct DatabasePool;
# impl DatabasePool { # impl DatabasePool {
# fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) } # fn get_connection(&self) -> FieldResult<DatabasePool> { Ok(DatabasePool) }
@ -37,15 +41,15 @@ use juniper::{FieldResult, EmptySubscription};
# fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? } # fn insert_human(&self, _human: &NewHuman) -> FieldResult<Human> { Err("")? }
# } # }
#[derive(juniper::GraphQLEnum)] #[derive(GraphQLEnum)]
enum Episode { enum Episode {
NewHope, NewHope,
Empire, Empire,
Jedi, Jedi,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")] #[graphql(description = "A humanoid creature in the Star Wars universe")]
struct Human { struct Human {
id: String, id: String,
name: String, name: String,
@ -55,8 +59,8 @@ struct Human {
// There is also a custom derive for mapping GraphQL input objects. // There is also a custom derive for mapping GraphQL input objects.
#[derive(juniper::GraphQLInputObject)] #[derive(GraphQLInputObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")] #[graphql(description = "A humanoid creature in the Star Wars universe")]
struct NewHuman { struct NewHuman {
name: String, name: String,
appears_in: Vec<Episode>, appears_in: Vec<Episode>,
@ -78,14 +82,13 @@ impl juniper::Context for Context {}
struct Query; struct Query;
#[juniper::graphql_object( #[graphql_object(
// Here we specify the context type for the object. // Here we specify the context type for the object.
// We need to do this in every type that // We need to do this in every type that
// needs access to the context. // needs access to the context.
Context = Context, context = Context,
)] )]
impl Query { impl Query {
fn apiVersion() -> &str { fn apiVersion() -> &str {
"1.0" "1.0"
} }
@ -109,14 +112,18 @@ impl Query {
struct Mutation; struct Mutation;
#[juniper::graphql_object( #[graphql_object(
Context = Context, context = Context,
)]
impl Mutation {
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human> { // If we need to use `ScalarValue` parametrization explicitly somewhere
let db = context.pool.get_connection()?; // in the object definition (like here in `FieldResult`), we should
let human: Human = db.insert_human(&new_human)?; // declare an explicit type parameter for that, and specify it.
scalar = S,
)]
impl<S: ScalarValue + Display> Mutation {
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human, S> {
let db = context.pool.get_connection().map_err(|e| e.map_scalar_value())?;
let human: Human = db.insert_human(&new_human).map_err(|e| e.map_scalar_value())?;
Ok(human) Ok(human)
} }
} }
@ -124,7 +131,7 @@ impl Mutation {
// A root schema consists of a query, a mutation, and a subscription. // A root schema consists of a query, a mutation, and a subscription.
// Request queries can be executed against a RootNode. // Request queries can be executed against a RootNode.
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>; type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
#
# fn main() { # fn main() {
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new()); # let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
# } # }
@ -143,10 +150,12 @@ You can invoke `juniper::execute` directly to run a GraphQL query:
```rust ```rust
# // Only needed due to 2018 edition because the macro is not accessible. # // Only needed due to 2018 edition because the macro is not accessible.
# #[macro_use] extern crate juniper; # #[macro_use] extern crate juniper;
use juniper::{FieldResult, Variables, EmptyMutation, EmptySubscription}; use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
GraphQLEnum, Variables,
};
#[derive(GraphQLEnum, Clone, Copy)]
#[derive(juniper::GraphQLEnum, Clone, Copy)]
enum Episode { enum Episode {
NewHope, NewHope,
Empire, Empire,
@ -160,16 +169,13 @@ impl juniper::Context for Ctx {}
struct Query; struct Query;
#[juniper::graphql_object( #[graphql_object(context = Ctx)]
Context = Ctx,
)]
impl Query { impl Query {
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> { fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
Ok(context.0) Ok(context.0)
} }
} }
// A root schema consists of a query, a mutation, and a subscription. // A root schema consists of a query, a mutation, and a subscription.
// Request queries can be executed against a RootNode. // Request queries can be executed against a RootNode.
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>; type Schema = juniper::RootNode<'static, Query, EmptyMutation<Ctx>, EmptySubscription<Ctx>>;

View file

@ -24,18 +24,18 @@ object in Juniper, most commonly using the `graphql_object` proc macro:
```rust ```rust
# #![allow(unused_variables)] # #![allow(unused_variables)]
# extern crate juniper; # extern crate juniper;
# use juniper::FieldResult; # use juniper::{graphql_object, FieldResult, GraphQLObject};
# #[derive(juniper::GraphQLObject)] struct User { name: String } # #[derive(GraphQLObject)] struct User { name: String }
struct Root; struct Root;
#[juniper::graphql_object] #[graphql_object]
impl Root { impl Root {
fn userWithUsername(username: String) -> FieldResult<Option<User>> { fn userWithUsername(username: String) -> FieldResult<Option<User>> {
// Look up user in database... // Look up user in database...
# unimplemented!() # unimplemented!()
} }
} }
#
# fn main() { } # fn main() { }
``` ```
@ -47,18 +47,18 @@ that performs some mutating side-effect such as updating a database.
```rust ```rust
# #![allow(unused_variables)] # #![allow(unused_variables)]
# extern crate juniper; # extern crate juniper;
# use juniper::FieldResult; # use juniper::{graphql_object, FieldResult, GraphQLObject};
# #[derive(juniper::GraphQLObject)] struct User { name: String } # #[derive(GraphQLObject)] struct User { name: String }
struct Mutations; struct Mutations;
#[juniper::graphql_object] #[graphql_object]
impl Mutations { impl Mutations {
fn signUpUser(name: String, email: String) -> FieldResult<User> { fn signUpUser(name: String, email: String) -> FieldResult<User> {
// Validate inputs and save user in database... // Validate inputs and save user in database...
# unimplemented!() # unimplemented!()
} }
} }
#
# fn main() { } # fn main() { }
``` ```
@ -68,11 +68,13 @@ Many tools in the GraphQL ecosystem require the schema to be defined in the [Gra
```rust ```rust
# extern crate juniper; # extern crate juniper;
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode}; use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode,
};
struct Query; struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn hello(&self) -> FieldResult<&str> { fn hello(&self) -> FieldResult<&str> {
Ok("hello world") Ok("hello world")

View file

@ -2,7 +2,7 @@
If you've got a struct that can't be mapped directly to GraphQL, that contains If you've got a struct that can't be mapped directly to GraphQL, that contains
computed fields or circular structures, you have to use a more powerful tool: computed fields or circular structures, you have to use a more powerful tool:
the `object` procedural macro. This macro lets you define GraphQL object the `#[graphql_object]` procedural macro. This macro lets you define GraphQL object
fields in a Rust `impl` block for a type. Note that only GraphQL fields fields in a Rust `impl` block for a type. Note that only GraphQL fields
can be specified in this `impl` block. If you want to define normal methods on the struct, can be specified in this `impl` block. If you want to define normal methods on the struct,
you have to do so in a separate, normal `impl` block. Continuing with the you have to do so in a separate, normal `impl` block. Continuing with the
@ -12,13 +12,14 @@ macro:
```rust ```rust
# #![allow(dead_code)] # #![allow(dead_code)]
# extern crate juniper; # extern crate juniper;
# use juniper::graphql_object;
# #
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
} }
#[juniper::graphql_object] #[graphql_object]
impl Person { impl Person {
fn name(&self) -> &str { fn name(&self) -> &str {
self.name.as_str() self.name.as_str()
@ -36,7 +37,7 @@ impl Person {
// [...] // [...]
} }
} }
#
# fn main() { } # fn main() { }
``` ```
@ -46,7 +47,9 @@ field resolver. With this syntax, fields can also take arguments:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::{graphql_object, GraphQLObject};
#
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
@ -56,14 +59,14 @@ struct House {
inhabitants: Vec<Person>, inhabitants: Vec<Person>,
} }
#[juniper::graphql_object] #[graphql_object]
impl House { impl House {
// Creates the field inhabitantWithName(name), returning a nullable person // Creates the field inhabitantWithName(name), returning a nullable person
fn inhabitant_with_name(&self, name: String) -> Option<&Person> { fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
self.inhabitants.iter().find(|p| p.name == name) self.inhabitants.iter().find(|p| p.name == name)
} }
} }
#
# fn main() {} # fn main() {}
``` ```
@ -79,20 +82,20 @@ the field. Also, the type name can be changed with an alias:
```rust ```rust
# extern crate juniper; # extern crate juniper;
# use juniper::graphql_object;
struct Person { #
} struct Person;
/// Doc comments are used as descriptions for GraphQL. /// Doc comments are used as descriptions for GraphQL.
#[juniper::graphql_object( #[graphql_object(
// With this attribute you can change the public GraphQL name of the type. // With this attribute you can change the public GraphQL name of the type.
name = "PersonObject", name = "PersonObject",
// You can also specify a description here, which will overwrite // You can also specify a description here, which will overwrite
// a doc comment description. // a doc comment description.
description = "...", description = "...",
)] )]
impl Person { impl Person {
/// A doc comment on the field will also be used for GraphQL. /// A doc comment on the field will also be used for GraphQL.
#[graphql( #[graphql(
// Or provide a description here. // Or provide a description here.
@ -103,9 +106,7 @@ impl Person {
} }
// Fields can also be renamed if required. // Fields can also be renamed if required.
#[graphql( #[graphql(name = "myCustomFieldName")]
name = "myCustomFieldName",
)]
fn renamed_field() -> bool { fn renamed_field() -> bool {
true true
} }
@ -122,7 +123,7 @@ impl Person {
true true
} }
} }
#
# fn main() { } # fn main() { }
``` ```
@ -137,10 +138,11 @@ This will become better once the [Rust RFC 2565](https://github.com/rust-lang/ru
```rust ```rust
# extern crate juniper; # extern crate juniper;
# use juniper::graphql_object;
#
struct Person {} struct Person {}
#[juniper::graphql_object] #[graphql_object]
impl Person { impl Person {
#[graphql( #[graphql(
arguments( arguments(
@ -160,7 +162,7 @@ impl Person {
format!("{} {}", arg1, arg2) format!("{} {}", arg1, arg2)
} }
} }
#
# fn main() { } # fn main() { }
``` ```
@ -172,5 +174,5 @@ GraphQL fields expose more features than Rust's standard method syntax gives us:
* Per-argument default values * Per-argument default values
* Per-argument descriptions * Per-argument descriptions
These, and more features, are described more thorougly in [the reference These, and more features, are described more thoroughly in [the reference
documentation](https://docs.rs/juniper/latest/juniper/macro.object.html). documentation](https://docs.rs/juniper/latest/juniper/macro.object.html).

View file

@ -10,12 +10,13 @@ chapter.
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
} }
#
# fn main() {} # fn main() {}
``` ```
@ -33,7 +34,8 @@ descriptions:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
/// Information about a person /// Information about a person
struct Person { struct Person {
/// The person's full name, including both first and last names /// The person's full name, including both first and last names
@ -41,7 +43,7 @@ struct Person {
/// The person's age in years, rounded down /// The person's age in years, rounded down
age: i32, age: i32,
} }
#
# fn main() {} # fn main() {}
``` ```
@ -52,15 +54,16 @@ via the `graphql` attribute. The following example is equivalent to the above:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[graphql(description="Information about a person")] #[derive(GraphQLObject)]
#[graphql(description = "Information about a person")]
struct Person { struct Person {
#[graphql(description="The person's full name, including both first and last names")] #[graphql(description = "The person's full name, including both first and last names")]
name: String, name: String,
#[graphql(description="The person's age in years, rounded down")] #[graphql(description = "The person's age in years, rounded down")]
age: i32, age: i32,
} }
#
# fn main() {} # fn main() {}
``` ```
@ -70,17 +73,18 @@ documentation to differ:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[graphql(description="This description shows up in GraphQL")] #[derive(GraphQLObject)]
#[graphql(description = "This description shows up in GraphQL")]
/// This description shows up in RustDoc /// This description shows up in RustDoc
struct Person { struct Person {
#[graphql(description="This description shows up in GraphQL")] #[graphql(description = "This description shows up in GraphQL")]
/// This description shows up in RustDoc /// This description shows up in RustDoc
name: String, name: String,
/// This description shows up in both RustDoc and GraphQL /// This description shows up in both RustDoc and GraphQL
age: i32, age: i32,
} }
#
# fn main() {} # fn main() {}
``` ```
@ -100,18 +104,19 @@ Let's see what that means for building relationships between objects:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
struct House { struct House {
address: Option<String>, // Converted into String (nullable) address: Option<String>, // Converted into String (nullable)
inhabitants: Vec<Person>, // Converted into [Person!]! inhabitants: Vec<Person>, // Converted into [Person!]!
} }
#
# fn main() {} # fn main() {}
``` ```
@ -126,12 +131,13 @@ convention into GraphQL's `camelCase` convention:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
first_name: String, // Would be exposed as firstName in the GraphQL schema first_name: String, // Would be exposed as firstName in the GraphQL schema
last_name: String, // Exposed as lastName last_name: String, // Exposed as lastName
} }
#
# fn main() {} # fn main() {}
``` ```
@ -140,14 +146,15 @@ fields:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
#[graphql(name="websiteURL")] #[graphql(name = "websiteURL")]
website_url: Option<String>, // Now exposed as websiteURL in the schema website_url: Option<String>, // Now exposed as websiteURL in the schema
} }
#
# fn main() {} # fn main() {}
``` ```
@ -158,14 +165,15 @@ attribute:
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
#[graphql(deprecated = "Please use the name field instead")] #[graphql(deprecated = "Please use the name field instead")]
first_name: String, first_name: String,
} }
#
# fn main() {} # fn main() {}
``` ```
@ -179,7 +187,8 @@ By default all fields in a `GraphQLObject` are included in the generated GraphQL
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::GraphQLObject;
#[derive(GraphQLObject)]
struct Person { struct Person {
name: String, name: String,
age: i32, age: i32,
@ -187,6 +196,6 @@ struct Person {
# #[allow(dead_code)] # #[allow(dead_code)]
password_hash: String, // This cannot be queried or modified from GraphQL password_hash: String, // This cannot be queried or modified from GraphQL
} }
#
# fn main() {} # fn main() {}
``` ```

View file

@ -28,13 +28,13 @@ use std::{
fs::{File}, fs::{File},
io::{Read}, io::{Read},
}; };
use juniper::FieldResult; use juniper::{graphql_object, FieldResult};
struct Example { struct Example {
filename: PathBuf, filename: PathBuf,
} }
#[juniper::graphql_object] #[graphql_object]
impl Example { impl Example {
fn contents() -> FieldResult<String> { fn contents() -> FieldResult<String> {
let mut file = File::open(&self.filename)?; let mut file = File::open(&self.filename)?;
@ -53,7 +53,7 @@ impl Example {
} }
} }
} }
#
# fn main() {} # fn main() {}
``` ```
@ -137,14 +137,16 @@ to clients. This can be accomplished by implementing [`IntoFieldError`](https://
```rust ```rust
# #[macro_use] extern crate juniper; # #[macro_use] extern crate juniper;
# use juniper::{graphql_object, FieldError, IntoFieldError, ScalarValue};
#
enum CustomError { enum CustomError {
WhateverNotSet, WhateverNotSet,
} }
impl juniper::IntoFieldError for CustomError { impl<S: ScalarValue> IntoFieldError<S> for CustomError {
fn into_field_error(self) -> juniper::FieldError { fn into_field_error(self) -> FieldError<S> {
match self { match self {
CustomError::WhateverNotSet => juniper::FieldError::new( CustomError::WhateverNotSet => FieldError::new(
"Whatever does not exist", "Whatever does not exist",
graphql_value!({ graphql_value!({
"type": "NO_WHATEVER" "type": "NO_WHATEVER"
@ -158,7 +160,7 @@ struct Example {
whatever: Option<bool>, whatever: Option<bool>,
} }
#[juniper::graphql_object] #[graphql_object]
impl Example { impl Example {
fn whatever() -> Result<bool, CustomError> { fn whatever() -> Result<bool, CustomError> {
if let Some(value) = self.whatever { if let Some(value) = self.whatever {
@ -167,21 +169,21 @@ impl Example {
Err(CustomError::WhateverNotSet) Err(CustomError::WhateverNotSet)
} }
} }
#
# fn main() {} # fn main() {}
``` ```
The specified structured error information is included in the [`extensions`](https://facebook.github.io/graphql/June2018/#sec-Errors) key: The specified structured error information is included in the [`extensions`](https://facebook.github.io/graphql/June2018/#sec-Errors) key:
```js ```json
{ {
"errors": [ "errors": [{
"message": "Whatever does not exist", "message": "Whatever does not exist",
"locations": [{ "line": 2, "column": 4 }]), "locations": [{"line": 2, "column": 4}],
"extensions": { "extensions": {
"type": "NO_WHATEVER" "type": "NO_WHATEVER"
} }
] }]
} }
``` ```
@ -211,24 +213,26 @@ possible to return a unique string identifier and have the client present a loca
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
#
#[derive(GraphQLObject)]
pub struct Item { pub struct Item {
name: String, name: String,
quantity: i32, quantity: i32,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct ValidationError { pub struct ValidationError {
field: String, field: String,
message: String, message: String,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct ValidationErrors { pub struct ValidationErrors {
errors: Vec<ValidationError>, errors: Vec<ValidationError>,
} }
#[derive(juniper::GraphQLUnion)] #[derive(GraphQLUnion)]
pub enum GraphQLResult { pub enum GraphQLResult {
Ok(Item), Ok(Item),
Err(ValidationErrors), Err(ValidationErrors),
@ -236,7 +240,7 @@ pub enum GraphQLResult {
pub struct Mutation; pub struct Mutation;
#[juniper::graphql_object] #[graphql_object]
impl Mutation { impl Mutation {
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult { fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
let mut errors = Vec::new(); let mut errors = Vec::new();
@ -262,7 +266,7 @@ impl Mutation {
} }
} }
} }
#
# fn main() {} # fn main() {}
``` ```
@ -308,19 +312,21 @@ contains only fields provided by the function.
```rust ```rust
# extern crate juniper; # extern crate juniper;
#[derive(juniper::GraphQLObject)] # use juniper::{graphql_object, GraphQLObject, GraphQLUnion};
#
#[derive(GraphQLObject)]
pub struct Item { pub struct Item {
name: String, name: String,
quantity: i32, quantity: i32,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct ValidationError { pub struct ValidationError {
name: Option<String>, name: Option<String>,
quantity: Option<String>, quantity: Option<String>,
} }
#[derive(juniper::GraphQLUnion)] #[derive(GraphQLUnion)]
pub enum GraphQLResult { pub enum GraphQLResult {
Ok(Item), Ok(Item),
Err(ValidationError), Err(ValidationError),
@ -328,7 +334,7 @@ pub enum GraphQLResult {
pub struct Mutation; pub struct Mutation;
#[juniper::graphql_object] #[graphql_object]
impl Mutation { impl Mutation {
fn addItem(&self, name: String, quantity: i32) -> GraphQLResult { fn addItem(&self, name: String, quantity: i32) -> GraphQLResult {
let mut error = ValidationError { let mut error = ValidationError {
@ -351,7 +357,7 @@ impl Mutation {
} }
} }
} }
#
# fn main() {} # fn main() {}
``` ```
@ -385,22 +391,23 @@ and would generate errors. Since it is not common for the database to
fail, the corresponding error is returned as a critical error: fail, the corresponding error is returned as a critical error:
```rust ```rust
# // Only needed due to 2018 edition because the macro is not accessible. # extern crate juniper;
# #[macro_use] extern crate juniper; #
use juniper::{graphql_object, graphql_value, FieldError, GraphQLObject, GraphQLUnion, ScalarValue};
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct Item { pub struct Item {
name: String, name: String,
quantity: i32, quantity: i32,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct ValidationErrorItem { pub struct ValidationErrorItem {
name: Option<String>, name: Option<String>,
quantity: Option<String>, quantity: Option<String>,
} }
#[derive(juniper::GraphQLUnion)] #[derive(GraphQLUnion)]
pub enum GraphQLResult { pub enum GraphQLResult {
Ok(Item), Ok(Item),
Err(ValidationErrorItem), Err(ValidationErrorItem),
@ -410,10 +417,10 @@ pub enum ApiError {
Database, Database,
} }
impl juniper::IntoFieldError for ApiError { impl<S: ScalarValue> juniper::IntoFieldError<S> for ApiError {
fn into_field_error(self) -> juniper::FieldError { fn into_field_error(self) -> FieldError<S> {
match self { match self {
ApiError::Database => juniper::FieldError::new( ApiError::Database => FieldError::new(
"Internal database error", "Internal database error",
graphql_value!({ graphql_value!({
"type": "DATABASE" "type": "DATABASE"
@ -425,7 +432,7 @@ impl juniper::IntoFieldError for ApiError {
pub struct Mutation; pub struct Mutation;
#[juniper::graphql_object] #[graphql_object]
impl Mutation { impl Mutation {
fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> { fn addItem(&self, name: String, quantity: i32) -> Result<GraphQLResult, ApiError> {
let mut error = ValidationErrorItem { let mut error = ValidationErrorItem {
@ -448,7 +455,7 @@ impl Mutation {
} }
} }
} }
#
# fn main() {} # fn main() {}
``` ```

View file

@ -13,7 +13,7 @@ resolvers. Let's say that we have a simple user database in a `HashMap`:
```rust ```rust
# #![allow(dead_code)] # #![allow(dead_code)]
# use std::collections::HashMap; # use std::collections::HashMap;
#
struct Database { struct Database {
users: HashMap<i32, User>, users: HashMap<i32, User>,
} }
@ -23,7 +23,7 @@ struct User {
name: String, name: String,
friend_ids: Vec<i32>, friend_ids: Vec<i32>,
} }
#
# fn main() { } # fn main() { }
``` ```
@ -38,9 +38,10 @@ type as the specified `Context` for the type:
```rust ```rust
# extern crate juniper;
# use std::collections::HashMap; # use std::collections::HashMap;
extern crate juniper; # use juniper::graphql_object;
#
// This struct represents our context. // This struct represents our context.
struct Database { struct Database {
users: HashMap<i32, User>, users: HashMap<i32, User>,
@ -55,11 +56,8 @@ struct User {
friend_ids: Vec<i32>, friend_ids: Vec<i32>,
} }
// Assign Database as the context type for User // Assign Database as the context type for User
#[juniper::graphql_object( #[graphql_object(context = Database)]
Context = Database,
)]
impl User { impl User {
// 3. Inject the context by specifying an argument // 3. Inject the context by specifying an argument
// with the context type. // with the context type.
@ -82,7 +80,7 @@ impl User {
self.id self.id
} }
} }
#
# fn main() { } # fn main() { }
``` ```

View file

@ -4,6 +4,7 @@ use actix_cors::Cors;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer}; use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use juniper::{ use juniper::{
graphql_object, graphql_subscription,
tests::fixtures::starwars::schema::{Character as _, Database, Query}, tests::fixtures::starwars::schema::{Character as _, Database, Query},
DefaultScalarValue, EmptyMutation, FieldError, RootNode, DefaultScalarValue, EmptyMutation, FieldError, RootNode,
}; };
@ -37,7 +38,7 @@ struct RandomHuman {
} }
// TODO: remove this when async interfaces are merged // TODO: remove this when async interfaces are merged
#[juniper::graphql_object(Context = Database)] #[graphql_object(context = Database)]
impl RandomHuman { impl RandomHuman {
fn id(&self) -> &str { fn id(&self) -> &str {
&self.id &self.id
@ -51,7 +52,7 @@ impl RandomHuman {
type RandomHumanStream = type RandomHumanStream =
Pin<Box<dyn futures::Stream<Item = Result<RandomHuman, FieldError>> + Send>>; Pin<Box<dyn futures::Stream<Item = Result<RandomHuman, FieldError>> + Send>>;
#[juniper::graphql_subscription(Context = Database)] #[graphql_subscription(context = Database)]
impl Subscription { impl Subscription {
#[graphql( #[graphql(
description = "A random humanoid creature in the Star Wars universe every 3 seconds. Second result will be an error." description = "A random humanoid creature in the Star Wars universe every 3 seconds. Second result will be an error."

View file

@ -4,8 +4,8 @@ use std::pin::Pin;
use futures::{Stream, StreamExt}; use futures::{Stream, StreamExt};
use juniper::{ use juniper::{
http::GraphQLRequest, DefaultScalarValue, EmptyMutation, FieldError, RootNode, graphql_object, graphql_subscription, http::GraphQLRequest, DefaultScalarValue, EmptyMutation,
SubscriptionCoordinator, FieldError, RootNode, SubscriptionCoordinator,
}; };
use juniper_subscriptions::Coordinator; use juniper_subscriptions::Coordinator;
@ -22,7 +22,7 @@ impl Database {
pub struct Query; pub struct Query;
#[juniper::graphql_object(Context = Database)] #[graphql_object(context = Database)]
impl Query { impl Query {
fn hello_world() -> &str { fn hello_world() -> &str {
"Hello World!" "Hello World!"
@ -33,7 +33,7 @@ pub struct Subscription;
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>; type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
#[juniper::graphql_subscription(Context = Database)] #[graphql_subscription(context = Database)]
impl Subscription { impl Subscription {
async fn hello_world() -> StringStream { async fn hello_world() -> StringStream {
let stream = let stream =

View file

@ -23,7 +23,7 @@ struct User {
name: String, name: String,
} }
#[graphql_object(Context = Context)] #[graphql_object(context = Context)]
impl User { impl User {
fn id(&self) -> i32 { fn id(&self) -> i32 {
self.id self.id
@ -45,7 +45,7 @@ impl User {
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
struct Query; struct Query;
#[graphql_object(Context = Context)] #[graphql_object(context = Context)]
impl Query { impl Query {
async fn users() -> Vec<User> { async fn users() -> Vec<User> {
vec![User { vec![User {

View file

@ -3,7 +3,10 @@
use std::{env, pin::Pin, sync::Arc, time::Duration}; use std::{env, pin::Pin, sync::Arc, time::Duration};
use futures::{FutureExt as _, Stream}; use futures::{FutureExt as _, Stream};
use juniper::{DefaultScalarValue, EmptyMutation, FieldError, RootNode}; use juniper::{
graphql_object, graphql_subscription, DefaultScalarValue, EmptyMutation, FieldError,
GraphQLEnum, RootNode,
};
use juniper_graphql_ws::ConnectionConfig; use juniper_graphql_ws::ConnectionConfig;
use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws}; use juniper_warp::{playground_filter, subscriptions::serve_graphql_ws};
use warp::{http::Response, Filter}; use warp::{http::Response, Filter};
@ -13,7 +16,7 @@ struct Context {}
impl juniper::Context for Context {} impl juniper::Context for Context {}
#[derive(Clone, Copy, juniper::GraphQLEnum)] #[derive(Clone, Copy, GraphQLEnum)]
enum UserKind { enum UserKind {
Admin, Admin,
User, User,
@ -27,7 +30,7 @@ struct User {
} }
// Field resolvers implementation // Field resolvers implementation
#[juniper::graphql_object(Context = Context)] #[graphql_object(context = Context)]
impl User { impl User {
fn id(&self) -> i32 { fn id(&self) -> i32 {
self.id self.id
@ -87,7 +90,7 @@ impl User {
struct Query; struct Query;
#[juniper::graphql_object(Context = Context)] #[graphql_object(context = Context)]
impl Query { impl Query {
async fn users(id: i32) -> Vec<User> { async fn users(id: i32) -> Vec<User> {
vec![User { vec![User {
@ -102,7 +105,7 @@ type UsersStream = Pin<Box<dyn Stream<Item = Result<User, FieldError>> + Send>>;
struct Subscription; struct Subscription;
#[juniper::graphql_subscription(Context = Context)] #[graphql_subscription(context = Context)]
impl Subscription { impl Subscription {
async fn users() -> UsersStream { async fn users() -> UsersStream {
let mut counter = 0; let mut counter = 0;

View file

@ -1,7 +1,9 @@
#[cfg(test)] use juniper::{
use juniper::{graphql_value, EmptyMutation, EmptySubscription, GraphQLError, RootNode, Value}; graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLError,
RootNode, Value,
};
#[derive(juniper::GraphQLEnum)] #[derive(GraphQLEnum)]
enum UserKind { enum UserKind {
Admin, Admin,
User, User,
@ -15,7 +17,7 @@ struct User {
kind: UserKind, kind: UserKind,
} }
#[juniper::graphql_object] #[graphql_object]
impl User { impl User {
async fn id(&self) -> i32 { async fn id(&self) -> i32 {
self.id self.id
@ -47,7 +49,7 @@ impl User {
struct Query; struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn field_sync(&self) -> &'static str { fn field_sync(&self) -> &'static str {
"field_sync" "field_sync"

View file

@ -1,16 +1,29 @@
error[E0277]: the trait bound `Obj: IsInputType<DefaultScalarValue>` is not satisfied error[E0277]: the trait bound `Obj: IsInputType<__S>` is not satisfied
--> $DIR/impl_argument_no_object.rs:8:1 --> $DIR/impl_argument_no_object.rs:8:1
| |
8 | #[juniper::graphql_object] 8 | #[juniper::graphql_object]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<DefaultScalarValue>` is not implemented for `Obj` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IsInputType<__S>` is not implemented for `Obj`
| |
= note: required by `juniper::marker::IsInputType::mark` = note: required by `juniper::marker::IsInputType::mark`
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Obj: FromInputValue<__S>` is not satisfied
--> $DIR/impl_argument_no_object.rs:8:1
|
8 | #[juniper::graphql_object]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue<__S>` is not implemented for `Obj`
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0277]: the trait bound `Obj: FromInputValue` is not satisfied error[E0277]: the trait bound `Obj: FromInputValue` is not satisfied
--> $DIR/impl_argument_no_object.rs:8:1 --> $DIR/impl_argument_no_object.rs:8:1
| |
8 | #[juniper::graphql_object] 8 | #[juniper::graphql_object]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj` | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj`
| |
::: $WORKSPACE/juniper/src/ast.rs
|
| pub trait FromInputValue<S = DefaultScalarValue>: Sized {
| ------------------------------------------------------- required by this bound in `FromInputValue`
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info) = note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)

View file

@ -1,14 +1,14 @@
#[cfg(test)]
use fnv::FnvHashMap; use fnv::FnvHashMap;
use juniper::{
#[cfg(test)] DefaultScalarValue, FromInputValue, GraphQLEnum, GraphQLType, InputValue, Registry,
use juniper::{self, DefaultScalarValue, FromInputValue, GraphQLType, InputValue, ToInputValue}; ToInputValue,
};
pub struct CustomContext {} pub struct CustomContext {}
impl juniper::Context for CustomContext {} impl juniper::Context for CustomContext {}
#[derive(juniper::GraphQLEnum, Debug, PartialEq)] #[derive(GraphQLEnum, Debug, PartialEq)]
#[graphql(name = "Some", description = "enum descr")] #[graphql(name = "Some", description = "enum descr")]
enum SomeEnum { enum SomeEnum {
Regular, Regular,
@ -24,7 +24,7 @@ enum NoRenameEnum {
} }
/// Enum doc. /// Enum doc.
#[derive(juniper::GraphQLEnum)] #[derive(GraphQLEnum)]
enum DocEnum { enum DocEnum {
/// Variant doc. /// Variant doc.
Foo, Foo,
@ -34,7 +34,7 @@ enum DocEnum {
/// Doc 2. /// Doc 2.
/// ///
/// Doc 4. /// Doc 4.
#[derive(juniper::GraphQLEnum, Debug, PartialEq)] #[derive(GraphQLEnum, Debug, PartialEq)]
enum MultiDocEnum { enum MultiDocEnum {
/// Variant 1. /// Variant 1.
/// Variant 2. /// Variant 2.
@ -42,7 +42,7 @@ enum MultiDocEnum {
} }
/// This is not used as the description. /// This is not used as the description.
#[derive(juniper::GraphQLEnum, Debug, PartialEq)] #[derive(GraphQLEnum, Debug, PartialEq)]
#[graphql(description = "enum override")] #[graphql(description = "enum override")]
enum OverrideDocEnum { enum OverrideDocEnum {
/// This is not used as the description. /// This is not used as the description.
@ -50,7 +50,7 @@ enum OverrideDocEnum {
Foo, Foo,
} }
#[derive(juniper::GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(context = CustomContext, noasync)] #[graphql(context = CustomContext, noasync)]
enum ContextEnum { enum ContextEnum {
A, A,
@ -65,7 +65,7 @@ fn test_derived_enum() {
); );
// Ensure validity of meta info. // Ensure validity of meta info.
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = SomeEnum::meta(&(), &mut registry); let meta = SomeEnum::meta(&(), &mut registry);
assert_eq!(meta.name(), Some("Some")); assert_eq!(meta.name(), Some("Some"));
@ -100,14 +100,14 @@ fn test_derived_enum() {
#[test] #[test]
fn test_doc_comment() { fn test_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = DocEnum::meta(&(), &mut registry); let meta = DocEnum::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"Enum doc.".to_string())); assert_eq!(meta.description(), Some(&"Enum doc.".to_string()));
} }
#[test] #[test]
fn test_multi_doc_comment() { fn test_multi_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = MultiDocEnum::meta(&(), &mut registry); let meta = MultiDocEnum::meta(&(), &mut registry);
assert_eq!( assert_eq!(
meta.description(), meta.description(),
@ -117,7 +117,7 @@ fn test_multi_doc_comment() {
#[test] #[test]
fn test_doc_comment_override() { fn test_doc_comment_override() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = OverrideDocEnum::meta(&(), &mut registry); let meta = OverrideDocEnum::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"enum override".to_string())); assert_eq!(meta.description(), Some(&"enum override".to_string()));
} }

View file

@ -1,8 +1,7 @@
use fnv::FnvHashMap; use fnv::FnvHashMap;
use juniper::{ use juniper::{
marker, DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue, marker, DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue,
InputValue, ToInputValue, InputValue, Registry, ToInputValue,
}; };
#[derive(GraphQLInputObject, Debug, PartialEq)] #[derive(GraphQLInputObject, Debug, PartialEq)]
@ -74,7 +73,7 @@ impl<'a> GraphQLType<DefaultScalarValue> for &'a Fake {
fn name(_: &()) -> Option<&'static str> { fn name(_: &()) -> Option<&'static str> {
None None
} }
fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> juniper::meta::MetaType<'r>
where where
DefaultScalarValue: 'r, DefaultScalarValue: 'r,
{ {
@ -113,7 +112,7 @@ fn test_derived_input_object() {
); );
// Validate meta info. // Validate meta info.
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = Input::meta(&(), &mut registry); let meta = Input::meta(&(), &mut registry);
assert_eq!(meta.name(), Some("MyInput")); assert_eq!(meta.name(), Some("MyInput"));
assert_eq!(meta.description(), Some(&"input descr".to_string())); assert_eq!(meta.description(), Some(&"input descr".to_string()));
@ -172,14 +171,14 @@ fn test_derived_input_object() {
#[test] #[test]
fn test_doc_comment() { fn test_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = DocComment::meta(&(), &mut registry); let meta = DocComment::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"Object comment.".to_string())); assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
} }
#[test] #[test]
fn test_multi_doc_comment() { fn test_multi_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = MultiDocComment::meta(&(), &mut registry); let meta = MultiDocComment::meta(&(), &mut registry);
assert_eq!( assert_eq!(
meta.description(), meta.description(),
@ -189,7 +188,7 @@ fn test_multi_doc_comment() {
#[test] #[test]
fn test_doc_comment_override() { fn test_doc_comment_override() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = OverrideDocComment::meta(&(), &mut registry); let meta = OverrideDocComment::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"obj override".to_string())); assert_eq!(meta.description(), Some(&"obj override".to_string()));
} }

View file

@ -1,20 +1,11 @@
#[cfg(test)]
use fnv::FnvHashMap; use fnv::FnvHashMap;
#[cfg(test)]
use juniper::Object;
use juniper::{DefaultScalarValue, GraphQLObject};
#[cfg(test)]
use juniper::{ use juniper::{
self, execute, EmptyMutation, EmptySubscription, GraphQLType, RootNode, Value, Variables, execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, GraphQLObject,
GraphQLType, Object, Registry, RootNode, Value, Variables,
}; };
#[derive(GraphQLObject, Debug, PartialEq)] #[derive(GraphQLObject, Debug, PartialEq)]
#[graphql( #[graphql(name = "MyObj", description = "obj descr")]
name = "MyObj",
description = "obj descr",
scalar = DefaultScalarValue
)]
struct Obj { struct Obj {
regular_field: bool, regular_field: bool,
#[graphql( #[graphql(
@ -26,14 +17,10 @@ struct Obj {
} }
#[derive(GraphQLObject, Debug, PartialEq)] #[derive(GraphQLObject, Debug, PartialEq)]
#[graphql(scalar = DefaultScalarValue)]
struct Nested { struct Nested {
obj: Obj, obj: Obj,
} }
struct Query;
struct NoRenameQuery;
/// Object comment. /// Object comment.
#[derive(GraphQLObject, Debug, PartialEq)] #[derive(GraphQLObject, Debug, PartialEq)]
struct DocComment { struct DocComment {
@ -79,16 +66,19 @@ struct NoRenameObj {
one_field: bool, one_field: bool,
another_field: i32, another_field: i32,
} }
struct Context; struct Context;
impl juniper::Context for Context {} impl juniper::Context for Context {}
#[derive(GraphQLObject, Debug)] #[derive(GraphQLObject, Debug)]
#[graphql(Context = Context)] #[graphql(context = Context)]
struct WithCustomContext { struct WithCustomContext {
a: bool, a: bool,
} }
#[juniper::graphql_object] struct Query;
#[graphql_object]
impl Query { impl Query {
fn obj() -> Obj { fn obj() -> Obj {
Obj { Obj {
@ -139,7 +129,9 @@ impl Query {
} }
} }
#[juniper::graphql_object(rename = "none")] struct NoRenameQuery;
#[graphql_object(rename = "none")]
impl NoRenameQuery { impl NoRenameQuery {
fn obj() -> Obj { fn obj() -> Obj {
Obj { Obj {
@ -158,7 +150,7 @@ impl NoRenameQuery {
#[tokio::test] #[tokio::test]
async fn test_doc_comment_simple() { async fn test_doc_comment_simple() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = DocComment::meta(&(), &mut registry); let meta = DocComment::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"Object comment.".to_string())); assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
@ -173,7 +165,7 @@ async fn test_doc_comment_simple() {
#[tokio::test] #[tokio::test]
async fn test_multi_doc_comment() { async fn test_multi_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = MultiDocComment::meta(&(), &mut registry); let meta = MultiDocComment::meta(&(), &mut registry);
assert_eq!( assert_eq!(
meta.description(), meta.description(),
@ -191,7 +183,7 @@ async fn test_multi_doc_comment() {
#[tokio::test] #[tokio::test]
async fn test_doc_comment_override() { async fn test_doc_comment_override() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = OverrideDocComment::meta(&(), &mut registry); let meta = OverrideDocComment::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"obj override".to_string())); assert_eq!(meta.description(), Some(&"obj override".to_string()));
@ -204,98 +196,6 @@ async fn test_doc_comment_override() {
.await; .await;
} }
#[tokio::test]
async fn test_no_rename_root() {
let doc = r#"
{
no_rename_obj {
one_field
another_field
}
obj {
regularField
}
}"#;
let schema = RootNode::new(
NoRenameQuery,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
assert_eq!(
execute(doc, None, &schema, &Variables::new(), &()).await,
Ok((
Value::object(
vec![
(
"no_rename_obj",
Value::object(
vec![
("one_field", Value::scalar(true)),
("another_field", Value::scalar(146)),
]
.into_iter()
.collect(),
),
),
(
"obj",
Value::object(
vec![("regularField", Value::scalar(false)),]
.into_iter()
.collect(),
),
)
]
.into_iter()
.collect()
),
vec![]
))
);
}
#[tokio::test]
async fn test_no_rename_obj() {
let doc = r#"
{
noRenameObj {
one_field
another_field
}
}"#;
let schema = RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
assert_eq!(
execute(doc, None, &schema, &Variables::new(), &()).await,
Ok((
Value::object(
vec![(
"noRenameObj",
Value::object(
vec![
("one_field", Value::scalar(true)),
("another_field", Value::scalar(146)),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect()
),
vec![]
))
);
}
#[tokio::test] #[tokio::test]
async fn test_derived_object() { async fn test_derived_object() {
assert_eq!( assert_eq!(
@ -304,7 +204,7 @@ async fn test_derived_object() {
); );
// Verify meta info. // Verify meta info.
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = Obj::meta(&(), &mut registry); let meta = Obj::meta(&(), &mut registry);
assert_eq!(meta.name(), Some("MyObj")); assert_eq!(meta.name(), Some("MyObj"));
@ -432,7 +332,98 @@ async fn test_derived_object_nested() {
); );
} }
#[cfg(test)] #[tokio::test]
async fn test_no_rename_root() {
let doc = r#"
{
no_rename_obj {
one_field
another_field
}
obj {
regularField
}
}"#;
let schema = RootNode::new(
NoRenameQuery,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
assert_eq!(
execute(doc, None, &schema, &Variables::new(), &()).await,
Ok((
Value::object(
vec![
(
"no_rename_obj",
Value::object(
vec![
("one_field", Value::scalar(true)),
("another_field", Value::scalar(146)),
]
.into_iter()
.collect(),
),
),
(
"obj",
Value::object(
vec![("regularField", Value::scalar(false)),]
.into_iter()
.collect(),
),
)
]
.into_iter()
.collect()
),
vec![]
))
);
}
#[tokio::test]
async fn test_no_rename_obj() {
let doc = r#"
{
noRenameObj {
one_field
another_field
}
}"#;
let schema = RootNode::new(
Query,
EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(),
);
assert_eq!(
execute(doc, None, &schema, &Variables::new(), &()).await,
Ok((
Value::object(
vec![(
"noRenameObj",
Value::object(
vec![
("one_field", Value::scalar(true)),
("another_field", Value::scalar(146)),
]
.into_iter()
.collect(),
),
)]
.into_iter()
.collect()
),
vec![]
))
);
}
async fn check_descriptions( async fn check_descriptions(
object_name: &str, object_name: &str,
object_description: &Value, object_description: &Value,
@ -475,7 +466,6 @@ async fn check_descriptions(
.await; .await;
} }
#[cfg(test)]
async fn run_type_info_query<F>(doc: &str, f: F) async fn run_type_info_query<F>(doc: &str, f: F)
where where
F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (), F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (),

View file

@ -1,12 +1,11 @@
#[cfg(test)]
use juniper::{ use juniper::{
self, execute, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, RootNode, execute, graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject,
Value, Variables, RootNode, Value, Variables,
}; };
pub struct Query; pub struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn r#type(r#fn: MyInputType) -> Vec<String> { fn r#type(r#fn: MyInputType) -> Vec<String> {
let _ = r#fn; let _ = r#fn;
@ -87,7 +86,6 @@ async fn supports_raw_idents_in_fields_of_input_types() {
); );
} }
#[cfg(test)]
async fn run_type_info_query(doc: &str) -> Value { async fn run_type_info_query(doc: &str) -> Value {
let schema = RootNode::new( let schema = RootNode::new(
Query, Query,

View file

@ -1,13 +1,11 @@
use juniper::DefaultScalarValue; use juniper::{
#[cfg(test)] execute, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, Object,
use juniper::Object; RootNode, Value, Variables,
};
#[cfg(test)]
use juniper::{execute, EmptyMutation, EmptySubscription, FieldError, RootNode, Value, Variables};
pub struct MyObject; pub struct MyObject;
#[juniper::graphql_object] #[graphql_object]
impl MyObject { impl MyObject {
#[graphql(arguments(arg(name = "test")))] #[graphql(arguments(arg(name = "test")))]
fn test(&self, arg: String) -> String { fn test(&self, arg: String) -> String {
@ -49,7 +47,6 @@ async fn check_argument_rename() {
.await; .await;
} }
#[cfg(test)]
async fn run_type_info_query<F>(doc: &str, f: F) async fn run_type_info_query<F>(doc: &str, f: F)
where where
F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (), F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (),
@ -86,11 +83,11 @@ where
} }
mod fallible { mod fallible {
use super::*; use juniper::{graphql_object, FieldError};
struct Obj; struct Obj;
#[juniper::graphql_object] #[graphql_object]
impl Obj { impl Obj {
fn test(&self, arg: String) -> Result<String, FieldError> { fn test(&self, arg: String) -> Result<String, FieldError> {
Ok(arg) Ok(arg)

View file

@ -86,7 +86,7 @@ impl GraphQLScalar for ScalarDescription {
} }
} }
#[graphql_object] #[graphql_object(scalar = DefaultScalarValue)]
impl Root { impl Root {
fn default_name() -> DefaultName { fn default_name() -> DefaultName {
DefaultName(0) DefaultName(0)
@ -278,7 +278,7 @@ async fn scalar_description_introspection() {
async fn resolves_with_custom_scalar_value() { async fn resolves_with_custom_scalar_value() {
const DOC: &str = r#"{ withCustomScalarValue }"#; const DOC: &str = r#"{ withCustomScalarValue }"#;
let schema = RootNode::<_, _, _, MyScalarValue>::new( let schema = RootNode::<_, _, _, MyScalarValue>::new_with_scalar_value(
RootWithCustomScalarValue, RootWithCustomScalarValue,
EmptyMutation::<()>::new(), EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(), EmptySubscription::<()>::new(),

View file

@ -6,12 +6,25 @@ use juniper::{
IntoFieldError, RootNode, ScalarValue, Variables, IntoFieldError, RootNode, ScalarValue, Variables,
}; };
fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S> fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>>
where
Q: GraphQLType<DefaultScalarValue, Context = C, TypeInfo = ()> + 'q,
{
RootNode::new(
query_root,
EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(),
)
}
fn schema_with_scalar<'q, S, C, Q>(
query_root: Q,
) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S>
where where
Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q, Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q,
S: ScalarValue + 'q, S: ScalarValue + 'q,
{ {
RootNode::new( RootNode::new_with_scalar_value(
query_root, query_root,
EmptyMutation::<C>::new(), EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(), EmptySubscription::<C>::new(),
@ -39,7 +52,7 @@ mod no_implers {
unimplemented!() unimplemented!()
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, __S>> {
unimplemented!() unimplemented!()
} }
} }
@ -170,8 +183,8 @@ mod trivial {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -187,8 +200,8 @@ mod trivial {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -692,8 +705,8 @@ mod trivial_async {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -709,8 +722,8 @@ mod trivial_async {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -1055,8 +1068,8 @@ mod explicit_async {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -1072,8 +1085,8 @@ mod explicit_async {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -1305,8 +1318,8 @@ mod fallible_field {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -1322,8 +1335,8 @@ mod fallible_field {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -1571,8 +1584,8 @@ mod generic {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -1588,8 +1601,8 @@ mod generic {
} }
} }
fn hero(&self) -> Box<DynHero<'_, u8, ()>> { fn hero(&self) -> Box<DynHero<'_, u8, (), S>> {
let ch: Box<DynHero<'_, u8, ()>> = match self { let ch: Box<DynHero<'_, u8, (), _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -1815,8 +1828,8 @@ mod generic_async {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -1832,8 +1845,8 @@ mod generic_async {
} }
} }
fn hero(&self) -> Box<DynHero<'_, u8, ()>> { fn hero(&self) -> Box<DynHero<'_, u8, (), S>> {
let ch: Box<DynHero<'_, u8, ()>> = match self { let ch: Box<DynHero<'_, u8, (), _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -2059,8 +2072,8 @@ mod generic_lifetime_async {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue<'_, ()> { fn character(&self) -> CharacterValue<'_, ()> {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -2076,8 +2089,8 @@ mod generic_lifetime_async {
} }
} }
fn hero(&self) -> Box<DynHero<'_, '_, ()>> { fn hero(&self) -> Box<DynHero<'_, '_, (), S>> {
let ch: Box<DynHero<'_, '_, ()>> = match self { let ch: Box<DynHero<'_, '_, (), _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -2286,8 +2299,8 @@ mod argument {
struct QueryRoot; struct QueryRoot;
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
Human { Human {
id: "human-32".to_string(), id: "human-32".to_string(),
@ -2296,7 +2309,7 @@ mod argument {
.into() .into()
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
Box::new(Human { Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -3011,7 +3024,7 @@ mod explicit_scalar {
} }
}"#; }"#;
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); let schema = schema(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3033,7 +3046,7 @@ mod explicit_scalar {
} }
}"#; }"#;
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); let schema = schema(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3055,7 +3068,7 @@ mod explicit_scalar {
} }
}"#; }"#;
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); let schema = schema(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3077,7 +3090,7 @@ mod explicit_scalar {
} }
}"#; }"#;
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); let schema = schema(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3100,7 +3113,7 @@ mod explicit_scalar {
(QueryRoot::Human, "human-32"), (QueryRoot::Human, "human-32"),
(QueryRoot::Droid, "droid-99"), (QueryRoot::Droid, "droid-99"),
] { ] {
let schema = schema::<_, DefaultScalarValue, _>(*root); let schema = schema(*root);
let expected_id: &str = *expected_id; let expected_id: &str = *expected_id;
assert_eq!( assert_eq!(
@ -3119,7 +3132,7 @@ mod explicit_scalar {
}"#; }"#;
for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] { for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
let schema = schema::<_, DefaultScalarValue, _>(*root); let schema = schema(*root);
let expected_info: &str = *expected_info; let expected_info: &str = *expected_info;
assert_eq!( assert_eq!(
@ -3237,7 +3250,7 @@ mod custom_scalar {
} }
}"#; }"#;
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3259,7 +3272,7 @@ mod custom_scalar {
} }
}"#; }"#;
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3281,7 +3294,7 @@ mod custom_scalar {
} }
}"#; }"#;
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3303,7 +3316,7 @@ mod custom_scalar {
} }
}"#; }"#;
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -3326,7 +3339,7 @@ mod custom_scalar {
(QueryRoot::Human, "human-32"), (QueryRoot::Human, "human-32"),
(QueryRoot::Droid, "droid-99"), (QueryRoot::Droid, "droid-99"),
] { ] {
let schema = schema::<_, MyScalarValue, _>(*root); let schema = schema_with_scalar::<MyScalarValue, _, _>(*root);
let expected_id: &str = *expected_id; let expected_id: &str = *expected_id;
assert_eq!( assert_eq!(
@ -3345,7 +3358,7 @@ mod custom_scalar {
}"#; }"#;
for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] { for (root, expected_info) in &[(QueryRoot::Human, "earth"), (QueryRoot::Droid, "run")] {
let schema = schema::<_, MyScalarValue, _>(*root); let schema = schema_with_scalar::<MyScalarValue, _, _>(*root);
let expected_info: &str = *expected_info; let expected_info: &str = *expected_info;
assert_eq!( assert_eq!(
@ -3417,9 +3430,9 @@ mod explicit_generic_scalar {
Droid, Droid,
} }
#[graphql_object(scalar = DefaultScalarValue)] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue<S> {
match self { match self {
Self::Human => Human { Self::Human => Human {
id: "human-32".to_string(), id: "human-32".to_string(),
@ -3434,8 +3447,8 @@ mod explicit_generic_scalar {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -3685,8 +3698,8 @@ mod explicit_custom_context {
Droid, Droid,
} }
#[graphql_object(context = CustomContext)] #[graphql_object(context = CustomContext, scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -3702,8 +3715,8 @@ mod explicit_custom_context {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -3931,8 +3944,8 @@ mod inferred_custom_context_from_field {
Droid, Droid,
} }
#[graphql_object(context = CustomContext)] #[graphql_object(context = CustomContext, scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -3948,8 +3961,8 @@ mod inferred_custom_context_from_field {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -4183,8 +4196,8 @@ mod inferred_custom_context_from_downcast {
Droid, Droid,
} }
#[graphql_object(context = Database)] #[graphql_object(context = Database, scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -4200,8 +4213,8 @@ mod inferred_custom_context_from_downcast {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -4793,8 +4806,8 @@ mod downcast_method {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -4810,8 +4823,8 @@ mod downcast_method {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),
@ -5058,8 +5071,8 @@ mod external_downcast {
Droid, Droid,
} }
#[graphql_object(context = Database)] #[graphql_object(context = Database, scalar = S)]
impl QueryRoot { impl<S: ScalarValue + Send + Sync> QueryRoot {
fn character(&self) -> CharacterValue { fn character(&self) -> CharacterValue {
match self { match self {
Self::Human => Human { Self::Human => Human {
@ -5075,8 +5088,8 @@ mod external_downcast {
} }
} }
fn hero(&self) -> Box<DynHero<'_>> { fn hero(&self) -> Box<DynHero<'_, S>> {
let ch: Box<DynHero<'_>> = match self { let ch: Box<DynHero<'_, _>> = match self {
Self::Human => Box::new(Human { Self::Human => Box::new(Human {
id: "human-32".to_string(), id: "human-32".to_string(),
home_planet: "earth".to_string(), home_planet: "earth".to_string(),

View file

@ -1,20 +1,23 @@
use fnv::FnvHashMap; use fnv::FnvHashMap;
use juniper::{DefaultScalarValue, FromInputValue, GraphQLType, InputValue, ToInputValue}; use juniper::{
graphql_object, DefaultScalarValue, FromInputValue, GraphQLObject, GraphQLScalarValue,
GraphQLType, InputValue, Registry, ToInputValue,
};
#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] #[derive(GraphQLScalarValue, Debug, Eq, PartialEq)]
#[graphql(transparent)] #[graphql(transparent)]
struct UserId(String); struct UserId(String);
#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] #[derive(GraphQLScalarValue, Debug, Eq, PartialEq)]
#[graphql(transparent, name = "MyUserId", description = "custom description...")] #[graphql(transparent, name = "MyUserId", description = "custom description...")]
struct CustomUserId(String); struct CustomUserId(String);
/// The doc comment... /// The doc comment...
#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)] #[derive(GraphQLScalarValue, Debug, Eq, PartialEq)]
#[graphql(transparent)] #[graphql(transparent)]
struct IdWithDocComment(i32); struct IdWithDocComment(i32);
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
struct User { struct User {
id: UserId, id: UserId,
id_custom: CustomUserId, id_custom: CustomUserId,
@ -22,7 +25,7 @@ struct User {
struct User2; struct User2;
#[juniper::graphql_object] #[graphql_object]
impl User2 { impl User2 {
fn id(&self) -> UserId { fn id(&self) -> UserId {
UserId("id".to_string()) UserId("id".to_string())
@ -36,7 +39,7 @@ fn test_scalar_value_simple() {
Some("UserId") Some("UserId")
); );
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = UserId::meta(&(), &mut registry); let meta = UserId::meta(&(), &mut registry);
assert_eq!(meta.name(), Some("UserId")); assert_eq!(meta.name(), Some("UserId"));
assert_eq!(meta.description(), None); assert_eq!(meta.description(), None);
@ -57,7 +60,7 @@ fn test_scalar_value_custom() {
Some("MyUserId") Some("MyUserId")
); );
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = CustomUserId::meta(&(), &mut registry); let meta = CustomUserId::meta(&(), &mut registry);
assert_eq!(meta.name(), Some("MyUserId")); assert_eq!(meta.name(), Some("MyUserId"));
assert_eq!( assert_eq!(
@ -76,7 +79,7 @@ fn test_scalar_value_custom() {
#[test] #[test]
fn test_scalar_value_doc_comment() { fn test_scalar_value_doc_comment() {
let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let mut registry: Registry = Registry::new(FnvHashMap::default());
let meta = IdWithDocComment::meta(&(), &mut registry); let meta = IdWithDocComment::meta(&(), &mut registry);
assert_eq!(meta.description(), Some(&"The doc comment...".to_string())); assert_eq!(meta.description(), Some(&"The doc comment...".to_string()));
} }

View file

@ -51,12 +51,25 @@ struct EwokCustomContext {
funny: bool, funny: bool,
} }
fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S> fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>>
where
Q: GraphQLType<DefaultScalarValue, Context = C, TypeInfo = ()> + 'q,
{
RootNode::new(
query_root,
EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(),
)
}
fn schema_with_scalar<'q, S, C, Q>(
query_root: Q,
) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S>
where where
Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q, Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q,
S: ScalarValue + 'q, S: ScalarValue + 'q,
{ {
RootNode::new( RootNode::new_with_scalar_value(
query_root, query_root,
EmptyMutation::<C>::new(), EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(), EmptySubscription::<C>::new(),
@ -531,7 +544,7 @@ mod explicit_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_human() { async fn resolves_human() {
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); let schema = schema(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -544,7 +557,7 @@ mod explicit_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_droid() { async fn resolves_droid() {
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); let schema = schema(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -622,7 +635,7 @@ mod custom_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_human() { async fn resolves_human() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -635,7 +648,7 @@ mod custom_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_droid() { async fn resolves_droid() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -1054,7 +1067,7 @@ mod full_featured {
struct QueryRoot; struct QueryRoot;
#[graphql_object(context = CustomContext)] #[graphql_object(context = CustomContext, scalar = DefaultScalarValue)]
impl QueryRoot { impl QueryRoot {
fn character(&self, ctx: &CustomContext) -> Box<DynCharacter<'_, ()>> { fn character(&self, ctx: &CustomContext) -> Box<DynCharacter<'_, ()>> {
let ch: Box<DynCharacter<'_, ()>> = match ctx { let ch: Box<DynCharacter<'_, ()>> = match ctx {

View file

@ -53,12 +53,25 @@ struct EwokCustomContext {
funny: bool, funny: bool,
} }
fn schema<'q, C, S, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S> fn schema<'q, C, Q>(query_root: Q) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>>
where
Q: GraphQLType<DefaultScalarValue, Context = C, TypeInfo = ()> + 'q,
{
RootNode::new(
query_root,
EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(),
)
}
fn schema_with_scalar<'q, S, C, Q>(
query_root: Q,
) -> RootNode<'q, Q, EmptyMutation<C>, EmptySubscription<C>, S>
where where
Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q, Q: GraphQLType<S, Context = C, TypeInfo = ()> + 'q,
S: ScalarValue + 'q, S: ScalarValue + 'q,
{ {
RootNode::new( RootNode::new_with_scalar_value(
query_root, query_root,
EmptyMutation::<C>::new(), EmptyMutation::<C>::new(),
EmptySubscription::<C>::new(), EmptySubscription::<C>::new(),
@ -429,7 +442,7 @@ mod explicit_scalar {
Droid, Droid,
} }
#[graphql_object] #[graphql_object(scalar = DefaultScalarValue)]
impl QueryRoot { impl QueryRoot {
fn character(&self) -> Character { fn character(&self) -> Character {
match self { match self {
@ -460,7 +473,7 @@ mod explicit_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_human() { async fn resolves_human() {
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Human); let schema = schema(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -473,7 +486,7 @@ mod explicit_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_droid() { async fn resolves_droid() {
let schema = schema::<_, DefaultScalarValue, _>(QueryRoot::Droid); let schema = schema(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -533,7 +546,7 @@ mod custom_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_human() { async fn resolves_human() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Human); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Human);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -546,7 +559,7 @@ mod custom_scalar {
#[tokio::test] #[tokio::test]
async fn resolves_droid() { async fn resolves_droid() {
let schema = schema::<_, MyScalarValue, _>(QueryRoot::Droid); let schema = schema_with_scalar::<MyScalarValue, _, _>(QueryRoot::Droid);
assert_eq!( assert_eq!(
execute(DOC, None, &schema, &Variables::new(), &()).await, execute(DOC, None, &schema, &Variables::new(), &()).await,
@ -993,7 +1006,7 @@ mod full_featured_enum {
struct QueryRoot; struct QueryRoot;
#[graphql_object(context = CustomContext)] #[graphql_object(context = CustomContext, scalar = DefaultScalarValue)]
impl QueryRoot { impl QueryRoot {
fn character(&self, ctx: &CustomContext) -> Character<()> { fn character(&self, ctx: &CustomContext) -> Character<()> {
match ctx { match ctx {
@ -1349,7 +1362,7 @@ mod full_featured_struct {
Droid, Droid,
} }
#[graphql_object(context = Database)] #[graphql_object(context = Database, scalar = DefaultScalarValue)]
impl QueryRoot { impl QueryRoot {
fn character(&self) -> Character<()> { fn character(&self) -> Character<()> {
Character { Character {

View file

@ -1,14 +1,15 @@
use std::fmt; use std::{fmt, pin::Pin};
use futures::{stream, Stream};
use juniper::{ use juniper::{
execute, execute, graphql_object, graphql_scalar, graphql_subscription,
parser::{ParseError, ScalarToken, Spanning, Token}, parser::{ParseError, ScalarToken, Spanning, Token},
serde::de, serde::de,
EmptyMutation, FieldResult, InputValue, Object, ParseScalarResult, RootNode, ScalarValue, EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult,
Value, Variables, RootNode, ScalarValue, Value, Variables,
}; };
#[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)] #[derive(GraphQLScalarValue, Clone, Debug, PartialEq)]
pub(crate) enum MyScalarValue { pub(crate) enum MyScalarValue {
Int(i32), Int(i32),
Long(i64), Long(i64),
@ -22,42 +23,49 @@ impl ScalarValue for MyScalarValue {
fn as_int(&self) -> Option<i32> { fn as_int(&self) -> Option<i32> {
match *self { match *self {
MyScalarValue::Int(ref i) => Some(*i), Self::Int(ref i) => Some(*i),
_ => None, _ => None,
} }
} }
fn as_string(&self) -> Option<String> { fn as_string(&self) -> Option<String> {
match *self { match *self {
MyScalarValue::String(ref s) => Some(s.clone()), Self::String(ref s) => Some(s.clone()),
_ => None,
}
}
fn into_string(self) -> Option<String> {
match self {
Self::String(s) => Some(s),
_ => None, _ => None,
} }
} }
fn as_str(&self) -> Option<&str> { fn as_str(&self) -> Option<&str> {
match *self { match *self {
MyScalarValue::String(ref s) => Some(s.as_str()), Self::String(ref s) => Some(s.as_str()),
_ => None, _ => None,
} }
} }
fn as_float(&self) -> Option<f64> { fn as_float(&self) -> Option<f64> {
match *self { match *self {
MyScalarValue::Int(ref i) => Some(*i as f64), Self::Int(ref i) => Some(f64::from(*i)),
MyScalarValue::Float(ref f) => Some(*f), Self::Float(ref f) => Some(*f),
_ => None, _ => None,
} }
} }
fn as_boolean(&self) -> Option<bool> { fn as_boolean(&self) -> Option<bool> {
match *self { match *self {
MyScalarValue::Boolean(ref b) => Some(*b), Self::Boolean(ref b) => Some(*b),
_ => None, _ => None,
} }
} }
} }
#[derive(Default, Debug)] #[derive(Debug, Default)]
pub(crate) struct MyScalarValueVisitor; pub(crate) struct MyScalarValueVisitor;
impl<'de> de::Visitor<'de> for MyScalarValueVisitor { impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
@ -82,7 +90,7 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
where where
E: de::Error, E: de::Error,
{ {
if value <= i32::max_value() as i64 { if value <= i64::from(i32::max_value()) {
self.visit_i32(value as i32) self.visit_i32(value as i32)
} else { } else {
Ok(MyScalarValue::Long(value)) Ok(MyScalarValue::Long(value))
@ -132,7 +140,7 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
} }
} }
#[juniper::graphql_scalar(name = "Long")] #[graphql_scalar(name = "Long")]
impl GraphQLScalar for i64 { impl GraphQLScalar for i64 {
fn resolve(&self) -> Value { fn resolve(&self) -> Value {
Value::scalar(*self) Value::scalar(*self)
@ -158,12 +166,10 @@ impl GraphQLScalar for i64 {
struct TestType; struct TestType;
#[juniper::graphql_object( #[graphql_object(scalar = MyScalarValue)]
Scalar = MyScalarValue
)]
impl TestType { impl TestType {
fn long_field() -> i64 { fn long_field() -> i64 {
(::std::i32::MAX as i64) + 1 i64::from(i32::max_value()) + 1
} }
fn long_with_arg(long_arg: i64) -> i64 { fn long_with_arg(long_arg: i64) -> i64 {
@ -173,14 +179,10 @@ impl TestType {
struct TestSubscriptionType; struct TestSubscriptionType;
#[juniper::graphql_subscription( #[graphql_subscription(scalar = MyScalarValue)]
Scalar = MyScalarValue
)]
impl TestSubscriptionType { impl TestSubscriptionType {
async fn foo( async fn foo() -> Pin<Box<dyn Stream<Item = FieldResult<i32, MyScalarValue>> + Send>> {
) -> std::pin::Pin<Box<dyn futures::Stream<Item = FieldResult<i32, MyScalarValue>> + Send>> Box::pin(stream::empty())
{
Box::pin(futures::stream::empty())
} }
} }
@ -188,7 +190,8 @@ async fn run_variable_query<F>(query: &str, vars: Variables<MyScalarValue>, f: F
where where
F: Fn(&Object<MyScalarValue>) -> (), F: Fn(&Object<MyScalarValue>) -> (),
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new(), TestSubscriptionType); let schema =
RootNode::new_with_scalar_value(TestType, EmptyMutation::<()>::new(), TestSubscriptionType);
let (result, errs) = execute(query, None, &schema, &vars, &()) let (result, errs) = execute(query, None, &schema, &vars, &())
.await .await
@ -215,7 +218,7 @@ async fn querying_long() {
run_query("{ longField }", |result| { run_query("{ longField }", |result| {
assert_eq!( assert_eq!(
result.get_field_value("longField"), result.get_field_value("longField"),
Some(&Value::scalar((::std::i32::MAX as i64) + 1)) Some(&Value::scalar(i64::from(i32::max_value()) + 1))
); );
}) })
.await; .await;
@ -226,12 +229,12 @@ async fn querying_long_arg() {
run_query( run_query(
&format!( &format!(
"{{ longWithArg(longArg: {}) }}", "{{ longWithArg(longArg: {}) }}",
(::std::i32::MAX as i64) + 3 i64::from(i32::max_value()) + 3
), ),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("longWithArg"), result.get_field_value("longWithArg"),
Some(&Value::scalar((::std::i32::MAX as i64) + 3)) Some(&Value::scalar(i64::from(i32::max_value()) + 3))
); );
}, },
) )
@ -244,14 +247,14 @@ async fn querying_long_variable() {
"query q($test: Long!){ longWithArg(longArg: $test) }", "query q($test: Long!){ longWithArg(longArg: $test) }",
vec![( vec![(
"test".to_owned(), "test".to_owned(),
InputValue::Scalar(MyScalarValue::Long((::std::i32::MAX as i64) + 42)), InputValue::Scalar(MyScalarValue::Long(i64::from(i32::max_value()) + 42)),
)] )]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("longWithArg"), result.get_field_value("longWithArg"),
Some(&Value::scalar((::std::i32::MAX as i64) + 42)) Some(&Value::scalar(i64::from(i32::max_value()) + 42))
); );
}, },
) )
@ -260,7 +263,7 @@ async fn querying_long_variable() {
#[test] #[test]
fn deserialize_variable() { fn deserialize_variable() {
let json = format!("{{\"field\": {}}}", (::std::i32::MAX as i64) + 42); let json = format!("{{\"field\": {}}}", i64::from(i32::max_value()) + 42);
let input_value: InputValue<MyScalarValue> = serde_json::from_str(&json).unwrap(); let input_value: InputValue<MyScalarValue> = serde_json::from_str(&json).unwrap();
assert_eq!( assert_eq!(
@ -268,7 +271,7 @@ fn deserialize_variable() {
InputValue::Object(vec![( InputValue::Object(vec![(
Spanning::unlocated("field".into()), Spanning::unlocated("field".into()),
Spanning::unlocated(InputValue::Scalar(MyScalarValue::Long( Spanning::unlocated(InputValue::Scalar(MyScalarValue::Long(
(::std::i32::MAX as i64) + 42 i64::from(i32::max_value()) + 42
))) )))
)]) )])
); );

View file

@ -1,5 +1,8 @@
// Original author of this test is <https://github.com/davidpdrsn>. // Original author of this test is <https://github.com/davidpdrsn>.
use juniper::*;
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, LookAheadMethods as _, RootNode, Variables,
};
pub struct Context; pub struct Context;
@ -7,9 +10,7 @@ impl juniper::Context for Context {}
pub struct Query; pub struct Query;
#[graphql_object( #[graphql_object(context = Context)]
Context = Context
)]
impl Query { impl Query {
fn users(exec: &Executor) -> Vec<User> { fn users(exec: &Executor) -> Vec<User> {
let lh = exec.look_ahead(); let lh = exec.look_ahead();
@ -27,9 +28,7 @@ impl Query {
#[derive(Clone)] #[derive(Clone)]
pub struct User; pub struct User;
#[graphql_object( #[graphql_object(context = Context)]
Context = Context
)]
impl User { impl User {
fn id() -> i32 { fn id() -> i32 {
1 1
@ -46,7 +45,7 @@ impl Country {
} }
} }
type Schema = juniper::RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>; type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>;
#[tokio::test] #[tokio::test]
async fn users() { async fn users() {
@ -109,7 +108,7 @@ async fn both() {
EmptyMutation::<Context>::new(), EmptyMutation::<Context>::new(),
EmptySubscription::<Context>::new(), EmptySubscription::<Context>::new(),
), ),
&juniper::Variables::new(), &Variables::new(),
&ctx, &ctx,
) )
.await .await
@ -137,7 +136,7 @@ async fn both_in_different_order() {
EmptyMutation::<Context>::new(), EmptyMutation::<Context>::new(),
EmptySubscription::<Context>::new(), EmptySubscription::<Context>::new(),
), ),
&juniper::Variables::new(), &Variables::new(),
&ctx, &ctx,
) )
.await .await

View file

@ -1,9 +1,10 @@
// Original author of this test is <https://github.com/davidpdrsn>. // Original author of this test is <https://github.com/davidpdrsn>.
use juniper::*;
use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode, Variables};
struct Query; struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn users(executor: &Executor) -> Vec<User> { fn users(executor: &Executor) -> Vec<User> {
// This doesn't cause a panic // This doesn't cause a panic
@ -19,7 +20,7 @@ struct User {
country: Country, country: Country,
} }
#[juniper::graphql_object] #[graphql_object]
impl User { impl User {
fn country(&self, executor: &Executor) -> &Country { fn country(&self, executor: &Executor) -> &Country {
// This panics! // This panics!
@ -33,14 +34,14 @@ struct Country {
id: i32, id: i32,
} }
#[juniper::graphql_object] #[graphql_object]
impl Country { impl Country {
fn id(&self) -> i32 { fn id(&self) -> i32 {
self.id self.id
} }
} }
type Schema = juniper::RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>; type Schema = RootNode<'static, Query, EmptyMutation<()>, EmptySubscription<()>>;
#[tokio::test] #[tokio::test]
async fn test_lookahead_from_fragment_with_nested_type() { async fn test_lookahead_from_fragment_with_nested_type() {

View file

@ -37,7 +37,6 @@
- Added support for distinguishing between between implicit and explicit null. ([#795](https://github.com/graphql-rust/juniper/pull/795)) - Added support for distinguishing between between implicit and explicit null. ([#795](https://github.com/graphql-rust/juniper/pull/795))
- Implement `IntoFieldError` for `std::convert::Infallible`. ([#796](https://github.com/graphql-rust/juniper/pull/796)) - Implement `IntoFieldError` for `std::convert::Infallible`. ([#796](https://github.com/graphql-rust/juniper/pull/796))
## Fixes ## Fixes
@ -97,6 +96,7 @@
- Renamed the `object` proc macro to `graphql_object`. - Renamed the `object` proc macro to `graphql_object`.
- Removed the `graphql_object!` macro. Use the `#[graphql_object]` proc macro instead. - Removed the `graphql_object!` macro. Use the `#[graphql_object]` proc macro instead.
- Made `#[graphql_object]` macro to generate code generic over `ScalarValue` by default. ([#779](https://github.com/graphql-rust/juniper/pull/779))
- Renamed the `scalar` proc macro to `graphql_scalar`. - Renamed the `scalar` proc macro to `graphql_scalar`.
- Removed the `graphql_scalar!` macro. Use the `#[graphql_scalar]` proc macro instead. - Removed the `graphql_scalar!` macro. Use the `#[graphql_scalar]` proc macro instead.
@ -130,6 +130,8 @@
- Renamed `http::tests::HTTPIntegration` as `http::tests::HttpIntegration`. - Renamed `http::tests::HTTPIntegration` as `http::tests::HttpIntegration`.
- Added support for `application/graphql` POST request. - Added support for `application/graphql` POST request.
- `RootNode::new()` now returns `RootNode` parametrized with `DefaultScalarValue`. For custom `ScalarValue` use `RootNode::new_with_scalar_value()` instead. ([#779](https://github.com/graphql-rust/juniper/pull/779))
- When using `LookAheadMethods` to access child selections, children are always found using their alias if it exists rather than their name. ([#662](https://github.com/graphql-rust/juniper/pull/662)) - When using `LookAheadMethods` to access child selections, children are always found using their alias if it exists rather than their name. ([#662](https://github.com/graphql-rust/juniper/pull/662))
- These methods are also deprecated in favor of the new `LookAheadMethods::children()` method. - These methods are also deprecated in favor of the new `LookAheadMethods::children()` method.

View file

@ -216,6 +216,18 @@ impl<S> FieldError<S> {
pub fn extensions(&self) -> &Value<S> { pub fn extensions(&self) -> &Value<S> {
&self.extensions &self.extensions
} }
/// Maps the [`ScalarValue`] type of this [`FieldError`] into the specified one.
pub fn map_scalar_value<Into>(self) -> FieldError<Into>
where
S: ScalarValue,
Into: ScalarValue,
{
FieldError {
message: self.message,
extensions: self.extensions.map_scalar_value(),
}
}
} }
/// The result of resolving the value of a field of type `T` /// The result of resolving the value of a field of type `T`
@ -240,9 +252,10 @@ pub trait IntoFieldError<S = DefaultScalarValue> {
fn into_field_error(self) -> FieldError<S>; fn into_field_error(self) -> FieldError<S>;
} }
impl<S> IntoFieldError<S> for FieldError<S> { impl<S1: ScalarValue, S2: ScalarValue> IntoFieldError<S2> for FieldError<S1> {
fn into_field_error(self) -> FieldError<S> { #[inline]
self fn into_field_error(self) -> FieldError<S2> {
self.map_scalar_value()
} }
} }
@ -315,28 +328,31 @@ where
} }
} }
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'a T::Context, T), S> impl<'a, S1, S2, T, C> IntoResolvable<'a, S2, T, C> for FieldResult<(&'a T::Context, T), S1>
where where
S: ScalarValue, S1: ScalarValue,
T: GraphQLValue<S>, S2: ScalarValue,
T: GraphQLValue<S2>,
{ {
type Type = T; type Type = T;
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> { fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S2> {
self.map(Some) self.map(Some).map_err(FieldError::map_scalar_value)
} }
} }
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C> impl<'a, S1, S2, T, C> IntoResolvable<'a, S2, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>, S> for FieldResult<Option<(&'a T::Context, T)>, S1>
where where
S: ScalarValue, S1: ScalarValue,
T: GraphQLValue<S>, S2: ScalarValue,
T: GraphQLValue<S2>,
{ {
type Type = T; type Type = T;
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> { fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S2> {
self.map(|o| o.map(|(ctx, v)| (ctx, Some(v)))) self.map(|o| o.map(|(ctx, v)| (ctx, Some(v))))
.map_err(FieldError::map_scalar_value)
} }
} }

View file

@ -1,6 +1,6 @@
use crate::{EmptyMutation, RootNode, Value}; use crate::{graphql_object, EmptyMutation, EmptySubscription, GraphQLEnum, RootNode, Value};
#[derive(crate::GraphQLEnum)] #[derive(GraphQLEnum)]
enum UserKind { enum UserKind {
Admin, Admin,
User, User,
@ -14,7 +14,7 @@ struct User {
kind: UserKind, kind: UserKind,
} }
#[crate::graphql_object] #[graphql_object]
impl User { impl User {
async fn id(&self) -> i32 { async fn id(&self) -> i32 {
self.id self.id
@ -46,7 +46,7 @@ impl User {
struct Query; struct Query;
#[crate::graphql_object] #[graphql_object]
impl Query { impl Query {
fn field_sync(&self) -> &'static str { fn field_sync(&self) -> &'static str {
"field_sync" "field_sync"
@ -72,7 +72,7 @@ impl Query {
#[tokio::test] #[tokio::test]
async fn async_simple() { async fn async_simple() {
let schema = RootNode::new(Query, EmptyMutation::new(), crate::EmptySubscription::new()); let schema = RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new());
let doc = r#" let doc = r#"
query { query {
fieldSync fieldSync

View file

@ -58,7 +58,7 @@ mod field_execution {
#[tokio::test] #[tokio::test]
async fn test() { async fn test() {
let schema = RootNode::<_, _, _, crate::DefaultScalarValue>::new( let schema = RootNode::new(
DataType, DataType,
EmptyMutation::<()>::new(), EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(), EmptySubscription::<()>::new(),
@ -470,6 +470,7 @@ mod dynamic_context_switching {
use crate::{ use crate::{
executor::{Context, ExecutionError, FieldError, FieldResult}, executor::{Context, ExecutionError, FieldError, FieldResult},
graphql_object,
parser::SourcePosition, parser::SourcePosition,
schema::model::RootNode, schema::model::RootNode,
types::scalars::{EmptyMutation, EmptySubscription}, types::scalars::{EmptyMutation, EmptySubscription},
@ -491,7 +492,7 @@ mod dynamic_context_switching {
struct ItemRef; struct ItemRef;
#[crate::graphql_object(Context = OuterContext)] #[graphql_object(context = OuterContext)]
impl Schema { impl Schema {
fn item_opt(_context: &OuterContext, key: i32) -> Option<(&InnerContext, ItemRef)> { fn item_opt(_context: &OuterContext, key: i32) -> Option<(&InnerContext, ItemRef)> {
executor.context().items.get(&key).map(|c| (c, ItemRef)) executor.context().items.get(&key).map(|c| (c, ItemRef))
@ -521,7 +522,7 @@ mod dynamic_context_switching {
} }
} }
#[crate::graphql_object(Context = InnerContext)] #[graphql_object(context = InnerContext)]
impl ItemRef { impl ItemRef {
fn value(context: &InnerContext) -> String { fn value(context: &InnerContext) -> String {
context.value.clone() context.value.clone()
@ -828,6 +829,7 @@ mod dynamic_context_switching {
mod propagates_errors_to_nullable_fields { mod propagates_errors_to_nullable_fields {
use crate::{ use crate::{
executor::{ExecutionError, FieldError, FieldResult, IntoFieldError}, executor::{ExecutionError, FieldError, FieldResult, IntoFieldError},
graphql_object,
parser::SourcePosition, parser::SourcePosition,
schema::model::RootNode, schema::model::RootNode,
types::scalars::{EmptyMutation, EmptySubscription}, types::scalars::{EmptyMutation, EmptySubscription},
@ -857,7 +859,7 @@ mod propagates_errors_to_nullable_fields {
} }
} }
#[crate::graphql_object] #[graphql_object]
impl Schema { impl Schema {
fn inner() -> Inner { fn inner() -> Inner {
Inner Inner
@ -870,7 +872,7 @@ mod propagates_errors_to_nullable_fields {
} }
} }
#[crate::graphql_object] #[graphql_object]
impl Inner { impl Inner {
fn nullable_field() -> Option<Inner> { fn nullable_field() -> Option<Inner> {
Some(Inner) Some(Inner)
@ -1176,7 +1178,7 @@ mod named_operations {
#[tokio::test] #[tokio::test]
async fn uses_inline_operation_if_no_name_provided() { async fn uses_inline_operation_if_no_name_provided() {
let schema = RootNode::<_, _, _, crate::DefaultScalarValue>::new( let schema = RootNode::new(
Schema, Schema,
EmptyMutation::<()>::new(), EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(), EmptySubscription::<()>::new(),

View file

@ -7,10 +7,10 @@ use self::input_object::{NamedPublic, NamedPublicWithDescription};
use crate::{ use crate::{
executor::Variables, executor::Variables,
graphql_interface, graphql_object, graphql_interface, graphql_object, graphql_scalar,
schema::model::RootNode, schema::model::RootNode,
types::scalars::{EmptyMutation, EmptySubscription}, types::scalars::{EmptyMutation, EmptySubscription},
value::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, Value}, value::{ParseScalarResult, ParseScalarValue, ScalarValue, Value},
GraphQLEnum, GraphQLEnum,
}; };
@ -23,23 +23,23 @@ enum Sample {
struct Scalar(i32); struct Scalar(i32);
#[crate::graphql_scalar(name = "SampleScalar")] #[graphql_scalar(name = "SampleScalar")]
impl GraphQLScalar for Scalar { impl<S: ScalarValue> GraphQLScalar for Scalar {
fn resolve(&self) -> Value { fn resolve(&self) -> Value {
Value::scalar(self.0) Value::scalar(self.0)
} }
fn from_input_value(v: &InputValue) -> Option<Scalar> { fn from_input_value(v: &InputValue) -> Option<Scalar> {
v.as_scalar_value().map(|i: &i32| Scalar(*i)) v.as_scalar().and_then(ScalarValue::as_int).map(Scalar)
} }
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<i32 as ParseScalarValue>::from_str(value) <i32 as ParseScalarValue<S>>::from_str(value)
} }
} }
/// A sample interface /// A sample interface
#[graphql_interface(name = "SampleInterface", for = Root, scalar = DefaultScalarValue)] #[graphql_interface(name = "SampleInterface", for = Root)]
trait Interface { trait Interface {
/// A sample field in the interface /// A sample field in the interface
fn sample_enum(&self) -> Sample { fn sample_enum(&self) -> Sample {
@ -50,7 +50,7 @@ trait Interface {
struct Root; struct Root;
/// The root query object in the schema /// The root query object in the schema
#[graphql_object(interfaces = InterfaceValue)] #[graphql_object(impl = InterfaceValue)]
impl Root { impl Root {
fn sample_enum() -> Sample { fn sample_enum() -> Sample {
Sample::One Sample::One

View file

@ -1,11 +1,12 @@
use crate::{ use crate::{
ast::InputValue, ast::InputValue,
executor::Variables, executor::Variables,
graphql_object, graphql_scalar,
parser::SourcePosition, parser::SourcePosition,
schema::model::RootNode, schema::model::RootNode,
types::scalars::{EmptyMutation, EmptySubscription}, types::scalars::{EmptyMutation, EmptySubscription},
validation::RuleError, validation::RuleError,
value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, Value}, value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarValue, Value},
GraphQLError::ValidationError, GraphQLError::ValidationError,
GraphQLInputObject, GraphQLInputObject,
}; };
@ -13,17 +14,15 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
struct TestComplexScalar; struct TestComplexScalar;
struct TestType; #[graphql_scalar]
impl<S: ScalarValue> GraphQLScalar for TestComplexScalar {
#[crate::graphql_scalar]
impl GraphQLScalar for TestComplexScalar {
fn resolve(&self) -> Value { fn resolve(&self) -> Value {
Value::scalar(String::from("SerializedValue")) Value::scalar(String::from("SerializedValue"))
} }
fn from_input_value(v: &InputValue) -> Option<TestComplexScalar> { fn from_input_value(v: &InputValue) -> Option<TestComplexScalar> {
if let Some(s) = v.as_scalar_value::<String>() { if let Some(s) = v.as_scalar().and_then(ScalarValue::as_str) {
if *s == "SerializedValue" { if s == "SerializedValue" {
return Some(TestComplexScalar); return Some(TestComplexScalar);
} }
} }
@ -31,13 +30,12 @@ impl GraphQLScalar for TestComplexScalar {
None None
} }
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<String as ParseScalarValue>::from_str(value) <String as ParseScalarValue<S>>::from_str(value)
} }
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(scalar = DefaultScalarValue)]
struct TestInputObject { struct TestInputObject {
a: Option<String>, a: Option<String>,
b: Option<Vec<Option<String>>>, b: Option<Vec<Option<String>>>,
@ -46,7 +44,6 @@ struct TestInputObject {
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(scalar = DefaultScalarValue)]
struct TestNestedInputObject { struct TestNestedInputObject {
na: TestInputObject, na: TestInputObject,
nb: String, nb: String,
@ -64,7 +61,9 @@ struct InputWithDefaults {
a: i32, a: i32,
} }
#[crate::graphql_object] struct TestType;
#[graphql_object]
impl TestType { impl TestType {
fn field_with_object_input(input: Option<TestInputObject>) -> String { fn field_with_object_input(input: Option<TestInputObject>) -> String {
format!("{:?}", input) format!("{:?}", input)

View file

@ -5,7 +5,7 @@
use futures::Stream; use futures::Stream;
use crate::{FieldError, GraphQLValue, ScalarValue}; use crate::{FieldError, GraphQLValue, IntoFieldError, ScalarValue};
/// Trait for wrapping [`Stream`] into [`Ok`] if it's not [`Result`]. /// Trait for wrapping [`Stream`] into [`Ok`] if it's not [`Result`].
/// ///
@ -22,12 +22,12 @@ pub trait IntoFieldResult<T, S> {
impl<T, E, S> IntoFieldResult<T, S> for Result<T, E> impl<T, E, S> IntoFieldResult<T, S> for Result<T, E>
where where
T: IntoFieldResult<T, S>, T: IntoFieldResult<T, S>,
E: Into<FieldError<S>>, E: IntoFieldError<S>,
{ {
type Item = T::Item; type Item = T::Item;
fn into_result(self) -> Result<T, FieldError<S>> { fn into_result(self) -> Result<T, FieldError<S>> {
self.map_err(|e| e.into()) self.map_err(E::into_field_error)
} }
} }

View file

@ -24,7 +24,7 @@ use crate::{
#[derive(Debug)] #[derive(Debug)]
struct Root; struct Root;
#[graphql_object(interfaces = [InterfaceValue])] #[graphql_object(impl = InterfaceValue)]
impl Root { impl Root {
fn simple() -> i32 { fn simple() -> i32 {
0 0
@ -104,7 +104,7 @@ impl Root {
} }
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl Interface for Root { impl Interface for Root {
fn simple(&self) -> i32 { fn simple(&self) -> i32 {
0 0
@ -147,7 +147,7 @@ impl Interface for Root {
} }
} }
#[graphql_interface(for = Root, scalar = DefaultScalarValue)] #[graphql_interface(for = Root)]
trait Interface { trait Interface {
fn simple(&self) -> i32; fn simple(&self) -> i32;

View file

@ -29,44 +29,44 @@ impl Concrete {
} }
} }
#[graphql_interface(for = Concrete, name = "ACustomNamedInterface", scalar = DefaultScalarValue)] #[graphql_interface(for = Concrete, name = "ACustomNamedInterface")]
trait CustomName { trait CustomName {
fn simple(&self) -> i32; fn simple(&self) -> i32;
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl CustomName for Concrete { impl CustomName for Concrete {
fn simple(&self) -> i32 { fn simple(&self) -> i32 {
0 0
} }
} }
#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)] #[graphql_interface(for = Concrete)]
trait WithLifetime<'a> { trait WithLifetime<'a> {
fn simple(&self) -> i32; fn simple(&self) -> i32;
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl<'a> WithLifetime<'a> for Concrete { impl<'a> WithLifetime<'a> for Concrete {
fn simple(&self) -> i32 { fn simple(&self) -> i32 {
0 0
} }
} }
#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)] #[graphql_interface(for = Concrete)]
trait WithGenerics<T> { trait WithGenerics<T> {
fn simple(&self) -> i32; fn simple(&self) -> i32;
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl<T> WithGenerics<T> for Concrete { impl<T> WithGenerics<T> for Concrete {
fn simple(&self) -> i32 { fn simple(&self) -> i32 {
0 0
} }
} }
#[graphql_interface(for = Concrete, desc = "A description", scalar = DefaultScalarValue)] #[graphql_interface(for = Concrete, desc = "A description")]
trait Description { trait Description {
fn simple(&self) -> i32; fn simple(&self) -> i32;
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl Description for Concrete { impl Description for Concrete {
fn simple(&self) -> i32 { fn simple(&self) -> i32 {
0 0
@ -75,7 +75,7 @@ impl Description for Concrete {
struct Root; struct Root;
#[crate::graphql_object] #[graphql_object]
impl Root { impl Root {
fn custom_name() -> CustomNameValue { fn custom_name() -> CustomNameValue {
Concrete.into() Concrete.into()

View file

@ -31,13 +31,13 @@ impl Concrete {
} }
#[derive(GraphQLUnion)] #[derive(GraphQLUnion)]
#[graphql(name = "ACustomNamedUnion", scalar = DefaultScalarValue)] #[graphql(name = "ACustomNamedUnion")]
enum CustomName { enum CustomName {
Concrete(Concrete), Concrete(Concrete),
} }
#[derive(GraphQLUnion)] #[derive(GraphQLUnion)]
#[graphql(on Concrete = WithLifetime::resolve, scalar = DefaultScalarValue)] #[graphql(on Concrete = WithLifetime::resolve)]
enum WithLifetime<'a> { enum WithLifetime<'a> {
#[graphql(ignore)] #[graphql(ignore)]
Int(PhantomData<&'a i32>), Int(PhantomData<&'a i32>),
@ -54,7 +54,7 @@ impl<'a> WithLifetime<'a> {
} }
#[derive(GraphQLUnion)] #[derive(GraphQLUnion)]
#[graphql(on Concrete = WithGenerics::resolve, scalar = DefaultScalarValue)] #[graphql(on Concrete = WithGenerics::resolve)]
enum WithGenerics<T> { enum WithGenerics<T> {
#[graphql(ignore)] #[graphql(ignore)]
Generic(T), Generic(T),
@ -71,20 +71,19 @@ impl<T> WithGenerics<T> {
} }
#[derive(GraphQLUnion)] #[derive(GraphQLUnion)]
#[graphql(description = "A description", scalar = DefaultScalarValue)] #[graphql(description = "A description")]
enum DescriptionFirst { enum DescriptionFirst {
Concrete(Concrete), Concrete(Concrete),
} }
struct Root; struct Root;
// FIXME: make async work #[graphql_object]
#[crate::graphql_object(noasync)] impl Root {
impl<'a> Root {
fn custom_name() -> CustomName { fn custom_name() -> CustomName {
CustomName::Concrete(Concrete) CustomName::Concrete(Concrete)
} }
fn with_lifetime() -> WithLifetime<'a> { fn with_lifetime() -> WithLifetime<'_> {
WithLifetime::Int(PhantomData) WithLifetime::Int(PhantomData)
} }
fn with_generics() -> WithGenerics<i32> { fn with_generics() -> WithGenerics<i32> {

View file

@ -162,11 +162,11 @@ fn issue_427_panic_is_not_expected() {
} }
} }
let schema = SchemaType::new::<QueryWithoutFloat, EmptyMutation<()>, EmptySubscription<()>>( let schema = <SchemaType<DefaultScalarValue>>::new::<
&(), QueryWithoutFloat,
&(), EmptyMutation<()>,
&(), EmptySubscription<()>,
); >(&(), &(), &());
let parse_result = parse_document_source(r##"{ echo(value: 123.0) }"##, &schema); let parse_result = parse_document_source(r##"{ echo(value: 123.0) }"##, &schema);
assert_eq!( assert_eq!(

View file

@ -88,6 +88,20 @@ pub enum DirectiveLocation {
InlineFragment, InlineFragment,
} }
impl<'a, QueryT, MutationT, SubscriptionT>
RootNode<'a, QueryT, MutationT, SubscriptionT, DefaultScalarValue>
where
QueryT: GraphQLType<DefaultScalarValue, TypeInfo = ()>,
MutationT: GraphQLType<DefaultScalarValue, TypeInfo = ()>,
SubscriptionT: GraphQLType<DefaultScalarValue, TypeInfo = ()>,
{
/// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes,
/// parametrizing it with a [`DefaultScalarValue`].
pub fn new(query: QueryT, mutation: MutationT, subscription: SubscriptionT) -> Self {
Self::new_with_info(query, mutation, subscription, (), (), ())
}
}
impl<'a, QueryT, MutationT, SubscriptionT, S> RootNode<'a, QueryT, MutationT, SubscriptionT, S> impl<'a, QueryT, MutationT, SubscriptionT, S> RootNode<'a, QueryT, MutationT, SubscriptionT, S>
where where
S: ScalarValue + 'a, S: ScalarValue + 'a,
@ -95,16 +109,14 @@ where
MutationT: GraphQLType<S, TypeInfo = ()>, MutationT: GraphQLType<S, TypeInfo = ()>,
SubscriptionT: GraphQLType<S, TypeInfo = ()>, SubscriptionT: GraphQLType<S, TypeInfo = ()>,
{ {
/// Construct a new root node from query, mutation, and subscription nodes /// Constructs a new [`RootNode`] from `query`, `mutation` and `subscription` nodes,
/// /// parametrizing it with the provided [`ScalarValue`].
/// If the schema should not support mutations, use the pub fn new_with_scalar_value(
/// `new` constructor instead. query: QueryT,
pub fn new( mutation: MutationT,
query_obj: QueryT, subscription: SubscriptionT,
mutation_obj: MutationT,
subscription_obj: SubscriptionT,
) -> Self { ) -> Self {
RootNode::new_with_info(query_obj, mutation_obj, subscription_obj, (), (), ()) RootNode::new_with_info(query, mutation, subscription, (), (), ())
} }
#[cfg(feature = "schema-language")] #[cfg(feature = "schema-language")]
@ -560,19 +572,18 @@ mod test {
#[cfg(feature = "graphql-parser-integration")] #[cfg(feature = "graphql-parser-integration")]
mod graphql_parser_integration { mod graphql_parser_integration {
use crate as juniper; use crate::{graphql_object, EmptyMutation, EmptySubscription, RootNode};
use crate::{EmptyMutation, EmptySubscription};
#[test] #[test]
fn graphql_parser_doc() { fn graphql_parser_doc() {
struct Query; struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn blah() -> bool { fn blah() -> bool {
true true
} }
}; };
let schema = crate::RootNode::new( let schema = RootNode::new(
Query, Query,
EmptyMutation::<()>::new(), EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(), EmptySubscription::<()>::new(),
@ -598,10 +609,9 @@ mod test {
#[cfg(feature = "schema-language")] #[cfg(feature = "schema-language")]
mod schema_language { mod schema_language {
use crate as juniper;
use crate::{ use crate::{
EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLInputObject, GraphQLObject, graphql_object, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLInputObject,
GraphQLUnion, GraphQLObject, GraphQLUnion, RootNode,
}; };
#[test] #[test]
@ -630,7 +640,7 @@ mod test {
longitude: f64, longitude: f64,
} }
struct Query; struct Query;
#[juniper::graphql_object] #[graphql_object]
impl Query { impl Query {
fn blah() -> bool { fn blah() -> bool {
true true
@ -666,7 +676,7 @@ mod test {
} }
}; };
let schema = crate::RootNode::new( let schema = RootNode::new(
Query, Query,
EmptyMutation::<()>::new(), EmptyMutation::<()>::new(),
EmptySubscription::<()>::new(), EmptySubscription::<()>::new(),

View file

@ -2,14 +2,11 @@
use std::{collections::HashMap, pin::Pin}; use std::{collections::HashMap, pin::Pin};
use crate::{ use crate::{graphql_interface, graphql_object, graphql_subscription, Context, GraphQLEnum};
graphql_interface, graphql_object, graphql_subscription, Context, DefaultScalarValue,
GraphQLEnum,
};
pub struct Query; pub struct Query;
#[graphql_object(context = Database, scalar = DefaultScalarValue)] #[graphql_object(context = Database)]
/// The root query object of the schema /// The root query object of the schema
impl Query { impl Query {
#[graphql(arguments(id(description = "id of the human")))] #[graphql(arguments(id(description = "id of the human")))]
@ -51,7 +48,7 @@ pub enum Episode {
Jedi, Jedi,
} }
#[graphql_interface(for = [Human, Droid], context = Database, scalar = DefaultScalarValue)] #[graphql_interface(for = [Human, Droid], context = Database)]
/// A character in the Star Wars Trilogy /// A character in the Star Wars Trilogy
pub trait Character { pub trait Character {
/// The id of the character /// The id of the character
@ -105,11 +102,7 @@ impl Human {
} }
/// A humanoid creature in the Star Wars universe. /// A humanoid creature in the Star Wars universe.
#[graphql_object( #[graphql_object(context = Database, impl = CharacterValue)]
context = Database,
scalar = DefaultScalarValue,
interfaces = CharacterValue,
)]
impl Human { impl Human {
/// The id of the human /// The id of the human
fn id(&self) -> &str { fn id(&self) -> &str {
@ -137,7 +130,7 @@ impl Human {
} }
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl Character for Human { impl Character for Human {
fn id(&self) -> &str { fn id(&self) -> &str {
&self.id &self.id
@ -195,11 +188,7 @@ impl Droid {
} }
/// A mechanical creature in the Star Wars universe. /// A mechanical creature in the Star Wars universe.
#[graphql_object( #[graphql_object(context = Database, impl = CharacterValue)]
context = Database,
scalar = DefaultScalarValue,
interfaces = CharacterValue,
)]
impl Droid { impl Droid {
/// The id of the droid /// The id of the droid
fn id(&self) -> &str { fn id(&self) -> &str {
@ -227,7 +216,7 @@ impl Droid {
} }
} }
#[graphql_interface(scalar = DefaultScalarValue)] #[graphql_interface]
impl Character for Droid { impl Character for Droid {
fn id(&self) -> &str { fn id(&self) -> &str {
&self.id &self.id

View file

@ -5,6 +5,8 @@ pub const STATIC_GRAPHQL_SCHEMA_DEFINITION: &str = include_str!("starwars.graphq
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use pretty_assertions::assert_eq;
use crate::{ use crate::{
schema::model::RootNode, schema::model::RootNode,
tests::fixtures::starwars::{ tests::fixtures::starwars::{
@ -13,7 +15,6 @@ mod tests {
}, },
types::scalars::{EmptyMutation, EmptySubscription}, types::scalars::{EmptyMutation, EmptySubscription},
}; };
use pretty_assertions::assert_eq;
#[test] #[test]
fn dynamic_schema_language_matches_static() { fn dynamic_schema_language_matches_static() {

View file

@ -827,7 +827,7 @@ where
V: Visitor<'a, S> + 'a, V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
let mut root = RootNode::new(r, m, s); let mut root = RootNode::new_with_scalar_value(r, m, s);
root.schema.add_directive(DirectiveType::new( root.schema.add_directive(DirectiveType::new(
"onQuery", "onQuery",

View file

@ -1,14 +1,21 @@
mod object;
mod scalar;
use std::{
any::TypeId,
fmt::{self, Display, Formatter},
mem,
};
use crate::{ use crate::{
ast::{InputValue, ToInputValue}, ast::{InputValue, ToInputValue},
parser::Spanning, parser::Spanning,
}; };
use std::fmt::{self, Display, Formatter};
mod object;
mod scalar;
pub use self::object::Object; pub use self::{
object::Object,
pub use self::scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue}; scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue},
};
/// Serializable value returned from query and field execution. /// Serializable value returned from query and field execution.
/// ///
@ -28,15 +35,12 @@ pub enum Value<S = DefaultScalarValue> {
Object(Object<S>), Object(Object<S>),
} }
impl<S> Value<S> impl<S: ScalarValue> Value<S> {
where
S: ScalarValue,
{
// CONSTRUCTORS // CONSTRUCTORS
/// Construct a null value. /// Construct a null value.
pub fn null() -> Self { pub fn null() -> Self {
Value::Null Self::Null
} }
/// Construct an integer value. /// Construct an integer value.
@ -65,12 +69,12 @@ where
/// Construct a list value. /// Construct a list value.
pub fn list(l: Vec<Self>) -> Self { pub fn list(l: Vec<Self>) -> Self {
Value::List(l) Self::List(l)
} }
/// Construct an object value. /// Construct an object value.
pub fn object(o: Object<S>) -> Self { pub fn object(o: Object<S>) -> Self {
Value::Object(o) Self::Object(o)
} }
/// Construct a scalar value /// Construct a scalar value
@ -78,7 +82,7 @@ where
where where
T: Into<S>, T: Into<S>,
{ {
Value::Scalar(s.into()) Self::Scalar(s.into())
} }
// DISCRIMINATORS // DISCRIMINATORS
@ -86,7 +90,7 @@ where
/// Does this value represent null? /// Does this value represent null?
pub fn is_null(&self) -> bool { pub fn is_null(&self) -> bool {
match *self { match *self {
Value::Null => true, Self::Null => true,
_ => false, _ => false,
} }
} }
@ -97,16 +101,15 @@ where
&'a S: Into<Option<&'a T>>, &'a S: Into<Option<&'a T>>,
{ {
match *self { match *self {
Value::Scalar(ref s) => s.into(), Self::Scalar(ref s) => s.into(),
_ => None, _ => None,
} }
} }
/// View the underlying float value, if present. /// View the underlying float value, if present.
pub fn as_float_value(&self) -> Option<f64> pub fn as_float_value(&self) -> Option<f64> {
where {
match self { match self {
Value::Scalar(ref s) => s.as_float(), Self::Scalar(ref s) => s.as_float(),
_ => None, _ => None,
} }
} }
@ -114,7 +117,7 @@ where {
/// View the underlying object value, if present. /// View the underlying object value, if present.
pub fn as_object_value(&self) -> Option<&Object<S>> { pub fn as_object_value(&self) -> Option<&Object<S>> {
match *self { match *self {
Value::Object(ref o) => Some(o), Self::Object(ref o) => Some(o),
_ => None, _ => None,
} }
} }
@ -124,7 +127,7 @@ where {
/// Returns None if value is not an Object. /// Returns None if value is not an Object.
pub fn into_object(self) -> Option<Object<S>> { pub fn into_object(self) -> Option<Object<S>> {
match self { match self {
Value::Object(o) => Some(o), Self::Object(o) => Some(o),
_ => None, _ => None,
} }
} }
@ -132,7 +135,7 @@ where {
/// Mutable view into the underlying object value, if present. /// Mutable view into the underlying object value, if present.
pub fn as_mut_object_value(&mut self) -> Option<&mut Object<S>> { pub fn as_mut_object_value(&mut self) -> Option<&mut Object<S>> {
match *self { match *self {
Value::Object(ref mut o) => Some(o), Self::Object(ref mut o) => Some(o),
_ => None, _ => None,
} }
} }
@ -140,7 +143,7 @@ where {
/// View the underlying list value, if present. /// View the underlying list value, if present.
pub fn as_list_value(&self) -> Option<&Vec<Self>> { pub fn as_list_value(&self) -> Option<&Vec<Self>> {
match *self { match *self {
Value::List(ref l) => Some(l), Self::List(ref l) => Some(l),
_ => None, _ => None,
} }
} }
@ -148,7 +151,7 @@ where {
/// View the underlying scalar value, if present /// View the underlying scalar value, if present
pub fn as_scalar(&self) -> Option<&S> { pub fn as_scalar(&self) -> Option<&S> {
match *self { match *self {
Value::Scalar(ref s) => Some(s), Self::Scalar(ref s) => Some(s),
_ => None, _ => None,
} }
} }
@ -160,6 +163,27 @@ where {
{ {
self.as_scalar_value::<String>().map(|s| s as &str) self.as_scalar_value::<String>().map(|s| s as &str)
} }
/// Maps the [`ScalarValue`] type of this [`Value`] into the specified one.
pub fn map_scalar_value<Into: ScalarValue>(self) -> Value<Into> {
if TypeId::of::<Into>() == TypeId::of::<S>() {
// This is totally safe, because we're transmuting the value into itself,
// so no invariants may change and we're just satisfying the type checker.
let val = mem::ManuallyDrop::new(self);
unsafe { mem::transmute_copy(&*val) }
} else {
match self {
Self::Null => Value::Null,
Self::Scalar(s) => Value::Scalar(s.into_another()),
Self::List(l) => Value::List(l.into_iter().map(Value::map_scalar_value).collect()),
Self::Object(o) => Value::Object(
o.into_iter()
.map(|(k, v)| (k, v.map_scalar_value()))
.collect(),
),
}
}
}
} }
impl<S: ScalarValue> ToInputValue<S> for Value<S> { impl<S: ScalarValue> ToInputValue<S> for Value<S> {

View file

@ -53,36 +53,43 @@ pub trait ParseScalarValue<S = DefaultScalarValue> {
/// ///
/// fn as_int(&self) -> Option<i32> { /// fn as_int(&self) -> Option<i32> {
/// match *self { /// match *self {
/// MyScalarValue::Int(ref i) => Some(*i), /// Self::Int(ref i) => Some(*i),
/// _ => None, /// _ => None,
/// } /// }
/// } /// }
/// ///
/// fn as_string(&self) -> Option<String> { /// fn as_string(&self) -> Option<String> {
/// match *self { /// match *self {
/// MyScalarValue::String(ref s) => Some(s.clone()), /// Self::String(ref s) => Some(s.clone()),
/// _ => None,
/// }
/// }
///
/// fn into_string(self) -> Option<String> {
/// match self {
/// Self::String(s) => Some(s),
/// _ => None, /// _ => None,
/// } /// }
/// } /// }
/// ///
/// fn as_str(&self) -> Option<&str> { /// fn as_str(&self) -> Option<&str> {
/// match *self { /// match *self {
/// MyScalarValue::String(ref s) => Some(s.as_str()), /// Self::String(ref s) => Some(s.as_str()),
/// _ => None, /// _ => None,
/// } /// }
/// } /// }
/// ///
/// fn as_float(&self) -> Option<f64> { /// fn as_float(&self) -> Option<f64> {
/// match *self { /// match *self {
/// MyScalarValue::Int(ref i) => Some(*i as f64), /// Self::Int(ref i) => Some(*i as f64),
/// MyScalarValue::Float(ref f) => Some(*f), /// Self::Float(ref f) => Some(*f),
/// _ => None, /// _ => None,
/// } /// }
/// } /// }
/// ///
/// fn as_boolean(&self) -> Option<bool> { /// fn as_boolean(&self) -> Option<bool> {
/// match *self { /// match *self {
/// MyScalarValue::Boolean(ref b) => Some(*b), /// Self::Boolean(ref b) => Some(*b),
/// _ => None, /// _ => None,
/// } /// }
/// } /// }
@ -175,6 +182,7 @@ pub trait ScalarValue:
+ From<bool> + From<bool>
+ From<i32> + From<i32>
+ From<f64> + From<f64>
+ 'static
{ {
/// Serde visitor used to deserialize this scalar value /// Serde visitor used to deserialize this scalar value
type Visitor: for<'de> de::Visitor<'de, Value = Self> + Default; type Visitor: for<'de> de::Visitor<'de, Value = Self> + Default;
@ -205,12 +213,18 @@ pub trait ScalarValue:
/// types with 32 bit or less to an integer if requested. /// types with 32 bit or less to an integer if requested.
fn as_int(&self) -> Option<i32>; fn as_int(&self) -> Option<i32>;
/// Convert the given scalar value into a string value /// Represents this [`ScalarValue`] a [`String`] value.
/// ///
/// This function is used for implementing `GraphQLValue` for `String` for all /// This function is used for implementing `GraphQLValue` for `String` for all
/// scalar values /// scalar values
fn as_string(&self) -> Option<String>; fn as_string(&self) -> Option<String>;
/// Converts this [`ScalarValue`] into a [`String`] value.
///
/// Same as [`ScalarValue::as_string`], but takes ownership, so allows to omit redundant
/// cloning.
fn into_string(self) -> Option<String>;
/// Convert the given scalar value into a string value /// Convert the given scalar value into a string value
/// ///
/// This function is used for implementing `GraphQLValue` for `String` for all /// This function is used for implementing `GraphQLValue` for `String` for all
@ -230,6 +244,21 @@ pub trait ScalarValue:
/// This function is used for implementing `GraphQLValue` for `bool` for all /// This function is used for implementing `GraphQLValue` for `bool` for all
/// scalar values. /// scalar values.
fn as_boolean(&self) -> Option<bool>; fn as_boolean(&self) -> Option<bool>;
/// Converts this [`ScalarValue`] into another one.
fn into_another<S: ScalarValue>(self) -> S {
if let Some(i) = self.as_int() {
S::from(i)
} else if let Some(f) = self.as_float() {
S::from(f)
} else if let Some(b) = self.as_boolean() {
S::from(b)
} else if let Some(s) = self.into_string() {
S::from(s)
} else {
unreachable!("`ScalarValue` must represent at least one of the GraphQL spec types")
}
}
} }
/// The default scalar value representation in juniper /// The default scalar value representation in juniper
@ -249,44 +278,60 @@ impl ScalarValue for DefaultScalarValue {
fn as_int(&self) -> Option<i32> { fn as_int(&self) -> Option<i32> {
match *self { match *self {
DefaultScalarValue::Int(ref i) => Some(*i), Self::Int(ref i) => Some(*i),
_ => None, _ => None,
} }
} }
fn as_float(&self) -> Option<f64> { fn as_float(&self) -> Option<f64> {
match *self { match *self {
DefaultScalarValue::Int(ref i) => Some(*i as f64), Self::Int(ref i) => Some(*i as f64),
DefaultScalarValue::Float(ref f) => Some(*f), Self::Float(ref f) => Some(*f),
_ => None, _ => None,
} }
} }
fn as_str(&self) -> Option<&str> { fn as_str(&self) -> Option<&str> {
match *self { match *self {
DefaultScalarValue::String(ref s) => Some(s.as_str()), Self::String(ref s) => Some(s.as_str()),
_ => None, _ => None,
} }
} }
fn as_string(&self) -> Option<String> { fn as_string(&self) -> Option<String> {
match *self { match *self {
DefaultScalarValue::String(ref s) => Some(s.clone()), Self::String(ref s) => Some(s.clone()),
_ => None,
}
}
fn into_string(self) -> Option<String> {
match self {
Self::String(s) => Some(s),
_ => None, _ => None,
} }
} }
fn as_boolean(&self) -> Option<bool> { fn as_boolean(&self) -> Option<bool> {
match *self { match *self {
DefaultScalarValue::Boolean(ref b) => Some(*b), Self::Boolean(ref b) => Some(*b),
_ => None, _ => None,
} }
} }
fn into_another<S: ScalarValue>(self) -> S {
match self {
Self::Int(i) => S::from(i),
Self::Float(f) => S::from(f),
Self::String(s) => S::from(s),
Self::Boolean(b) => S::from(b),
}
}
} }
impl<'a> From<&'a str> for DefaultScalarValue { impl<'a> From<&'a str> for DefaultScalarValue {
fn from(s: &'a str) -> Self { fn from(s: &'a str) -> Self {
DefaultScalarValue::String(s.into()) Self::String(s.into())
} }
} }

View file

@ -481,7 +481,6 @@ pub mod subscriptions {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use actix_web::{dev::ServiceResponse, http, http::header::CONTENT_TYPE, test, App}; use actix_web::{dev::ServiceResponse, http, http::header::CONTENT_TYPE, test, App};
use juniper::{ use juniper::{
futures::stream::StreamExt, futures::stream::StreamExt,
@ -490,6 +489,8 @@ mod tests {
EmptyMutation, EmptySubscription, RootNode, EmptyMutation, EmptySubscription, RootNode,
}; };
use super::*;
type Schema = type Schema =
juniper::RootNode<'static, Query, EmptyMutation<Database>, EmptySubscription<Database>>; juniper::RootNode<'static, Query, EmptyMutation<Database>, EmptySubscription<Database>>;
@ -713,7 +714,7 @@ mod tests {
impl TestActixWebIntegration { impl TestActixWebIntegration {
fn make_request(&self, req: test::TestRequest) -> TestResponse { fn make_request(&self, req: test::TestRequest) -> TestResponse {
actix_web::rt::System::new("request").block_on(async move { actix_web::rt::System::new("request").block_on(async move {
let schema = RootNode::new( let schema = Schema::new(
Query, Query,
EmptyMutation::<Database>::new(), EmptyMutation::<Database>::new(),
EmptySubscription::<Database>::new(), EmptySubscription::<Database>::new(),

View file

@ -1,6 +1,6 @@
use juniper::{ use juniper::{
graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, ExecutionError, graphql_object, DefaultScalarValue, EmptyMutation, EmptySubscription, ExecutionError,
FieldError, GraphQLEnum, Value, Variables, FieldError, GraphQLEnum, GraphQLObject, RootNode, Value, Variables,
}; };
pub type QueryResult = Result< pub type QueryResult = Result<
@ -37,7 +37,7 @@ pub enum UserKind {
Guest, Guest,
} }
#[derive(juniper::GraphQLObject)] #[derive(GraphQLObject)]
pub struct User { pub struct User {
pub id: i32, pub id: i32,
pub kind: UserKind, pub kind: UserKind,
@ -60,7 +60,7 @@ impl User {
pub struct Query; pub struct Query;
#[graphql_object(Context = Context)] #[graphql_object(context = Context)]
impl Query { impl Query {
fn user_sync_instant(id: i32) -> Result<User, FieldError> { fn user_sync_instant(id: i32) -> Result<User, FieldError> {
Ok(User::new(id)) Ok(User::new(id))
@ -89,9 +89,9 @@ impl Query {
} }
} }
pub fn new_schema( pub fn new_schema() -> RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>
) -> juniper::RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>> { {
juniper::RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new()) RootNode::new(Query, EmptyMutation::new(), EmptySubscription::new())
} }
pub fn execute_sync(query: &str, vars: Variables) -> QueryResult { pub fn execute_sync(query: &str, vars: Variables) -> QueryResult {

View file

@ -223,7 +223,7 @@ fn create(
.map(SpanContainer::into_inner) .map(SpanContainer::into_inner)
.collect(), .collect(),
include_type_generics: false, include_type_generics: false,
generic_scalar: false, generic_scalar: true,
no_async: _impl.attrs.no_async.is_some(), no_async: _impl.attrs.no_async.is_some(),
}; };

View file

@ -1232,11 +1232,10 @@ impl GraphQLTypeDefiniton {
// No custom scalar specified, but always generic specified. // No custom scalar specified, but always generic specified.
// Therefore we inject the generic scalar. // Therefore we inject the generic scalar.
generics.params.push(parse_quote!(__S));
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
// Insert ScalarValue constraint. // Insert ScalarValue constraint.
where_clause generics.params.push(parse_quote!(__S));
generics
.make_where_clause()
.predicates .predicates
.push(parse_quote!(__S: ::juniper::ScalarValue)); .push(parse_quote!(__S: ::juniper::ScalarValue));
} }
@ -1248,6 +1247,15 @@ impl GraphQLTypeDefiniton {
}; };
let (impl_generics, _, where_clause) = generics.split_for_impl(); let (impl_generics, _, where_clause) = generics.split_for_impl();
let mut generics_with_send_sync = generics.clone();
if self.scalar.is_none() && self.generic_scalar {
generics_with_send_sync
.make_where_clause()
.predicates
.push(parse_quote!(__S: Send + Sync));
}
let (_, _, where_clause_with_send_sync) = generics_with_send_sync.split_for_impl();
let resolve_matches_async = self.fields.iter().filter(|field| field.is_async).map( let resolve_matches_async = self.fields.iter().filter(|field| field.is_async).map(
|field| { |field| {
let name = &field.name; let name = &field.name;
@ -1378,7 +1386,7 @@ impl GraphQLTypeDefiniton {
let subscription_implementation = quote!( let subscription_implementation = quote!(
impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens
#where_clause #where_clause_with_send_sync
{ {
#[allow(unused_variables)] #[allow(unused_variables)]
fn resolve_field_into_stream< fn resolve_field_into_stream<

View file

@ -3,7 +3,7 @@ use std::env;
use iron::prelude::*; use iron::prelude::*;
use juniper::{ use juniper::{
tests::fixtures::starwars::schema::{Database, Query}, tests::fixtures::starwars::schema::{Database, Query},
EmptyMutation, EmptySubscription, DefaultScalarValue, EmptyMutation, EmptySubscription,
}; };
use juniper_iron::{GraphQLHandler, GraphiQLHandler}; use juniper_iron::{GraphQLHandler, GraphiQLHandler};
use logger::Logger; use logger::Logger;
@ -16,7 +16,7 @@ fn context_factory(_: &mut Request) -> IronResult<Database> {
fn main() { fn main() {
let mut mount = Mount::new(); let mut mount = Mount::new();
let graphql_endpoint = GraphQLHandler::new( let graphql_endpoint = <GraphQLHandler<_, _, _, _, _, DefaultScalarValue>>::new(
context_factory, context_factory,
Query, Query,
EmptyMutation::<Database>::new(), EmptyMutation::<Database>::new(),

View file

@ -34,7 +34,7 @@ use juniper::{Context, EmptyMutation, EmptySubscription};
# struct QueryRoot; # struct QueryRoot;
# struct Database { users: HashMap<String, User> } # struct Database { users: HashMap<String, User> }
# #
# #[juniper::graphql_object( Context = Database )] # #[juniper::graphql_object(context = Database)]
# impl User { # impl User {
# fn id(&self) -> FieldResult<&String> { # fn id(&self) -> FieldResult<&String> {
# Ok(&self.id) # Ok(&self.id)
@ -51,7 +51,7 @@ use juniper::{Context, EmptyMutation, EmptySubscription};
# } # }
# } # }
# #
# #[juniper::graphql_object( Context = Database )] # #[juniper::graphql_object(context = Database, scalar = juniper::DefaultScalarValue)]
# impl QueryRoot { # impl QueryRoot {
# fn user(context: &Database, id: String) -> FieldResult<Option<&User>> { # fn user(context: &Database, id: String) -> FieldResult<Option<&User>> {
# Ok(executor.context().users.get(&id)) # Ok(executor.context().users.get(&id))
@ -220,7 +220,7 @@ where
) -> Self { ) -> Self {
GraphQLHandler { GraphQLHandler {
context_factory, context_factory,
root_node: RootNode::new(query, mutation, subscription), root_node: RootNode::new_with_scalar_value(query, mutation, subscription),
} }
} }
@ -412,7 +412,7 @@ mod tests {
use juniper::{ use juniper::{
http::tests as http_tests, http::tests as http_tests,
tests::fixtures::starwars::schema::{Database, Query}, tests::fixtures::starwars::schema::{Database, Query},
EmptyMutation, EmptySubscription, DefaultScalarValue, EmptyMutation, EmptySubscription,
}; };
use super::GraphQLHandler; use super::GraphQLHandler;
@ -511,7 +511,7 @@ mod tests {
} }
fn make_handler() -> Box<dyn Handler> { fn make_handler() -> Box<dyn Handler> {
Box::new(GraphQLHandler::new( Box::new(<GraphQLHandler<_, _, _, _, _, DefaultScalarValue>>::new(
context_factory, context_factory,
Query, Query,
EmptyMutation::<Database>::new(), EmptyMutation::<Database>::new(),

View file

@ -64,7 +64,7 @@ use warp::{body, filters::BoxedFilter, http, query, Filter};
/// ``` /// ```
/// # use std::sync::Arc; /// # use std::sync::Arc;
/// # use warp::Filter; /// # use warp::Filter;
/// # use juniper::{EmptyMutation, EmptySubscription, RootNode}; /// # use juniper::{graphql_object, EmptyMutation, EmptySubscription, RootNode};
/// # use juniper_warp::make_graphql_filter; /// # use juniper_warp::make_graphql_filter;
/// # /// #
/// type UserId = String; /// type UserId = String;
@ -74,9 +74,7 @@ use warp::{body, filters::BoxedFilter, http, query, Filter};
/// ///
/// struct QueryRoot; /// struct QueryRoot;
/// ///
/// #[juniper::graphql_object( /// #[graphql_object(context = ExampleContext)]
/// Context = ExampleContext
/// )]
/// impl QueryRoot { /// impl QueryRoot {
/// fn say_hello(context: &ExampleContext) -> String { /// fn say_hello(context: &ExampleContext) -> String {
/// format!( /// format!(
@ -683,18 +681,18 @@ mod tests {
#[cfg(test)] #[cfg(test)]
mod tests_http_harness { mod tests_http_harness {
use super::*;
use juniper::{ use juniper::{
http::tests::{run_http_test_suite, HttpIntegration, TestResponse}, http::tests::{run_http_test_suite, HttpIntegration, TestResponse},
tests::fixtures::starwars::schema::{Database, Query}, tests::fixtures::starwars::schema::{Database, Query},
EmptyMutation, EmptySubscription, RootNode, EmptyMutation, EmptySubscription, RootNode,
}; };
use warp::{ use warp::{
self,
filters::{path, BoxedFilter}, filters::{path, BoxedFilter},
Filter, Filter,
}; };
use super::*;
struct TestWarpIntegration { struct TestWarpIntegration {
filter: BoxedFilter<(http::Response<Vec<u8>>,)>, filter: BoxedFilter<(http::Response<Vec<u8>>,)>,
} }