From 5caea1f90802f123e6ecfdb76f42db54f7cb0a4b Mon Sep 17 00:00:00 2001 From: Magnus Hallin <mhallin@fastmail.com> Date: Sun, 16 Oct 2016 16:02:40 +0200 Subject: [PATCH] Make FieldResults in field resolvers optional :D --- README.md | 2 +- src/executor.rs | 28 ++++++ src/executor_tests/introspection.rs | 19 ++-- src/lib.rs | 32 ++++--- src/macros/enums.rs | 6 ++ src/macros/field.rs | 2 +- src/macros/interface.rs | 27 +++--- src/macros/object.rs | 77 +++++++-------- src/macros/scalar.rs | 6 ++ src/macros/tests/args.rs | 31 +++--- src/macros/tests/enums.rs | 13 ++- src/macros/tests/field.rs | 17 ++-- src/macros/tests/input_object.rs | 5 +- src/macros/tests/object.rs | 50 +++++----- src/macros/tests/scalar.rs | 9 +- src/schema/schema.rs | 144 ++++++++++++++-------------- src/tests/schema.rs | 69 +++++++------ src/types/containers.rs | 20 +++- src/types/pointers.rs | 14 ++- src/types/scalars.rs | 15 ++- 20 files changed, 328 insertions(+), 258 deletions(-) diff --git a/README.md b/README.md index 385deeae..ad45ff9d 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/executor.rs b/src/executor.rs index 2c0c4c98..ddb6ed50 100644 --- a/src/executor.rs +++ b/src/executor.rs @@ -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 { diff --git a/src/executor_tests/introspection.rs b/src/executor_tests/introspection.rs index 70058a7a..1d0ad324 100644 --- a/src/executor_tests/introspection.rs +++ b/src/executor_tests/introspection.rs @@ -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())); diff --git a/src/lib.rs b/src/lib.rs index 4db4caeaf..3397114d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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; diff --git a/src/macros/enums.rs b/src/macros/enums.rs index 77f8fbba..e758bff8 100644 --- a/src/macros/enums.rs +++ b/src/macros/enums.rs @@ -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 diff --git a/src/macros/field.rs b/src/macros/field.rs index 8ba74b65..d983096d 100644 --- a/src/macros/field.rs +++ b/src/macros/field.rs @@ -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); diff --git a/src/macros/interface.rs b/src/macros/interface.rs index 74f619aa..5f1f839d 100644 --- a/src/macros/interface.rs +++ b/src/macros/interface.rs @@ -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 )*); diff --git a/src/macros/object.rs b/src/macros/object.rs index 77d9e62e..8d6df382 100644 --- a/src/macros/object.rs +++ b/src/macros/object.rs @@ -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) + } + } }; ( diff --git a/src/macros/scalar.rs b/src/macros/scalar.rs index a28e17cc..3948b9fd 100644 --- a/src/macros/scalar.rs +++ b/src/macros/scalar.rs @@ -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 diff --git a/src/macros/tests/args.rs b/src/macros/tests/args.rs index 54741a95..12396943 100644 --- a/src/macros/tests/args.rs +++ b/src/macros/tests/args.rs @@ -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) diff --git a/src/macros/tests/enums.rs b/src/macros/tests/enums.rs index 5b667dc5..0782e62d 100644 --- a/src/macros/tests/enums.rs +++ b/src/macros/tests/enums.rs @@ -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>)) -> () { diff --git a/src/macros/tests/field.rs b/src/macros/tests/field.rs index 72575bb2..c004bccc 100644 --- a/src/macros/tests/field.rs +++ b/src/macros/tests/field.rs @@ -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 {}), diff --git a/src/macros/tests/input_object.rs b/src/macros/tests/input_object.rs index 86ab5573..0e8d1605 100644 --- a/src/macros/tests/input_object.rs +++ b/src/macros/tests/input_object.rs @@ -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 } }); diff --git a/src/macros/tests/object.rs b/src/macros/tests/object.rs index 36157273..cfcecccb 100644 --- a/src/macros/tests/object.rs +++ b/src/macros/tests/object.rs @@ -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 {} } }); diff --git a/src/macros/tests/scalar.rs b/src/macros/tests/scalar.rs index 8a33522e..bc1fb2ef 100644 --- a/src/macros/tests/scalar.rs +++ b/src/macros/tests/scalar.rs @@ -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>) -> () { diff --git a/src/schema/schema.rs b/src/schema/schema.rs index 34fca9ca..28588a59 100644 --- a/src/schema/schema.rs +++ b/src/schema/schema.rs @@ -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 } }); diff --git a/src/tests/schema.rs b/src/tests/schema.rs index 210c19e2..cde33b12 100644 --- a/src/tests/schema.rs +++ b/src/tests/schema.rs @@ -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()) } }); diff --git a/src/types/containers.rs b/src/types/containers.rs index 744d06e8..11501524 100644 --- a/src/types/containers.rs +++ b/src/types/containers.rs @@ -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) + } +} diff --git a/src/types/pointers.rs b/src/types/pointers.rs index 6882d5ca..01a7e5dc 100644 --- a/src/types/pointers.rs +++ b/src/types/pointers.rs @@ -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) + } +} diff --git a/src/types/scalars.rs b/src/types/scalars.rs index ba1a2d6e..f4b7eb9d 100644 --- a/src/types/scalars.rs +++ b/src/types/scalars.rs @@ -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) + } +}