use indexmap::IndexMap;
use ast::{Directive, FromInputValue, InputValue, Selection};
use executor::Variables;
use value::{DefaultScalarValue, Object, ScalarRefValue, ScalarValue, Value};
use executor::{ExecutionResult, Executor, Registry};
use parser::Spanning;
use schema::meta::{Argument, MetaType};
#[derive(Clone, Eq, PartialEq, Debug, GraphQLEnumInternal)]
#[graphql(name = "__TypeKind")]
pub enum TypeKind {
Scalar,
Object,
Interface,
Union,
Enum,
#[graphql(name = "INPUT_OBJECT")]
InputObject,
List,
#[graphql(name = "NON_NULL")]
NonNull,
}
#[derive(Debug)]
pub struct Arguments<'a, S = DefaultScalarValue> {
args: Option<IndexMap<&'a str, InputValue<S>>>,
}
impl<'a, S> Arguments<'a, S>
where
S: ScalarValue,
{
#[doc(hidden)]
pub fn new(
mut args: Option<IndexMap<&'a str, InputValue<S>>>,
meta_args: &'a Option<Vec<Argument<S>>>,
) -> Self {
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<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
match self.args {
Some(ref args) => match args.get(key) {
Some(v) => v.convert(),
None => None,
},
None => None,
}
}
}
pub trait GraphQLType<S = DefaultScalarValue>: Sized
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context;
type TypeInfo;
fn name(info: &Self::TypeInfo) -> Option<&str>;
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r;
#[allow(unused_variables)]
fn resolve_field(
&self,
info: &Self::TypeInfo,
field_name: &str,
arguments: &Arguments<S>,
executor: &Executor<Self::Context, S>,
) -> ExecutionResult<S> {
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<S>]>,
executor: &Executor<Self::Context, S>,
) -> ExecutionResult<S> {
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<S>]>,
executor: &Executor<Self::Context, S>,
) -> Value<S> {
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, S>(
instance: &T,
info: &T::TypeInfo,
selection_set: &[Selection<S>],
executor: &Executor<CtxT, S>,
result: &mut Object<S>,
) -> bool
where
T: GraphQLType<S, Context = CtxT>,
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
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::scalar(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<S>(directives: &Option<Vec<Spanning<Directive<S>>>>, vars: &Variables<S>) -> bool
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
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<S>(result: &mut Object<S>, response_name: &str, value: Value<S>) {
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<S>(dest: &mut Object<S>, src: Object<S>) {
for (key, value) in src {
if dest.contains_field(&key) {
merge_key_into(dest, &key, value);
} else {
dest.add_field(key, value);
}
}
}