Make FieldResults in field resolvers optional :D

This commit is contained in:
Magnus Hallin 2016-10-16 16:02:40 +02:00
parent f3469dd270
commit 5caea1f908
20 changed files with 328 additions and 258 deletions

View file

@ -132,7 +132,7 @@ as well.
* [X] `graphql_input_object!` helper completely missing
* [X] Add support for deprecating things
* [X] Custom enum values and descriptions
* [ ] Improved syntax for fields that can't fail resolution - make
* [X] Improved syntax for fields that can't fail resolution - make
`FieldResult<T>` optional maybe?
* [ ] Investigate asynchronous execution - implementing it is not necessary, but
at least look at what API changes will be needed for us to hook into

View file

@ -61,6 +61,23 @@ pub type FieldResult<T> = Result<T, String>;
/// The result of resolving an unspecified field
pub type ExecutionResult = Result<Value, String>;
/// Convert a value into a successful field result
///
/// Used by the helper macros to support both returning a naked value
/// *and* a `FieldResult` from a field.
pub trait IntoFieldResult<T>: Sized {
/// Wrap `self` in a `Result`
///
/// The implementation of this should always be `Ok(self)`.
fn into(self) -> FieldResult<T>;
}
impl<T> IntoFieldResult<T> for FieldResult<T> {
fn into(self) -> FieldResult<T> {
self
}
}
impl<'a, CtxT> Executor<'a, CtxT> {
/// Resolve a single arbitrary value into an `ExecutionResult`
pub fn resolve<T: GraphQLType<CtxT>>(&mut self, value: &T) -> ExecutionResult {
@ -293,6 +310,17 @@ impl<CtxT> Registry<CtxT> {
}
}
#[doc(hidden)]
pub fn field_convert<T: IntoFieldResult<I>, I>(&mut self, name: &str) -> Field where I: GraphQLType<CtxT> {
Field {
name: name.to_owned(),
description: None,
arguments: None,
field_type: self.get_type::<I>(),
deprecation_reason: None,
}
}
#[doc(hidden)]
pub fn field_inside_result<T>(&mut self, name: &str, _: FieldResult<T>) -> Field where T: GraphQLType<CtxT> {
Field {

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -32,9 +31,9 @@ graphql_enum!(Sample as "SampleEnum" {
graphql_interface!(Interface: () as "SampleInterface" |&self| {
description: "A sample interface"
field sample_enum() -> FieldResult<Sample> as "A sample field in the interface" {
Ok(Sample::One)
field sample_enum() -> Sample as "A sample field in the interface" {
Sample::One
}
instance_resolvers: |&_| [
@ -47,15 +46,15 @@ graphql_object!(Root: () as "Root" |&self| {
interfaces: [Interface]
field sample_enum() -> FieldResult<Sample> {
Ok(Sample::One)
field sample_enum() -> Sample {
Sample::One
}
field sample_scalar(
first: i64 as "The first number",
first: i64 as "The first number",
second = 123: i64 as "The second number"
) -> FieldResult<Scalar> as "A sample scalar field on the object" {
Ok(Scalar(first + second))
) -> Scalar as "A sample scalar field on the object" {
Scalar(first + second)
}
});
@ -121,7 +120,7 @@ fn enum_introspection() {
assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum")));
assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM")));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get("interfaces"), Some(&Value::null()));
assert_eq!(type_info.get("possibleTypes"), Some(&Value::null()));
assert_eq!(type_info.get("inputFields"), Some(&Value::null()));

View file

@ -28,8 +28,8 @@ existing object types as GraphQL objects:
```rust
#[macro_use] extern crate juniper;
use juniper::FieldResult;
# use std::collections::HashMap;
use juniper::FieldResult;
struct User { id: String, name: String, friend_ids: Vec<String> }
struct QueryRoot;
@ -42,15 +42,19 @@ struct Database { users: HashMap<String, User> }
graphql_object!(User: Database as "User" |&self| {
// Expose a simple field as a GraphQL string.
field id() -> &String {
&self.id
}
field name() -> &String {
&self.name
}
// FieldResult<T> is an alias for Result<T, String> - simply return
// a string from this method and it will be correctly inserted into
// the execution response.
field id() -> FieldResult<&String> {
Ok(&self.id)
}
field name() -> FieldResult<&String> {
Ok(&self.name)
field secret() -> FieldResult<&String> {
Err("Can't touch this".to_owned())
}
// Field accessors can optionally take an "executor" as their first
@ -59,10 +63,10 @@ graphql_object!(User: Database as "User" |&self| {
//
// In this example, the context is used to convert the friend_ids array
// into actual User objects.
field friends(&mut executor) -> FieldResult<Vec<&User>> {
Ok(self.friend_ids.iter()
field friends(&mut executor) -> Vec<&User> {
self.friend_ids.iter()
.filter_map(|id| executor.context().users.get(id))
.collect())
.collect()
}
});
@ -71,8 +75,8 @@ graphql_object!(User: Database as "User" |&self| {
graphql_object!(QueryRoot: Database as "Query" |&self| {
// Arguments work just like they do on functions.
field user(&mut executor, id: String) -> FieldResult<Option<&User>> {
Ok(executor.context().users.get(&id))
field user(&mut executor, id: String) -> Option<&User> {
executor.context().users.get(&id)
}
});
@ -197,13 +201,13 @@ use rustc_serialize::json::{ToJson, Json};
use parser::{parse_document_source, ParseError, Spanning, SourcePosition};
use validation::{RuleError, ValidatorContext, visit_all_rules};
use executor::execute_validated_query;
pub use ast::{ToInputValue, FromInputValue, InputValue, Type, Selection};
pub use value::Value;
pub use types::base::{Arguments, GraphQLType, TypeKind};
pub use executor::{
Executor, Registry, ExecutionResult, ExecutionError, FieldResult,
execute_validated_query,
Executor, Registry, ExecutionResult, ExecutionError, FieldResult, IntoFieldResult,
};
pub use types::scalars::ID;
pub use schema::model::RootNode;

View file

@ -110,6 +110,12 @@ macro_rules! graphql_enum {
}
}
}
impl $crate::IntoFieldResult<$name> for $name {
fn into(self) -> $crate::FieldResult<$name> {
Ok(self)
}
}
};
// No more items to parse

View file

@ -77,7 +77,7 @@ macro_rules! __graphql__build_field_matches {
$body
};
return result.and_then(|r| $executorvar.resolve(&r))
return ($crate::IntoFieldResult::into(result)).and_then(|r| $executorvar.resolve(&r))
}
)*
panic!("Field {} not found on type {}", $fieldvar, $outname);

View file

@ -38,7 +38,6 @@ shared context to implement downcasts.
```rust
# #[macro_use] extern crate juniper;
# use juniper::FieldResult;
# use std::collections::HashMap;
struct Human { id: String }
struct Droid { id: String }
@ -60,16 +59,16 @@ impl Character for Droid {
}
graphql_object!(Human: Database as "Human" |&self| {
field id() -> FieldResult<&str> { Ok(&self.id) }
field id() -> &str { &self.id }
});
graphql_object!(Droid: Database as "Droid" |&self| {
field id() -> FieldResult<&str> { Ok(&self.id) }
field id() -> &str { &self.id }
});
// You can introduce lifetimes or generic parameters by < > before the name.
graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
field id() -> FieldResult<&str> { Ok(self.id()) }
field id() -> &str { self.id() }
instance_resolvers: |&context| [
context.humans.get(self.id()),
@ -97,9 +96,8 @@ macro_rules! graphql_interface {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.description($desc)
.deprecated($reason),
$args));
@ -116,9 +114,8 @@ macro_rules! graphql_interface {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.deprecated($reason),
$args));
@ -134,9 +131,8 @@ macro_rules! graphql_interface {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.description($desc),
$args));
@ -152,9 +148,8 @@ macro_rules! graphql_interface {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t),
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name))),
$args));
graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*);

View file

@ -11,23 +11,22 @@ The simplest case exposes fields on a struct:
```rust
# #[macro_use] extern crate juniper;
# use juniper::FieldResult;
struct User { id: String, name: String, group_ids: Vec<String> }
graphql_object!(User: () as "User" |&self| {
field id() -> FieldResult<&String> {
Ok(&self.id)
field id() -> &String {
&self.id
}
field name() -> FieldResult<&String> {
Ok(&self.name)
field name() -> &String {
&self.name
}
// Field and argument names will be converted from snake case to camel case,
// as is the common naming convention in GraphQL. The following field would
// be named "memberOfGroup", and the argument "groupId".
field member_of_group(group_id: String) -> FieldResult<bool> {
Ok(self.group_ids.iter().any(|gid| gid == &group_id))
field member_of_group(group_id: String) -> bool {
self.group_ids.iter().any(|gid| gid == &group_id)
}
});
@ -41,24 +40,23 @@ arguments:
```rust
# #[macro_use] extern crate juniper;
# use juniper::FieldResult;
struct User { id: String, name: String, group_ids: Vec<String> }
graphql_object!(User: () as "User" |&self| {
description: "A user in the database"
field id() -> FieldResult<&String> as "The user's unique identifier" {
Ok(&self.id)
field id() -> &String as "The user's unique identifier" {
&self.id
}
field name() -> FieldResult<&String> as "The user's name" {
Ok(&self.name)
field name() -> &String as "The user's name" {
&self.name
}
field member_of_group(
group_id: String as "The group id you want to test membership against"
) -> FieldResult<bool> as "Test if a user is member of a group" {
Ok(self.group_ids.iter().any(|gid| gid == &group_id))
) -> bool as "Test if a user is member of a group" {
self.group_ids.iter().any(|gid| gid == &group_id)
}
});
@ -72,26 +70,16 @@ generic parameters:
```rust
# #[macro_use] extern crate juniper;
# use juniper::FieldResult;
trait SomeTrait { fn id(&self) -> &str; }
graphql_object!(<'a> &'a SomeTrait: () as "SomeTrait" |&self| {
field id() -> FieldResult<&str> { Ok(self.id()) }
field id() -> &str { self.id() }
});
struct GenericType<T> { items: Vec<T> }
graphql_object!(<T> GenericType<T>: () as "GenericType" |&self| {
field count() -> FieldResult<i64> { Ok(self.items.len() as i64) }
});
struct SelfContained { name: String }
// If the type does not require access to a specific context, you can make it
// generic on the context type. This statically ensures that the fields only
// can access what's available from the type itself.
graphql_object!(<Context> SelfContained: Context as "SelfContained" |&self| {
field name() -> FieldResult<&String> { Ok(&self.name) }
field count() -> i64 { self.items.len() as i64 }
});
# fn main() { }
@ -103,7 +91,6 @@ You can use the `interfaces` item to implement interfaces:
```rust
# #[macro_use] extern crate juniper;
# use juniper::FieldResult;
trait Interface {
fn id(&self) -> &str;
fn as_implementor(&self) -> Option<Implementor>;
@ -111,7 +98,7 @@ trait Interface {
struct Implementor { id: String }
graphql_interface!(<'a> &'a Interface: () as "Interface" |&self| {
field id() -> FieldResult<&str> { Ok(self.id()) }
field id() -> &str { self.id() }
instance_resolvers: |&context| [
self.as_implementor(),
@ -119,7 +106,7 @@ graphql_interface!(<'a> &'a Interface: () as "Interface" |&self| {
});
graphql_object!(Implementor: () as "Implementor" |&self| {
field id() -> FieldResult<&str> { Ok(&self.id) }
field id() -> &str { &self.id }
interfaces: [&Interface]
});
@ -194,8 +181,10 @@ need to have any connection, only what's exposed in the schema matters.
### Fields
```text
field name(args...) -> FieldResult<Type> { }
field name(args...) -> FieldResult<Type> as "Field description" { }
field name(args...) -> Type { }
field name(args...) -> Type as "Field description" { }
field deprecated "Reason" name(args...) -> Type { }
field deprecated "Reason" name(args...) -> Type as "Field description" { }
```
Defines a field on the object. The name is converted to camel case, e.g.
@ -248,9 +237,8 @@ macro_rules! graphql_object {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.description($desc)
.deprecated($reason),
$args));
@ -267,9 +255,8 @@ macro_rules! graphql_object {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.deprecated($reason),
$args));
@ -285,9 +272,8 @@ macro_rules! graphql_object {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t)
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name)))
.description($desc),
$args));
@ -303,9 +289,8 @@ macro_rules! graphql_object {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_inside_result(
&$crate::to_snake_case(stringify!($name)),
Err("dummy".to_owned()) as $t),
$reg.field_convert::<$t, _>(
&$crate::to_snake_case(stringify!($name))),
$args));
graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*);
@ -407,6 +392,12 @@ macro_rules! graphql_object {
$($items)*);
}
});
impl<$($lifetime)*> $crate::IntoFieldResult<$name> for $name {
fn into(self) -> $crate::FieldResult<$name> {
Ok(self)
}
}
};
(

View file

@ -92,6 +92,12 @@ macro_rules! graphql_scalar {
$fiv_body
}
}
impl $crate::IntoFieldResult<$name> for $name {
fn into(self) -> $crate::FieldResult<$name> {
Ok(self)
}
}
};
// No more items to parse

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -20,49 +19,49 @@ Syntax to validate:
*/
graphql_object!(Root: () as "Root" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field exec_arg(&mut executor) -> FieldResult<i64> { Ok(0) }
field exec_arg_and_more(&mut executor, arg: i64) -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
field exec_arg(&mut executor) -> i64 { 0 }
field exec_arg_and_more(&mut executor, arg: i64) -> i64 { 0 }
field single_arg(arg: i64) -> FieldResult<i64> { Ok(0) }
field single_arg(arg: i64) -> i64 { 0 }
field multi_args(
arg1: i64,
arg2: i64
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field multi_args_trailing_comma(
arg1: i64,
arg2: i64,
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field single_arg_descr(arg: i64 as "The arg") -> FieldResult<i64> { Ok(0) }
field single_arg_descr(arg: i64 as "The arg") -> i64 { 0 }
field multi_args_descr(
arg1: i64 as "The first arg",
arg2: i64 as "The second arg"
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field multi_args_descr_trailing_comma(
arg1: i64 as "The first arg",
arg2: i64 as "The second arg",
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field arg_with_default(arg = 123: i64) -> FieldResult<i64> { Ok(0) }
field arg_with_default(arg = 123: i64) -> i64 { 0 }
field multi_args_with_default(
arg1 = 123: i64,
arg2 = 456: i64
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field multi_args_with_default_trailing_comma(
arg1 = 123: i64,
arg2 = 456: i64,
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field arg_with_default_descr(arg = 123: i64 as "The arg") -> FieldResult<i64> { Ok(0) }
field arg_with_default_descr(arg = 123: i64 as "The arg") -> i64 { 0 }
field multi_args_with_default_descr(
arg1 = 123: i64 as "The first arg",
arg2 = 456: i64 as "The second arg"
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
field multi_args_with_default_trailing_comma_descr(
arg1 = 123: i64 as "The first arg",
arg2 = 456: i64 as "The second arg",
) -> FieldResult<i64> { Ok(0) }
) -> i64 { 0 }
});
fn run_args_info_query<F>(field_name: &str, f: F)

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -59,12 +58,12 @@ graphql_enum!(EnumDeprecation {
});
graphql_object!(Root: () as "Root" |&self| {
field default_name() -> FieldResult<DefaultName> { Ok(DefaultName::Foo) }
field named() -> FieldResult<Named> { Ok(Named::Foo) }
field no_trailing_comma() -> FieldResult<NoTrailingComma> { Ok(NoTrailingComma::Foo) }
field enum_description() -> FieldResult<EnumDescription> { Ok(EnumDescription::Foo) }
field enum_value_description() -> FieldResult<EnumValueDescription> { Ok(EnumValueDescription::Foo) }
field enum_deprecation() -> FieldResult<EnumDeprecation> { Ok(EnumDeprecation::Foo) }
field default_name() -> DefaultName { DefaultName::Foo }
field named() -> Named { Named::Foo }
field no_trailing_comma() -> NoTrailingComma { NoTrailingComma::Foo }
field enum_description() -> EnumDescription { EnumDescription::Foo }
field enum_value_description() -> EnumValueDescription { EnumValueDescription::Foo }
field enum_deprecation() -> EnumDeprecation { EnumDeprecation::Foo }
});
fn run_type_info_query<F>(doc: &str, f: F) where F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> () {

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use executor::FieldResult;
use value::Value;
use ast::InputValue;
use schema::model::RootNode;
@ -19,29 +18,29 @@ Syntax to validate:
*/
graphql_object!(Root: () as "Root" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
field description() -> FieldResult<i64> as "Field description" { Ok(0) }
field description() -> i64 as "Field description" { 0 }
field deprecated "Deprecation reason"
deprecated() -> FieldResult<i64> { Ok(0) }
deprecated() -> i64 { 0 }
field deprecated "Deprecation reason"
deprecated_descr() -> FieldResult<i64> as "Field description" { Ok(0) }
deprecated_descr() -> i64 as "Field description" { 0 }
interfaces: [Interface]
});
graphql_interface!(Interface: () as "Interface" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
field description() -> FieldResult<i64> as "Field description" { Ok(0) }
field description() -> i64 as "Field description" { 0 }
field deprecated "Deprecation reason"
deprecated() -> FieldResult<i64> { Ok(0) }
deprecated() -> i64 { 0 }
field deprecated "Deprecation reason"
deprecated_descr() -> FieldResult<i64> as "Field description" { Ok(0) }
deprecated_descr() -> i64 as "Field description" { 0 }
instance_resolvers: |&_| [
Some(Root {}),

View file

@ -1,7 +1,6 @@
use std::collections::HashMap;
use ast::{InputValue, FromInputValue};
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -57,8 +56,8 @@ graphql_object!(Root: () as "Root" |&self| {
a4: Named,
a5: Description,
a6: FieldDescription
) -> FieldResult<i64> {
Ok(0)
) -> i64 {
0
}
});

View file

@ -1,7 +1,7 @@
use std::collections::HashMap;
use std::marker::PhantomData;
use ast::InputValue;
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -20,8 +20,11 @@ struct Interface;
struct DefaultName;
struct WithLifetime;
struct WithGenerics;
#[allow(dead_code)]
struct WithLifetime<'a> { data: PhantomData<&'a i64> }
#[allow(dead_code)]
struct WithGenerics<T> { data: T }
struct DescriptionFirst;
struct FieldsFirst;
@ -33,20 +36,21 @@ struct CommasOnMeta;
struct Root;
graphql_object!(DefaultName: () |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
});
graphql_object!(<'a> &'a WithLifetime: () as "WithLifetime" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
graphql_object!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
field simple() -> i64 { 0 }
});
graphql_object!(<CtxT> WithGenerics: CtxT as "WithGenerics" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
graphql_object!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
field simple() -> i64 { 0 }
});
graphql_interface!(Interface: () as "Interface" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
instance_resolvers: |_| [
Some(DescriptionFirst {}),
@ -56,13 +60,13 @@ graphql_interface!(Interface: () as "Interface" |&self| {
graphql_object!(DescriptionFirst: () as "DescriptionFirst" |&self| {
description: "A description"
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
interfaces: [Interface]
});
graphql_object!(FieldsFirst: () as "FieldsFirst" |&self| {
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
description: "A description"
@ -72,7 +76,7 @@ graphql_object!(FieldsFirst: () as "FieldsFirst" |&self| {
graphql_object!(InterfacesFirst: () as "InterfacesFirst" |&self| {
interfaces: [Interface]
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
description: "A description"
});
@ -80,7 +84,7 @@ graphql_object!(InterfacesFirst: () as "InterfacesFirst" |&self| {
graphql_object!(CommasWithTrailing: () as "CommasWithTrailing" |&self| {
interfaces: [Interface],
field simple() -> FieldResult<i64> { Ok(0) },
field simple() -> i64 { 0 },
description: "A description",
});
@ -90,21 +94,21 @@ graphql_object!(CommasOnMeta: () as "CommasOnMeta" |&self| {
interfaces: [Interface],
description: "A description",
field simple() -> FieldResult<i64> { Ok(0) }
field simple() -> i64 { 0 }
});
graphql_object!(Root: () as "Root" |&self| {
field default_name() -> FieldResult<DefaultName> { Ok(DefaultName {}) }
graphql_object!(<'a> Root: () as "Root" |&self| {
field default_name() -> DefaultName { DefaultName {} }
field with_lifetime() -> FieldResult<&WithLifetime> { Err("Nope".to_owned()) }
field with_generics() -> FieldResult<WithGenerics> { Ok(WithGenerics {}) }
field with_lifetime() -> WithLifetime<'a> { WithLifetime { data: PhantomData } }
field with_generics() -> WithGenerics<i64> { WithGenerics { data: 123 } }
field description_first() -> FieldResult<DescriptionFirst> { Ok(DescriptionFirst {}) }
field fields_first() -> FieldResult<FieldsFirst> { Ok(FieldsFirst {}) }
field interfaces_first() -> FieldResult<InterfacesFirst> { Ok(InterfacesFirst {}) }
field description_first() -> DescriptionFirst { DescriptionFirst {} }
field fields_first() -> FieldsFirst { FieldsFirst {} }
field interfaces_first() -> InterfacesFirst { InterfacesFirst {} }
field commas_with_trailing() -> FieldResult<CommasWithTrailing> { Ok(CommasWithTrailing {}) }
field commas_on_meta() -> FieldResult<CommasOnMeta> { Ok(CommasOnMeta {}) }
field commas_with_trailing() -> CommasWithTrailing { CommasWithTrailing {} }
field commas_on_meta() -> CommasOnMeta { CommasOnMeta {} }
});

View file

@ -1,6 +1,5 @@
use std::collections::HashMap;
use executor::FieldResult;
use value::Value;
use schema::model::RootNode;
@ -63,10 +62,10 @@ graphql_scalar!(ScalarDescription {
});
graphql_object!(Root: () as "Root" |&self| {
field default_name() -> FieldResult<DefaultName> { Ok(DefaultName(0)) }
field other_order() -> FieldResult<OtherOrder> { Ok(OtherOrder(0)) }
field named() -> FieldResult<Named> { Ok(Named(0)) }
field scalar_description() -> FieldResult<ScalarDescription> { Ok(ScalarDescription(0)) }
field default_name() -> DefaultName { DefaultName(0) }
field other_order() -> OtherOrder { OtherOrder(0) }
field named() -> Named { Named(0) }
field scalar_description() -> ScalarDescription { ScalarDescription(0) }
});
fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>) -> () {

View file

@ -1,7 +1,7 @@
use rustc_serialize::json::ToJson;
use types::base::{GraphQLType, Arguments, TypeKind};
use executor::{Executor, Registry, FieldResult, ExecutionResult};
use executor::{Executor, Registry, ExecutionResult};
use schema::meta::{MetaType, ObjectMeta, EnumMeta, InputObjectMeta, UnionMeta, InterfaceMeta,
Field, Argument, EnumValue};
@ -32,48 +32,48 @@ impl<CtxT, QueryT, MutationT> GraphQLType<CtxT> for RootNode<CtxT, QueryT, Mutat
}
graphql_object!(SchemaType: SchemaType as "__Schema" |&self| {
field types() -> FieldResult<Vec<TypeType>> {
Ok(self.type_list())
field types() -> Vec<TypeType> {
self.type_list()
}
field query_type() -> FieldResult<TypeType> {
Ok(self.query_type())
field query_type() -> TypeType {
self.query_type()
}
field mutation_type() -> FieldResult<Option<TypeType>> {
Ok(self.mutation_type())
field mutation_type() -> Option<TypeType> {
self.mutation_type()
}
field directives() -> FieldResult<Vec<&DirectiveType>> {
Ok(self.directive_list())
field directives() -> Vec<&DirectiveType> {
self.directive_list()
}
});
graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
field name() -> FieldResult<Option<&str>> {
Ok(match *self {
field name() -> Option<&str> {
match *self {
TypeType::Concrete(t) => t.name(),
_ => None,
})
}
}
field description() -> FieldResult<Option<&String>> {
Ok(match *self {
field description() -> Option<&String> {
match *self {
TypeType::Concrete(t) => t.description(),
_ => None,
})
}
}
field kind() -> FieldResult<TypeKind> {
Ok(match *self {
field kind() -> TypeKind {
match *self {
TypeType::Concrete(t) => t.type_kind(),
TypeType::List(_) => TypeKind::List,
TypeType::NonNull(_) => TypeKind::NonNull,
})
}
}
field fields(include_deprecated = false: bool) -> FieldResult<Option<Vec<&Field>>> {
Ok(match *self {
field fields(include_deprecated = false: bool) -> Option<Vec<&Field>> {
match *self {
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) |
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) =>
Some(fields
@ -81,26 +81,26 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
.filter(|f| include_deprecated || f.deprecation_reason.is_none())
.collect()),
_ => None,
})
}
}
field of_type() -> FieldResult<Option<&TypeType>> {
Ok(match *self {
field of_type() -> Option<&TypeType> {
match *self {
TypeType::Concrete(_) => None,
TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l),
})
}
}
field input_fields() -> FieldResult<Option<&Vec<Argument>>> {
Ok(match *self {
field input_fields() -> Option<&Vec<Argument>> {
match *self {
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) =>
Some(input_fields),
_ => None,
})
}
}
field interfaces(&mut executor) -> FieldResult<Option<Vec<TypeType>>> {
Ok(match *self {
field interfaces(&mut executor) -> Option<Vec<TypeType>> {
match *self {
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
let schema = executor.context();
Some(interface_names
@ -109,12 +109,12 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
.collect())
}
_ => None,
})
}
}
field possible_types(&mut executor) -> FieldResult<Option<Vec<TypeType>>> {
field possible_types(&mut executor) -> Option<Vec<TypeType>> {
let schema = executor.context();
Ok(match *self {
match *self {
TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => {
Some(of_type_names
.iter()
@ -134,80 +134,80 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
.collect())
}
_ => None,
})
}
}
field enum_values(include_deprecated = false: bool) -> FieldResult<Option<Vec<&EnumValue>>> {
Ok(match *self {
field enum_values(include_deprecated = false: bool) -> Option<Vec<&EnumValue>> {
match *self {
TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) =>
Some(values
.iter()
.filter(|f| include_deprecated || f.deprecation_reason.is_none())
.collect()),
_ => None,
})
}
}
});
graphql_object!(Field: SchemaType as "__Field" |&self| {
field name() -> FieldResult<&String> {
Ok(&self.name)
field name() -> &String {
&self.name
}
field description() -> FieldResult<&Option<String>> {
Ok(&self.description)
field description() -> &Option<String> {
&self.description
}
field args() -> FieldResult<Vec<&Argument>> {
Ok(self.arguments.as_ref().map_or_else(|| Vec::new(), |v| v.iter().collect()))
field args() -> Vec<&Argument> {
self.arguments.as_ref().map_or_else(|| Vec::new(), |v| v.iter().collect())
}
field type(&mut executor) -> FieldResult<TypeType> {
Ok(executor.context().make_type(&self.field_type))
field type(&mut executor) -> TypeType {
executor.context().make_type(&self.field_type)
}
field is_deprecated() -> FieldResult<bool> {
Ok(self.deprecation_reason.is_some())
field is_deprecated() -> bool {
self.deprecation_reason.is_some()
}
field deprecation_reason() -> FieldResult<&Option<String>> {
Ok(&self.deprecation_reason)
field deprecation_reason() -> &Option<String> {
&self.deprecation_reason
}
});
graphql_object!(Argument: SchemaType as "__InputValue" |&self| {
field name() -> FieldResult<&String> {
Ok(&self.name)
field name() -> &String {
&self.name
}
field description() -> FieldResult<&Option<String>> {
Ok(&self.description)
field description() -> &Option<String> {
&self.description
}
field type(&mut executor) -> FieldResult<TypeType> {
Ok(executor.context().make_type(&self.arg_type))
field type(&mut executor) -> TypeType {
executor.context().make_type(&self.arg_type)
}
field default_value() -> FieldResult<Option<String>> {
Ok(self.default_value.as_ref().map(|v| v.to_json().to_string()))
field default_value() -> Option<String> {
self.default_value.as_ref().map(|v| v.to_json().to_string())
}
});
graphql_object!(EnumValue: SchemaType as "__EnumValue" |&self| {
field name() -> FieldResult<&String> {
Ok(&self.name)
field name() -> &String {
&self.name
}
field description() -> FieldResult<&Option<String>> {
Ok(&self.description)
field description() -> &Option<String> {
&self.description
}
field is_deprecated() -> FieldResult<bool> {
Ok(self.deprecation_reason.is_some())
field is_deprecated() -> bool {
self.deprecation_reason.is_some()
}
field deprecation_reason() -> FieldResult<&Option<String>> {
Ok(&self.deprecation_reason)
field deprecation_reason() -> &Option<String> {
&self.deprecation_reason
}
});
@ -224,20 +224,20 @@ graphql_enum!(TypeKind as "__TypeKind" {
graphql_object!(DirectiveType: SchemaType as "__Directive" |&self| {
field name() -> FieldResult<&String> {
Ok(&self.name)
field name() -> &String {
&self.name
}
field description() -> FieldResult<&Option<String>> {
Ok(&self.description)
field description() -> &Option<String> {
&self.description
}
field locations() -> FieldResult<&Vec<DirectiveLocation>> {
Ok(&self.locations)
field locations() -> &Vec<DirectiveLocation> {
&self.locations
}
field args() -> FieldResult<&Vec<Argument>> {
Ok(&self.arguments)
field args() -> &Vec<Argument> {
&self.arguments
}
});

View file

@ -1,4 +1,3 @@
use executor::FieldResult;
use tests::model::{Character, Human, Droid, Database, Episode};
graphql_enum!(Episode {
@ -10,21 +9,21 @@ graphql_enum!(Episode {
graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
description: "A character in the Star Wars Trilogy"
field id() -> FieldResult<&str> as "The id of the character" {
Ok(self.id())
field id() -> &str as "The id of the character" {
self.id()
}
field name() -> FieldResult<Option<&str>> as "The name of the character" {
Ok(Some(self.name()))
field name() -> Option<&str> as "The name of the character" {
Some(self.name())
}
field friends(&mut executor) -> FieldResult<Vec<&Character>>
field friends(&mut executor) -> Vec<&Character>
as "The friends of the character" {
Ok(executor.context().get_friends(self.as_character()))
executor.context().get_friends(self.as_character())
}
field appears_in() -> FieldResult<&[Episode]> as "Which movies they appear in" {
Ok(self.appears_in())
field appears_in() -> &[Episode] as "Which movies they appear in" {
self.appears_in()
}
instance_resolvers: |&context| [
@ -38,25 +37,25 @@ graphql_object!(<'a> &'a Human: Database as "Human" |&self| {
interfaces: [&Character]
field id() -> FieldResult<&str> as "The id of the human"{
Ok(self.id())
field id() -> &str as "The id of the human"{
self.id()
}
field name() -> FieldResult<Option<&str>> as "The name of the human" {
Ok(Some(self.name()))
field name() -> Option<&str> as "The name of the human" {
Some(self.name())
}
field friends(&mut executor) -> FieldResult<Vec<&Character>>
field friends(&mut executor) -> Vec<&Character>
as "The friends of the human" {
Ok(executor.context().get_friends(self.as_character()))
executor.context().get_friends(self.as_character())
}
field appears_in() -> FieldResult<&[Episode]> as "Which movies they appear in" {
Ok(self.appears_in())
field appears_in() -> &[Episode] as "Which movies they appear in" {
self.appears_in()
}
field home_planet() -> FieldResult<&Option<String>> as "The home planet of the human" {
Ok(self.home_planet())
field home_planet() -> &Option<String> as "The home planet of the human" {
self.home_planet()
}
});
@ -65,25 +64,25 @@ graphql_object!(<'a> &'a Droid: Database as "Droid" |&self| {
interfaces: [&Character]
field id() -> FieldResult<&str> as "The id of the droid" {
Ok(self.id())
field id() -> &str as "The id of the droid" {
self.id()
}
field name() -> FieldResult<Option<&str>> as "The name of the droid" {
Ok(Some(self.name()))
field name() -> Option<&str> as "The name of the droid" {
Some(self.name())
}
field friends(&mut executor) -> FieldResult<Vec<&Character>>
field friends(&mut executor) -> Vec<&Character>
as "The friends of the droid" {
Ok(executor.context().get_friends(self.as_character()))
executor.context().get_friends(self.as_character())
}
field appears_in() -> FieldResult<&[Episode]> as "Which movies they appear in" {
Ok(self.appears_in())
field appears_in() -> &[Episode] as "Which movies they appear in" {
self.appears_in()
}
field primary_function() -> FieldResult<&Option<String>> as "The primary function of the droid" {
Ok(self.primary_function())
field primary_function() -> &Option<String> as "The primary function of the droid" {
self.primary_function()
}
});
@ -93,21 +92,21 @@ graphql_object!(Database: Database as "Query" |&self| {
field human(
id: String as "id of the human"
) -> FieldResult<Option<&Human>> {
Ok(self.get_human(&id))
) -> Option<&Human> {
self.get_human(&id)
}
field droid(
id: String as "id of the droid"
) -> FieldResult<Option<&Droid>> {
Ok(self.get_droid(&id))
) -> Option<&Droid> {
self.get_droid(&id)
}
field hero(
episode: Option<Episode> as
"If omitted, returns the hero of the whole saga. If provided, returns \
the hero of that particular episode"
) -> FieldResult<Option<&Character>> {
Ok(Some(self.get_hero(episode).as_character()))
) -> Option<&Character> {
Some(self.get_hero(episode).as_character())
}
});

View file

@ -2,7 +2,7 @@ use ast::{InputValue, ToInputValue, FromInputValue, Selection};
use value::Value;
use schema::meta::MetaType;
use executor::{Executor, Registry};
use executor::{Executor, Registry, IntoFieldResult, FieldResult};
use types::base::{GraphQLType};
impl<T, CtxT> GraphQLType<CtxT> for Option<T> where T: GraphQLType<CtxT> {
@ -43,6 +43,12 @@ impl<T> ToInputValue for Option<T> where T: ToInputValue {
}
}
impl<T> IntoFieldResult<Option<T>> for Option<T> {
fn into(self) -> FieldResult<Option<T>> {
Ok(self)
}
}
impl<T, CtxT> GraphQLType<CtxT> for Vec<T> where T: GraphQLType<CtxT> {
fn name() -> Option<&'static str> {
@ -84,6 +90,12 @@ impl<T> ToInputValue for Vec<T> where T: ToInputValue {
}
}
impl<T> IntoFieldResult<Vec<T>> for Vec<T> {
fn into(self) -> FieldResult<Vec<T>> {
Ok(self)
}
}
impl<'a, T, CtxT> GraphQLType<CtxT> for &'a [T] where T: GraphQLType<CtxT> {
fn name() -> Option<&'static str> {
@ -106,3 +118,9 @@ impl<'a, T> ToInputValue for &'a [T] where T: ToInputValue {
InputValue::list(self.iter().map(|v| v.to()).collect())
}
}
impl<'a, T> IntoFieldResult<&'a [T]> for &'a [T] {
fn into(self) -> FieldResult<&'a [T]> {
Ok(self)
}
}

View file

@ -2,7 +2,7 @@ use ast::{Selection, InputValue, ToInputValue, FromInputValue};
use value::Value;
use schema::meta::MetaType;
use executor::{Executor, Registry, ExecutionResult};
use executor::{Executor, Registry, ExecutionResult, IntoFieldResult, FieldResult};
use types::base::{Arguments, GraphQLType};
impl<T, CtxT> GraphQLType<CtxT> for Box<T> where T: GraphQLType<CtxT> {
@ -43,6 +43,12 @@ impl<T> ToInputValue for Box<T> where T: ToInputValue {
}
}
impl<T> IntoFieldResult<Box<T>> for Box<T> {
fn into(self) -> FieldResult<Box<T>> {
Ok(self)
}
}
impl<'a, T, CtxT> GraphQLType<CtxT> for &'a T where T: GraphQLType<CtxT> {
fn name() -> Option<&'static str> {
T::name()
@ -71,3 +77,9 @@ impl<'a, T> ToInputValue for &'a T where T: ToInputValue {
(**self).to()
}
}
impl<'a, T> IntoFieldResult<&'a T> for &'a T {
fn into(self) -> FieldResult<&'a T> {
Ok(self)
}
}

View file

@ -3,7 +3,7 @@ use value::Value;
use schema::meta::MetaType;
use executor::{Executor, Registry};
use executor::{Executor, Registry, FieldResult, IntoFieldResult};
use types::base::GraphQLType;
/// An ID as defined by the GraphQL specification
@ -60,6 +60,13 @@ impl<'a> ToInputValue for &'a str {
}
}
impl<'a> IntoFieldResult<&'a str> for &'a str {
fn into(self) -> FieldResult<&'a str> {
Ok(self)
}
}
graphql_scalar!(bool as "Boolean" {
resolve(&self) -> Value {
@ -119,3 +126,9 @@ impl FromInputValue for () {
None
}
}
impl IntoFieldResult<()> for () {
fn into(self) -> FieldResult<()> {
Ok(self)
}
}