use indexmap::IndexMap;
use crate::{
ast::{Directive, FromInputValue, InputValue, Selection},
executor::{ExecutionResult, Executor, Registry, Variables},
parser::Spanning,
schema::meta::{Argument, MetaType},
value::{DefaultScalarValue, Object, ScalarValue, Value},
GraphQLEnum,
};
#[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)]
#[graphql(name = "__TypeKind", internal)]
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());
}
}
}
}
Arguments { args }
}
pub fn get<T>(&self, key: &str) -> Option<T>
where
T: FromInputValue<S>,
{
self.args
.as_ref()
.and_then(|args| args.get(key))
.and_then(InputValue::convert)
}
}
pub trait GraphQLValue<S = DefaultScalarValue>
where
S: ScalarValue,
{
type Context;
type TypeInfo;
fn type_name<'i>(&self, info: &'i Self::TypeInfo) -> Option<&'i str>;
fn resolve_field(
&self,
_info: &Self::TypeInfo,
_field_name: &str,
_arguments: &Arguments<S>,
_executor: &Executor<Self::Context, S>,
) -> ExecutionResult<S> {
panic!("GraphQLValue::resolve_field() must be implemented by objects and interfaces");
}
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.type_name(info).unwrap() == type_name {
self.resolve(info, selection_set, executor)
} else {
panic!(
"GraphQLValue::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!(
"GraphQLValue::concrete_type_name() must be implemented by unions, interfaces \
and objects",
);
}
fn resolve(
&self,
info: &Self::TypeInfo,
selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context, S>,
) -> ExecutionResult<S> {
if let Some(sel) = selection_set {
let mut res = Object::with_capacity(sel.len());
Ok(
if resolve_selection_set_into(self, info, sel, executor, &mut res) {
Value::Object(res)
} else {
Value::null()
},
)
} else {
panic!("GraphQLValue::resolve() must be implemented by non-object output types");
}
}
}
crate::sa::assert_obj_safe!(GraphQLValue<Context = (), TypeInfo = ()>);
pub type DynGraphQLValue<S, C, TI> =
dyn GraphQLValue<S, Context = C, TypeInfo = TI> + Send + Sync + 'static;
pub trait GraphQLType<S = DefaultScalarValue>: GraphQLValue<S>
where
S: ScalarValue,
{
fn name(info: &Self::TypeInfo) -> Option<&str>;
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r;
}
pub(crate) fn resolve_selection_set_into<T, S>(
instance: &T,
info: &T::TypeInfo,
selection_set: &[Selection<S>],
executor: &Executor<T::Context, S>,
result: &mut Object<S>,
) -> bool
where
T: GraphQLValue<S> + ?Sized,
S: ScalarValue,
{
let meta_type = executor
.schema()
.concrete_type_by_name(
instance
.type_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!(
"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,
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);
if meta_field.field_type.is_non_null() {
return false;
}
result.add_field(response_name, Value::null());
}
}
}
Selection::FragmentSpread(Spanning {
item: ref spread,
start: ref start_pos,
..
}) => {
if is_excluded(&spread.directives, executor.variables()) {
continue;
}
let fragment = &executor
.fragment_by_name(spread.name.item)
.expect("Fragment could not be found");
let sub_exec = executor.type_sub_executor(
Some(fragment.type_condition.item),
Some(&fragment.selection_set[..]),
);
let concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
let type_name = instance.type_name(info);
if fragment.type_condition.item == concrete_type_name
|| Some(fragment.type_condition.item) == type_name
{
let sub_result = instance.resolve_into_type(
info,
&concrete_type_name,
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);
}
}
}
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 concrete_type_name = instance.concrete_type_name(sub_exec.context(), info);
if type_condition.item == concrete_type_name {
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);
}
}
} else if !resolve_selection_set_into(
instance,
info,
&fragment.selection_set[..],
&sub_exec,
result,
) {
return false;
}
}
}
}
true
}
pub(super) fn is_excluded<S>(
directives: &Option<Vec<Spanning<Directive<S>>>>,
vars: &Variables<S>,
) -> bool
where
S: ScalarValue,
{
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
}
pub(crate) fn merge_key_into<S>(result: &mut Object<S>, response_name: &str, value: Value<S>) {
if let Some(v) = result.get_mut_field_value(response_name) {
match v {
Value::Object(dest_obj) => {
if let Value::Object(src_obj) = value {
merge_maps(dest_obj, src_obj);
}
}
Value::List(dest_list) => {
if let Value::List(src_list) = value {
dest_list
.iter_mut()
.zip(src_list.into_iter())
.for_each(|(d, s)| {
if let Value::Object(d_obj) = d {
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);
}
}
}