use indexmap::IndexMap;
use ast::{Directive, FromInputValue, InputValue, Selection};
use executor::Variables;
use value::{Object, Value};
use executor::{ExecutionResult, Executor, Registry};
use parser::Spanning;
use schema::meta::{Argument, MetaType};
#[derive(GraphQLEnum, Clone, Eq, PartialEq, Debug)]
#[graphql(name = "__TypeKind", _internal)]
pub enum TypeKind {
Scalar,
Object,
Interface,
Union,
Enum,
#[graphql(name = "INPUT_OBJECT")]
InputObject,
List,
#[graphql(name = "NON_NULL")]
NonNull,
}
pub struct Arguments<'a> {
args: Option<IndexMap<&'a str, InputValue>>,
}
impl<'a> Arguments<'a> {
#[doc(hidden)]
pub fn new(
mut args: Option<IndexMap<&'a str, InputValue>>,
meta_args: &'a Option<Vec<Argument>>,
) -> Arguments<'a> {
if meta_args.is_some() && args.is_none() {
args = Some(IndexMap::new());
}
if let (&mut Some(ref mut args), &Some(ref meta_args)) = (&mut args, meta_args) {
for arg in meta_args {
if !args.contains_key(arg.name.as_str()) || args[arg.name.as_str()].is_null() {
if let Some(ref default_value) = arg.default_value {
args.insert(arg.name.as_str(), default_value.clone());
} else {
args.insert(arg.name.as_str(), InputValue::null());
}
}
}
}
Arguments { args: args }
}
pub fn get<T>(&self, key: &str) -> Option<T>
where
T: FromInputValue,
{
match self.args {
Some(ref args) => match args.get(key) {
Some(v) => v.convert(),
None => None,
},
None => None,
}
}
}
pub trait GraphQLType: Sized {
type Context;
type TypeInfo;
fn name(info: &Self::TypeInfo) -> Option<&str>;
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r>;
#[allow(unused_variables)]
fn resolve_field(
&self,
info: &Self::TypeInfo,
field_name: &str,
arguments: &Arguments,
executor: &Executor<Self::Context>,
) -> ExecutionResult {
panic!("resolve_field must be implemented by object types");
}
#[allow(unused_variables)]
fn resolve_into_type(
&self,
info: &Self::TypeInfo,
type_name: &str,
selection_set: Option<&[Selection]>,
executor: &Executor<Self::Context>,
) -> ExecutionResult {
if Self::name(info).unwrap() == type_name {
Ok(self.resolve(info, selection_set, executor))
} else {
panic!("resolve_into_type must be implemented by unions and interfaces");
}
}
#[allow(unused_variables)]
fn concrete_type_name(&self, context: &Self::Context, info: &Self::TypeInfo) -> String {
panic!("concrete_type_name must be implemented by unions and interfaces");
}
fn resolve(
&self,
info: &Self::TypeInfo,
selection_set: Option<&[Selection]>,
executor: &Executor<Self::Context>,
) -> Value {
if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
Value::Object(result)
} else {
Value::null()
}
} else {
panic!("resolve() must be implemented by non-object output types");
}
}
}
pub(crate) fn resolve_selection_set_into<T, CtxT>(
instance: &T,
info: &T::TypeInfo,
selection_set: &[Selection],
executor: &Executor<CtxT>,
result: &mut Object,
) -> bool
where
T: GraphQLType<Context = CtxT>,
{
let meta_type = executor
.schema()
.concrete_type_by_name(
T::name(info)
.expect("Resolving named type's selection set")
.as_ref(),
)
.expect("Type not found in schema");
for selection in selection_set {
match *selection {
Selection::Field(Spanning {
item: ref f,
start: ref start_pos,
..
}) => {
if is_excluded(&f.directives, executor.variables()) {
continue;
}
let response_name = f.alias.as_ref().unwrap_or(&f.name).item;
if f.name.item == "__typename" {
result.add_field(
response_name,
Value::string(instance.concrete_type_name(executor.context(), info)),
);
continue;
}
let meta_field = meta_type.field_by_name(f.name.item).unwrap_or_else(|| {
panic!(format!(
"Field {} not found on type {:?}",
f.name.item,
meta_type.name()
))
});
let exec_vars = executor.variables();
let sub_exec = executor.field_sub_executor(
response_name,
f.name.item,
start_pos.clone(),
f.selection_set.as_ref().map(|v| &v[..]),
);
let field_result = instance.resolve_field(
info,
f.name.item,
&Arguments::new(
f.arguments.as_ref().map(|m| {
m.item
.iter()
.map(|&(ref k, ref v)| {
(k.item, v.item.clone().into_const(exec_vars))
})
.collect()
}),
&meta_field.arguments,
),
&sub_exec,
);
match field_result {
Ok(Value::Null) if meta_field.field_type.is_non_null() => return false,
Ok(v) => merge_key_into(result, response_name, v),
Err(e) => {
sub_exec.push_error_at(e, start_pos.clone());
if meta_field.field_type.is_non_null() {
return false;
}
result.add_field(response_name, Value::null());
}
}
}
Selection::FragmentSpread(Spanning {
item: ref spread, ..
}) => {
if is_excluded(&spread.directives, executor.variables()) {
continue;
}
let fragment = &executor
.fragment_by_name(spread.name.item)
.expect("Fragment could not be found");
if !resolve_selection_set_into(
instance,
info,
&fragment.selection_set[..],
executor,
result,
) {
return false;
}
}
Selection::InlineFragment(Spanning {
item: ref fragment,
start: ref start_pos,
..
}) => {
if is_excluded(&fragment.directives, executor.variables()) {
continue;
}
let sub_exec = executor.type_sub_executor(
fragment.type_condition.as_ref().map(|c| c.item),
Some(&fragment.selection_set[..]),
);
if let Some(ref type_condition) = fragment.type_condition {
let sub_result = instance.resolve_into_type(
info,
type_condition.item,
Some(&fragment.selection_set[..]),
&sub_exec,
);
if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
}
} else if let Err(e) = sub_result {
sub_exec.push_error_at(e, start_pos.clone());
}
} else {
if !resolve_selection_set_into(
instance,
info,
&fragment.selection_set[..],
&sub_exec,
result,
) {
return false;
}
}
}
}
}
true
}
fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables) -> bool {
if let Some(ref directives) = *directives {
for &Spanning {
item: ref directive,
..
} in directives
{
let condition: bool = directive
.arguments
.iter()
.flat_map(|m| m.item.get("if"))
.flat_map(|v| v.item.clone().into_const(vars).convert())
.next()
.unwrap();
if (directive.name.item == "skip" && condition)
|| (directive.name.item == "include" && !condition)
{
return true;
}
}
}
false
}
fn merge_key_into(result: &mut Object, response_name: &str, value: Value) {
if let Some(&mut (_, ref mut e)) = result.iter_mut().find(|&&mut (ref key, _)| key == response_name)
{
match *e {
Value::Object(ref mut dest_obj) => {
if let Value::Object(src_obj) = value {
merge_maps(dest_obj, src_obj);
}
}
Value::List(ref mut dest_list) => {
if let Value::List(src_list) = value {
dest_list
.iter_mut()
.zip(src_list.into_iter())
.for_each(|(d, s)| match d {
&mut Value::Object(ref mut d_obj) => {
if let Value::Object(s_obj) = s {
merge_maps(d_obj, s_obj);
}
}
_ => {}
});
}
}
_ => {}
}
return;
}
result.add_field(response_name, value);
}
fn merge_maps(dest: &mut Object, src: Object) {
for (key, value) in src {
if dest.contains_field(&key) {
merge_key_into(dest, &key, value);
} else {
dest.add_field(key, value);
}
}
}