Pass the executor immutably through resolution

The executor is now using internal mutability through RwLock to make
it thread-safe.
This commit is contained in:
Magnus Hallin 2016-12-11 20:09:52 +01:00
parent cd33093dee
commit b61ca14f53
16 changed files with 55 additions and 51 deletions

View file

@ -1,5 +1,6 @@
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::RwLock;
use ::GraphQLError;
use ast::{InputValue, ToInputValue, Document, Selection, Fragment, Definition, Type, FromInputValue, OperationType};
@ -41,7 +42,7 @@ pub struct Executor<'a, CtxT> where CtxT: 'a {
current_selection_set: Option<Vec<Selection>>,
schema: &'a SchemaType,
context: &'a CtxT,
errors: &'a mut Vec<ExecutionError>,
errors: &'a RwLock<Vec<ExecutionError>>,
field_path: FieldPath<'a>,
}
@ -81,7 +82,7 @@ impl<T> IntoFieldResult<T> for FieldResult<T> {
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 {
pub fn resolve<T: GraphQLType<CtxT>>(&self, value: &T) -> ExecutionResult {
Ok(value.resolve(
match self.current_selection_set {
Some(ref sel) => Some(sel.clone()),
@ -93,7 +94,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
/// Resolve a single arbitrary value into a return value
///
/// If the field fails to resolve, `null` will be returned.
pub fn resolve_into_value<T: GraphQLType<CtxT>>(&mut self, value: &T) -> Value {
pub fn resolve_into_value<T: GraphQLType<CtxT>>(&self, value: &T) -> Value {
match self.resolve(value) {
Ok(v) => v,
Err(e) => {
@ -108,7 +109,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
///
/// This can be used to connect different types, e.g. from different Rust
/// libraries, that require different context types.
pub fn replaced_context<'b, NewCtxT>(&'b mut self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT> {
pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT> {
Executor {
fragments: self.fragments,
variables: self.variables,
@ -122,7 +123,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
#[doc(hidden)]
pub fn sub_executor(
&mut self,
&self,
field_name: Option<String>,
location: SourcePosition,
selection_set: Option<Vec<Selection>>,
@ -167,11 +168,13 @@ impl<'a, CtxT> Executor<'a, CtxT> {
}
/// Add an error to the execution engine
pub fn push_error(&mut self, error: String, location: SourcePosition) {
pub fn push_error(&self, error: String, location: SourcePosition) {
let mut path = Vec::new();
self.field_path.construct_path(&mut path);
self.errors.push(ExecutionError {
let mut errors = self.errors.write().unwrap();
errors.push(ExecutionError {
location: location,
path: path,
message: error,
@ -261,17 +264,17 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
None => return Err(GraphQLError::UnknownOperationName),
};
let mut errors = Vec::new();
let errors = RwLock::new(Vec::new());
let value;
{
let mut executor = Executor {
let executor = Executor {
fragments: &fragments.into_iter().map(|f| (f.item.name.item.clone(), f.item)).collect(),
variables: variables,
current_selection_set: Some(op.item.selection_set),
schema: &root_node.schema,
context: context,
errors: &mut errors,
errors: &errors,
field_path: FieldPath::Root(op.start),
};
@ -281,6 +284,7 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
};
}
let mut errors = errors.into_inner().unwrap();
errors.sort();
Ok((value, errors))

View file

@ -167,7 +167,7 @@ mod threads_context_correctly {
struct Schema;
graphql_object!(Schema: String |&self| {
field a(&mut executor) -> String { executor.context().clone() }
field a(&executor) -> String { executor.context().clone() }
});
#[test]

View file

@ -67,7 +67,7 @@ graphql_object!(User: Database |&self| {
//
// In this example, the context is used to convert the friend_ids array
// into actual User objects.
field friends(&mut executor) -> Vec<&User> {
field friends(&executor) -> Vec<&User> {
self.friend_ids.iter()
.filter_map(|id| executor.context().users.get(id))
.collect()
@ -79,7 +79,7 @@ graphql_object!(User: Database |&self| {
graphql_object!(QueryRoot: Database |&self| {
// Arguments work just like they do on functions.
field user(&mut executor, id: String) -> Option<&User> {
field user(&executor, id: String) -> Option<&User> {
executor.context().users.get(&id)
}
});
@ -121,7 +121,7 @@ use juniper::iron_handlers::GraphQLHandler;
# Ok(&self.name)
# }
#
# field friends(&mut executor) -> FieldResult<Vec<&User>> {
# field friends(&executor) -> FieldResult<Vec<&User>> {
# Ok(self.friend_ids.iter()
# .filter_map(|id| executor.context().users.get(id))
# .collect())
@ -129,7 +129,7 @@ use juniper::iron_handlers::GraphQLHandler;
# });
#
# graphql_object!(QueryRoot: Database |&self| {
# field user(&mut executor, id: String) -> FieldResult<Option<&User>> {
# field user(&executor, id: String) -> FieldResult<Option<&User>> {
# Ok(executor.context().users.get(&id))
# }
# });

View file

@ -15,9 +15,9 @@ macro_rules! __graphql__args {
(
@assign_arg_vars,
$args:ident, $executorvar:ident, &mut $exec:ident $($rest:tt)*
$args:ident, $executorvar:ident, &$exec:ident $($rest:tt)*
) => {
let __graphql__args!(@as_pattern, $exec) = &mut $executorvar;
let __graphql__args!(@as_pattern, $exec) = &$executorvar;
__graphql__args!(@assign_arg_vars, $args, $executorvar, $($rest)*);
};
@ -59,7 +59,7 @@ macro_rules! __graphql__args {
(
@apply_args,
$reg:expr, $base:expr, ( &mut executor $( $rest:tt )* )
$reg:expr, $base:expr, ( &executor $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,

View file

@ -81,7 +81,7 @@ macro_rules! graphql_enum {
.into_meta()
}
fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &mut $crate::Executor<CtxT>) -> $crate::Value {
fn resolve(&self, _: Option<Vec<$crate::Selection>>, _: &$crate::Executor<CtxT>) -> $crate::Value {
match self {
$(
&graphql_enum!(@as_pattern, $eval) =>

View file

@ -249,7 +249,7 @@ macro_rules! graphql_interface {
#[allow(unused_variables)]
#[allow(unused_mut)]
fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &mut $crate::Executor<$ctxt>) -> $crate::ExecutionResult {
fn resolve_field(&$mainself, field: &str, args: &$crate::Arguments, mut executor: &$crate::Executor<$ctxt>) -> $crate::ExecutionResult {
__graphql__build_field_matches!(
($outname, $mainself, field, args, executor),
(),
@ -267,7 +267,7 @@ macro_rules! graphql_interface {
&$mainself,
type_name: &str,
_: Option<Vec<$crate::Selection>>,
executor: &mut $crate::Executor<$ctxt>,
executor: &$crate::Executor<$ctxt>,
)
-> $crate::ExecutionResult
{

View file

@ -194,7 +194,7 @@ string as documentation on the field.
### Field arguments
```text
&mut executor
&executor
arg_name: ArgType
arg_name = default_value: ArgType
arg_name: ArgType as "Argument description"
@ -202,7 +202,7 @@ arg_name = default_value: ArgType as "Argument description"
```
Field arguments can take many forms. If the field needs access to the executor
or context, it can take an [Executor][1] instance by specifying `&mut executor`
or context, it can take an [Executor][1] instance by specifying `&executor`
as the first argument.
The other cases are similar to regular Rust arguments, with two additions:
@ -382,7 +382,7 @@ macro_rules! graphql_object {
&$mainself,
field: &str,
args: &$crate::Arguments,
mut executor: &mut $crate::Executor<$ctxt>
executor: &$crate::Executor<$ctxt>
)
-> $crate::ExecutionResult
{

View file

@ -76,7 +76,7 @@ macro_rules! graphql_scalar {
fn resolve(
&$resolve_selfvar,
_: Option<Vec<$crate::Selection>>,
_: &mut $crate::Executor<CtxT>) -> $crate::Value {
_: &$crate::Executor<CtxT>) -> $crate::Value {
$resolve_body
}
}

View file

@ -20,8 +20,8 @@ Syntax to validate:
graphql_object!(Root: () |&self| {
field simple() -> i64 { 0 }
field exec_arg(&mut executor) -> i64 { 0 }
field exec_arg_and_more(&mut executor, arg: i64) -> i64 { 0 }
field exec_arg(&executor) -> i64 { 0 }
field exec_arg_and_more(&executor, arg: i64) -> i64 { 0 }
field single_arg(arg: i64) -> i64 { 0 }
field multi_args(

View file

@ -136,7 +136,7 @@ macro_rules! graphql_union {
&$mainself,
type_name: &str,
_: Option<Vec<$crate::Selection>>,
executor: &mut $crate::Executor<$ctxt>,
executor: &$crate::Executor<$ctxt>,
)
-> $crate::ExecutionResult
{

View file

@ -19,7 +19,7 @@ impl<CtxT, QueryT, MutationT> GraphQLType<CtxT> for RootNode<CtxT, QueryT, Mutat
QueryT::meta(registry)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult {
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult {
match field {
"__schema" => executor.replaced_context(&self.schema).resolve(&self.schema),
"__type" => {
@ -99,7 +99,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
}
}
field interfaces(&mut executor) -> Option<Vec<TypeType>> {
field interfaces(&executor) -> Option<Vec<TypeType>> {
match *self {
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
let schema = executor.context();
@ -112,7 +112,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType as "__Type" |&self| {
}
}
field possible_types(&mut executor) -> Option<Vec<TypeType>> {
field possible_types(&executor) -> Option<Vec<TypeType>> {
let schema = executor.context();
match *self {
TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => {
@ -162,7 +162,7 @@ graphql_object!(Field: SchemaType as "__Field" |&self| {
self.arguments.as_ref().map_or_else(|| Vec::new(), |v| v.iter().collect())
}
field type(&mut executor) -> TypeType {
field type(&executor) -> TypeType {
executor.context().make_type(&self.field_type)
}
@ -184,7 +184,7 @@ graphql_object!(Argument: SchemaType as "__InputValue" |&self| {
&self.description
}
field type(&mut executor) -> TypeType {
field type(&executor) -> TypeType {
executor.context().make_type(&self.arg_type)
}

View file

@ -17,7 +17,7 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
Some(self.name())
}
field friends(&mut executor) -> Vec<&Character>
field friends(&executor) -> Vec<&Character>
as "The friends of the character" {
executor.context().get_friends(self.as_character())
}
@ -45,7 +45,7 @@ graphql_object!(<'a> &'a Human: Database as "Human" |&self| {
Some(self.name())
}
field friends(&mut executor) -> Vec<&Character>
field friends(&executor) -> Vec<&Character>
as "The friends of the human" {
executor.context().get_friends(self.as_character())
}
@ -72,7 +72,7 @@ graphql_object!(<'a> &'a Droid: Database as "Droid" |&self| {
Some(self.name())
}
field friends(&mut executor) -> Vec<&Character>
field friends(&executor) -> Vec<&Character>
as "The friends of the droid" {
executor.context().get_friends(self.as_character())
}

View file

@ -165,7 +165,7 @@ impl GraphQLType<Database> for User {
&self,
field_name: &str,
args: &Arguments,
executor: &mut Executor<Database>
executor: &Executor<Database>
)
-> ExecutionResult
{
@ -221,7 +221,7 @@ pub trait GraphQLType<CtxT>: Sized {
///
/// The default implementation panics.
#[allow(unused_variables)]
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &mut Executor<CtxT>)
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &Executor<CtxT>)
-> ExecutionResult
{
panic!("resolve_field must be implemented by object types");
@ -234,7 +234,7 @@ pub trait GraphQLType<CtxT>: Sized {
///
/// The default implementation panics.
#[allow(unused_variables)]
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
fn resolve_into_type(&self, type_name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
if Self::name().unwrap() == type_name {
Ok(self.resolve(selection_set, executor))
} else {
@ -260,7 +260,7 @@ pub trait GraphQLType<CtxT>: Sized {
/// The default implementation uses `resolve_field` to resolve all fields,
/// including those through fragment expansion, for object types. For
/// non-object types, this method panics.
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
if let Some(selection_set) = selection_set {
let mut result = HashMap::new();
resolve_selection_set_into(self, selection_set, executor, &mut result);
@ -275,7 +275,7 @@ pub trait GraphQLType<CtxT>: Sized {
fn resolve_selection_set_into<T, CtxT>(
instance: &T,
selection_set: Vec<Selection>,
executor: &mut Executor<CtxT>,
executor: &Executor<CtxT>,
result: &mut HashMap<String, Value>)
where T: GraphQLType<CtxT>
{

View file

@ -14,7 +14,7 @@ impl<T, CtxT> GraphQLType<CtxT> for Option<T> where T: GraphQLType<CtxT> {
registry.build_nullable_type::<T>().into_meta()
}
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
match *self {
Some(ref obj) => executor.resolve_into_value(obj),
None => Value::null(),
@ -59,7 +59,7 @@ impl<T, CtxT> GraphQLType<CtxT> for Vec<T> where T: GraphQLType<CtxT> {
registry.build_list_type::<T>().into_meta()
}
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
Value::list(
self.iter().map(|e| executor.resolve_into_value(e)).collect()
)
@ -111,7 +111,7 @@ impl<'a, T, CtxT> GraphQLType<CtxT> for &'a [T] where T: GraphQLType<CtxT> {
registry.build_list_type::<T>().into_meta()
}
fn resolve(&self, _: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, _: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
Value::list(
self.iter().map(|e| executor.resolve_into_value(e)).collect()
)

View file

@ -14,16 +14,16 @@ impl<T, CtxT> GraphQLType<CtxT> for Box<T> where T: GraphQLType<CtxT> {
T::meta(registry)
}
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
(**self).resolve_into_type(name, selection_set, executor)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
{
(**self).resolve_field(field, args, executor)
}
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
(**self).resolve(selection_set, executor)
}
}
@ -58,16 +58,16 @@ impl<'a, T, CtxT> GraphQLType<CtxT> for &'a T where T: GraphQLType<CtxT> {
T::meta(registry)
}
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> ExecutionResult {
fn resolve_into_type(&self, name: &str, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> ExecutionResult {
(**self).resolve_into_type(name, selection_set, executor)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &mut Executor<CtxT>) -> ExecutionResult
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
{
(**self).resolve_field(field, args, executor)
}
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &mut Executor<CtxT>) -> Value {
fn resolve(&self, selection_set: Option<Vec<Selection>>, executor: &Executor<CtxT>) -> Value {
(**self).resolve(selection_set, executor)
}
}

View file

@ -49,7 +49,7 @@ impl<'a, CtxT> GraphQLType<CtxT> for &'a str {
registry.build_scalar_type::<String>().into_meta()
}
fn resolve(&self, _: Option<Vec<Selection>>, _: &mut Executor<CtxT>) -> Value {
fn resolve(&self, _: Option<Vec<Selection>>, _: &Executor<CtxT>) -> Value {
Value::string(self)
}
}