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)]
# extern crate juniper;
# extern crate serde_json;
use juniper::{EmptyMutation, EmptySubscription, FieldResult, IntrospectionFormat};
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
GraphQLObject, IntrospectionFormat,
};
// Define our schema.
#[derive(juniper::GraphQLObject)]
#[derive(GraphQLObject)]
struct Example {
id: String,
}
@ -47,9 +50,7 @@ impl juniper::Context for Context {}
struct Query;
#[juniper::graphql_object(
Context = Context,
)]
#[graphql_object(context = Context)]
impl Query {
fn example(id: String) -> FieldResult<Example> {
unimplemented!()

View file

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

View file

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

View file

@ -28,8 +28,12 @@ resolvers, which you will use for the `Query` and `Mutation` roots.
```rust
# #![allow(unused_variables)]
# extern crate juniper;
use juniper::{FieldResult, EmptySubscription};
# use std::fmt::Display;
use juniper::{
graphql_object, EmptySubscription, FieldResult, GraphQLEnum,
GraphQLInputObject, GraphQLObject, ScalarValue,
};
#
# struct DatabasePool;
# impl 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("")? }
# }
#[derive(juniper::GraphQLEnum)]
#[derive(GraphQLEnum)]
enum Episode {
NewHope,
Empire,
Jedi,
}
#[derive(juniper::GraphQLObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")]
#[derive(GraphQLObject)]
#[graphql(description = "A humanoid creature in the Star Wars universe")]
struct Human {
id: String,
name: String,
@ -55,8 +59,8 @@ struct Human {
// There is also a custom derive for mapping GraphQL input objects.
#[derive(juniper::GraphQLInputObject)]
#[graphql(description="A humanoid creature in the Star Wars universe")]
#[derive(GraphQLInputObject)]
#[graphql(description = "A humanoid creature in the Star Wars universe")]
struct NewHuman {
name: String,
appears_in: Vec<Episode>,
@ -78,14 +82,13 @@ impl juniper::Context for Context {}
struct Query;
#[juniper::graphql_object(
#[graphql_object(
// Here we specify the context type for the object.
// We need to do this in every type that
// needs access to the context.
Context = Context,
context = Context,
)]
impl Query {
fn apiVersion() -> &str {
"1.0"
}
@ -109,14 +112,18 @@ impl Query {
struct Mutation;
#[juniper::graphql_object(
Context = Context,
)]
impl Mutation {
#[graphql_object(
context = Context,
fn createHuman(context: &Context, new_human: NewHuman) -> FieldResult<Human> {
let db = context.pool.get_connection()?;
let human: Human = db.insert_human(&new_human)?;
// If we need to use `ScalarValue` parametrization explicitly somewhere
// in the object definition (like here in `FieldResult`), we should
// 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)
}
}
@ -124,7 +131,7 @@ impl Mutation {
// A root schema consists of a query, a mutation, and a subscription.
// Request queries can be executed against a RootNode.
type Schema = juniper::RootNode<'static, Query, Mutation, EmptySubscription<Context>>;
#
# fn main() {
# let _ = Schema::new(Query, Mutation{}, EmptySubscription::new());
# }
@ -143,10 +150,12 @@ You can invoke `juniper::execute` directly to run a GraphQL query:
```rust
# // Only needed due to 2018 edition because the macro is not accessible.
# #[macro_use] extern crate juniper;
use juniper::{FieldResult, Variables, EmptyMutation, EmptySubscription};
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult,
GraphQLEnum, Variables,
};
#[derive(juniper::GraphQLEnum, Clone, Copy)]
#[derive(GraphQLEnum, Clone, Copy)]
enum Episode {
NewHope,
Empire,
@ -160,16 +169,13 @@ impl juniper::Context for Ctx {}
struct Query;
#[juniper::graphql_object(
Context = Ctx,
)]
#[graphql_object(context = Ctx)]
impl Query {
fn favoriteEpisode(context: &Ctx) -> FieldResult<Episode> {
Ok(context.0)
}
}
// A root schema consists of a query, a mutation, and a subscription.
// Request queries can be executed against a RootNode.
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
# #![allow(unused_variables)]
# extern crate juniper;
# use juniper::FieldResult;
# #[derive(juniper::GraphQLObject)] struct User { name: String }
# use juniper::{graphql_object, FieldResult, GraphQLObject};
# #[derive(GraphQLObject)] struct User { name: String }
struct Root;
#[juniper::graphql_object]
#[graphql_object]
impl Root {
fn userWithUsername(username: String) -> FieldResult<Option<User>> {
// Look up user in database...
# unimplemented!()
# unimplemented!()
}
}
#
# fn main() { }
```
@ -47,18 +47,18 @@ that performs some mutating side-effect such as updating a database.
```rust
# #![allow(unused_variables)]
# extern crate juniper;
# use juniper::FieldResult;
# #[derive(juniper::GraphQLObject)] struct User { name: String }
# use juniper::{graphql_object, FieldResult, GraphQLObject};
# #[derive(GraphQLObject)] struct User { name: String }
struct Mutations;
#[juniper::graphql_object]
#[graphql_object]
impl Mutations {
fn signUpUser(name: String, email: String) -> FieldResult<User> {
// Validate inputs and save user in database...
# unimplemented!()
# unimplemented!()
}
}
#
# fn main() { }
```
@ -68,11 +68,13 @@ Many tools in the GraphQL ecosystem require the schema to be defined in the [Gra
```rust
# extern crate juniper;
use juniper::{FieldResult, EmptyMutation, EmptySubscription, RootNode};
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldResult, RootNode,
};
struct Query;
#[juniper::graphql_object]
#[graphql_object]
impl Query {
fn hello(&self) -> FieldResult<&str> {
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
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
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
@ -12,13 +12,14 @@ macro:
```rust
# #![allow(dead_code)]
# extern crate juniper;
# use juniper::graphql_object;
#
struct Person {
name: String,
age: i32,
}
#[juniper::graphql_object]
#[graphql_object]
impl Person {
fn name(&self) -> &str {
self.name.as_str()
@ -36,7 +37,7 @@ impl Person {
// [...]
}
}
#
# fn main() { }
```
@ -46,7 +47,9 @@ field resolver. With this syntax, fields can also take arguments:
```rust
# extern crate juniper;
#[derive(juniper::GraphQLObject)]
# use juniper::{graphql_object, GraphQLObject};
#
#[derive(GraphQLObject)]
struct Person {
name: String,
age: i32,
@ -56,14 +59,14 @@ struct House {
inhabitants: Vec<Person>,
}
#[juniper::graphql_object]
#[graphql_object]
impl House {
// Creates the field inhabitantWithName(name), returning a nullable person
fn inhabitant_with_name(&self, name: String) -> Option<&Person> {
self.inhabitants.iter().find(|p| p.name == name)
}
}
#
# fn main() {}
```
@ -79,20 +82,20 @@ the field. Also, the type name can be changed with an alias:
```rust
# extern crate juniper;
struct Person {
}
# use juniper::graphql_object;
#
struct Person;
/// 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.
name = "PersonObject",
// You can also specify a description here, which will overwrite
// a doc comment description.
description = "...",
)]
impl Person {
/// A doc comment on the field will also be used for GraphQL.
#[graphql(
// Or provide a description here.
@ -103,9 +106,7 @@ impl Person {
}
// Fields can also be renamed if required.
#[graphql(
name = "myCustomFieldName",
)]
#[graphql(name = "myCustomFieldName")]
fn renamed_field() -> bool {
true
}
@ -122,7 +123,7 @@ impl Person {
true
}
}
#
# fn main() { }
```
@ -137,10 +138,11 @@ This will become better once the [Rust RFC 2565](https://github.com/rust-lang/ru
```rust
# extern crate juniper;
# use juniper::graphql_object;
#
struct Person {}
#[juniper::graphql_object]
#[graphql_object]
impl Person {
#[graphql(
arguments(
@ -160,7 +162,7 @@ impl Person {
format!("{} {}", arg1, arg2)
}
}
#
# 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 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).

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@ use actix_cors::Cors;
use actix_web::{middleware, web, App, Error, HttpRequest, HttpResponse, HttpServer};
use juniper::{
graphql_object, graphql_subscription,
tests::fixtures::starwars::schema::{Character as _, Database, Query},
DefaultScalarValue, EmptyMutation, FieldError, RootNode,
};
@ -37,7 +38,7 @@ struct RandomHuman {
}
// TODO: remove this when async interfaces are merged
#[juniper::graphql_object(Context = Database)]
#[graphql_object(context = Database)]
impl RandomHuman {
fn id(&self) -> &str {
&self.id
@ -51,7 +52,7 @@ impl RandomHuman {
type RandomHumanStream =
Pin<Box<dyn futures::Stream<Item = Result<RandomHuman, FieldError>> + Send>>;
#[juniper::graphql_subscription(Context = Database)]
#[graphql_subscription(context = Database)]
impl Subscription {
#[graphql(
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 juniper::{
http::GraphQLRequest, DefaultScalarValue, EmptyMutation, FieldError, RootNode,
SubscriptionCoordinator,
graphql_object, graphql_subscription, http::GraphQLRequest, DefaultScalarValue, EmptyMutation,
FieldError, RootNode, SubscriptionCoordinator,
};
use juniper_subscriptions::Coordinator;
@ -22,7 +22,7 @@ impl Database {
pub struct Query;
#[juniper::graphql_object(Context = Database)]
#[graphql_object(context = Database)]
impl Query {
fn hello_world() -> &str {
"Hello World!"
@ -33,7 +33,7 @@ pub struct Subscription;
type StringStream = Pin<Box<dyn Stream<Item = Result<String, FieldError>> + Send>>;
#[juniper::graphql_subscription(Context = Database)]
#[graphql_subscription(context = Database)]
impl Subscription {
async fn hello_world() -> StringStream {
let stream =

View file

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

View file

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

View file

@ -1,7 +1,9 @@
#[cfg(test)]
use juniper::{graphql_value, EmptyMutation, EmptySubscription, GraphQLError, RootNode, Value};
use juniper::{
graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLEnum, GraphQLError,
RootNode, Value,
};
#[derive(juniper::GraphQLEnum)]
#[derive(GraphQLEnum)]
enum UserKind {
Admin,
User,
@ -15,7 +17,7 @@ struct User {
kind: UserKind,
}
#[juniper::graphql_object]
#[graphql_object]
impl User {
async fn id(&self) -> i32 {
self.id
@ -47,7 +49,7 @@ impl User {
struct Query;
#[juniper::graphql_object]
#[graphql_object]
impl Query {
fn field_sync(&self) -> &'static str {
"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
|
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: 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<__S>` is not satisfied
--> $DIR/impl_argument_no_object.rs:8:1
|
8 | #[juniper::graphql_object]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromInputValue` is not implemented for `Obj`
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ 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
--> $DIR/impl_argument_no_object.rs:8:1
|
8 | #[juniper::graphql_object]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ 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)

View file

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

View file

@ -1,8 +1,7 @@
use fnv::FnvHashMap;
use juniper::{
marker, DefaultScalarValue, FromInputValue, GraphQLInputObject, GraphQLType, GraphQLValue,
InputValue, ToInputValue,
InputValue, Registry, ToInputValue,
};
#[derive(GraphQLInputObject, Debug, PartialEq)]
@ -74,7 +73,7 @@ impl<'a> GraphQLType<DefaultScalarValue> for &'a Fake {
fn name(_: &()) -> Option<&'static str> {
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
DefaultScalarValue: 'r,
{
@ -113,7 +112,7 @@ fn test_derived_input_object() {
);
// 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);
assert_eq!(meta.name(), Some("MyInput"));
assert_eq!(meta.description(), Some(&"input descr".to_string()));
@ -172,14 +171,14 @@ fn test_derived_input_object() {
#[test]
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);
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
}
#[test]
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);
assert_eq!(
meta.description(),
@ -189,7 +188,7 @@ fn test_multi_doc_comment() {
#[test]
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);
assert_eq!(meta.description(), Some(&"obj override".to_string()));
}

View file

@ -1,20 +1,11 @@
#[cfg(test)]
use fnv::FnvHashMap;
#[cfg(test)]
use juniper::Object;
use juniper::{DefaultScalarValue, GraphQLObject};
#[cfg(test)]
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)]
#[graphql(
name = "MyObj",
description = "obj descr",
scalar = DefaultScalarValue
)]
#[graphql(name = "MyObj", description = "obj descr")]
struct Obj {
regular_field: bool,
#[graphql(
@ -26,14 +17,10 @@ struct Obj {
}
#[derive(GraphQLObject, Debug, PartialEq)]
#[graphql(scalar = DefaultScalarValue)]
struct Nested {
obj: Obj,
}
struct Query;
struct NoRenameQuery;
/// Object comment.
#[derive(GraphQLObject, Debug, PartialEq)]
struct DocComment {
@ -79,16 +66,19 @@ struct NoRenameObj {
one_field: bool,
another_field: i32,
}
struct Context;
impl juniper::Context for Context {}
#[derive(GraphQLObject, Debug)]
#[graphql(Context = Context)]
#[graphql(context = Context)]
struct WithCustomContext {
a: bool,
}
#[juniper::graphql_object]
struct Query;
#[graphql_object]
impl Query {
fn obj() -> Obj {
Obj {
@ -139,7 +129,9 @@ impl Query {
}
}
#[juniper::graphql_object(rename = "none")]
struct NoRenameQuery;
#[graphql_object(rename = "none")]
impl NoRenameQuery {
fn obj() -> Obj {
Obj {
@ -158,7 +150,7 @@ impl NoRenameQuery {
#[tokio::test]
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);
assert_eq!(meta.description(), Some(&"Object comment.".to_string()));
@ -173,7 +165,7 @@ async fn test_doc_comment_simple() {
#[tokio::test]
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);
assert_eq!(
meta.description(),
@ -191,7 +183,7 @@ async fn test_multi_doc_comment() {
#[tokio::test]
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);
assert_eq!(meta.description(), Some(&"obj override".to_string()));
@ -204,98 +196,6 @@ async fn test_doc_comment_override() {
.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]
async fn test_derived_object() {
assert_eq!(
@ -304,7 +204,7 @@ async fn test_derived_object() {
);
// 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);
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(
object_name: &str,
object_description: &Value,
@ -475,7 +466,6 @@ async fn check_descriptions(
.await;
}
#[cfg(test)]
async fn run_type_info_query<F>(doc: &str, f: F)
where
F: Fn((&Object<DefaultScalarValue>, &Vec<Value>)) -> (),

View file

@ -1,12 +1,11 @@
#[cfg(test)]
use juniper::{
self, execute, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject, RootNode,
Value, Variables,
execute, graphql_object, graphql_value, EmptyMutation, EmptySubscription, GraphQLInputObject,
RootNode, Value, Variables,
};
pub struct Query;
#[juniper::graphql_object]
#[graphql_object]
impl Query {
fn r#type(r#fn: MyInputType) -> Vec<String> {
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 {
let schema = RootNode::new(
Query,

View file

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

View file

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

View file

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

View file

@ -1,20 +1,23 @@
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)]
struct UserId(String);
#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)]
#[derive(GraphQLScalarValue, Debug, Eq, PartialEq)]
#[graphql(transparent, name = "MyUserId", description = "custom description...")]
struct CustomUserId(String);
/// The doc comment...
#[derive(juniper::GraphQLScalarValue, PartialEq, Eq, Debug)]
#[derive(GraphQLScalarValue, Debug, Eq, PartialEq)]
#[graphql(transparent)]
struct IdWithDocComment(i32);
#[derive(juniper::GraphQLObject)]
#[derive(GraphQLObject)]
struct User {
id: UserId,
id_custom: CustomUserId,
@ -22,7 +25,7 @@ struct User {
struct User2;
#[juniper::graphql_object]
#[graphql_object]
impl User2 {
fn id(&self) -> UserId {
UserId("id".to_string())
@ -36,7 +39,7 @@ fn test_scalar_value_simple() {
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);
assert_eq!(meta.name(), Some("UserId"));
assert_eq!(meta.description(), None);
@ -57,7 +60,7 @@ fn test_scalar_value_custom() {
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);
assert_eq!(meta.name(), Some("MyUserId"));
assert_eq!(
@ -76,7 +79,7 @@ fn test_scalar_value_custom() {
#[test]
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);
assert_eq!(meta.description(), Some(&"The doc comment...".to_string()));
}

View file

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

View file

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

View file

@ -1,14 +1,15 @@
use std::fmt;
use std::{fmt, pin::Pin};
use futures::{stream, Stream};
use juniper::{
execute,
execute, graphql_object, graphql_scalar, graphql_subscription,
parser::{ParseError, ScalarToken, Spanning, Token},
serde::de,
EmptyMutation, FieldResult, InputValue, Object, ParseScalarResult, RootNode, ScalarValue,
Value, Variables,
EmptyMutation, FieldResult, GraphQLScalarValue, InputValue, Object, ParseScalarResult,
RootNode, ScalarValue, Value, Variables,
};
#[derive(Debug, Clone, PartialEq, juniper::GraphQLScalarValue)]
#[derive(GraphQLScalarValue, Clone, Debug, PartialEq)]
pub(crate) enum MyScalarValue {
Int(i32),
Long(i64),
@ -22,42 +23,49 @@ impl ScalarValue for MyScalarValue {
fn as_int(&self) -> Option<i32> {
match *self {
MyScalarValue::Int(ref i) => Some(*i),
Self::Int(ref i) => Some(*i),
_ => None,
}
}
fn as_string(&self) -> Option<String> {
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,
}
}
fn as_str(&self) -> Option<&str> {
match *self {
MyScalarValue::String(ref s) => Some(s.as_str()),
Self::String(ref s) => Some(s.as_str()),
_ => None,
}
}
fn as_float(&self) -> Option<f64> {
match *self {
MyScalarValue::Int(ref i) => Some(*i as f64),
MyScalarValue::Float(ref f) => Some(*f),
Self::Int(ref i) => Some(f64::from(*i)),
Self::Float(ref f) => Some(*f),
_ => None,
}
}
fn as_boolean(&self) -> Option<bool> {
match *self {
MyScalarValue::Boolean(ref b) => Some(*b),
Self::Boolean(ref b) => Some(*b),
_ => None,
}
}
}
#[derive(Default, Debug)]
#[derive(Debug, Default)]
pub(crate) struct MyScalarValueVisitor;
impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
@ -82,7 +90,7 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
where
E: de::Error,
{
if value <= i32::max_value() as i64 {
if value <= i64::from(i32::max_value()) {
self.visit_i32(value as i32)
} else {
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 {
fn resolve(&self) -> Value {
Value::scalar(*self)
@ -158,12 +166,10 @@ impl GraphQLScalar for i64 {
struct TestType;
#[juniper::graphql_object(
Scalar = MyScalarValue
)]
#[graphql_object(scalar = MyScalarValue)]
impl TestType {
fn long_field() -> i64 {
(::std::i32::MAX as i64) + 1
i64::from(i32::max_value()) + 1
}
fn long_with_arg(long_arg: i64) -> i64 {
@ -173,14 +179,10 @@ impl TestType {
struct TestSubscriptionType;
#[juniper::graphql_subscription(
Scalar = MyScalarValue
)]
#[graphql_subscription(scalar = MyScalarValue)]
impl TestSubscriptionType {
async fn foo(
) -> std::pin::Pin<Box<dyn futures::Stream<Item = FieldResult<i32, MyScalarValue>> + Send>>
{
Box::pin(futures::stream::empty())
async fn foo() -> Pin<Box<dyn Stream<Item = FieldResult<i32, MyScalarValue>> + Send>> {
Box::pin(stream::empty())
}
}
@ -188,7 +190,8 @@ async fn run_variable_query<F>(query: &str, vars: Variables<MyScalarValue>, f: F
where
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, &())
.await
@ -215,7 +218,7 @@ async fn querying_long() {
run_query("{ longField }", |result| {
assert_eq!(
result.get_field_value("longField"),
Some(&Value::scalar((::std::i32::MAX as i64) + 1))
Some(&Value::scalar(i64::from(i32::max_value()) + 1))
);
})
.await;
@ -226,12 +229,12 @@ async fn querying_long_arg() {
run_query(
&format!(
"{{ longWithArg(longArg: {}) }}",
(::std::i32::MAX as i64) + 3
i64::from(i32::max_value()) + 3
),
|result| {
assert_eq!(
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) }",
vec![(
"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()
.collect(),
|result| {
assert_eq!(
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]
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();
assert_eq!(
@ -268,7 +271,7 @@ fn deserialize_variable() {
InputValue::Object(vec![(
Spanning::unlocated("field".into()),
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>.
use juniper::*;
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, LookAheadMethods as _, RootNode, Variables,
};
pub struct Context;
@ -7,9 +10,7 @@ impl juniper::Context for Context {}
pub struct Query;
#[graphql_object(
Context = Context
)]
#[graphql_object(context = Context)]
impl Query {
fn users(exec: &Executor) -> Vec<User> {
let lh = exec.look_ahead();
@ -27,9 +28,7 @@ impl Query {
#[derive(Clone)]
pub struct User;
#[graphql_object(
Context = Context
)]
#[graphql_object(context = Context)]
impl User {
fn id() -> i32 {
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]
async fn users() {
@ -109,7 +108,7 @@ async fn both() {
EmptyMutation::<Context>::new(),
EmptySubscription::<Context>::new(),
),
&juniper::Variables::new(),
&Variables::new(),
&ctx,
)
.await
@ -137,7 +136,7 @@ async fn both_in_different_order() {
EmptyMutation::<Context>::new(),
EmptySubscription::<Context>::new(),
),
&juniper::Variables::new(),
&Variables::new(),
&ctx,
)
.await

View file

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

View file

@ -32,11 +32,10 @@
- `LexerError`
- `ParseError`
- `RuleError`
- Support `chrono-tz::Tz` scalar behind a `chrono-tz` feature flag. ([#519](https://github.com/graphql-rust/juniper/pull/519))
- 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))
@ -76,7 +75,7 @@
```rust
#[graphql(arguments(argA(name = "test")))]
```
- `SchemaType` is now public.
- This is helpful when using `context.getSchema()` inside of your field resolvers.
@ -94,9 +93,10 @@
- `juniper::graphiql` has moved to `juniper::http::graphiql`.
- `juniper::http::graphiql::graphiql_source()` now requires a second parameter for subscriptions.
- Renamed the `object` proc macro to `graphql_object`.
- 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`.
- Removed the `graphql_scalar!` macro. Use the `#[graphql_scalar]` proc macro instead.
@ -117,7 +117,7 @@
- The return type of `GraphQLType::resolve()` has been changed to `ExecutionResult`.
- This was done to unify the return type of all resolver methods. The previous `Value` return type was just an internal artifact of
error handling.
- Subscription-related:
- Add subscription type to `RootNode`.
- Add subscription endpoint to `playground_source()`.
@ -130,6 +130,8 @@
- Renamed `http::tests::HTTPIntegration` as `http::tests::HttpIntegration`.
- 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))
- 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> {
&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`
@ -240,9 +252,10 @@ pub trait IntoFieldError<S = DefaultScalarValue> {
fn into_field_error(self) -> FieldError<S>;
}
impl<S> IntoFieldError<S> for FieldError<S> {
fn into_field_error(self) -> FieldError<S> {
self
impl<S1: ScalarValue, S2: ScalarValue> IntoFieldError<S2> for FieldError<S1> {
#[inline]
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
S: ScalarValue,
T: GraphQLValue<S>,
S1: ScalarValue,
S2: ScalarValue,
T: GraphQLValue<S2>,
{
type Type = T;
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(Some)
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S2> {
self.map(Some).map_err(FieldError::map_scalar_value)
}
}
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>, S>
impl<'a, S1, S2, T, C> IntoResolvable<'a, S2, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>, S1>
where
S: ScalarValue,
T: GraphQLValue<S>,
S1: ScalarValue,
S2: ScalarValue,
T: GraphQLValue<S2>,
{
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))))
.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 {
Admin,
User,
@ -14,7 +14,7 @@ struct User {
kind: UserKind,
}
#[crate::graphql_object]
#[graphql_object]
impl User {
async fn id(&self) -> i32 {
self.id
@ -46,7 +46,7 @@ impl User {
struct Query;
#[crate::graphql_object]
#[graphql_object]
impl Query {
fn field_sync(&self) -> &'static str {
"field_sync"
@ -72,7 +72,7 @@ impl Query {
#[tokio::test]
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#"
query {
fieldSync

View file

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

View file

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

View file

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

View file

@ -5,7 +5,7 @@
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`].
///
@ -22,12 +22,12 @@ pub trait IntoFieldResult<T, S> {
impl<T, E, S> IntoFieldResult<T, S> for Result<T, E>
where
T: IntoFieldResult<T, S>,
E: Into<FieldError<S>>,
E: IntoFieldError<S>,
{
type Item = T::Item;
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)]
struct Root;
#[graphql_object(interfaces = [InterfaceValue])]
#[graphql_object(impl = InterfaceValue)]
impl Root {
fn simple() -> i32 {
0
@ -104,7 +104,7 @@ impl Root {
}
}
#[graphql_interface(scalar = DefaultScalarValue)]
#[graphql_interface]
impl Interface for Root {
fn simple(&self) -> i32 {
0
@ -147,7 +147,7 @@ impl Interface for Root {
}
}
#[graphql_interface(for = Root, scalar = DefaultScalarValue)]
#[graphql_interface(for = Root)]
trait Interface {
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 {
fn simple(&self) -> i32;
}
#[graphql_interface(scalar = DefaultScalarValue)]
#[graphql_interface]
impl CustomName for Concrete {
fn simple(&self) -> i32 {
0
}
}
#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)]
#[graphql_interface(for = Concrete)]
trait WithLifetime<'a> {
fn simple(&self) -> i32;
}
#[graphql_interface(scalar = DefaultScalarValue)]
#[graphql_interface]
impl<'a> WithLifetime<'a> for Concrete {
fn simple(&self) -> i32 {
0
}
}
#[graphql_interface(for = Concrete, scalar = DefaultScalarValue)]
#[graphql_interface(for = Concrete)]
trait WithGenerics<T> {
fn simple(&self) -> i32;
}
#[graphql_interface(scalar = DefaultScalarValue)]
#[graphql_interface]
impl<T> WithGenerics<T> for Concrete {
fn simple(&self) -> i32 {
0
}
}
#[graphql_interface(for = Concrete, desc = "A description", scalar = DefaultScalarValue)]
#[graphql_interface(for = Concrete, desc = "A description")]
trait Description {
fn simple(&self) -> i32;
}
#[graphql_interface(scalar = DefaultScalarValue)]
#[graphql_interface]
impl Description for Concrete {
fn simple(&self) -> i32 {
0
@ -75,7 +75,7 @@ impl Description for Concrete {
struct Root;
#[crate::graphql_object]
#[graphql_object]
impl Root {
fn custom_name() -> CustomNameValue {
Concrete.into()

View file

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

View file

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

View file

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

View file

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

View file

@ -827,7 +827,7 @@ where
V: Visitor<'a, S> + 'a,
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(
"onQuery",

View file

@ -1,14 +1,21 @@
mod object;
mod scalar;
use std::{
any::TypeId,
fmt::{self, Display, Formatter},
mem,
};
use crate::{
ast::{InputValue, ToInputValue},
parser::Spanning,
};
use std::fmt::{self, Display, Formatter};
mod object;
mod scalar;
pub use self::object::Object;
pub use self::scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue};
pub use self::{
object::Object,
scalar::{DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarValue},
};
/// Serializable value returned from query and field execution.
///
@ -28,15 +35,12 @@ pub enum Value<S = DefaultScalarValue> {
Object(Object<S>),
}
impl<S> Value<S>
where
S: ScalarValue,
{
impl<S: ScalarValue> Value<S> {
// CONSTRUCTORS
/// Construct a null value.
pub fn null() -> Self {
Value::Null
Self::Null
}
/// Construct an integer value.
@ -65,12 +69,12 @@ where
/// Construct a list value.
pub fn list(l: Vec<Self>) -> Self {
Value::List(l)
Self::List(l)
}
/// Construct an object value.
pub fn object(o: Object<S>) -> Self {
Value::Object(o)
Self::Object(o)
}
/// Construct a scalar value
@ -78,7 +82,7 @@ where
where
T: Into<S>,
{
Value::Scalar(s.into())
Self::Scalar(s.into())
}
// DISCRIMINATORS
@ -86,7 +90,7 @@ where
/// Does this value represent null?
pub fn is_null(&self) -> bool {
match *self {
Value::Null => true,
Self::Null => true,
_ => false,
}
}
@ -97,16 +101,15 @@ where
&'a S: Into<Option<&'a T>>,
{
match *self {
Value::Scalar(ref s) => s.into(),
Self::Scalar(ref s) => s.into(),
_ => None,
}
}
/// View the underlying float value, if present.
pub fn as_float_value(&self) -> Option<f64>
where {
pub fn as_float_value(&self) -> Option<f64> {
match self {
Value::Scalar(ref s) => s.as_float(),
Self::Scalar(ref s) => s.as_float(),
_ => None,
}
}
@ -114,7 +117,7 @@ where {
/// View the underlying object value, if present.
pub fn as_object_value(&self) -> Option<&Object<S>> {
match *self {
Value::Object(ref o) => Some(o),
Self::Object(ref o) => Some(o),
_ => None,
}
}
@ -124,7 +127,7 @@ where {
/// Returns None if value is not an Object.
pub fn into_object(self) -> Option<Object<S>> {
match self {
Value::Object(o) => Some(o),
Self::Object(o) => Some(o),
_ => None,
}
}
@ -132,7 +135,7 @@ where {
/// Mutable view into the underlying object value, if present.
pub fn as_mut_object_value(&mut self) -> Option<&mut Object<S>> {
match *self {
Value::Object(ref mut o) => Some(o),
Self::Object(ref mut o) => Some(o),
_ => None,
}
}
@ -140,7 +143,7 @@ where {
/// View the underlying list value, if present.
pub fn as_list_value(&self) -> Option<&Vec<Self>> {
match *self {
Value::List(ref l) => Some(l),
Self::List(ref l) => Some(l),
_ => None,
}
}
@ -148,7 +151,7 @@ where {
/// View the underlying scalar value, if present
pub fn as_scalar(&self) -> Option<&S> {
match *self {
Value::Scalar(ref s) => Some(s),
Self::Scalar(ref s) => Some(s),
_ => None,
}
}
@ -160,6 +163,27 @@ where {
{
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> {

View file

@ -53,36 +53,43 @@ pub trait ParseScalarValue<S = DefaultScalarValue> {
///
/// fn as_int(&self) -> Option<i32> {
/// match *self {
/// MyScalarValue::Int(ref i) => Some(*i),
/// Self::Int(ref i) => Some(*i),
/// _ => None,
/// }
/// }
///
/// fn as_string(&self) -> Option<String> {
/// 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,
/// }
/// }
///
/// fn as_str(&self) -> Option<&str> {
/// match *self {
/// MyScalarValue::String(ref s) => Some(s.as_str()),
/// Self::String(ref s) => Some(s.as_str()),
/// _ => None,
/// }
/// }
///
/// fn as_float(&self) -> Option<f64> {
/// match *self {
/// MyScalarValue::Int(ref i) => Some(*i as f64),
/// MyScalarValue::Float(ref f) => Some(*f),
/// Self::Int(ref i) => Some(*i as f64),
/// Self::Float(ref f) => Some(*f),
/// _ => None,
/// }
/// }
///
/// fn as_boolean(&self) -> Option<bool> {
/// match *self {
/// MyScalarValue::Boolean(ref b) => Some(*b),
/// Self::Boolean(ref b) => Some(*b),
/// _ => None,
/// }
/// }
@ -175,6 +182,7 @@ pub trait ScalarValue:
+ From<bool>
+ From<i32>
+ From<f64>
+ 'static
{
/// Serde visitor used to deserialize this scalar value
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.
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
/// scalar values
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
///
/// 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
/// scalar values.
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
@ -249,44 +278,60 @@ impl ScalarValue for DefaultScalarValue {
fn as_int(&self) -> Option<i32> {
match *self {
DefaultScalarValue::Int(ref i) => Some(*i),
Self::Int(ref i) => Some(*i),
_ => None,
}
}
fn as_float(&self) -> Option<f64> {
match *self {
DefaultScalarValue::Int(ref i) => Some(*i as f64),
DefaultScalarValue::Float(ref f) => Some(*f),
Self::Int(ref i) => Some(*i as f64),
Self::Float(ref f) => Some(*f),
_ => None,
}
}
fn as_str(&self) -> Option<&str> {
match *self {
DefaultScalarValue::String(ref s) => Some(s.as_str()),
Self::String(ref s) => Some(s.as_str()),
_ => None,
}
}
fn as_string(&self) -> Option<String> {
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,
}
}
fn as_boolean(&self) -> Option<bool> {
match *self {
DefaultScalarValue::Boolean(ref b) => Some(*b),
Self::Boolean(ref b) => Some(*b),
_ => 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 {
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)]
mod tests {
use super::*;
use actix_web::{dev::ServiceResponse, http, http::header::CONTENT_TYPE, test, App};
use juniper::{
futures::stream::StreamExt,
@ -490,6 +489,8 @@ mod tests {
EmptyMutation, EmptySubscription, RootNode,
};
use super::*;
type Schema =
juniper::RootNode<'static, Query, EmptyMutation<Database>, EmptySubscription<Database>>;
@ -713,7 +714,7 @@ mod tests {
impl TestActixWebIntegration {
fn make_request(&self, req: test::TestRequest) -> TestResponse {
actix_web::rt::System::new("request").block_on(async move {
let schema = RootNode::new(
let schema = Schema::new(
Query,
EmptyMutation::<Database>::new(),
EmptySubscription::<Database>::new(),

View file

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

View file

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

View file

@ -1232,11 +1232,10 @@ impl GraphQLTypeDefiniton {
// No custom scalar specified, but always generic specified.
// 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.
where_clause
generics.params.push(parse_quote!(__S));
generics
.make_where_clause()
.predicates
.push(parse_quote!(__S: ::juniper::ScalarValue));
}
@ -1248,6 +1247,15 @@ impl GraphQLTypeDefiniton {
};
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(
|field| {
let name = &field.name;
@ -1378,7 +1386,7 @@ impl GraphQLTypeDefiniton {
let subscription_implementation = quote!(
impl#impl_generics ::juniper::GraphQLSubscriptionValue<#scalar> for #ty #type_generics_tokens
#where_clause
#where_clause_with_send_sync
{
#[allow(unused_variables)]
fn resolve_field_into_stream<

View file

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

View file

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

View file

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