Refactor GraphQLType::resolve to return Result<>

This unifies the output type of all resolvers.

Required for future step of making the output a associated type.
This commit is contained in:
Christoph Herzog 2019-11-14 09:51:32 +01:00
parent 6fcdd32c84
commit ad16093b88
8 changed files with 40 additions and 36 deletions
juniper/src
juniper_codegen/src

View file

@ -361,7 +361,7 @@ where
where where
T: GraphQLType<S, Context = CtxT>, T: GraphQLType<S, Context = CtxT>,
{ {
Ok(value.resolve(info, self.current_selection_set, self)) value.resolve(info, self.current_selection_set, self)
} }
/// Resolve a single arbitrary value into an `ExecutionResult` /// Resolve a single arbitrary value into an `ExecutionResult`

View file

@ -107,8 +107,8 @@ macro_rules! graphql_scalar {
_: &$crate::Executor< _: &$crate::Executor<
Self::Context, Self::Context,
$crate::__juniper_insert_generic!($($scalar)+) $crate::__juniper_insert_generic!($($scalar)+)
>) -> $crate::Value<$crate::__juniper_insert_generic!($($scalar)+)> { >) -> $crate::ExecutionResult<$crate::__juniper_insert_generic!($($scalar)+)> {
$resolve_body Ok($resolve_body)
} }
}); });

View file

@ -59,16 +59,17 @@ where
info: &Self::TypeInfo, info: &Self::TypeInfo,
selection_set: Option<&[Selection<S>]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context, S>, executor: &Executor<Self::Context, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
use crate::{types::base::resolve_selection_set_into, value::Object}; use crate::{types::base::resolve_selection_set_into, value::Object};
if let Some(selection_set) = selection_set { if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len()); let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) { if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
Value::Object(result) Ok(Value::Object(result))
} else { } else {
Value::null() Ok(Value::null())
} }
} else { } else {
// TODO: this panic seems useless, investigate why it is here.
panic!("resolve() must be implemented by non-object output types"); panic!("resolve() must be implemented by non-object output types");
} }
} }

View file

@ -165,7 +165,7 @@ struct Database { users: HashMap<String, User> }
impl Context for Database {} impl Context for Database {}
impl GraphQLType for User impl GraphQLType<DefaultScalarValue> for User
{ {
type Context = Database; type Context = Database;
type TypeInfo = (); type TypeInfo = ();
@ -298,7 +298,7 @@ where
executor: &Executor<Self::Context, S>, executor: &Executor<Self::Context, S>,
) -> ExecutionResult<S> { ) -> ExecutionResult<S> {
if Self::name(info).unwrap() == type_name { if Self::name(info).unwrap() == type_name {
Ok(self.resolve(info, selection_set, executor)) self.resolve(info, selection_set, executor)
} else { } else {
panic!("resolve_into_type must be implemented by unions and interfaces"); panic!("resolve_into_type must be implemented by unions and interfaces");
} }
@ -318,23 +318,26 @@ where
/// of the object should simply be returned. /// of the object should simply be returned.
/// ///
/// For objects, all fields in the selection set should be resolved. /// For objects, all fields in the selection set should be resolved.
///
/// The default implementation uses `resolve_field` to resolve all fields, /// The default implementation uses `resolve_field` to resolve all fields,
/// including those through fragment expansion, for object types. For /// including those through fragment expansion.
/// non-object types, this method panics. ///
/// Since the GraphQL spec specificies that errors during field processing
/// should result in a null-value, this might return Ok(Null) in case of
/// failure. Errors are recorded internally.
fn resolve( fn resolve(
&self, &self,
info: &Self::TypeInfo, info: &Self::TypeInfo,
selection_set: Option<&[Selection<S>]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context, S>, executor: &Executor<Self::Context, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
if let Some(selection_set) = selection_set { if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len()); let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) { let out = if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
Value::Object(result) Value::Object(result)
} else { } else {
Value::null() Value::null()
} };
Ok(out)
} else { } else {
panic!("resolve() must be implemented by non-object output types"); panic!("resolve() must be implemented by non-object output types");
} }

View file

@ -2,6 +2,7 @@ use crate::{
ast::{FromInputValue, InputValue, Selection, ToInputValue}, ast::{FromInputValue, InputValue, Selection, ToInputValue},
schema::meta::MetaType, schema::meta::MetaType,
value::{ScalarValue, Value}, value::{ScalarValue, Value},
executor::ExecutionResult,
}; };
use crate::{ use crate::{
@ -33,10 +34,10 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection<S>]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT, S>, executor: &Executor<CtxT, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
match *self { match *self {
Some(ref obj) => executor.resolve_into_value(info, obj), Some(ref obj) => executor.resolve(info, obj),
None => Value::null(), None => Ok(Value::null()),
} }
} }
} }
@ -94,7 +95,7 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection<S>]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT, S>, executor: &Executor<CtxT, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
resolve_into_list(executor, info, self.iter()) resolve_into_list(executor, info, self.iter())
} }
} }
@ -161,7 +162,7 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection<S>]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT, S>, executor: &Executor<CtxT, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
resolve_into_list(executor, info, self.iter()) resolve_into_list(executor, info, self.iter())
} }
} }
@ -180,7 +181,7 @@ fn resolve_into_list<S, T, I>(
executor: &Executor<T::Context, S>, executor: &Executor<T::Context, S>,
info: &T::TypeInfo, info: &T::TypeInfo,
iter: I, iter: I,
) -> Value<S> ) -> ExecutionResult<S>
where where
S: ScalarValue, S: ScalarValue,
I: Iterator<Item = T> + ExactSizeIterator, I: Iterator<Item = T> + ExactSizeIterator,
@ -191,19 +192,17 @@ where
.list_contents() .list_contents()
.expect("Current type is not a list type") .expect("Current type is not a list type")
.is_non_null(); .is_non_null();
let mut result = Vec::with_capacity(iter.len()); let mut result = Vec::with_capacity(iter.len());
for o in iter { for o in iter {
let value = executor.resolve_into_value(info, &o); match executor.resolve(info, &o) {
if stop_on_null && value.is_null() { Ok(value) if stop_on_null && value.is_null() => { return Ok(value) },
return value; Ok(value) => { result.push(value) },
Err(e) => { return Err(e) },
} }
result.push(value);
} }
Value::list(result) Ok(Value::list(result))
} }
#[cfg(feature = "async")] #[cfg(feature = "async")]

View file

@ -52,7 +52,7 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection<S>]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT, S>, executor: &Executor<CtxT, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }
@ -124,7 +124,7 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection<S>]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT, S>, executor: &Executor<CtxT, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }
@ -211,7 +211,7 @@ where
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection<S>]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<T::Context, S>, executor: &Executor<T::Context, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }

View file

@ -3,7 +3,7 @@ use std::{char, convert::From, marker::PhantomData, ops::Deref, u32};
use crate::{ use crate::{
ast::{InputValue, Selection, ToInputValue}, ast::{InputValue, Selection, ToInputValue},
executor::{Executor, Registry}, executor::{Executor, Registry, ExecutionResult},
parser::{LexerError, ParseError, ScalarToken, Token}, parser::{LexerError, ParseError, ScalarToken, Token},
schema::meta::MetaType, schema::meta::MetaType,
types::base::GraphQLType, types::base::GraphQLType,
@ -189,8 +189,8 @@ where
_: &(), _: &(),
_: Option<&[Selection<S>]>, _: Option<&[Selection<S>]>,
_: &Executor<Self::Context, S>, _: &Executor<Self::Context, S>,
) -> Value<S> { ) -> ExecutionResult<S> {
Value::scalar(String::from(*self)) Ok(Value::scalar(String::from(*self)))
} }
} }

View file

@ -257,10 +257,11 @@ pub fn impl_enum(ast: &syn::DeriveInput, is_internal: bool) -> TokenStream {
_: &(), _: &(),
_: Option<&[#juniper_path::Selection<__S>]>, _: Option<&[#juniper_path::Selection<__S>]>,
_: &#juniper_path::Executor<Self::Context, __S> _: &#juniper_path::Executor<Self::Context, __S>
) -> #juniper_path::Value<__S> { ) -> #juniper_path::ExecutionResult<__S> {
match self { let v = match self {
#resolves #resolves
} };
Ok(v)
} }
} }