use std::borrow::Cow;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::fmt::Display;
use std::sync::RwLock;
use fnv::FnvHashMap;
use ast::{
Definition, Document, Fragment, FromInputValue, InputValue, OperationType, Selection,
ToInputValue, Type,
};
use parser::SourcePosition;
use value::Value;
use GraphQLError;
use schema::meta::{
Argument, DeprecationStatus, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta,
ListMeta, MetaType, NullableMeta, ObjectMeta, PlaceholderMeta, ScalarMeta, UnionMeta,
};
use schema::model::{RootNode, SchemaType, TypeType};
use types::base::GraphQLType;
use types::name::Name;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
mod look_ahead;
pub use self::look_ahead::{
Applies, ChildSelection, ConcreteLookAheadSelection, LookAheadArgument, LookAheadMethods,
LookAheadSelection, LookAheadValue,
};
pub struct Registry<'r, S = DefaultScalarValue> {
pub types: FnvHashMap<Name, MetaType<'r, S>>,
}
#[derive(Clone)]
pub enum FieldPath<'a> {
Root(SourcePosition),
Field(&'a str, SourcePosition, &'a FieldPath<'a>),
}
pub struct Executor<'a, CtxT, S = DefaultScalarValue>
where
CtxT: 'a,
S: 'a,
{
fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
variables: &'a Variables<S>,
current_selection_set: Option<&'a [Selection<'a, S>]>,
parent_selection_set: Option<&'a [Selection<'a, S>]>,
current_type: TypeType<'a, S>,
schema: &'a SchemaType<'a, S>,
context: &'a CtxT,
errors: &'a RwLock<Vec<ExecutionError<S>>>,
field_path: FieldPath<'a>,
}
#[derive(Debug, PartialEq)]
pub struct ExecutionError<S> {
location: SourcePosition,
path: Vec<String>,
error: FieldError<S>,
}
impl<S> Eq for ExecutionError<S> where Self: PartialEq {}
impl<S> ExecutionError<S> {
pub fn at_origin(error: FieldError<S>) -> ExecutionError<S> {
ExecutionError {
location: SourcePosition::new_origin(),
path: Vec::new(),
error: error,
}
}
}
impl<S> PartialOrd for ExecutionError<S>
where
Self: PartialEq,
{
fn partial_cmp(&self, other: &ExecutionError<S>) -> Option<Ordering> {
(&self.location, &self.path, &self.error.message).partial_cmp(&(
&other.location,
&other.path,
&other.error.message,
))
}
}
impl<S> Ord for ExecutionError<S>
where
Self: Eq,
{
fn cmp(&self, other: &ExecutionError<S>) -> Ordering {
(&self.location, &self.path, &self.error.message).cmp(&(
&other.location,
&other.path,
&other.error.message,
))
}
}
#[derive(Debug, PartialEq)]
pub struct FieldError<S = DefaultScalarValue> {
message: String,
extensions: Value<S>,
}
impl<T: Display, S> From<T> for FieldError<S>
where
S: ::value::ScalarValue,
{
fn from(e: T) -> FieldError<S> {
FieldError {
message: format!("{}", e),
extensions: Value::null(),
}
}
}
impl<S> FieldError<S> {
pub fn new<T: Display>(e: T, extensions: Value<S>) -> FieldError<S> {
FieldError {
message: format!("{}", e),
extensions,
}
}
#[doc(hidden)]
pub fn message(&self) -> &str {
&self.message
}
#[doc(hidden)]
pub fn extensions(&self) -> &Value<S> {
&self.extensions
}
}
pub type FieldResult<T, S = DefaultScalarValue> = Result<T, FieldError<S>>;
pub type ExecutionResult<S = DefaultScalarValue> = Result<Value<S>, FieldError<S>>;
pub type Variables<S = DefaultScalarValue> = HashMap<String, InputValue<S>>;
pub trait IntoFieldError<S = DefaultScalarValue> {
#[doc(hidden)]
fn into_field_error(self) -> FieldError<S>;
}
impl<S> IntoFieldError<S> for FieldError<S> {
fn into_field_error(self) -> FieldError<S> {
self
}
}
#[doc(hidden)]
pub trait IntoResolvable<'a, S, T: GraphQLType<S>, C>: Sized
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
#[doc(hidden)]
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S>;
}
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for T
where
T: GraphQLType<S>,
S: ScalarValue,
T::Context: FromContext<C>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
Ok(Some((FromContext::from(ctx), self)))
}
}
impl<'a, S, T, C, E: IntoFieldError<S>> IntoResolvable<'a, S, T, C> for Result<T, E>
where
S: ScalarValue,
T: GraphQLType<S>,
T::Context: FromContext<C>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(|v: T| Some((<T::Context as FromContext<C>>::from(ctx), v)))
.map_err(|e| e.into_field_error())
}
}
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for (&'a T::Context, T)
where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
Ok(Some(self))
}
}
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C> for Option<(&'a T::Context, T)>
where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
Ok(self.map(|(ctx, v)| (ctx, Some(v))))
}
}
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'a T::Context, T), S>
where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(Some)
}
}
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>, S>
where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
self.map(|o| o.map(|(ctx, v)| (ctx, Some(v))))
}
}
pub trait FromContext<T> {
fn from(value: &T) -> &Self;
}
pub trait Context {}
impl<'a, C: Context> Context for &'a C {}
static NULL_CONTEXT: () = ();
impl<T> FromContext<T> for () {
fn from(_: &T) -> &Self {
&NULL_CONTEXT
}
}
impl<T> FromContext<T> for T
where
T: Context,
{
fn from(value: &T) -> &Self {
value
}
}
impl<'a, CtxT, S> Executor<'a, CtxT, S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
pub fn resolve_with_ctx<NewCtxT, T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
where
NewCtxT: FromContext<CtxT>,
T: GraphQLType<S, Context = NewCtxT>,
{
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
.resolve(info, value)
}
pub fn resolve<T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
where
T: GraphQLType<S, Context = CtxT>,
{
Ok(value.resolve(info, self.current_selection_set, self))
}
pub fn resolve_into_value<T>(&self, info: &T::TypeInfo, value: &T) -> Value<S>
where
T: GraphQLType<S, Context = CtxT>,
{
match self.resolve(info, value) {
Ok(v) => v,
Err(e) => {
self.push_error(e);
Value::null()
}
}
}
pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT, S> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: self.current_selection_set,
parent_selection_set: self.parent_selection_set,
current_type: self.current_type.clone(),
schema: self.schema,
context: ctx,
errors: self.errors,
field_path: self.field_path.clone(),
}
}
#[doc(hidden)]
pub fn field_sub_executor(
&self,
field_alias: &'a str,
field_name: &'a str,
location: SourcePosition,
selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT, S> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: selection_set,
parent_selection_set: self.current_selection_set,
current_type: self.schema.make_type(
&self
.current_type
.innermost_concrete()
.field_by_name(field_name)
.expect("Field not found on inner type")
.field_type,
),
schema: self.schema,
context: self.context,
errors: self.errors,
field_path: FieldPath::Field(field_alias, location, &self.field_path),
}
}
#[doc(hidden)]
pub fn type_sub_executor(
&self,
type_name: Option<&'a str>,
selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT, S> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: selection_set,
parent_selection_set: self.current_selection_set,
current_type: match type_name {
Some(type_name) => self.schema.type_by_name(type_name).expect("Type not found"),
None => self.current_type.clone(),
},
schema: self.schema,
context: self.context,
errors: self.errors,
field_path: self.field_path.clone(),
}
}
pub fn context(&self) -> &'a CtxT {
self.context
}
pub fn schema(&self) -> &'a SchemaType<S> {
self.schema
}
#[doc(hidden)]
pub fn current_type(&self) -> &TypeType<'a, S> {
&self.current_type
}
#[doc(hidden)]
pub fn variables(&self) -> &'a Variables<S> {
self.variables
}
#[doc(hidden)]
pub fn fragment_by_name(&self, name: &str) -> Option<&'a Fragment<S>> {
self.fragments.get(name).map(|f| *f)
}
pub fn location(&self) -> &SourcePosition {
self.field_path.location()
}
pub fn push_error(&self, error: FieldError<S>) {
let location = self.location().clone();
self.push_error_at(error, location);
}
pub fn push_error_at(&self, error: FieldError<S>, location: SourcePosition) {
let mut path = Vec::new();
self.field_path.construct_path(&mut path);
let mut errors = self.errors.write().unwrap();
errors.push(ExecutionError {
location: location,
path: path,
error: error,
});
}
pub fn look_ahead(&'a self) -> LookAheadSelection<'a, S> {
self.parent_selection_set
.map(|p| {
LookAheadSelection::build_from_selection(&p[0], self.variables, self.fragments)
}).unwrap_or_else(|| LookAheadSelection {
name: self.current_type.innermost_concrete().name().unwrap_or(""),
alias: None,
arguments: Vec::new(),
children: self
.current_selection_set
.map(|s| {
s.iter()
.map(|s| ChildSelection {
inner: LookAheadSelection::build_from_selection(
s,
self.variables,
self.fragments,
),
applies_for: Applies::All,
}).collect()
}).unwrap_or_else(Vec::new),
})
}
}
impl<'a> FieldPath<'a> {
fn construct_path(&self, acc: &mut Vec<String>) {
match *self {
FieldPath::Root(_) => (),
FieldPath::Field(name, _, parent) => {
parent.construct_path(acc);
acc.push(name.to_owned());
}
}
}
fn location(&self) -> &SourcePosition {
match *self {
FieldPath::Root(ref pos) | FieldPath::Field(_, ref pos, _) => pos,
}
}
}
impl<S> ExecutionError<S> {
#[doc(hidden)]
pub fn new(location: SourcePosition, path: &[&str], error: FieldError<S>) -> ExecutionError<S> {
ExecutionError {
location: location,
path: path.iter().map(|s| (*s).to_owned()).collect(),
error: error,
}
}
pub fn error(&self) -> &FieldError<S> {
&self.error
}
pub fn location(&self) -> &SourcePosition {
&self.location
}
pub fn path(&self) -> &[String] {
&self.path
}
}
pub fn execute_validated_query<'a, QueryT, MutationT, CtxT, S>(
document: Document<S>,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT, S>,
variables: &Variables<S>,
context: &CtxT,
) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
where
S: ScalarValue,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut fragments = vec![];
let mut operation = None;
for def in document {
match def {
Definition::Operation(op) => {
if operation_name.is_none() && operation.is_some() {
return Err(GraphQLError::MultipleOperationsProvided);
}
let move_op = operation_name.is_none()
|| op.item.name.as_ref().map(|s| s.item) == operation_name;
if move_op {
operation = Some(op);
}
}
Definition::Fragment(f) => fragments.push(f),
};
}
let op = match operation {
Some(op) => op,
None => return Err(GraphQLError::UnknownOperationName),
};
let default_variable_values = op.item.variable_definitions.map(|defs| {
defs.item
.items
.iter()
.filter_map(|&(ref name, ref def)| {
def.default_value
.as_ref()
.map(|i| (name.item.to_owned(), i.item.clone()))
}).collect::<HashMap<String, InputValue<S>>>()
});
let errors = RwLock::new(Vec::new());
let value;
{
let mut all_vars;
let mut final_vars = variables;
if let Some(defaults) = default_variable_values {
all_vars = variables.clone();
for (name, value) in defaults {
all_vars.entry(name).or_insert(value);
}
final_vars = &all_vars;
}
let root_type = match op.item.operation_type {
OperationType::Query => root_node.schema.query_type(),
OperationType::Mutation => root_node
.schema
.mutation_type()
.expect("No mutation type found"),
};
let executor = Executor {
fragments: &fragments
.iter()
.map(|f| (f.item.name.item, &f.item))
.collect(),
variables: final_vars,
current_selection_set: Some(&op.item.selection_set[..]),
parent_selection_set: None,
current_type: root_type,
schema: &root_node.schema,
context: context,
errors: &errors,
field_path: FieldPath::Root(op.start),
};
value = match op.item.operation_type {
OperationType::Query => executor.resolve_into_value(&root_node.query_info, &root_node),
OperationType::Mutation => {
executor.resolve_into_value(&root_node.mutation_info, &root_node.mutation_type)
}
};
}
let mut errors = errors.into_inner().unwrap();
errors.sort();
Ok((value, errors))
}
impl<'r, S> Registry<'r, S>
where
S: ScalarValue + 'r,
{
pub fn new(types: FnvHashMap<Name, MetaType<'r, S>>) -> Registry<'r, S> {
Registry { types: types }
}
pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r>
where
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
if let Some(name) = T::name(info) {
let validated_name = name.parse::<Name>().unwrap();
if !self.types.contains_key(name) {
self.insert_placeholder(
validated_name.clone(),
Type::NonNullNamed(Cow::Owned(name.to_string())),
);
let meta = T::meta(info, self);
self.types.insert(validated_name, meta);
}
self.types[name].as_type()
} else {
T::meta(info, self).as_type()
}
}
pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S>
where
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Field {
name: name.to_owned(),
description: None,
arguments: None,
field_type: self.get_type::<T>(info),
deprecation_status: DeprecationStatus::Current,
}
}
#[doc(hidden)]
pub fn field_convert<'a, T: IntoResolvable<'a, S, I, C>, I, C>(
&mut self,
name: &str,
info: &I::TypeInfo,
) -> Field<'r, S>
where
I: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Field {
name: name.to_owned(),
description: None,
arguments: None,
field_type: self.get_type::<I>(info),
deprecation_status: DeprecationStatus::Current,
}
}
pub fn arg<T>(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r, S>
where
T: GraphQLType<S> + FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Argument::new(name, self.get_type::<T>(info))
}
pub fn arg_with_default<T>(
&mut self,
name: &str,
value: &T,
info: &T::TypeInfo,
) -> Argument<'r, S>
where
T: GraphQLType<S> + ToInputValue<S> + FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Argument::new(name, self.get_type::<Option<T>>(info)).default_value(value.to_input_value())
}
fn insert_placeholder(&mut self, name: Name, of_type: Type<'r>) {
if !self.types.contains_key(&name) {
self.types.insert(
name,
MetaType::Placeholder(PlaceholderMeta { of_type: of_type }),
);
}
}
pub fn build_scalar_type<T>(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S>
where
T: FromInputValue<S> + GraphQLType<S> + ParseScalarValue<S> + 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Scalar types must be named. Implement name()");
ScalarMeta::new::<T>(Cow::Owned(name.to_string()))
}
pub fn build_list_type<T: GraphQLType<S>>(&mut self, info: &T::TypeInfo) -> ListMeta<'r>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
let of_type = self.get_type::<T>(info);
ListMeta::new(of_type)
}
pub fn build_nullable_type<T: GraphQLType<S>>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
let of_type = self.get_type::<T>(info);
NullableMeta::new(of_type)
}
pub fn build_object_type<T>(
&mut self,
info: &T::TypeInfo,
fields: &[Field<'r, S>],
) -> ObjectMeta<'r, S>
where
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Object types must be named. Implement name()");
let mut v = fields.to_vec();
v.push(self.field::<String>("__typename", &()));
ObjectMeta::new(Cow::Owned(name.to_string()), &v)
}
pub fn build_enum_type<T>(
&mut self,
info: &T::TypeInfo,
values: &[EnumValue],
) -> EnumMeta<'r, S>
where
T: FromInputValue<S> + GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Enum types must be named. Implement name()");
EnumMeta::new::<T>(Cow::Owned(name.to_string()), values)
}
pub fn build_interface_type<T>(
&mut self,
info: &T::TypeInfo,
fields: &[Field<'r, S>],
) -> InterfaceMeta<'r, S>
where
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Interface types must be named. Implement name()");
let mut v = fields.to_vec();
v.push(self.field::<String>("__typename", &()));
InterfaceMeta::new(Cow::Owned(name.to_string()), &v)
}
pub fn build_union_type<T>(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r>
where
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Union types must be named. Implement name()");
UnionMeta::new(Cow::Owned(name.to_string()), types)
}
pub fn build_input_object_type<T>(
&mut self,
info: &T::TypeInfo,
args: &[Argument<'r, S>],
) -> InputObjectMeta<'r, S>
where
T: FromInputValue<S> + GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let name = T::name(info).expect("Input object types must be named. Implement name()");
InputObjectMeta::new::<T>(Cow::Owned(name.to_string()), args)
}
}