use std::cmp::Ordering;
use std::fmt::Display;
use std::borrow::Cow;
use std::collections::HashMap;
use std::sync::RwLock;
use fnv::FnvHashMap;
use GraphQLError;
use ast::{Definition, Document, Fragment, FromInputValue, InputValue, OperationType, Selection,
ToInputValue, Type};
use value::Value;
use parser::SourcePosition;
use schema::meta::{Argument, 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;
pub struct Registry<'r> {
pub types: FnvHashMap<Name, MetaType<'r>>,
}
#[derive(Clone)]
pub enum FieldPath<'a> {
Root(SourcePosition),
Field(&'a str, SourcePosition, &'a FieldPath<'a>),
}
pub struct Executor<'a, CtxT>
where
CtxT: 'a,
{
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>,
variables: &'a Variables,
current_selection_set: Option<&'a [Selection<'a>]>,
current_type: TypeType<'a>,
schema: &'a SchemaType<'a>,
context: &'a CtxT,
errors: &'a RwLock<Vec<ExecutionError>>,
field_path: FieldPath<'a>,
}
#[derive(Debug, PartialEq)]
pub struct ExecutionError {
location: SourcePosition,
path: Vec<String>,
error: FieldError,
}
impl Eq for ExecutionError {}
impl PartialOrd for ExecutionError {
fn partial_cmp(&self, other: &ExecutionError) -> Option<Ordering> {
(&self.location, &self.path, &self.error.message)
.partial_cmp(&(&other.location, &other.path, &other.error.message))
}
}
impl Ord for ExecutionError {
fn cmp(&self, other: &ExecutionError) -> Ordering {
(&self.location, &self.path, &self.error.message)
.cmp(&(&other.location, &other.path, &other.error.message))
}
}
#[derive(Debug, PartialEq)]
pub struct FieldError {
message: String,
data: Value,
}
impl<T: Display> From<T> for FieldError {
fn from(e: T) -> FieldError {
FieldError {
message: format!("{}", e),
data: Value::null(),
}
}
}
impl FieldError {
pub fn new<T: Display>(e: T, data: Value) -> FieldError {
FieldError {
message: format!("{}", e),
data: data,
}
}
#[doc(hidden)]
pub fn message(&self) -> &str {
&self.message
}
#[doc(hidden)]
pub fn data(&self) -> &Value {
&self.data
}
}
pub type FieldResult<T> = Result<T, FieldError>;
pub type ExecutionResult = Result<Value, FieldError>;
pub type Variables = HashMap<String, InputValue>;
#[doc(hidden)]
pub trait IntoResolvable<'a, T: GraphQLType, C>: Sized {
#[doc(hidden)]
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>>;
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T
where
T::Context: FromContext<C>,
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
Ok(Some((FromContext::from(ctx), self)))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<T>
where
T::Context: FromContext<C>,
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
self.map(|v| Some((FromContext::from(ctx), v)))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for (&'a T::Context, T) {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
Ok(Some(self))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C> for Option<(&'a T::Context, T)> {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>> {
Ok(self.map(|(ctx, v)| (ctx, Some(v))))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<(&'a T::Context, T)> {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
self.map(Some)
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C> for FieldResult<Option<(&'a T::Context, T)>> {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>> {
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> Executor<'a, CtxT> {
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> ExecutionResult
where
NewCtxT: FromContext<CtxT>,
{
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
.resolve(info, value)
}
pub fn resolve<T: GraphQLType<Context = CtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> ExecutionResult {
Ok(value.resolve(info, self.current_selection_set, self))
}
pub fn resolve_into_value<T: GraphQLType<Context = CtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> Value {
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> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: self.current_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]>,
) -> Executor<CtxT> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: 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]>,
) -> Executor<CtxT> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: 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 {
self.schema
}
#[doc(hidden)]
pub fn current_type(&self) -> &TypeType<'a> {
&self.current_type
}
#[doc(hidden)]
pub fn variables(&self) -> &'a Variables {
self.variables
}
#[doc(hidden)]
pub fn fragment_by_name(&self, name: &str) -> Option<&'a Fragment> {
self.fragments.get(name).map(|f| *f)
}
pub fn location(&self) -> &SourcePosition {
self.field_path.location()
}
pub fn push_error(&self, error: FieldError) {
let location = self.location().clone();
self.push_error_at(error, location);
}
pub fn push_error_at(&self, error: FieldError, 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,
});
}
}
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 ExecutionError {
#[doc(hidden)]
pub fn new(location: SourcePosition, path: &[&str], error: FieldError) -> ExecutionError {
ExecutionError {
location: location,
path: path.iter().map(|s| (*s).to_owned()).collect(),
error: error,
}
}
pub fn error(&self) -> &FieldError {
&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>(
document: Document,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>,
variables: &Variables,
context: &CtxT,
) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
{
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.as_ref()) == 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>>()
});
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[..]),
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> Registry<'r> {
pub fn new(types: FnvHashMap<Name, MetaType<'r>>) -> Registry<'r> {
Registry { types: types }
}
pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r>
where
T: GraphQLType,
{
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>
where
T: GraphQLType,
{
Field {
name: name.to_owned(),
description: None,
arguments: None,
field_type: self.get_type::<T>(info),
deprecation_reason: None,
}
}
#[doc(hidden)]
pub fn field_convert<'a, T: IntoResolvable<'a, I, C>, I, C>(
&mut self,
name: &str,
info: &I::TypeInfo,
) -> Field<'r>
where
I: GraphQLType,
{
Field {
name: name.to_owned(),
description: None,
arguments: None,
field_type: self.get_type::<I>(info),
deprecation_reason: None,
}
}
pub fn arg<T>(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r>
where
T: GraphQLType + FromInputValue,
{
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>
where
T: GraphQLType + ToInputValue + FromInputValue,
{
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>
where
T: FromInputValue + GraphQLType,
{
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>(&mut self, info: &T::TypeInfo) -> ListMeta<'r> {
let of_type = self.get_type::<T>(info);
ListMeta::new(of_type)
}
pub fn build_nullable_type<T: GraphQLType>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> {
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>],
) -> ObjectMeta<'r>
where
T: GraphQLType,
{
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>
where
T: FromInputValue + GraphQLType,
{
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>],
) -> InterfaceMeta<'r>
where
T: GraphQLType,
{
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,
{
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>],
) -> InputObjectMeta<'r>
where
T: FromInputValue + GraphQLType,
{
let name = T::name(info).expect("Input object types must be named. Implement name()");
InputObjectMeta::new::<T>(Cow::Owned(name.to_string()), args)
}
}