Rust fmt the whole codebase
This commit is contained in:
parent
81ce7780fa
commit
0c7e39f14e
85 changed files with 7205 additions and 4502 deletions
|
@ -4,7 +4,7 @@ extern crate juniper;
|
|||
|
||||
use bencher::Bencher;
|
||||
|
||||
use juniper::{execute, RootNode, EmptyMutation, Variables};
|
||||
use juniper::{execute, EmptyMutation, RootNode, Variables};
|
||||
use juniper::tests::model::Database;
|
||||
|
||||
fn query_type_name(b: &mut Bencher) {
|
||||
|
|
|
@ -168,8 +168,7 @@ impl<'a> Type<'a> {
|
|||
/// Only applies to named types; lists will return `None`.
|
||||
pub fn name(&self) -> Option<&str> {
|
||||
match *self {
|
||||
Type::Named(n) |
|
||||
Type::NonNullNamed(n) => Some(n),
|
||||
Type::Named(n) | Type::NonNullNamed(n) => Some(n),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -179,18 +178,15 @@ impl<'a> Type<'a> {
|
|||
/// All type literals contain exactly one named type.
|
||||
pub fn innermost_name(&self) -> &str {
|
||||
match *self {
|
||||
Type::Named(n) |
|
||||
Type::NonNullNamed(n) => n,
|
||||
Type::List(ref l) |
|
||||
Type::NonNullList(ref l) => l.innermost_name(),
|
||||
Type::Named(n) | Type::NonNullNamed(n) => n,
|
||||
Type::List(ref l) | Type::NonNullList(ref l) => l.innermost_name(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines if a type only can represent non-null values.
|
||||
pub fn is_non_null(&self) -> bool {
|
||||
match *self {
|
||||
Type::NonNullNamed(_) |
|
||||
Type::NonNullList(_) => true,
|
||||
Type::NonNullNamed(_) | Type::NonNullList(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -262,14 +258,19 @@ impl InputValue {
|
|||
/// Similar to `InputValue::list`, it makes each key and value in the given
|
||||
/// hash map not contain any location information.
|
||||
pub fn object<K>(o: HashMap<K, InputValue>) -> InputValue
|
||||
where K: AsRef<str> + Eq + Hash
|
||||
where
|
||||
K: AsRef<str> + Eq + Hash,
|
||||
{
|
||||
InputValue::Object(o.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(Spanning::unlocated(k.as_ref().to_owned()),
|
||||
Spanning::unlocated(v))
|
||||
})
|
||||
.collect())
|
||||
InputValue::Object(
|
||||
o.into_iter()
|
||||
.map(|(k, v)| {
|
||||
(
|
||||
Spanning::unlocated(k.as_ref().to_owned()),
|
||||
Spanning::unlocated(v),
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Construct a located object.
|
||||
|
@ -281,23 +282,24 @@ impl InputValue {
|
|||
pub fn into_const(self, vars: &Variables) -> InputValue {
|
||||
match self {
|
||||
InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone),
|
||||
InputValue::List(l) => {
|
||||
InputValue::List(l.into_iter()
|
||||
.map(|s| s.map(|v| v.into_const(vars)))
|
||||
.collect())
|
||||
}
|
||||
InputValue::Object(o) => {
|
||||
InputValue::Object(o.into_iter()
|
||||
.map(|(sk, sv)| (sk, sv.map(|v| v.into_const(vars))))
|
||||
.collect())
|
||||
}
|
||||
InputValue::List(l) => InputValue::List(
|
||||
l.into_iter()
|
||||
.map(|s| s.map(|v| v.into_const(vars)))
|
||||
.collect(),
|
||||
),
|
||||
InputValue::Object(o) => InputValue::Object(
|
||||
o.into_iter()
|
||||
.map(|(sk, sv)| (sk, sv.map(|v| v.into_const(vars))))
|
||||
.collect(),
|
||||
),
|
||||
v => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorthand form of invoking `FromInputValue::from()`.
|
||||
pub fn convert<T>(&self) -> Option<T>
|
||||
where T: FromInputValue
|
||||
where
|
||||
T: FromInputValue,
|
||||
{
|
||||
<T as FromInputValue>::from(self)
|
||||
}
|
||||
|
@ -348,11 +350,11 @@ impl InputValue {
|
|||
/// and values in `self`.
|
||||
pub fn to_object_value(&self) -> Option<HashMap<&str, &InputValue>> {
|
||||
match *self {
|
||||
InputValue::Object(ref o) => {
|
||||
Some(o.iter()
|
||||
.map(|&(ref sk, ref sv)| (sk.item.as_str(), &sv.item))
|
||||
.collect())
|
||||
}
|
||||
InputValue::Object(ref o) => Some(
|
||||
o.iter()
|
||||
.map(|&(ref sk, ref sv)| (sk.item.as_str(), &sv.item))
|
||||
.collect(),
|
||||
),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -372,16 +374,12 @@ impl InputValue {
|
|||
pub fn referenced_variables(&self) -> Vec<&str> {
|
||||
match *self {
|
||||
InputValue::Variable(ref name) => vec![name],
|
||||
InputValue::List(ref l) => {
|
||||
l.iter()
|
||||
.flat_map(|v| v.item.referenced_variables())
|
||||
.collect()
|
||||
}
|
||||
InputValue::Object(ref obj) => {
|
||||
obj.iter()
|
||||
.flat_map(|&(_, ref v)| v.item.referenced_variables())
|
||||
.collect()
|
||||
}
|
||||
InputValue::List(ref l) => l.iter()
|
||||
.flat_map(|v| v.item.referenced_variables())
|
||||
.collect(),
|
||||
InputValue::Object(ref obj) => obj.iter()
|
||||
.flat_map(|&(_, ref v)| v.item.referenced_variables())
|
||||
.collect(),
|
||||
_ => vec![],
|
||||
}
|
||||
}
|
||||
|
@ -398,20 +396,15 @@ impl InputValue {
|
|||
(&Enum(ref s1), &Enum(ref s2)) |
|
||||
(&Variable(ref s1), &Variable(ref s2)) => s1 == s2,
|
||||
(&Boolean(b1), &Boolean(b2)) => b1 == b2,
|
||||
(&List(ref l1), &List(ref l2)) => {
|
||||
l1.iter()
|
||||
.zip(l2.iter())
|
||||
.all(|(v1, v2)| v1.item.unlocated_eq(&v2.item))
|
||||
}
|
||||
(&List(ref l1), &List(ref l2)) => l1.iter()
|
||||
.zip(l2.iter())
|
||||
.all(|(v1, v2)| v1.item.unlocated_eq(&v2.item)),
|
||||
(&Object(ref o1), &Object(ref o2)) => {
|
||||
o1.len() == o2.len() &&
|
||||
o1.iter()
|
||||
.all(|&(ref sk1, ref sv1)| {
|
||||
o2.iter()
|
||||
.any(|&(ref sk2, ref sv2)| {
|
||||
sk1.item == sk2.item && sv1.item.unlocated_eq(&sv2.item)
|
||||
})
|
||||
})
|
||||
o1.len() == o2.len() && o1.iter().all(|&(ref sk1, ref sv1)| {
|
||||
o2.iter().any(|&(ref sk2, ref sv2)| {
|
||||
sk1.item == sk2.item && sv1.item.unlocated_eq(&sv2.item)
|
||||
})
|
||||
})
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
|
@ -521,9 +514,16 @@ mod tests {
|
|||
let value = InputValue::list(list);
|
||||
assert_eq!(format!("{}", value), "[1, 2]");
|
||||
|
||||
let object =
|
||||
vec![(Spanning::unlocated("foo".to_owned()), Spanning::unlocated(InputValue::int(1))),
|
||||
(Spanning::unlocated("bar".to_owned()), Spanning::unlocated(InputValue::int(2)))];
|
||||
let object = vec![
|
||||
(
|
||||
Spanning::unlocated("foo".to_owned()),
|
||||
Spanning::unlocated(InputValue::int(1)),
|
||||
),
|
||||
(
|
||||
Spanning::unlocated("bar".to_owned()),
|
||||
Spanning::unlocated(InputValue::int(2)),
|
||||
),
|
||||
];
|
||||
let value = InputValue::parsed_object(object);
|
||||
assert_eq!(format!("{}", value), "{foo: 1, bar: 2}");
|
||||
}
|
||||
|
|
|
@ -2,14 +2,13 @@ use std::collections::HashMap;
|
|||
use std::sync::RwLock;
|
||||
|
||||
use GraphQLError;
|
||||
use ast::{InputValue, ToInputValue, Document, Selection, Fragment, Definition, Type,
|
||||
FromInputValue, OperationType};
|
||||
use ast::{Definition, Document, Fragment, FromInputValue, InputValue, OperationType, Selection,
|
||||
ToInputValue, Type};
|
||||
use value::Value;
|
||||
use parser::SourcePosition;
|
||||
|
||||
use schema::meta::{MetaType, ScalarMeta, ListMeta, NullableMeta, ObjectMeta, EnumMeta,
|
||||
InterfaceMeta, UnionMeta, InputObjectMeta, PlaceholderMeta, Field, Argument,
|
||||
EnumValue};
|
||||
use schema::meta::{Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, ListMeta,
|
||||
MetaType, NullableMeta, ObjectMeta, PlaceholderMeta, ScalarMeta, UnionMeta};
|
||||
use schema::model::{RootNode, SchemaType};
|
||||
|
||||
use types::base::GraphQLType;
|
||||
|
@ -35,7 +34,8 @@ pub enum FieldPath<'a> {
|
|||
/// The executor helps drive the query execution in a schema. It keeps track
|
||||
/// of the current field stack, context, variables, and errors.
|
||||
pub struct Executor<'a, CtxT>
|
||||
where CtxT: 'a
|
||||
where
|
||||
CtxT: 'a,
|
||||
{
|
||||
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>,
|
||||
variables: &'a Variables,
|
||||
|
@ -73,7 +73,8 @@ pub trait IntoResolvable<'a, T: GraphQLType, C>: Sized {
|
|||
}
|
||||
|
||||
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T
|
||||
where T::Context: FromContext<C>
|
||||
where
|
||||
T::Context: FromContext<C>,
|
||||
{
|
||||
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
|
||||
Ok(Some((FromContext::from(ctx), self)))
|
||||
|
@ -81,7 +82,8 @@ impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T
|
|||
}
|
||||
|
||||
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<T>
|
||||
where T::Context: FromContext<C>
|
||||
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)))
|
||||
|
@ -142,7 +144,8 @@ impl<T> FromContext<T> for () {
|
|||
}
|
||||
|
||||
impl<T> FromContext<T> for T
|
||||
where T: Context
|
||||
where
|
||||
T: Context,
|
||||
{
|
||||
fn from(value: &T) -> &Self {
|
||||
value
|
||||
|
@ -151,10 +154,12 @@ impl<T> FromContext<T> for T
|
|||
|
||||
impl<'a, CtxT> Executor<'a, CtxT> {
|
||||
/// Resolve a single arbitrary value, mapping the context to a new type
|
||||
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>(&self,
|
||||
value: &T)
|
||||
-> ExecutionResult
|
||||
where NewCtxT: FromContext<CtxT>
|
||||
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>(
|
||||
&self,
|
||||
value: &T,
|
||||
) -> ExecutionResult
|
||||
where
|
||||
NewCtxT: FromContext<CtxT>,
|
||||
{
|
||||
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
|
||||
.resolve(value)
|
||||
|
@ -196,11 +201,12 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn sub_executor(&self,
|
||||
field_name: Option<&'a str>,
|
||||
location: SourcePosition,
|
||||
selection_set: Option<&'a [Selection]>)
|
||||
-> Executor<CtxT> {
|
||||
pub fn sub_executor(
|
||||
&self,
|
||||
field_name: Option<&'a str>,
|
||||
location: SourcePosition,
|
||||
selection_set: Option<&'a [Selection]>,
|
||||
) -> Executor<CtxT> {
|
||||
Executor {
|
||||
fragments: self.fragments,
|
||||
variables: self.variables,
|
||||
|
@ -246,10 +252,10 @@ impl<'a, CtxT> Executor<'a, CtxT> {
|
|||
let mut errors = self.errors.write().unwrap();
|
||||
|
||||
errors.push(ExecutionError {
|
||||
location: location,
|
||||
path: path,
|
||||
message: error,
|
||||
});
|
||||
location: location,
|
||||
path: path,
|
||||
message: error,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,8 +272,7 @@ impl<'a> FieldPath<'a> {
|
|||
|
||||
fn location(&self) -> &SourcePosition {
|
||||
match *self {
|
||||
FieldPath::Root(ref pos) |
|
||||
FieldPath::Field(_, ref pos, _) => pos,
|
||||
FieldPath::Root(ref pos) | FieldPath::Field(_, ref pos, _) => pos,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,15 +303,16 @@ impl ExecutionError {
|
|||
}
|
||||
}
|
||||
|
||||
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>
|
||||
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;
|
||||
|
@ -319,7 +325,7 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>
|
|||
}
|
||||
|
||||
let move_op = operation_name.is_none() ||
|
||||
op.item.name.as_ref().map(|s| s.item.as_ref()) == operation_name;
|
||||
op.item.name.as_ref().map(|s| s.item.as_ref()) == operation_name;
|
||||
|
||||
if move_op {
|
||||
operation = Some(op);
|
||||
|
@ -334,19 +340,17 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>
|
|||
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 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;
|
||||
|
@ -367,9 +371,9 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>
|
|||
|
||||
let executor = Executor {
|
||||
fragments: &fragments
|
||||
.iter()
|
||||
.map(|f| (f.item.name.item, &f.item))
|
||||
.collect(),
|
||||
.iter()
|
||||
.map(|f| (f.item.name.item, &f.item))
|
||||
.collect(),
|
||||
variables: final_vars,
|
||||
current_selection_set: Some(&op.item.selection_set[..]),
|
||||
schema: &root_node.schema,
|
||||
|
@ -401,7 +405,8 @@ impl<'r> Registry<'r> {
|
|||
/// If the registry hasn't seen a type with this name before, it will
|
||||
/// construct its metadata and store it.
|
||||
pub fn get_type<T>(&mut self) -> Type<'r>
|
||||
where T: GraphQLType
|
||||
where
|
||||
T: GraphQLType,
|
||||
{
|
||||
if let Some(name) = T::name() {
|
||||
if !self.types.contains_key(name) {
|
||||
|
@ -417,7 +422,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create a field with the provided name
|
||||
pub fn field<T>(&mut self, name: &str) -> Field<'r>
|
||||
where T: GraphQLType
|
||||
where
|
||||
T: GraphQLType,
|
||||
{
|
||||
Field {
|
||||
name: name.to_owned(),
|
||||
|
@ -430,7 +436,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn field_convert<'a, T: IntoResolvable<'a, I, C>, I, C>(&mut self, name: &str) -> Field<'r>
|
||||
where I: GraphQLType
|
||||
where
|
||||
I: GraphQLType,
|
||||
{
|
||||
Field {
|
||||
name: name.to_owned(),
|
||||
|
@ -443,7 +450,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create an argument with the provided name
|
||||
pub fn arg<T>(&mut self, name: &str) -> Argument<'r>
|
||||
where T: GraphQLType + FromInputValue
|
||||
where
|
||||
T: GraphQLType + FromInputValue,
|
||||
{
|
||||
Argument::new(name, self.get_type::<T>())
|
||||
}
|
||||
|
@ -453,16 +461,18 @@ impl<'r> Registry<'r> {
|
|||
/// When called with type `T`, the actual argument will be given the type
|
||||
/// `Option<T>`.
|
||||
pub fn arg_with_default<T>(&mut self, name: &str, value: &T) -> Argument<'r>
|
||||
where T: GraphQLType + ToInputValue + FromInputValue
|
||||
where
|
||||
T: GraphQLType + ToInputValue + FromInputValue,
|
||||
{
|
||||
Argument::new(name, self.get_type::<Option<T>>()).default_value(value.to())
|
||||
}
|
||||
|
||||
fn insert_placeholder(&mut self, name: &str, of_type: Type<'r>) {
|
||||
if !self.types.contains_key(name) {
|
||||
self.types
|
||||
.insert(name.to_owned(),
|
||||
MetaType::Placeholder(PlaceholderMeta { of_type: of_type }));
|
||||
self.types.insert(
|
||||
name.to_owned(),
|
||||
MetaType::Placeholder(PlaceholderMeta { of_type: of_type }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -470,7 +480,8 @@ impl<'r> Registry<'r> {
|
|||
///
|
||||
/// This expects the type to implement `FromInputValue`.
|
||||
pub fn build_scalar_type<T>(&mut self) -> ScalarMeta<'r>
|
||||
where T: FromInputValue + GraphQLType
|
||||
where
|
||||
T: FromInputValue + GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Scalar types must be named. Implement name()");
|
||||
ScalarMeta::new::<T>(name)
|
||||
|
@ -493,7 +504,8 @@ impl<'r> Registry<'r> {
|
|||
/// To prevent infinite recursion by enforcing ordering, this returns a
|
||||
/// function that needs to be called with the list of fields on the object.
|
||||
pub fn build_object_type<T>(&mut self, fields: &[Field<'r>]) -> ObjectMeta<'r>
|
||||
where T: GraphQLType
|
||||
where
|
||||
T: GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Object types must be named. Implement name()");
|
||||
|
||||
|
@ -504,7 +516,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create an enum meta type
|
||||
pub fn build_enum_type<T>(&mut self, values: &[EnumValue]) -> EnumMeta<'r>
|
||||
where T: FromInputValue + GraphQLType
|
||||
where
|
||||
T: FromInputValue + GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Enum types must be named. Implement name()");
|
||||
|
||||
|
@ -513,7 +526,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create an interface meta type builder
|
||||
pub fn build_interface_type<T>(&mut self, fields: &[Field<'r>]) -> InterfaceMeta<'r>
|
||||
where T: GraphQLType
|
||||
where
|
||||
T: GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Interface types must be named. Implement name()");
|
||||
|
||||
|
@ -524,7 +538,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create a union meta type builder
|
||||
pub fn build_union_type<T>(&mut self, types: &[Type<'r>]) -> UnionMeta<'r>
|
||||
where T: GraphQLType
|
||||
where
|
||||
T: GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Union types must be named. Implement name()");
|
||||
|
||||
|
@ -533,7 +548,8 @@ impl<'r> Registry<'r> {
|
|||
|
||||
/// Create an input object meta type builder
|
||||
pub fn build_input_object_type<T>(&mut self, args: &[Argument<'r>]) -> InputObjectMeta<'r>
|
||||
where T: FromInputValue + GraphQLType
|
||||
where
|
||||
T: FromInputValue + GraphQLType,
|
||||
{
|
||||
let name = T::name().expect("Input object types must be named. Implement name()");
|
||||
|
||||
|
|
|
@ -18,7 +18,8 @@ graphql_object!(TestType: () |&self| {
|
|||
});
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
|
||||
|
||||
|
@ -34,7 +35,8 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
|||
}
|
||||
|
||||
fn run_query<F>(query: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
run_variable_query(query, Variables::new(), f);
|
||||
}
|
||||
|
@ -114,20 +116,24 @@ fn fragment_spread_skip_true() {
|
|||
|
||||
#[test]
|
||||
fn inline_fragment_include_true() {
|
||||
run_query("{ a, ... on TestType @include(if: true) { b } }",
|
||||
|result| {
|
||||
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||
});
|
||||
run_query(
|
||||
"{ a, ... on TestType @include(if: true) { b } }",
|
||||
|result| {
|
||||
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||
assert_eq!(result.get("b"), Some(&Value::string("b")));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_fragment_include_false() {
|
||||
run_query("{ a, ... on TestType @include(if: false) { b } }",
|
||||
|result| {
|
||||
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||
assert_eq!(result.get("b"), None);
|
||||
});
|
||||
run_query(
|
||||
"{ a, ... on TestType @include(if: false) { b } }",
|
||||
|result| {
|
||||
assert_eq!(result.get("a"), Some(&Value::string("a")));
|
||||
assert_eq!(result.get("b"), None);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -34,7 +34,8 @@ graphql_object!(TestType: () |&self| {
|
|||
});
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
|
||||
|
||||
|
@ -50,7 +51,8 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
|||
}
|
||||
|
||||
fn run_query<F>(query: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
run_variable_query(query, Variables::new(), f);
|
||||
}
|
||||
|
@ -92,17 +94,18 @@ fn does_not_accept_string_literals() {
|
|||
|
||||
#[test]
|
||||
fn accepts_strings_in_variables() {
|
||||
run_variable_query("query q($color: Color!) { toString(color: $color) }",
|
||||
vec![
|
||||
run_variable_query(
|
||||
"query q($color: Color!) { toString(color: $color) }",
|
||||
vec![
|
||||
("color".to_owned(), InputValue::string("RED")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("toString"),
|
||||
Some(&Value::string("Color::Red")));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -112,9 +115,8 @@ fn does_not_accept_incorrect_enum_name_in_variables() {
|
|||
let query = r#"query q($color: Color!) { toString(color: $color) }"#;
|
||||
let vars = vec![
|
||||
("color".to_owned(), InputValue::string("BLURPLE")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -133,9 +135,8 @@ fn does_not_accept_incorrect_type_in_variables() {
|
|||
let query = r#"query q($color: Color!) { toString(color: $color) }"#;
|
||||
let vars = vec![
|
||||
("color".to_owned(), InputValue::int(123)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
|
|
@ -63,9 +63,8 @@ mod field_execution {
|
|||
|
||||
let vars = vec![
|
||||
("size".to_owned(), InputValue::int(100))
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
|
@ -187,12 +186,15 @@ mod threads_context_correctly {
|
|||
|
||||
let vars = vec![].into_iter().collect();
|
||||
|
||||
let (result, errs) = ::execute(doc,
|
||||
None,
|
||||
&schema,
|
||||
&vars,
|
||||
&TestContext { value: "Context value".to_owned() })
|
||||
.expect("Execution failed");
|
||||
let (result, errs) = ::execute(
|
||||
doc,
|
||||
None,
|
||||
&schema,
|
||||
&vars,
|
||||
&TestContext {
|
||||
value: "Context value".to_owned(),
|
||||
},
|
||||
).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -213,7 +215,7 @@ mod dynamic_context_switching {
|
|||
use types::scalars::EmptyMutation;
|
||||
use schema::model::RootNode;
|
||||
use parser::SourcePosition;
|
||||
use executor::{FieldResult, Context, ExecutionError};
|
||||
use executor::{Context, ExecutionError, FieldResult};
|
||||
|
||||
struct Schema;
|
||||
|
||||
|
@ -272,9 +274,8 @@ mod dynamic_context_switching {
|
|||
items: vec![
|
||||
(0, InnerContext { value: "First value".to_owned() }),
|
||||
(1, InnerContext { value: "Second value".to_owned() }),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
|
||||
|
@ -309,9 +310,8 @@ mod dynamic_context_switching {
|
|||
items: vec![
|
||||
(0, InnerContext { value: "First value".to_owned() }),
|
||||
(1, InnerContext { value: "Second value".to_owned() }),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
|
||||
|
@ -353,9 +353,8 @@ mod dynamic_context_switching {
|
|||
items: vec![
|
||||
(0, InnerContext { value: "First value".to_owned() }),
|
||||
(1, InnerContext { value: "Second value".to_owned() }),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
|
||||
|
@ -392,9 +391,8 @@ mod dynamic_context_switching {
|
|||
items: vec![
|
||||
(0, InnerContext { value: "First value".to_owned() }),
|
||||
(1, InnerContext { value: "Second value".to_owned() }),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
|
||||
|
@ -512,8 +510,8 @@ mod named_operations {
|
|||
|
||||
let vars = vec![].into_iter().collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, Some("OtherExample"), &schema, &vars, &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, Some("OtherExample"), &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
|
|
@ -77,13 +77,15 @@ mod interface {
|
|||
|
||||
#[test]
|
||||
fn test() {
|
||||
let schema = RootNode::new(Schema {
|
||||
pets: vec![
|
||||
let schema = RootNode::new(
|
||||
Schema {
|
||||
pets: vec![
|
||||
Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
|
||||
Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
|
||||
],
|
||||
},
|
||||
EmptyMutation::<()>::new());
|
||||
},
|
||||
EmptyMutation::<()>::new(),
|
||||
);
|
||||
let doc = r"
|
||||
{
|
||||
pets {
|
||||
|
@ -190,13 +192,15 @@ mod union {
|
|||
|
||||
#[test]
|
||||
fn test() {
|
||||
let schema = RootNode::new(Schema {
|
||||
pets: vec![
|
||||
let schema = RootNode::new(
|
||||
Schema {
|
||||
pets: vec![
|
||||
Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
|
||||
Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
|
||||
],
|
||||
},
|
||||
EmptyMutation::<()>::new());
|
||||
},
|
||||
EmptyMutation::<()>::new(),
|
||||
);
|
||||
let doc = r"
|
||||
{
|
||||
pets {
|
||||
|
|
|
@ -69,8 +69,8 @@ fn test_execution() {
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -106,8 +106,8 @@ fn enum_introspection() {
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -189,8 +189,8 @@ fn interface_introspection() {
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -297,8 +297,8 @@ fn object_introspection() {
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -414,8 +414,8 @@ fn scalar_introspection() {
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
|
|
@ -119,7 +119,8 @@ graphql_object!(TestType: () |&self| {
|
|||
});
|
||||
|
||||
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
|
||||
|
||||
|
@ -135,7 +136,8 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
|
|||
}
|
||||
|
||||
fn run_query<F>(query: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
run_variable_query(query, Variables::new(), f);
|
||||
}
|
||||
|
@ -237,9 +239,8 @@ fn variable_error_on_nested_non_null() {
|
|||
("b", InputValue::string("bar")),
|
||||
("c", InputValue::null()),
|
||||
].into_iter().collect()))
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -258,9 +259,8 @@ fn variable_error_on_incorrect_type() {
|
|||
let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#;
|
||||
let vars = vec![
|
||||
("input".to_owned(), InputValue::string("foo bar")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -282,9 +282,8 @@ fn variable_error_on_omit_non_null() {
|
|||
("a", InputValue::string("foo")),
|
||||
("b", InputValue::string("bar")),
|
||||
].into_iter().collect()))
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -308,9 +307,8 @@ fn variable_multiple_errors_with_nesting() {
|
|||
("a", InputValue::string("foo")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()))
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -338,9 +336,8 @@ fn variable_error_on_additional_field() {
|
|||
("c", InputValue::string("baz")),
|
||||
("extra", InputValue::string("dog")),
|
||||
].into_iter().collect()))
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -373,12 +370,14 @@ fn allow_nullable_inputs_to_be_omitted_in_variable() {
|
|||
|
||||
#[test]
|
||||
fn allow_nullable_inputs_to_be_explicitly_null() {
|
||||
run_query(r#"{ fieldWithNullableStringInput(input: null) }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
run_query(
|
||||
r#"{ fieldWithNullableStringInput(input: null) }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("fieldWithNullableStringInput"),
|
||||
Some(&Value::string(r#"None"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -411,12 +410,14 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
|
|||
|
||||
#[test]
|
||||
fn allow_nullable_inputs_to_be_set_to_value_directly() {
|
||||
run_query(r#"{ fieldWithNullableStringInput(input: "a") }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
run_query(
|
||||
r#"{ fieldWithNullableStringInput(input: "a") }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("fieldWithNullableStringInput"),
|
||||
Some(&Value::string(r#"Some("a")"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -443,9 +444,8 @@ fn does_not_allow_non_nullable_input_to_be_set_to_null_in_variable() {
|
|||
let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#;
|
||||
let vars = vec![
|
||||
("value".to_owned(), InputValue::null()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -473,63 +473,68 @@ fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
|
|||
|
||||
#[test]
|
||||
fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
|
||||
run_query(r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
run_query(
|
||||
r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("fieldWithNonNullableStringInput"),
|
||||
Some(&Value::string(r#""a""#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_lists_to_be_null() {
|
||||
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
("input".to_owned(), InputValue::null()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("list"),
|
||||
Some(&Value::string(r#"None"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_lists_to_contain_values() {
|
||||
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
("input".to_owned(), InputValue::list(vec![
|
||||
InputValue::string("A"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("list"),
|
||||
Some(&Value::string(r#"Some([Some("A")])"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allow_lists_to_contain_null() {
|
||||
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($input: [String]) { list(input: $input) }"#,
|
||||
vec![
|
||||
("input".to_owned(), InputValue::list(vec![
|
||||
InputValue::string("A"),
|
||||
InputValue::null(),
|
||||
InputValue::string("B"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("list"),
|
||||
Some(&Value::string(r#"Some([Some("A"), None, Some("B")])"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -539,9 +544,8 @@ fn does_not_allow_non_null_lists_to_be_null() {
|
|||
let query = r#"query q($input: [String]!) { nnList(input: $input) }"#;
|
||||
let vars = vec![
|
||||
("input".to_owned(), InputValue::null()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -631,9 +635,8 @@ fn does_not_allow_lists_of_non_null_to_contain_null() {
|
|||
InputValue::null(),
|
||||
InputValue::string("B"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -656,9 +659,8 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() {
|
|||
InputValue::null(),
|
||||
InputValue::string("B"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -677,9 +679,8 @@ fn does_not_allow_non_null_lists_of_non_null_to_be_null() {
|
|||
let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#;
|
||||
let vars = vec![
|
||||
("value".to_owned(), InputValue::null()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -718,9 +719,8 @@ fn does_not_allow_invalid_types_to_be_used_as_values() {
|
|||
InputValue::string("A"),
|
||||
InputValue::string("B"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -742,9 +742,8 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
|
|||
InputValue::string("A"),
|
||||
InputValue::string("B"),
|
||||
])),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -897,9 +896,8 @@ fn does_not_allow_null_variable_for_required_field() {
|
|||
let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#;
|
||||
let vars = vec![
|
||||
("var".to_owned(), InputValue::null()),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -958,29 +956,31 @@ mod integers {
|
|||
|
||||
#[test]
|
||||
fn positive_and_negative_should_work() {
|
||||
run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($var: Int!) { integerInput(value: $var) }"#,
|
||||
vec![
|
||||
("var".to_owned(), InputValue::int(1)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("integerInput"),
|
||||
Some(&Value::string(r#"value: 1"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($var: Int!) { integerInput(value: $var) }"#,
|
||||
vec![
|
||||
("var".to_owned(), InputValue::int(-1)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("integerInput"),
|
||||
Some(&Value::string(r#"value: -1"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -990,9 +990,8 @@ mod integers {
|
|||
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
|
||||
let vars = vec![
|
||||
("var".to_owned(), InputValue::float(10.0)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -1011,9 +1010,8 @@ mod integers {
|
|||
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
|
||||
let vars = vec![
|
||||
("var".to_owned(), InputValue::string("10")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
@ -1032,32 +1030,34 @@ mod floats {
|
|||
|
||||
#[test]
|
||||
fn float_values_should_work() {
|
||||
run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($var: Float!) { floatInput(value: $var) }"#,
|
||||
vec![
|
||||
("var".to_owned(), InputValue::float(10.0)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("floatInput"),
|
||||
Some(&Value::string(r#"value: 10"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coercion_from_integers_should_work() {
|
||||
run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
|
||||
vec![
|
||||
run_variable_query(
|
||||
r#"query q($var: Float!) { floatInput(value: $var) }"#,
|
||||
vec![
|
||||
("var".to_owned(), InputValue::int(-1)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
].into_iter()
|
||||
.collect(),
|
||||
|result| {
|
||||
assert_eq!(
|
||||
result.get("floatInput"),
|
||||
Some(&Value::string(r#"value: -1"#)));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -1067,9 +1067,8 @@ mod floats {
|
|||
let query = r#"query q($var: Float!) { floatInput(value: $var) }"#;
|
||||
let vars = vec![
|
||||
("var".to_owned(), InputValue::string("10")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
use serde::ser;
|
||||
use serde::ser::SerializeMap;
|
||||
|
||||
use {GraphQLError, Value, Variables, GraphQLType, RootNode};
|
||||
use {GraphQLError, GraphQLType, RootNode, Value, Variables};
|
||||
use ast::InputValue;
|
||||
use executor::ExecutionError;
|
||||
|
||||
|
@ -31,21 +31,21 @@ impl GraphQLRequest {
|
|||
self.variables
|
||||
.as_ref()
|
||||
.and_then(|iv| {
|
||||
iv.to_object_value()
|
||||
.map(|o| {
|
||||
o.into_iter()
|
||||
.map(|(k, v)| (k.to_owned(), v.clone()))
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
iv.to_object_value().map(|o| {
|
||||
o.into_iter()
|
||||
.map(|(k, v)| (k.to_owned(), v.clone()))
|
||||
.collect()
|
||||
})
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Construct a new GraphQL request from parts
|
||||
pub fn new(query: String,
|
||||
operation_name: Option<String>,
|
||||
variables: Option<InputValue>)
|
||||
-> GraphQLRequest {
|
||||
pub fn new(
|
||||
query: String,
|
||||
operation_name: Option<String>,
|
||||
variables: Option<InputValue>,
|
||||
) -> GraphQLRequest {
|
||||
GraphQLRequest {
|
||||
query: query,
|
||||
operation_name: operation_name,
|
||||
|
@ -57,18 +57,22 @@ impl GraphQLRequest {
|
|||
///
|
||||
/// This is a simple wrapper around the `execute` function exposed at the
|
||||
/// top level of this crate.
|
||||
pub fn execute<'a, CtxT, QueryT, MutationT>(&'a self,
|
||||
root_node: &RootNode<QueryT, MutationT>,
|
||||
context: &CtxT)
|
||||
-> GraphQLResponse<'a>
|
||||
where QueryT: GraphQLType<Context = CtxT>,
|
||||
MutationT: GraphQLType<Context = CtxT>
|
||||
pub fn execute<'a, CtxT, QueryT, MutationT>(
|
||||
&'a self,
|
||||
root_node: &RootNode<QueryT, MutationT>,
|
||||
context: &CtxT,
|
||||
) -> GraphQLResponse<'a>
|
||||
where
|
||||
QueryT: GraphQLType<Context = CtxT>,
|
||||
MutationT: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
GraphQLResponse(::execute(&self.query,
|
||||
self.operation_name(),
|
||||
root_node,
|
||||
&self.variables(),
|
||||
context))
|
||||
GraphQLResponse(::execute(
|
||||
&self.query,
|
||||
self.operation_name(),
|
||||
root_node,
|
||||
&self.variables(),
|
||||
context,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +95,8 @@ impl<'a> GraphQLResponse<'a> {
|
|||
|
||||
impl<'a> ser::Serialize for GraphQLResponse<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
match self.0 {
|
||||
Ok((ref res, ref err)) => {
|
||||
|
@ -117,7 +122,7 @@ impl<'a> ser::Serialize for GraphQLResponse<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature="expose-test-schema"))]
|
||||
#[cfg(any(test, feature = "expose-test-schema"))]
|
||||
pub mod tests {
|
||||
use serde_json::Value as Json;
|
||||
use serde_json;
|
||||
|
@ -150,11 +155,12 @@ pub mod tests {
|
|||
}
|
||||
|
||||
fn unwrap_json_response(response: &TestResponse) -> Json {
|
||||
serde_json::from_str::<Json>(response
|
||||
.body
|
||||
.as_ref()
|
||||
.expect("No data returned from request"))
|
||||
.expect("Could not parse JSON object")
|
||||
serde_json::from_str::<Json>(
|
||||
response
|
||||
.body
|
||||
.as_ref()
|
||||
.expect("No data returned from request"),
|
||||
).expect("Could not parse JSON object")
|
||||
}
|
||||
|
||||
fn test_simple_get<T: HTTPIntegration>(integration: &T) {
|
||||
|
@ -166,7 +172,8 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
unwrap_json_response(&response),
|
||||
serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
|
||||
.expect("Invalid JSON constant in test"));
|
||||
.expect("Invalid JSON constant in test")
|
||||
);
|
||||
}
|
||||
|
||||
fn test_encoded_get<T: HTTPIntegration>(integration: &T) {
|
||||
|
@ -178,7 +185,8 @@ pub mod tests {
|
|||
|
||||
assert_eq!(
|
||||
unwrap_json_response(&response),
|
||||
serde_json::from_str::<Json>(r#"{
|
||||
serde_json::from_str::<Json>(
|
||||
r#"{
|
||||
"data": {
|
||||
"human": {
|
||||
"appearsIn": [
|
||||
|
@ -191,8 +199,9 @@ pub mod tests {
|
|||
"id": "1000"
|
||||
}
|
||||
}
|
||||
}"#)
|
||||
.expect("Invalid JSON constant in test"));
|
||||
}"#
|
||||
).expect("Invalid JSON constant in test")
|
||||
);
|
||||
}
|
||||
|
||||
fn test_get_with_variables<T: HTTPIntegration>(integration: &T) {
|
||||
|
@ -204,7 +213,8 @@ pub mod tests {
|
|||
|
||||
assert_eq!(
|
||||
unwrap_json_response(&response),
|
||||
serde_json::from_str::<Json>(r#"{
|
||||
serde_json::from_str::<Json>(
|
||||
r#"{
|
||||
"data": {
|
||||
"human": {
|
||||
"appearsIn": [
|
||||
|
@ -217,8 +227,9 @@ pub mod tests {
|
|||
"id": "1000"
|
||||
}
|
||||
}
|
||||
}"#)
|
||||
.expect("Invalid JSON constant in test"));
|
||||
}"#
|
||||
).expect("Invalid JSON constant in test")
|
||||
);
|
||||
}
|
||||
|
||||
fn test_simple_post<T: HTTPIntegration>(integration: &T) {
|
||||
|
@ -230,6 +241,7 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
unwrap_json_response(&response),
|
||||
serde_json::from_str::<Json>(r#"{"data": {"hero": {"name": "R2-D2"}}}"#)
|
||||
.expect("Invalid JSON constant in test"));
|
||||
.expect("Invalid JSON constant in test")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,14 @@ use std::collections::HashMap;
|
|||
use {GraphQLError, Value};
|
||||
use ast::InputValue;
|
||||
use executor::ExecutionError;
|
||||
use parser::{ParseError, Spanning, SourcePosition};
|
||||
use parser::{ParseError, SourcePosition, Spanning};
|
||||
use validation::RuleError;
|
||||
|
||||
|
||||
impl ser::Serialize for ExecutionError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
let mut map = try!(serializer.serialize_map(Some(3)));
|
||||
|
||||
|
@ -32,7 +33,8 @@ impl ser::Serialize for ExecutionError {
|
|||
|
||||
impl<'a> ser::Serialize for GraphQLError<'a> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
match *self {
|
||||
GraphQLError::ParseError(ref err) => vec![err].serialize(serializer),
|
||||
|
@ -40,9 +42,9 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
|
|||
GraphQLError::NoOperationProvided => {
|
||||
serializer.serialize_str("Must provide an operation")
|
||||
}
|
||||
GraphQLError::MultipleOperationsProvided => {
|
||||
serializer.serialize_str("Must provide operation name if query contains multiple operations")
|
||||
}
|
||||
GraphQLError::MultipleOperationsProvided => serializer.serialize_str(
|
||||
"Must provide operation name if query contains multiple operations",
|
||||
),
|
||||
GraphQLError::UnknownOperationName => serializer.serialize_str("Unknown operation"),
|
||||
}
|
||||
}
|
||||
|
@ -50,7 +52,8 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
|
|||
|
||||
impl<'de> de::Deserialize<'de> for InputValue {
|
||||
fn deserialize<D>(deserializer: D) -> Result<InputValue, D::Error>
|
||||
where D: de::Deserializer<'de>
|
||||
where
|
||||
D: de::Deserializer<'de>,
|
||||
{
|
||||
struct InputValueVisitor;
|
||||
|
||||
|
@ -66,7 +69,8 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
}
|
||||
|
||||
fn visit_i64<E>(self, value: i64) -> Result<InputValue, E>
|
||||
where E: de::Error
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
|
||||
Ok(InputValue::int(value as i32))
|
||||
|
@ -76,7 +80,8 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
}
|
||||
|
||||
fn visit_u64<E>(self, value: u64) -> Result<InputValue, E>
|
||||
where E: de::Error
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
if value <= i32::max_value() as u64 {
|
||||
self.visit_i64(value as i64)
|
||||
|
@ -90,7 +95,8 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<InputValue, E>
|
||||
where E: de::Error
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
self.visit_string(value.into())
|
||||
}
|
||||
|
@ -108,7 +114,8 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
}
|
||||
|
||||
fn visit_seq<V>(self, mut visitor: V) -> Result<InputValue, V::Error>
|
||||
where V: de::SeqAccess<'de>
|
||||
where
|
||||
V: de::SeqAccess<'de>,
|
||||
{
|
||||
let mut values = Vec::new();
|
||||
|
||||
|
@ -120,7 +127,8 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<InputValue, V::Error>
|
||||
where V: de::MapAccess<'de>
|
||||
where
|
||||
V: de::MapAccess<'de>,
|
||||
{
|
||||
let mut values: HashMap<String, InputValue> = HashMap::new();
|
||||
|
||||
|
@ -138,35 +146,31 @@ impl<'de> de::Deserialize<'de> for InputValue {
|
|||
|
||||
impl ser::Serialize for InputValue {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
match *self {
|
||||
InputValue::Null |
|
||||
InputValue::Variable(_) => serializer.serialize_unit(),
|
||||
InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(),
|
||||
InputValue::Int(v) => serializer.serialize_i64(v as i64),
|
||||
InputValue::Float(v) => serializer.serialize_f64(v),
|
||||
InputValue::String(ref v) |
|
||||
InputValue::Enum(ref v) => serializer.serialize_str(v),
|
||||
InputValue::String(ref v) | InputValue::Enum(ref v) => serializer.serialize_str(v),
|
||||
InputValue::Boolean(v) => serializer.serialize_bool(v),
|
||||
InputValue::List(ref v) => {
|
||||
v.iter()
|
||||
.map(|x| x.item.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.serialize(serializer)
|
||||
}
|
||||
InputValue::Object(ref v) => {
|
||||
v.iter()
|
||||
.map(|&(ref k, ref v)| (k.item.clone(), v.item.clone()))
|
||||
.collect::<HashMap<_, _>>()
|
||||
.serialize(serializer)
|
||||
}
|
||||
InputValue::List(ref v) => v.iter()
|
||||
.map(|x| x.item.clone())
|
||||
.collect::<Vec<_>>()
|
||||
.serialize(serializer),
|
||||
InputValue::Object(ref v) => v.iter()
|
||||
.map(|&(ref k, ref v)| (k.item.clone(), v.item.clone()))
|
||||
.collect::<HashMap<_, _>>()
|
||||
.serialize(serializer),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ser::Serialize for RuleError {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
let mut map = try!(serializer.serialize_map(Some(2)));
|
||||
|
||||
|
@ -182,7 +186,8 @@ impl ser::Serialize for RuleError {
|
|||
|
||||
impl ser::Serialize for SourcePosition {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
let mut map = try!(serializer.serialize_map(Some(2)));
|
||||
|
||||
|
@ -200,7 +205,8 @@ impl ser::Serialize for SourcePosition {
|
|||
|
||||
impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
let mut map = try!(serializer.serialize_map(Some(2)));
|
||||
|
||||
|
@ -223,7 +229,8 @@ impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
|
|||
|
||||
impl ser::Serialize for Value {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where S: ser::Serializer
|
||||
where
|
||||
S: ser::Serializer,
|
||||
{
|
||||
match *self {
|
||||
Value::Null => serializer.serialize_unit(),
|
||||
|
|
|
@ -109,17 +109,16 @@ To support this, Juniper offers additional crates that integrate with popular we
|
|||
[Rocket]: https://rocket.rs
|
||||
|
||||
*/
|
||||
|
||||
#![cfg_attr(feature="nightly", feature(test))]
|
||||
#![cfg_attr(feature = "nightly", feature(test))]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
#[cfg(feature="nightly")]
|
||||
#[cfg(feature = "nightly")]
|
||||
extern crate test;
|
||||
extern crate serde;
|
||||
#[macro_use]
|
||||
extern crate serde_derive;
|
||||
|
||||
#[cfg(any(test, feature="expose-test-schema"))]
|
||||
#[cfg(any(test, feature = "expose-test-schema"))]
|
||||
extern crate serde_json;
|
||||
|
||||
use std::borrow::Cow;
|
||||
|
@ -139,23 +138,23 @@ pub mod http;
|
|||
#[macro_use]
|
||||
mod result_ext;
|
||||
|
||||
#[cfg(all(test, not(feature="expose-test-schema")))]
|
||||
#[cfg(all(test, not(feature = "expose-test-schema")))]
|
||||
mod tests;
|
||||
#[cfg(feature="expose-test-schema")]
|
||||
#[cfg(feature = "expose-test-schema")]
|
||||
pub mod tests;
|
||||
|
||||
#[cfg(test)]
|
||||
mod executor_tests;
|
||||
|
||||
use parser::{parse_document_source, ParseError, Spanning};
|
||||
use validation::{ValidatorContext, visit_all_rules, validate_input_values};
|
||||
use validation::{validate_input_values, visit_all_rules, ValidatorContext};
|
||||
use executor::execute_validated_query;
|
||||
|
||||
pub use ast::{ToInputValue, FromInputValue, InputValue, Type, Selection};
|
||||
pub use ast::{FromInputValue, InputValue, Selection, ToInputValue, Type};
|
||||
pub use value::Value;
|
||||
pub use types::base::{Arguments, GraphQLType, TypeKind};
|
||||
pub use executor::{Executor, ExecutionError, Registry, Context, FromContext, IntoResolvable,
|
||||
FieldResult, ExecutionResult, Variables};
|
||||
pub use executor::{Context, ExecutionError, ExecutionResult, Executor, FieldResult, FromContext,
|
||||
IntoResolvable, Registry, Variables};
|
||||
pub use validation::RuleError;
|
||||
pub use types::scalars::{EmptyMutation, ID};
|
||||
pub use schema::model::RootNode;
|
||||
|
@ -175,15 +174,16 @@ pub enum GraphQLError<'a> {
|
|||
}
|
||||
|
||||
/// Execute a query in a provided schema
|
||||
pub fn execute<'a, CtxT, QueryT, MutationT>
|
||||
(document_source: &'a str,
|
||||
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>
|
||||
pub fn execute<'a, CtxT, QueryT, MutationT>(
|
||||
document_source: &'a str,
|
||||
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 document = try!(parse_document_source(document_source));
|
||||
|
||||
|
|
|
@ -78,7 +78,8 @@ graphql_object!(Root: () |&self| {
|
|||
});
|
||||
|
||||
fn run_args_info_query<F>(field_name: &str, f: F)
|
||||
where F: Fn(&Vec<Value>) -> ()
|
||||
where
|
||||
F: Fn(&Vec<Value>) -> (),
|
||||
{
|
||||
let doc = r#"
|
||||
{
|
||||
|
@ -102,8 +103,8 @@ fn run_args_info_query<F>(field_name: &str, f: F)
|
|||
"#;
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -126,13 +127,13 @@ fn run_args_info_query<F>(field_name: &str, f: F)
|
|||
let field = fields
|
||||
.into_iter()
|
||||
.filter(|f| {
|
||||
f.as_object_value()
|
||||
.expect("Field not an object")
|
||||
.get("name")
|
||||
.expect("name field missing from field")
|
||||
.as_string_value()
|
||||
.expect("name is not a string") == field_name
|
||||
})
|
||||
f.as_object_value()
|
||||
.expect("Field not an object")
|
||||
.get("name")
|
||||
.expect("name field missing from field")
|
||||
.as_string_value()
|
||||
.expect("name is not a string") == field_name
|
||||
})
|
||||
.next()
|
||||
.expect("Field not found")
|
||||
.as_object_value()
|
||||
|
|
|
@ -87,12 +87,13 @@ graphql_object!(Root: () |&self| {
|
|||
});
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> ()
|
||||
where
|
||||
F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> (),
|
||||
{
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
|
|
@ -58,7 +58,8 @@ graphql_interface!(Interface: () |&self| {
|
|||
});
|
||||
|
||||
fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
let doc = r#"
|
||||
query ($typeName: String!) {
|
||||
|
@ -75,9 +76,8 @@ fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
|
|||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
let vars = vec![
|
||||
("typeName".to_owned(), InputValue::string(type_name)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
|
@ -102,13 +102,13 @@ fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
|
|||
let field = fields
|
||||
.into_iter()
|
||||
.filter(|f| {
|
||||
f.as_object_value()
|
||||
.expect("Field not an object")
|
||||
.get("name")
|
||||
.expect("name field missing from field")
|
||||
.as_string_value()
|
||||
.expect("name is not a string") == field_name
|
||||
})
|
||||
f.as_object_value()
|
||||
.expect("Field not an object")
|
||||
.get("name")
|
||||
.expect("name field missing from field")
|
||||
.as_string_value()
|
||||
.expect("name is not a string") == field_name
|
||||
})
|
||||
.next()
|
||||
.expect("Field not found")
|
||||
.as_object_value()
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use ast::{InputValue, FromInputValue};
|
||||
use ast::{FromInputValue, InputValue};
|
||||
use executor::Variables;
|
||||
use value::Value;
|
||||
use schema::model::RootNode;
|
||||
|
@ -104,12 +104,13 @@ graphql_object!(Root: () |&self| {
|
|||
});
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>, &Vec<Value>) -> (),
|
||||
{
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
@ -185,12 +186,13 @@ fn default_name_introspection() {
|
|||
|
||||
#[test]
|
||||
fn default_name_input_value() {
|
||||
let iv = InputValue::object(vec![
|
||||
let iv = InputValue::object(
|
||||
vec![
|
||||
("fieldOne", InputValue::string("number one")),
|
||||
("fieldTwo", InputValue::string("number two")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect());
|
||||
].into_iter()
|
||||
.collect(),
|
||||
);
|
||||
|
||||
let dv: Option<DefaultName> = FromInputValue::from(&iv);
|
||||
|
||||
|
|
|
@ -134,7 +134,8 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
|
|||
|
||||
|
||||
fn run_type_info_query<F>(type_name: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>, &Vec<Value>) -> (),
|
||||
{
|
||||
let doc = r#"
|
||||
query ($typeName: String!) {
|
||||
|
@ -150,9 +151,8 @@ fn run_type_info_query<F>(type_name: &str, f: F)
|
|||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
let vars = vec![
|
||||
("typeName".to_owned(), InputValue::string(type_name)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
|
|
|
@ -118,7 +118,8 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
|
|||
|
||||
|
||||
fn run_type_info_query<F>(type_name: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>, &Vec<Value>) -> (),
|
||||
{
|
||||
let doc = r#"
|
||||
query ($typeName: String!) {
|
||||
|
@ -138,9 +139,8 @@ fn run_type_info_query<F>(type_name: &str, f: F)
|
|||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
let vars = vec![
|
||||
("typeName".to_owned(), InputValue::string(type_name)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
|
|
|
@ -71,12 +71,13 @@ graphql_object!(Root: () |&self| {
|
|||
});
|
||||
|
||||
fn run_type_info_query<F>(doc: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>) -> (),
|
||||
{
|
||||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
|
||||
.expect("Execution failed");
|
||||
let (result, errs) =
|
||||
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
|
||||
|
||||
assert_eq!(errs, []);
|
||||
|
||||
|
|
|
@ -111,7 +111,8 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
|
|||
|
||||
|
||||
fn run_type_info_query<F>(type_name: &str, f: F)
|
||||
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
|
||||
where
|
||||
F: Fn(&HashMap<String, Value>, &Vec<Value>) -> (),
|
||||
{
|
||||
let doc = r#"
|
||||
query ($typeName: String!) {
|
||||
|
@ -127,9 +128,8 @@ fn run_type_info_query<F>(type_name: &str, f: F)
|
|||
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
|
||||
let vars = vec![
|
||||
("typeName".to_owned(), InputValue::string(type_name)),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
].into_iter()
|
||||
.collect();
|
||||
|
||||
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use ast::{Definition, Document, OperationType, VariableDefinitions, VariableDefinition,
|
||||
InputValue, Operation, Fragment, Selection, Directive, Field, Arguments, FragmentSpread,
|
||||
InlineFragment, Type};
|
||||
use ast::{Arguments, Definition, Directive, Document, Field, Fragment, FragmentSpread,
|
||||
InlineFragment, InputValue, Operation, OperationType, Selection, Type,
|
||||
VariableDefinition, VariableDefinitions};
|
||||
|
||||
use parser::{Lexer, Parser, Spanning, UnlocatedParseResult, OptionParseResult, ParseResult,
|
||||
ParseError, Token};
|
||||
use parser::{Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token,
|
||||
UnlocatedParseResult};
|
||||
use parser::value::parse_value_literal;
|
||||
|
||||
#[doc(hidden)]
|
||||
|
@ -27,14 +27,12 @@ fn parse_document<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Docum
|
|||
|
||||
fn parse_definition<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Definition<'a>> {
|
||||
match parser.peek().item {
|
||||
Token::CurlyOpen |
|
||||
Token::Name("query") |
|
||||
Token::Name("mutation") => {
|
||||
Ok(Definition::Operation(try!(parse_operation_definition(parser))))
|
||||
}
|
||||
Token::Name("fragment") => {
|
||||
Ok(Definition::Fragment(try!(parse_fragment_definition(parser))))
|
||||
}
|
||||
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Ok(
|
||||
Definition::Operation(try!(parse_operation_definition(parser))),
|
||||
),
|
||||
Token::Name("fragment") => Ok(Definition::Fragment(
|
||||
try!(parse_fragment_definition(parser)),
|
||||
)),
|
||||
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
|
||||
}
|
||||
}
|
||||
|
@ -43,15 +41,17 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
|
|||
if parser.peek().item == Token::CurlyOpen {
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Spanning::start_end(&selection_set.start,
|
||||
&selection_set.end,
|
||||
Operation {
|
||||
operation_type: OperationType::Query,
|
||||
name: None,
|
||||
variable_definitions: None,
|
||||
directives: None,
|
||||
selection_set: selection_set.item,
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&selection_set.start,
|
||||
&selection_set.end,
|
||||
Operation {
|
||||
operation_type: OperationType::Query,
|
||||
name: None,
|
||||
variable_definitions: None,
|
||||
directives: None,
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
let start_pos = parser.peek().start.clone();
|
||||
let operation_type = try!(parse_operation_type(parser));
|
||||
|
@ -63,28 +63,30 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
|
|||
let directives = try!(parse_directives(parser));
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Spanning::start_end(&start_pos,
|
||||
&selection_set.end,
|
||||
Operation {
|
||||
operation_type: operation_type.item,
|
||||
name: name,
|
||||
variable_definitions: variable_definitions,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&selection_set.end,
|
||||
Operation {
|
||||
operation_type: operation_type.item,
|
||||
name: name,
|
||||
variable_definitions: variable_definitions,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fragment<'a>> {
|
||||
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Name("fragment")));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(parser.expect(&Token::Name("fragment")));
|
||||
let name = match parser.expect_name() {
|
||||
Ok(n) => {
|
||||
if n.item == "on" {
|
||||
return Err(n.map(|_| ParseError::UnexpectedToken(Token::Name("on"))));
|
||||
} else {
|
||||
n
|
||||
}
|
||||
}
|
||||
Ok(n) => if n.item == "on" {
|
||||
return Err(n.map(|_| ParseError::UnexpectedToken(Token::Name("on"))));
|
||||
} else {
|
||||
n
|
||||
},
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
|
@ -93,18 +95,21 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
|
|||
let directives = try!(parse_directives(parser));
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Spanning::start_end(&start_pos,
|
||||
&selection_set.end,
|
||||
Fragment {
|
||||
name: name,
|
||||
type_condition: type_cond,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&selection_set.end,
|
||||
Fragment {
|
||||
name: name,
|
||||
type_condition: type_cond,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_optional_selection_set<'a>(parser: &mut Parser<'a>)
|
||||
-> OptionParseResult<'a, Vec<Selection<'a>>> {
|
||||
fn parse_optional_selection_set<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
) -> OptionParseResult<'a, Vec<Selection<'a>>> {
|
||||
if parser.peek().item == Token::CurlyOpen {
|
||||
Ok(Some(try!(parse_selection_set(parser))))
|
||||
} else {
|
||||
|
@ -124,7 +129,10 @@ fn parse_selection<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Sele
|
|||
}
|
||||
|
||||
fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selection<'a>> {
|
||||
let Spanning { start: ref start_pos, .. } = try!(parser.expect(&Token::Ellipsis));
|
||||
let Spanning {
|
||||
start: ref start_pos,
|
||||
..
|
||||
} = try!(parser.expect(&Token::Ellipsis));
|
||||
|
||||
match parser.peek().item {
|
||||
Token::Name("on") => {
|
||||
|
@ -133,57 +141,58 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
|
|||
let directives = try!(parse_directives(parser));
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: Some(name),
|
||||
directives: directives.map(|s| {
|
||||
s.item
|
||||
}),
|
||||
selection_set: selection_set.item,
|
||||
})))
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(
|
||||
&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: Some(name),
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
)))
|
||||
}
|
||||
Token::CurlyOpen => {
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: None,
|
||||
directives: None,
|
||||
selection_set: selection_set.item,
|
||||
})))
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(
|
||||
&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: None,
|
||||
directives: None,
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
)))
|
||||
}
|
||||
Token::Name(_) => {
|
||||
let frag_name = try!(parser.expect_name());
|
||||
let directives = try!(parse_directives(parser));
|
||||
|
||||
Ok(Selection::FragmentSpread(Spanning::start_end(&start_pos.clone(),
|
||||
&directives
|
||||
.as_ref()
|
||||
.map_or(&frag_name.end,
|
||||
|s| &s.end)
|
||||
.clone(),
|
||||
FragmentSpread {
|
||||
name: frag_name,
|
||||
directives: directives.map(|s| {
|
||||
s.item
|
||||
}),
|
||||
})))
|
||||
Ok(Selection::FragmentSpread(Spanning::start_end(
|
||||
&start_pos.clone(),
|
||||
&directives
|
||||
.as_ref()
|
||||
.map_or(&frag_name.end, |s| &s.end)
|
||||
.clone(),
|
||||
FragmentSpread {
|
||||
name: frag_name,
|
||||
directives: directives.map(|s| s.item),
|
||||
},
|
||||
)))
|
||||
}
|
||||
Token::At => {
|
||||
let directives = try!(parse_directives(parser));
|
||||
let selection_set = try!(parse_selection_set(parser));
|
||||
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: None,
|
||||
directives: directives.map(|s| {
|
||||
s.item
|
||||
}),
|
||||
selection_set: selection_set.item,
|
||||
})))
|
||||
Ok(Selection::InlineFragment(Spanning::start_end(
|
||||
&start_pos.clone(),
|
||||
&selection_set.end,
|
||||
InlineFragment {
|
||||
type_condition: None,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.item,
|
||||
},
|
||||
)))
|
||||
}
|
||||
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
|
||||
}
|
||||
|
@ -202,45 +211,54 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
|
|||
let directives = try!(parse_directives(parser));
|
||||
let selection_set = try!(parse_optional_selection_set(parser));
|
||||
|
||||
Ok(Spanning::start_end(&alias.as_ref().unwrap_or(&name).start.clone(),
|
||||
&selection_set
|
||||
.as_ref()
|
||||
.map(|s| &s.end)
|
||||
.or_else(|| directives.as_ref().map(|s| &s.end))
|
||||
.or_else(|| arguments.as_ref().map(|s| &s.end))
|
||||
.unwrap_or(&name.end)
|
||||
.clone(),
|
||||
Field {
|
||||
alias: alias,
|
||||
name: name,
|
||||
arguments: arguments,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.map(|s| s.item),
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&alias.as_ref().unwrap_or(&name).start.clone(),
|
||||
&selection_set
|
||||
.as_ref()
|
||||
.map(|s| &s.end)
|
||||
.or_else(|| directives.as_ref().map(|s| &s.end))
|
||||
.or_else(|| arguments.as_ref().map(|s| &s.end))
|
||||
.unwrap_or(&name.end)
|
||||
.clone(),
|
||||
Field {
|
||||
alias: alias,
|
||||
name: name,
|
||||
arguments: arguments,
|
||||
directives: directives.map(|s| s.item),
|
||||
selection_set: selection_set.map(|s| s.item),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_arguments<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Arguments<'a>> {
|
||||
if parser.peek().item != Token::ParenOpen {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(try!(parser.delimited_nonempty_list(
|
||||
&Token::ParenOpen,
|
||||
parse_argument,
|
||||
&Token::ParenClose
|
||||
))
|
||||
.map(|args| {
|
||||
Arguments { items: args.into_iter().map(|s| s.item).collect() }
|
||||
})))
|
||||
Ok(Some(
|
||||
try!(
|
||||
parser
|
||||
.delimited_nonempty_list(&Token::ParenOpen, parse_argument, &Token::ParenClose)
|
||||
).map(|args| {
|
||||
Arguments {
|
||||
items: args.into_iter().map(|s| s.item).collect(),
|
||||
}
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_argument<'a>(parser: &mut Parser<'a>)
|
||||
-> ParseResult<'a, (Spanning<&'a str>, Spanning<InputValue>)> {
|
||||
fn parse_argument<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
) -> ParseResult<'a, (Spanning<&'a str>, Spanning<InputValue>)> {
|
||||
let name = try!(parser.expect_name());
|
||||
try!(parser.expect(&Token::Colon));
|
||||
let value = try!(parse_value_literal(parser, false));
|
||||
|
||||
Ok(Spanning::start_end(&name.start.clone(), &value.end.clone(), (name, value)))
|
||||
Ok(Spanning::start_end(
|
||||
&name.start.clone(),
|
||||
&value.end.clone(),
|
||||
(name, value),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, OperationType> {
|
||||
|
@ -251,28 +269,32 @@ fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Operatio
|
|||
}
|
||||
}
|
||||
|
||||
fn parse_variable_definitions<'a>(parser: &mut Parser<'a>)
|
||||
-> OptionParseResult<'a, VariableDefinitions<'a>> {
|
||||
fn parse_variable_definitions<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
) -> OptionParseResult<'a, VariableDefinitions<'a>> {
|
||||
if parser.peek().item != Token::ParenOpen {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(try!(parser.delimited_nonempty_list(
|
||||
Ok(Some(
|
||||
try!(parser.delimited_nonempty_list(
|
||||
&Token::ParenOpen,
|
||||
parse_variable_definition,
|
||||
&Token::ParenClose
|
||||
))
|
||||
.map(|defs| {
|
||||
VariableDefinitions {
|
||||
items: defs.into_iter().map(|s| s.item).collect(),
|
||||
}
|
||||
})))
|
||||
)).map(|defs| {
|
||||
VariableDefinitions {
|
||||
items: defs.into_iter().map(|s| s.item).collect(),
|
||||
}
|
||||
}),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_variable_definition<'a>
|
||||
(parser: &mut Parser<'a>)
|
||||
-> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a>)> {
|
||||
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Dollar));
|
||||
fn parse_variable_definition<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
) -> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a>)> {
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(parser.expect(&Token::Dollar));
|
||||
let var_name = try!(parser.expect_name());
|
||||
try!(parser.expect(&Token::Colon));
|
||||
let var_type = try!(parse_type(parser));
|
||||
|
@ -283,20 +305,25 @@ fn parse_variable_definition<'a>
|
|||
None
|
||||
};
|
||||
|
||||
Ok(Spanning::start_end(&start_pos,
|
||||
&default_value
|
||||
.as_ref()
|
||||
.map_or(&var_type.end, |s| &s.end)
|
||||
.clone(),
|
||||
(Spanning::start_end(&start_pos, &var_name.end, var_name.item),
|
||||
VariableDefinition {
|
||||
var_type: var_type,
|
||||
default_value: default_value,
|
||||
})))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&default_value
|
||||
.as_ref()
|
||||
.map_or(&var_type.end, |s| &s.end)
|
||||
.clone(),
|
||||
(
|
||||
Spanning::start_end(&start_pos, &var_name.end, var_name.item),
|
||||
VariableDefinition {
|
||||
var_type: var_type,
|
||||
default_value: default_value,
|
||||
},
|
||||
),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_directives<'a>(parser: &mut Parser<'a>)
|
||||
-> OptionParseResult<'a, Vec<Spanning<Directive<'a>>>> {
|
||||
fn parse_directives<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
) -> OptionParseResult<'a, Vec<Spanning<Directive<'a>>>> {
|
||||
if parser.peek().item != Token::At {
|
||||
Ok(None)
|
||||
} else {
|
||||
|
@ -310,21 +337,27 @@ fn parse_directives<'a>(parser: &mut Parser<'a>)
|
|||
}
|
||||
|
||||
fn parse_directive<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Directive<'a>> {
|
||||
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::At));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(parser.expect(&Token::At));
|
||||
let name = try!(parser.expect_name());
|
||||
let arguments = try!(parse_arguments(parser));
|
||||
|
||||
Ok(Spanning::start_end(&start_pos,
|
||||
&arguments.as_ref().map_or(&name.end, |s| &s.end).clone(),
|
||||
Directive {
|
||||
name: name,
|
||||
arguments: arguments,
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&arguments.as_ref().map_or(&name.end, |s| &s.end).clone(),
|
||||
Directive {
|
||||
name: name,
|
||||
arguments: arguments,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> {
|
||||
let parsed_type = if let Some(Spanning { start: start_pos, .. }) =
|
||||
try!(parser.skip(&Token::BracketOpen)) {
|
||||
let parsed_type = if let Some(Spanning {
|
||||
start: start_pos, ..
|
||||
}) = try!(parser.skip(&Token::BracketOpen))
|
||||
{
|
||||
let inner_type = try!(parse_type(parser));
|
||||
let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::BracketClose));
|
||||
Spanning::start_end(&start_pos, &end_pos, Type::List(Box::new(inner_type.item)))
|
||||
|
@ -333,16 +366,18 @@ pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> {
|
|||
};
|
||||
|
||||
Ok(match *parser.peek() {
|
||||
Spanning { item: Token::ExclamationMark, .. } => {
|
||||
try!(wrap_non_null(parser, parsed_type))
|
||||
}
|
||||
_ => parsed_type,
|
||||
})
|
||||
Spanning {
|
||||
item: Token::ExclamationMark,
|
||||
..
|
||||
} => try!(wrap_non_null(parser, parsed_type)),
|
||||
_ => parsed_type,
|
||||
})
|
||||
}
|
||||
|
||||
fn wrap_non_null<'a>(parser: &mut Parser<'a>,
|
||||
inner: Spanning<Type<'a>>)
|
||||
-> ParseResult<'a, Type<'a>> {
|
||||
fn wrap_non_null<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
inner: Spanning<Type<'a>>,
|
||||
) -> ParseResult<'a, Type<'a>> {
|
||||
let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::ExclamationMark));
|
||||
|
||||
let wrapped = match inner.item {
|
||||
|
|
|
@ -127,8 +127,9 @@ impl<'a> Lexer<'a> {
|
|||
|
||||
let start_pos = self.position.clone();
|
||||
|
||||
self.next_char()
|
||||
.expect("Internal error in GraphQL lexer: emit_single_char reached EOF");
|
||||
self.next_char().expect(
|
||||
"Internal error in GraphQL lexer: emit_single_char reached EOF",
|
||||
);
|
||||
|
||||
Spanning::single_width(&start_pos, t)
|
||||
}
|
||||
|
@ -160,20 +161,31 @@ impl<'a> Lexer<'a> {
|
|||
let start_pos = self.position.clone();
|
||||
|
||||
for _ in 0..3 {
|
||||
let (_, ch) = try!(self.next_char().ok_or(Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (_, ch) = try!(self.next_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
if ch != '.' {
|
||||
return Err(Spanning::zero_width(&start_pos, LexerError::UnexpectedCharacter('.')));
|
||||
return Err(Spanning::zero_width(
|
||||
&start_pos,
|
||||
LexerError::UnexpectedCharacter('.'),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Spanning::start_end(&start_pos, &self.position, Token::Ellipsis))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&self.position,
|
||||
Token::Ellipsis,
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_name(&mut self) -> LexerResult<'a> {
|
||||
let start_pos = self.position.clone();
|
||||
let (start_idx, start_ch) =
|
||||
try!(self.next_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (start_idx, start_ch) = try!(self.next_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
assert!(is_name_start(start_ch));
|
||||
|
||||
let mut end_idx = start_idx;
|
||||
|
@ -187,16 +199,19 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(Spanning::start_end(&start_pos,
|
||||
&self.position,
|
||||
Token::Name(&self.source[start_idx..end_idx + 1])))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&self.position,
|
||||
Token::Name(&self.source[start_idx..end_idx + 1]),
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_string(&mut self) -> LexerResult<'a> {
|
||||
let start_pos = self.position.clone();
|
||||
let (_, start_ch) =
|
||||
try!(self.next_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (_, start_ch) = try!(self.next_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
assert!(start_ch == '"');
|
||||
|
||||
let mut acc = String::new();
|
||||
|
@ -204,7 +219,11 @@ impl<'a> Lexer<'a> {
|
|||
while let Some((_, ch)) = self.peek_char() {
|
||||
if ch == '"' {
|
||||
self.next_char();
|
||||
return Ok(Spanning::start_end(&start_pos, &self.position, Token::String(acc)));
|
||||
return Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&self.position,
|
||||
Token::String(acc),
|
||||
));
|
||||
} else if ch == '\\' {
|
||||
self.next_char();
|
||||
|
||||
|
@ -250,47 +269,64 @@ impl<'a> Lexer<'a> {
|
|||
let mut s = String::from("\\");
|
||||
s.push(ch);
|
||||
|
||||
return Err(Spanning::zero_width(&self.position,
|
||||
LexerError::UnknownEscapeSequence(s)));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnknownEscapeSequence(s),
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(Spanning::zero_width(&self.position,
|
||||
LexerError::UnterminatedString));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString,
|
||||
));
|
||||
}
|
||||
}
|
||||
if let Some((_, ch)) = self.peek_char() {
|
||||
if ch == 'n' {}
|
||||
} else {
|
||||
return Err(Spanning::zero_width(&self.position,
|
||||
LexerError::UnterminatedString));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString,
|
||||
));
|
||||
}
|
||||
} else if ch == '\n' || ch == '\r' {
|
||||
return Err(Spanning::zero_width(&self.position, LexerError::UnterminatedString));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString,
|
||||
));
|
||||
} else if !is_source_char(ch) {
|
||||
return Err(Spanning::zero_width(&self.position,
|
||||
LexerError::UnknownCharacterInString(ch)));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnknownCharacterInString(ch),
|
||||
));
|
||||
} else {
|
||||
self.next_char();
|
||||
acc.push(ch);
|
||||
}
|
||||
}
|
||||
|
||||
Err(Spanning::zero_width(&self.position, LexerError::UnterminatedString))
|
||||
Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString,
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_escaped_unicode(&mut self,
|
||||
start_pos: &SourcePosition)
|
||||
-> Result<char, Spanning<LexerError>> {
|
||||
let (start_idx, _) =
|
||||
try!(self.peek_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
|
||||
fn scan_escaped_unicode(
|
||||
&mut self,
|
||||
start_pos: &SourcePosition,
|
||||
) -> Result<char, Spanning<LexerError>> {
|
||||
let (start_idx, _) = try!(self.peek_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString
|
||||
)));
|
||||
let mut end_idx = start_idx;
|
||||
let mut len = 0;
|
||||
|
||||
for _ in 0..4 {
|
||||
let (idx, ch) =
|
||||
try!(self.next_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
|
||||
let (idx, ch) = try!(self.next_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnterminatedString
|
||||
)));
|
||||
|
||||
if !ch.is_alphanumeric() {
|
||||
break;
|
||||
|
@ -303,19 +339,24 @@ impl<'a> Lexer<'a> {
|
|||
let escape = &self.source[start_idx..end_idx + 1];
|
||||
|
||||
if len != 4 {
|
||||
return Err(Spanning::zero_width(start_pos,
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() +
|
||||
escape)));
|
||||
return Err(Spanning::zero_width(
|
||||
start_pos,
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape),
|
||||
));
|
||||
}
|
||||
|
||||
let code_point = try!(u32::from_str_radix(escape, 16).map_err(|_|
|
||||
let code_point = try!(u32::from_str_radix(escape, 16).map_err(|_| {
|
||||
Spanning::zero_width(
|
||||
start_pos,
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))));
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape),
|
||||
)
|
||||
}));
|
||||
|
||||
char::from_u32(code_point).ok_or_else(|| {
|
||||
Spanning::zero_width(start_pos,
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))
|
||||
Spanning::zero_width(
|
||||
start_pos,
|
||||
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -352,27 +393,32 @@ impl<'a> Lexer<'a> {
|
|||
let mantissa = frac_part
|
||||
.map(|f| f as f64)
|
||||
.map(|frac| if frac > 0f64 {
|
||||
frac / 10f64.powf(frac.log10().floor() + 1f64)
|
||||
} else {
|
||||
0f64
|
||||
})
|
||||
frac / 10f64.powf(frac.log10().floor() + 1f64)
|
||||
} else {
|
||||
0f64
|
||||
})
|
||||
.map(|m| if int_part < 0 { -m } else { m });
|
||||
|
||||
let exp = exp_part.map(|e| e as f64).map(|e| 10f64.powf(e));
|
||||
|
||||
Ok(Spanning::start_end(&start_pos, &self.position, match (mantissa, exp) {
|
||||
(None, None) => Token::Int(int_part),
|
||||
(None, Some(exp)) => Token::Float((int_part as f64) * exp),
|
||||
(Some(mantissa), None) => Token::Float((int_part as f64) + mantissa),
|
||||
(Some(mantissa), Some(exp)) => Token::Float(((int_part as f64) + mantissa) * exp),
|
||||
}))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&self.position,
|
||||
match (mantissa, exp) {
|
||||
(None, None) => Token::Int(int_part),
|
||||
(None, Some(exp)) => Token::Float((int_part as f64) * exp),
|
||||
(Some(mantissa), None) => Token::Float((int_part as f64) + mantissa),
|
||||
(Some(mantissa), Some(exp)) => Token::Float(((int_part as f64) + mantissa) * exp),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
fn scan_integer_part(&mut self) -> Result<i32, Spanning<LexerError>> {
|
||||
let is_negative = {
|
||||
let (_, init_ch) =
|
||||
try!(self.peek_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (_, init_ch) = try!(self.peek_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
|
||||
if init_ch == '-' {
|
||||
self.next_char();
|
||||
|
@ -382,16 +428,19 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
let (_, ch) = try!(self.peek_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (_, ch) = try!(self.peek_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
|
||||
if ch == '0' {
|
||||
self.next_char();
|
||||
|
||||
match self.peek_char() {
|
||||
Some((_, '0')) => {
|
||||
Err(Spanning::zero_width(&self.position, LexerError::UnexpectedCharacter(ch)))
|
||||
}
|
||||
Some((_, '0')) => Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedCharacter(ch),
|
||||
)),
|
||||
_ => Ok(0),
|
||||
}
|
||||
} else {
|
||||
|
@ -401,13 +450,17 @@ impl<'a> Lexer<'a> {
|
|||
|
||||
fn scan_digits(&mut self) -> Result<i32, Spanning<LexerError>> {
|
||||
let start_pos = self.position.clone();
|
||||
let (start_idx, ch) =
|
||||
try!(self.peek_char().ok_or(
|
||||
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
|
||||
let (start_idx, ch) = try!(self.peek_char().ok_or(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)));
|
||||
let mut end_idx = start_idx;
|
||||
|
||||
if !ch.is_digit(10) {
|
||||
return Err(Spanning::zero_width(&self.position, LexerError::UnexpectedCharacter(ch)));
|
||||
return Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnexpectedCharacter(ch),
|
||||
));
|
||||
}
|
||||
|
||||
while let Some((idx, ch)) = self.peek_char() {
|
||||
|
@ -419,8 +472,9 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
i32::from_str_radix(&self.source[start_idx..end_idx + 1], 10)
|
||||
.map_err(|_| Spanning::zero_width(&start_pos, LexerError::InvalidNumber))
|
||||
i32::from_str_radix(&self.source[start_idx..end_idx + 1], 10).map_err(|_| {
|
||||
Spanning::zero_width(&start_pos, LexerError::InvalidNumber)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,34 +491,35 @@ impl<'a> Iterator for Lexer<'a> {
|
|||
let ch = self.iterator.peek().map(|&(_, ch)| ch);
|
||||
|
||||
Some(match ch {
|
||||
Some('!') => Ok(self.emit_single_char(Token::ExclamationMark)),
|
||||
Some('$') => Ok(self.emit_single_char(Token::Dollar)),
|
||||
Some('(') => Ok(self.emit_single_char(Token::ParenOpen)),
|
||||
Some(')') => Ok(self.emit_single_char(Token::ParenClose)),
|
||||
Some('[') => Ok(self.emit_single_char(Token::BracketOpen)),
|
||||
Some(']') => Ok(self.emit_single_char(Token::BracketClose)),
|
||||
Some('{') => Ok(self.emit_single_char(Token::CurlyOpen)),
|
||||
Some('}') => Ok(self.emit_single_char(Token::CurlyClose)),
|
||||
Some(':') => Ok(self.emit_single_char(Token::Colon)),
|
||||
Some('=') => Ok(self.emit_single_char(Token::Equals)),
|
||||
Some('@') => Ok(self.emit_single_char(Token::At)),
|
||||
Some('|') => Ok(self.emit_single_char(Token::Pipe)),
|
||||
Some('.') => self.scan_ellipsis(),
|
||||
Some('"') => self.scan_string(),
|
||||
Some(ch) => {
|
||||
if is_number_start(ch) {
|
||||
self.scan_number()
|
||||
} else if is_name_start(ch) {
|
||||
self.scan_name()
|
||||
} else {
|
||||
Err(Spanning::zero_width(&self.position, LexerError::UnknownCharacter(ch)))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.has_reached_eof = true;
|
||||
Ok(Spanning::zero_width(&self.position, Token::EndOfFile))
|
||||
}
|
||||
})
|
||||
Some('!') => Ok(self.emit_single_char(Token::ExclamationMark)),
|
||||
Some('$') => Ok(self.emit_single_char(Token::Dollar)),
|
||||
Some('(') => Ok(self.emit_single_char(Token::ParenOpen)),
|
||||
Some(')') => Ok(self.emit_single_char(Token::ParenClose)),
|
||||
Some('[') => Ok(self.emit_single_char(Token::BracketOpen)),
|
||||
Some(']') => Ok(self.emit_single_char(Token::BracketClose)),
|
||||
Some('{') => Ok(self.emit_single_char(Token::CurlyOpen)),
|
||||
Some('}') => Ok(self.emit_single_char(Token::CurlyClose)),
|
||||
Some(':') => Ok(self.emit_single_char(Token::Colon)),
|
||||
Some('=') => Ok(self.emit_single_char(Token::Equals)),
|
||||
Some('@') => Ok(self.emit_single_char(Token::At)),
|
||||
Some('|') => Ok(self.emit_single_char(Token::Pipe)),
|
||||
Some('.') => self.scan_ellipsis(),
|
||||
Some('"') => self.scan_string(),
|
||||
Some(ch) => if is_number_start(ch) {
|
||||
self.scan_number()
|
||||
} else if is_name_start(ch) {
|
||||
self.scan_name()
|
||||
} else {
|
||||
Err(Spanning::zero_width(
|
||||
&self.position,
|
||||
LexerError::UnknownCharacter(ch),
|
||||
))
|
||||
},
|
||||
None => {
|
||||
self.has_reached_eof = true;
|
||||
Ok(Spanning::zero_width(&self.position, Token::EndOfFile))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,6 @@ mod tests;
|
|||
|
||||
pub use self::document::parse_document_source;
|
||||
|
||||
pub use self::parser::{Parser, ParseError, ParseResult, UnlocatedParseResult, OptionParseResult};
|
||||
pub use self::lexer::{Token, Lexer, LexerError};
|
||||
pub use self::utils::{Spanning, SourcePosition};
|
||||
pub use self::parser::{OptionParseResult, ParseError, ParseResult, Parser, UnlocatedParseResult};
|
||||
pub use self::lexer::{Lexer, LexerError, Token};
|
||||
pub use self::utils::{SourcePosition, Spanning};
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::result::Result;
|
||||
use std::fmt;
|
||||
|
||||
use parser::{Spanning, Token, LexerError, Lexer};
|
||||
use parser::{Lexer, LexerError, Spanning, Token};
|
||||
|
||||
/// Error while parsing a GraphQL query
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -54,9 +54,11 @@ impl<'a> Parser<'a> {
|
|||
#[doc(hidden)]
|
||||
pub fn next(&mut self) -> ParseResult<'a, Token<'a>> {
|
||||
if self.tokens.len() == 1 {
|
||||
Err(Spanning::start_end(&self.peek().start.clone(),
|
||||
&self.peek().end.clone(),
|
||||
ParseError::UnexpectedEndOfFile))
|
||||
Err(Spanning::start_end(
|
||||
&self.peek().start.clone(),
|
||||
&self.peek().end.clone(),
|
||||
ParseError::UnexpectedEndOfFile,
|
||||
))
|
||||
} else {
|
||||
Ok(self.tokens.remove(0))
|
||||
}
|
||||
|
@ -72,28 +74,36 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn skip(&mut self,
|
||||
expected: &Token)
|
||||
-> Result<Option<Spanning<Token<'a>>>, Spanning<ParseError<'a>>> {
|
||||
pub fn skip(
|
||||
&mut self,
|
||||
expected: &Token,
|
||||
) -> Result<Option<Spanning<Token<'a>>>, Spanning<ParseError<'a>>> {
|
||||
if &self.peek().item == expected {
|
||||
Ok(Some(self.next()?))
|
||||
} else if self.peek().item == Token::EndOfFile {
|
||||
Err(Spanning::zero_width(&self.peek().start, ParseError::UnexpectedEndOfFile))
|
||||
Err(Spanning::zero_width(
|
||||
&self.peek().start,
|
||||
ParseError::UnexpectedEndOfFile,
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn delimited_list<T, F>(&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token)
|
||||
-> ParseResult<'a, Vec<Spanning<T>>>
|
||||
where T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> ParseResult<'a, T>
|
||||
pub fn delimited_list<T, F>(
|
||||
&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token,
|
||||
) -> ParseResult<'a, Vec<Spanning<T>>>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> ParseResult<'a, T>,
|
||||
{
|
||||
let Spanning { start: start_pos, .. } = try!(self.expect(opening));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(self.expect(opening));
|
||||
let mut items = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -106,15 +116,19 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn delimited_nonempty_list<T, F>(&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token)
|
||||
-> ParseResult<'a, Vec<Spanning<T>>>
|
||||
where T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> ParseResult<'a, T>
|
||||
pub fn delimited_nonempty_list<T, F>(
|
||||
&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token,
|
||||
) -> ParseResult<'a, Vec<Spanning<T>>>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> ParseResult<'a, T>,
|
||||
{
|
||||
let Spanning { start: start_pos, .. } = try!(self.expect(opening));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(self.expect(opening));
|
||||
let mut items = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -127,15 +141,19 @@ impl<'a> Parser<'a> {
|
|||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn unlocated_delimited_nonempty_list<T, F>(&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token)
|
||||
-> ParseResult<'a, Vec<T>>
|
||||
where T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> UnlocatedParseResult<'a, T>
|
||||
pub fn unlocated_delimited_nonempty_list<T, F>(
|
||||
&mut self,
|
||||
opening: &Token,
|
||||
parser: F,
|
||||
closing: &Token,
|
||||
) -> ParseResult<'a, Vec<T>>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
F: Fn(&mut Parser<'a>) -> UnlocatedParseResult<'a, T>,
|
||||
{
|
||||
let Spanning { start: start_pos, .. } = try!(self.expect(opening));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(self.expect(opening));
|
||||
let mut items = Vec::new();
|
||||
|
||||
loop {
|
||||
|
@ -150,19 +168,22 @@ impl<'a> Parser<'a> {
|
|||
#[doc(hidden)]
|
||||
pub fn expect_name(&mut self) -> ParseResult<'a, &'a str> {
|
||||
match *self.peek() {
|
||||
Spanning { item: Token::Name(_), .. } => {
|
||||
Ok(self.next()?
|
||||
.map(|token| if let Token::Name(name) = token {
|
||||
name
|
||||
} else {
|
||||
panic!("Internal parse error in `expect_name`");
|
||||
}))
|
||||
}
|
||||
Spanning { item: Token::EndOfFile, .. } => {
|
||||
Err(Spanning::start_end(&self.peek().start.clone(),
|
||||
&self.peek().end.clone(),
|
||||
ParseError::UnexpectedEndOfFile))
|
||||
}
|
||||
Spanning {
|
||||
item: Token::Name(_),
|
||||
..
|
||||
} => Ok(self.next()?.map(|token| if let Token::Name(name) = token {
|
||||
name
|
||||
} else {
|
||||
panic!("Internal parse error in `expect_name`");
|
||||
})),
|
||||
Spanning {
|
||||
item: Token::EndOfFile,
|
||||
..
|
||||
} => Err(Spanning::start_end(
|
||||
&self.peek().start.clone(),
|
||||
&self.peek().end.clone(),
|
||||
ParseError::UnexpectedEndOfFile,
|
||||
)),
|
||||
_ => Err(self.next()?.map(ParseError::UnexpectedToken)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::{Definition, Operation, Document, OperationType, Field, Selection, InputValue, Arguments};
|
||||
use parser::{Spanning, SourcePosition, ParseError, Token};
|
||||
use ast::{Arguments, Definition, Document, Field, InputValue, Operation, OperationType, Selection};
|
||||
use parser::{ParseError, SourcePosition, Spanning, Token};
|
||||
use parser::document::parse_document_source;
|
||||
|
||||
fn parse_document(s: &str) -> Document {
|
||||
|
@ -16,14 +16,16 @@ fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> {
|
|||
#[test]
|
||||
fn simple_ast() {
|
||||
assert_eq!(
|
||||
parse_document(r#"
|
||||
parse_document(
|
||||
r#"
|
||||
{
|
||||
node(id: 4) {
|
||||
id
|
||||
name
|
||||
}
|
||||
}
|
||||
"#),
|
||||
"#
|
||||
),
|
||||
vec![
|
||||
Definition::Operation(Spanning::start_end(
|
||||
&SourcePosition::new(13, 1, 12),
|
||||
|
@ -34,68 +36,76 @@ fn simple_ast() {
|
|||
variable_definitions: None,
|
||||
directives: None,
|
||||
selection_set: vec![
|
||||
Selection::Field(
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(31, 2, 16),
|
||||
&SourcePosition::new(110, 5, 17),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(31, 2, 16),
|
||||
&SourcePosition::new(35, 2, 20),
|
||||
"node"),
|
||||
arguments: Some(Spanning::start_end(
|
||||
&SourcePosition::new(35, 2, 20),
|
||||
&SourcePosition::new(42, 2, 27),
|
||||
Arguments {
|
||||
items: vec![
|
||||
(
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(36, 2, 21),
|
||||
&SourcePosition::new(38, 2, 23),
|
||||
"id"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(40, 2, 25),
|
||||
&SourcePosition::new(41, 2, 26),
|
||||
InputValue::int(4))
|
||||
Selection::Field(Spanning::start_end(
|
||||
&SourcePosition::new(31, 2, 16),
|
||||
&SourcePosition::new(110, 5, 17),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(31, 2, 16),
|
||||
&SourcePosition::new(35, 2, 20),
|
||||
"node",
|
||||
),
|
||||
arguments: Some(Spanning::start_end(
|
||||
&SourcePosition::new(35, 2, 20),
|
||||
&SourcePosition::new(42, 2, 27),
|
||||
Arguments {
|
||||
items: vec![
|
||||
(
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(36, 2, 21),
|
||||
&SourcePosition::new(38, 2, 23),
|
||||
"id",
|
||||
),
|
||||
]
|
||||
})),
|
||||
directives: None,
|
||||
selection_set: Some(vec![
|
||||
Selection::Field(
|
||||
Spanning::start_end(
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(40, 2, 25),
|
||||
&SourcePosition::new(41, 2, 26),
|
||||
InputValue::int(4),
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
)),
|
||||
directives: None,
|
||||
selection_set: Some(vec![
|
||||
Selection::Field(Spanning::start_end(
|
||||
&SourcePosition::new(65, 3, 20),
|
||||
&SourcePosition::new(67, 3, 22),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(65, 3, 20),
|
||||
&SourcePosition::new(67, 3, 22),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(65, 3, 20),
|
||||
&SourcePosition::new(67, 3, 22),
|
||||
"id"),
|
||||
arguments: None,
|
||||
directives: None,
|
||||
selection_set: None,
|
||||
})),
|
||||
Selection::Field(
|
||||
Spanning::start_end(
|
||||
"id",
|
||||
),
|
||||
arguments: None,
|
||||
directives: None,
|
||||
selection_set: None,
|
||||
},
|
||||
)),
|
||||
Selection::Field(Spanning::start_end(
|
||||
&SourcePosition::new(88, 4, 20),
|
||||
&SourcePosition::new(92, 4, 24),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(88, 4, 20),
|
||||
&SourcePosition::new(92, 4, 24),
|
||||
Field {
|
||||
alias: None,
|
||||
name: Spanning::start_end(
|
||||
&SourcePosition::new(88, 4, 20),
|
||||
&SourcePosition::new(92, 4, 24),
|
||||
"name"),
|
||||
arguments: None,
|
||||
directives: None,
|
||||
selection_set: None,
|
||||
})),
|
||||
]),
|
||||
}))
|
||||
]
|
||||
}))
|
||||
])
|
||||
"name",
|
||||
),
|
||||
arguments: None,
|
||||
directives: None,
|
||||
selection_set: None,
|
||||
},
|
||||
)),
|
||||
]),
|
||||
},
|
||||
)),
|
||||
],
|
||||
},
|
||||
)),
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -104,19 +114,25 @@ fn errors() {
|
|||
parse_document_error("{"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
ParseError::UnexpectedEndOfFile));
|
||||
ParseError::UnexpectedEndOfFile
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_document_error("{ ...MissingOn }\nfragment MissingOn Type"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(36, 1, 19),
|
||||
&SourcePosition::new(40, 1, 23),
|
||||
ParseError::UnexpectedToken(Token::Name("Type"))));
|
||||
ParseError::UnexpectedToken(Token::Name("Type"))
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
parse_document_error("{ ...on }"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(8, 0, 8),
|
||||
&SourcePosition::new(9, 0, 9),
|
||||
ParseError::UnexpectedToken(Token::CurlyClose)));
|
||||
ParseError::UnexpectedToken(Token::CurlyClose)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use parser::{Lexer, SourcePosition, Spanning, Token, LexerError};
|
||||
use parser::{Lexer, LexerError, SourcePosition, Spanning, Token};
|
||||
|
||||
fn tokenize_to_vec<'a>(s: &'a str) -> Vec<Spanning<Token<'a>>> {
|
||||
let mut tokens = Vec::new();
|
||||
|
@ -35,11 +35,9 @@ fn tokenize_error(s: &str) -> Spanning<LexerError> {
|
|||
|
||||
loop {
|
||||
match lexer.next() {
|
||||
Some(Ok(t)) => {
|
||||
if t.item == Token::EndOfFile {
|
||||
panic!("Tokenizer did not return error for {:#?}", s);
|
||||
}
|
||||
}
|
||||
Some(Ok(t)) => if t.item == Token::EndOfFile {
|
||||
panic!("Tokenizer did not return error for {:#?}", s);
|
||||
},
|
||||
Some(Err(e)) => {
|
||||
return e;
|
||||
}
|
||||
|
@ -54,7 +52,8 @@ fn empty_source() {
|
|||
tokenize_to_vec(""),
|
||||
vec![
|
||||
Spanning::zero_width(&SourcePosition::new_origin(), Token::EndOfFile),
|
||||
]);
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -63,46 +62,50 @@ fn disallow_control_codes() {
|
|||
Lexer::new("\u{0007}").next(),
|
||||
Some(Err(Spanning::zero_width(
|
||||
&SourcePosition::new_origin(),
|
||||
LexerError::UnknownCharacter('\u{0007}')))));
|
||||
LexerError::UnknownCharacter('\u{0007}')
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_whitespace() {
|
||||
assert_eq!(
|
||||
tokenize_to_vec(r#"
|
||||
tokenize_to_vec(
|
||||
r#"
|
||||
|
||||
foo
|
||||
|
||||
"#),
|
||||
"#
|
||||
),
|
||||
vec![
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(14, 2, 12),
|
||||
&SourcePosition::new(17, 2, 15),
|
||||
Token::Name("foo"),
|
||||
),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(31, 4, 12),
|
||||
Token::EndOfFile),
|
||||
]);
|
||||
Spanning::zero_width(&SourcePosition::new(31, 4, 12), Token::EndOfFile),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn skip_comments() {
|
||||
assert_eq!(
|
||||
tokenize_to_vec(r#"
|
||||
tokenize_to_vec(
|
||||
r#"
|
||||
#comment
|
||||
foo#comment
|
||||
"#),
|
||||
"#
|
||||
),
|
||||
vec![
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(34, 2, 12),
|
||||
&SourcePosition::new(37, 2, 15),
|
||||
Token::Name("foo"),
|
||||
),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(58, 3, 12),
|
||||
Token::EndOfFile),
|
||||
]);
|
||||
Spanning::zero_width(&SourcePosition::new(58, 3, 12), Token::EndOfFile),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -115,23 +118,26 @@ fn skip_commas() {
|
|||
&SourcePosition::new(6, 0, 6),
|
||||
Token::Name("foo"),
|
||||
),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(9, 0, 9),
|
||||
Token::EndOfFile),
|
||||
]);
|
||||
Spanning::zero_width(&SourcePosition::new(9, 0, 9), Token::EndOfFile),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_positions() {
|
||||
assert_eq!(
|
||||
Lexer::new(r#"
|
||||
Lexer::new(
|
||||
r#"
|
||||
|
||||
?
|
||||
|
||||
"#).next(),
|
||||
"#
|
||||
).next(),
|
||||
Some(Err(Spanning::zero_width(
|
||||
&SourcePosition::new(14, 2, 12),
|
||||
LexerError::UnknownCharacter('?')))));
|
||||
LexerError::UnknownCharacter('?')
|
||||
)))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -141,42 +147,54 @@ fn strings() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(8, 0, 8),
|
||||
Token::String("simple".to_owned())));
|
||||
Token::String("simple".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(r#"" white space ""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(15, 0, 15),
|
||||
Token::String(" white space ".to_owned())));
|
||||
Token::String(" white space ".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(r#""quote \"""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(10, 0, 10),
|
||||
Token::String("quote \"".to_owned())));
|
||||
Token::String("quote \"".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(r#""escaped \n\r\b\t\f""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(20, 0, 20),
|
||||
Token::String("escaped \n\r\u{0008}\t\u{000c}".to_owned())));
|
||||
Token::String("escaped \n\r\u{0008}\t\u{000c}".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(r#""slashes \\ \/""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(15, 0, 15),
|
||||
Token::String("slashes \\ /".to_owned())));
|
||||
Token::String("slashes \\ /".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(r#""unicode \u1234\u5678\u90AB\uCDEF""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(34, 0, 34),
|
||||
Token::String("unicode \u{1234}\u{5678}\u{90ab}\u{cdef}".to_owned())));
|
||||
Token::String("unicode \u{1234}\u{5678}\u{90ab}\u{cdef}".to_owned())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -185,99 +203,131 @@ fn string_errors() {
|
|||
tokenize_error("\""),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\"no end quote"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(13, 0, 13),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\"contains unescaped \u{0007} control char\""),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(20, 0, 20),
|
||||
LexerError::UnknownCharacterInString('\u{0007}')));
|
||||
LexerError::UnknownCharacterInString('\u{0007}')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\"null-byte is not \u{0000} end of file\""),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(18, 0, 18),
|
||||
LexerError::UnknownCharacterInString('\u{0000}')));
|
||||
LexerError::UnknownCharacterInString('\u{0000}')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\"multi\nline\""),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\"multi\rline\""),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \z esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\z".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\z".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \x esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\x".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\x".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \u1 esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\u1".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\u1".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \u0XX1 esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\u0XX1".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\u0XX1".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \uXXXX esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\uXXXX".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\uXXXX".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \uFXXX esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\uFXXX".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\uFXXX".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""bad \uXXXF esc""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
LexerError::UnknownEscapeSequence("\\uXXXF".to_owned())));
|
||||
LexerError::UnknownEscapeSequence("\\uXXXF".to_owned())
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""unterminated in string \""#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(26, 0, 26),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(r#""unterminated \"#),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(15, 0, 15),
|
||||
LexerError::UnterminatedString));
|
||||
LexerError::UnterminatedString
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn numbers() {
|
||||
fn assert_float_token_eq(source: &str,
|
||||
start: SourcePosition,
|
||||
end: SourcePosition,
|
||||
expected: f64) {
|
||||
fn assert_float_token_eq(
|
||||
source: &str,
|
||||
start: SourcePosition,
|
||||
end: SourcePosition,
|
||||
expected: f64,
|
||||
) {
|
||||
let parsed = tokenize_single(source);
|
||||
assert_eq!(parsed.start, start);
|
||||
assert_eq!(parsed.end, end);
|
||||
|
@ -287,7 +337,10 @@ fn numbers() {
|
|||
let relative_error = ((expected - actual) / actual).abs();
|
||||
assert!(
|
||||
relative_error.abs() < 0.001,
|
||||
"[expected] {} != {} [actual]", expected, actual);
|
||||
"[expected] {} != {} [actual]",
|
||||
expected,
|
||||
actual
|
||||
);
|
||||
}
|
||||
_ => assert!(false),
|
||||
}
|
||||
|
@ -298,93 +351,127 @@ fn numbers() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
Token::Int(4)));
|
||||
Token::Int(4)
|
||||
)
|
||||
);
|
||||
|
||||
assert_float_token_eq("4.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
4.123);
|
||||
assert_float_token_eq(
|
||||
"4.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
4.123,
|
||||
);
|
||||
|
||||
assert_float_token_eq("4.0",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(3, 0, 3),
|
||||
4.0);
|
||||
assert_float_token_eq(
|
||||
"4.0",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(3, 0, 3),
|
||||
4.0,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("-4"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
Token::Int(-4)));
|
||||
Token::Int(-4)
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("9"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
Token::Int(9)));
|
||||
Token::Int(9)
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("0"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
Token::Int(0)));
|
||||
Token::Int(0)
|
||||
)
|
||||
);
|
||||
|
||||
assert_float_token_eq("-4.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
-4.123);
|
||||
assert_float_token_eq(
|
||||
"-4.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
-4.123,
|
||||
);
|
||||
|
||||
assert_float_token_eq("0.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
0.123);
|
||||
assert_float_token_eq(
|
||||
"0.123",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
0.123,
|
||||
);
|
||||
|
||||
assert_float_token_eq("123e4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
123e4);
|
||||
assert_float_token_eq(
|
||||
"123e4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("123E4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
123e4);
|
||||
assert_float_token_eq(
|
||||
"123E4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(5, 0, 5),
|
||||
123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("123e-4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
123e-4);
|
||||
assert_float_token_eq(
|
||||
"123e-4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
123e-4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("123e+4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
123e4);
|
||||
assert_float_token_eq(
|
||||
"123e+4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(6, 0, 6),
|
||||
123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("-1.123e4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(8, 0, 8),
|
||||
-1.123e4);
|
||||
assert_float_token_eq(
|
||||
"-1.123e4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(8, 0, 8),
|
||||
-1.123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("-1.123E4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(8, 0, 8),
|
||||
-1.123e4);
|
||||
assert_float_token_eq(
|
||||
"-1.123E4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(8, 0, 8),
|
||||
-1.123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("-1.123e-4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e-4);
|
||||
assert_float_token_eq(
|
||||
"-1.123e-4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e-4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("-1.123e+4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e4);
|
||||
assert_float_token_eq(
|
||||
"-1.123e+4",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e4,
|
||||
);
|
||||
|
||||
assert_float_token_eq("-1.123e45",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e45);
|
||||
assert_float_token_eq(
|
||||
"-1.123e45",
|
||||
SourcePosition::new(0, 0, 0),
|
||||
SourcePosition::new(9, 0, 9),
|
||||
-1.123e45,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -393,131 +480,137 @@ fn numbers_errors() {
|
|||
tokenize_error("00"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
LexerError::UnexpectedCharacter('0')));
|
||||
LexerError::UnexpectedCharacter('0')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("+1"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
LexerError::UnknownCharacter('+')));
|
||||
LexerError::UnknownCharacter('+')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("1."),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
LexerError::UnexpectedEndOfFile));
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error(".123"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
LexerError::UnexpectedCharacter('.')));
|
||||
LexerError::UnexpectedCharacter('.')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("1.A"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
LexerError::UnexpectedCharacter('A')));
|
||||
LexerError::UnexpectedCharacter('A')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("-A"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
LexerError::UnexpectedCharacter('A')));
|
||||
LexerError::UnexpectedCharacter('A')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("1.0e"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(4, 0, 4),
|
||||
LexerError::UnexpectedEndOfFile));
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("1.0eA"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(4, 0, 4),
|
||||
LexerError::UnexpectedCharacter('A')));
|
||||
LexerError::UnexpectedCharacter('A')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn punctuation() {
|
||||
assert_eq!(
|
||||
tokenize_single("!"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::ExclamationMark));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::ExclamationMark)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("$"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::Dollar));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::Dollar)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("("),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::ParenOpen));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::ParenOpen)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(")"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::ParenClose));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::ParenClose)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("..."),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(3, 0, 3),
|
||||
Token::Ellipsis));
|
||||
Token::Ellipsis
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single(":"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::Colon));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::Colon)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("="),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::Equals));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::Equals)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("@"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::At));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::At)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("["),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::BracketOpen));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::BracketOpen)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("]"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::BracketClose));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::BracketClose)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("{"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::CurlyOpen));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::CurlyOpen)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("}"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::CurlyClose));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::CurlyClose)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_single("|"),
|
||||
Spanning::single_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
Token::Pipe));
|
||||
Spanning::single_width(&SourcePosition::new(0, 0, 0), Token::Pipe)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -526,43 +619,42 @@ fn punctuation_error() {
|
|||
tokenize_error(".."),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
LexerError::UnexpectedEndOfFile));
|
||||
LexerError::UnexpectedEndOfFile
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("?"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
LexerError::UnknownCharacter('?')));
|
||||
LexerError::UnknownCharacter('?')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\u{203b}"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
LexerError::UnknownCharacter('\u{203b}')));
|
||||
LexerError::UnknownCharacter('\u{203b}')
|
||||
)
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
tokenize_error("\u{200b}"),
|
||||
Spanning::zero_width(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
LexerError::UnknownCharacter('\u{200b}')));
|
||||
LexerError::UnknownCharacter('\u{200b}')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn display() {
|
||||
assert_eq!(
|
||||
format!("{}", Token::Name("identifier")),
|
||||
"identifier"
|
||||
);
|
||||
assert_eq!(format!("{}", Token::Name("identifier")), "identifier");
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", Token::Int(123)),
|
||||
"123"
|
||||
);
|
||||
assert_eq!(format!("{}", Token::Int(123)), "123");
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", Token::Float(4.5)),
|
||||
"4.5"
|
||||
);
|
||||
assert_eq!(format!("{}", Token::Float(4.5)), "4.5");
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", Token::String("some string".to_owned())),
|
||||
|
@ -570,7 +662,10 @@ fn display() {
|
|||
);
|
||||
|
||||
assert_eq!(
|
||||
format!("{}", Token::String("string with \\ escape and \" quote".to_owned())),
|
||||
format!(
|
||||
"{}",
|
||||
Token::String("string with \\ escape and \" quote".to_owned())
|
||||
),
|
||||
"\"string with \\\\ escape and \\\" quote\""
|
||||
);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use ast::InputValue;
|
||||
use parser::{Lexer, Spanning, SourcePosition, Parser};
|
||||
use parser::{Lexer, Parser, SourcePosition, Spanning};
|
||||
use parser::value::parse_value_literal;
|
||||
|
||||
fn parse_value(s: &str) -> Spanning<InputValue> {
|
||||
|
@ -18,49 +18,65 @@ fn input_value_literals() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(3, 0, 3),
|
||||
InputValue::int(123)));
|
||||
InputValue::int(123)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("123.45"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
InputValue::float(123.45)));
|
||||
InputValue::float(123.45)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("true"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(4, 0, 4),
|
||||
InputValue::boolean(true)));
|
||||
InputValue::boolean(true)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("false"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(5, 0, 5),
|
||||
InputValue::boolean(false)));
|
||||
InputValue::boolean(false)
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value(r#""test""#),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
InputValue::string("test")));
|
||||
InputValue::string("test")
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("enum_value"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(10, 0, 10),
|
||||
InputValue::enum_value("enum_value")));
|
||||
InputValue::enum_value("enum_value")
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("$variable"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(9, 0, 9),
|
||||
InputValue::variable("variable")));
|
||||
InputValue::variable("variable")
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("[]"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
InputValue::list(vec![])));
|
||||
InputValue::list(vec![])
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("[1, [2, 3]]"),
|
||||
Spanning::start_end(
|
||||
|
@ -70,7 +86,8 @@ fn input_value_literals() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
InputValue::int(1)),
|
||||
InputValue::int(1),
|
||||
),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(4, 0, 4),
|
||||
&SourcePosition::new(10, 0, 10),
|
||||
|
@ -78,19 +95,26 @@ fn input_value_literals() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(5, 0, 5),
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
InputValue::int(2)),
|
||||
InputValue::int(2),
|
||||
),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(8, 0, 8),
|
||||
&SourcePosition::new(9, 0, 9),
|
||||
InputValue::int(3)),
|
||||
])),
|
||||
])));
|
||||
InputValue::int(3),
|
||||
),
|
||||
]),
|
||||
),
|
||||
])
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value("{}"),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(0, 0, 0),
|
||||
&SourcePosition::new(2, 0, 2),
|
||||
InputValue::object(HashMap::<String, InputValue>::new())));
|
||||
InputValue::object(HashMap::<String, InputValue>::new())
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
parse_value(r#"{key: 123, other: {foo: "bar"}}"#),
|
||||
Spanning::start_end(
|
||||
|
@ -101,17 +125,20 @@ fn input_value_literals() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(1, 0, 1),
|
||||
&SourcePosition::new(4, 0, 4),
|
||||
"key".to_owned()),
|
||||
"key".to_owned(),
|
||||
),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(6, 0, 6),
|
||||
&SourcePosition::new(9, 0, 9),
|
||||
InputValue::int(123))
|
||||
InputValue::int(123),
|
||||
),
|
||||
),
|
||||
(
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(11, 0, 11),
|
||||
&SourcePosition::new(16, 0, 16),
|
||||
"other".to_owned()),
|
||||
"other".to_owned(),
|
||||
),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(18, 0, 18),
|
||||
&SourcePosition::new(30, 0, 30),
|
||||
|
@ -120,13 +147,18 @@ fn input_value_literals() {
|
|||
Spanning::start_end(
|
||||
&SourcePosition::new(19, 0, 19),
|
||||
&SourcePosition::new(22, 0, 22),
|
||||
"foo".to_owned()),
|
||||
"foo".to_owned(),
|
||||
),
|
||||
Spanning::start_end(
|
||||
&SourcePosition::new(24, 0, 24),
|
||||
&SourcePosition::new(29, 0, 29),
|
||||
InputValue::string("bar"))
|
||||
)
|
||||
]))
|
||||
)
|
||||
])));
|
||||
InputValue::string("bar"),
|
||||
),
|
||||
),
|
||||
]),
|
||||
),
|
||||
),
|
||||
])
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -61,13 +61,15 @@ impl<T: fmt::Debug> Spanning<T> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn spanning(v: Vec<Spanning<T>>) -> Option<Spanning<Vec<Spanning<T>>>> {
|
||||
if let (Some(start), Some(end)) =
|
||||
(v.first().map(|s| s.start.clone()), v.last().map(|s| s.end.clone())) {
|
||||
if let (Some(start), Some(end)) = (
|
||||
v.first().map(|s| s.start.clone()),
|
||||
v.last().map(|s| s.end.clone()),
|
||||
) {
|
||||
Some(Spanning {
|
||||
item: v,
|
||||
start: start,
|
||||
end: end,
|
||||
})
|
||||
item: v,
|
||||
start: start,
|
||||
end: end,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -93,7 +95,8 @@ impl<T: fmt::Debug> Spanning<T> {
|
|||
}
|
||||
|
||||
impl<T> Clone for Spanning<T>
|
||||
where T: Clone + fmt::Debug
|
||||
where
|
||||
T: Clone + fmt::Debug,
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Spanning {
|
||||
|
@ -105,17 +108,23 @@ impl<T> Clone for Spanning<T>
|
|||
}
|
||||
|
||||
impl<T> PartialEq for Spanning<T>
|
||||
where T: PartialEq + fmt::Debug
|
||||
where
|
||||
T: PartialEq + fmt::Debug,
|
||||
{
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.start == other.start && self.end == other.end && self.item == other.item
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Eq for Spanning<T> where T: Eq + fmt::Debug {}
|
||||
impl<T> Eq for Spanning<T>
|
||||
where
|
||||
T: Eq + fmt::Debug,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> Hash for Spanning<T>
|
||||
where T: Hash + fmt::Debug
|
||||
where
|
||||
T: Hash + fmt::Debug,
|
||||
{
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.start.hash(state);
|
||||
|
|
|
@ -1,86 +1,122 @@
|
|||
use ast::InputValue;
|
||||
|
||||
use parser::{Parser, ParseResult, ParseError, Token, Spanning};
|
||||
use parser::{ParseError, ParseResult, Parser, Spanning, Token};
|
||||
|
||||
pub fn parse_value_literal<'a>(parser: &mut Parser<'a>,
|
||||
is_const: bool)
|
||||
-> ParseResult<'a, InputValue> {
|
||||
pub fn parse_value_literal<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
is_const: bool,
|
||||
) -> ParseResult<'a, InputValue> {
|
||||
match *parser.peek() {
|
||||
Spanning { item: Token::BracketOpen, .. } => parse_list_literal(parser, is_const),
|
||||
Spanning { item: Token::CurlyOpen, .. } => parse_object_literal(parser, is_const),
|
||||
Spanning { item: Token::Dollar, .. } if !is_const => parse_variable_literal(parser),
|
||||
Spanning { item: Token::Int(i), .. } => Ok(parser.next()?.map(|_| InputValue::int(i))),
|
||||
Spanning { item: Token::Float(f), .. } => Ok(parser.next()?.map(|_| InputValue::float(f))),
|
||||
Spanning { item: Token::String(_), .. } => {
|
||||
Ok(parser
|
||||
.next()?
|
||||
.map(|t| if let Token::String(s) = t {
|
||||
InputValue::string(s)
|
||||
} else {
|
||||
panic!("Internal parser error");
|
||||
}))
|
||||
}
|
||||
Spanning { item: Token::Name("true"), .. } => {
|
||||
Ok(parser.next()?.map(|_| InputValue::boolean(true)))
|
||||
}
|
||||
Spanning { item: Token::Name("false"), .. } => {
|
||||
Ok(parser.next()?.map(|_| InputValue::boolean(false)))
|
||||
}
|
||||
Spanning { item: Token::Name("null"), .. } => {
|
||||
Ok(parser.next()?.map(|_| InputValue::null()))
|
||||
}
|
||||
Spanning { item: Token::Name(name), .. } => {
|
||||
Ok(parser
|
||||
.next()?
|
||||
.map(|_| InputValue::enum_value(name.to_owned())))
|
||||
Spanning {
|
||||
item: Token::BracketOpen,
|
||||
..
|
||||
} => parse_list_literal(parser, is_const),
|
||||
Spanning {
|
||||
item: Token::CurlyOpen,
|
||||
..
|
||||
} => parse_object_literal(parser, is_const),
|
||||
Spanning {
|
||||
item: Token::Dollar,
|
||||
..
|
||||
} if !is_const =>
|
||||
{
|
||||
parse_variable_literal(parser)
|
||||
}
|
||||
Spanning {
|
||||
item: Token::Int(i),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|_| InputValue::int(i))),
|
||||
Spanning {
|
||||
item: Token::Float(f),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|_| InputValue::float(f))),
|
||||
Spanning {
|
||||
item: Token::String(_),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|t| if let Token::String(s) = t {
|
||||
InputValue::string(s)
|
||||
} else {
|
||||
panic!("Internal parser error");
|
||||
})),
|
||||
Spanning {
|
||||
item: Token::Name("true"),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|_| InputValue::boolean(true))),
|
||||
Spanning {
|
||||
item: Token::Name("false"),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|_| InputValue::boolean(false))),
|
||||
Spanning {
|
||||
item: Token::Name("null"),
|
||||
..
|
||||
} => Ok(parser.next()?.map(|_| InputValue::null())),
|
||||
Spanning {
|
||||
item: Token::Name(name),
|
||||
..
|
||||
} => Ok(
|
||||
parser
|
||||
.next()?
|
||||
.map(|_| InputValue::enum_value(name.to_owned())),
|
||||
),
|
||||
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_list_literal<'a>(parser: &mut Parser<'a>, is_const: bool) -> ParseResult<'a, InputValue> {
|
||||
Ok(try!(parser.delimited_list(
|
||||
Ok(
|
||||
try!(parser.delimited_list(
|
||||
&Token::BracketOpen,
|
||||
|p| parse_value_literal(p, is_const),
|
||||
&Token::BracketClose
|
||||
))
|
||||
.map(InputValue::parsed_list))
|
||||
)).map(InputValue::parsed_list),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_object_literal<'a>(parser: &mut Parser<'a>,
|
||||
is_const: bool)
|
||||
-> ParseResult<'a, InputValue> {
|
||||
Ok(try!(parser.delimited_list(
|
||||
fn parse_object_literal<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
is_const: bool,
|
||||
) -> ParseResult<'a, InputValue> {
|
||||
Ok(
|
||||
try!(parser.delimited_list(
|
||||
&Token::CurlyOpen,
|
||||
|p| parse_object_field(p, is_const),
|
||||
&Token::CurlyClose
|
||||
))
|
||||
.map(|items| {
|
||||
InputValue::parsed_object(items.into_iter().map(|s| s.item).collect())
|
||||
}))
|
||||
)).map(|items| {
|
||||
InputValue::parsed_object(items.into_iter().map(|s| s.item).collect())
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
fn parse_object_field<'a>(parser: &mut Parser<'a>,
|
||||
is_const: bool)
|
||||
-> ParseResult<'a, (Spanning<String>, Spanning<InputValue>)> {
|
||||
fn parse_object_field<'a>(
|
||||
parser: &mut Parser<'a>,
|
||||
is_const: bool,
|
||||
) -> ParseResult<'a, (Spanning<String>, Spanning<InputValue>)> {
|
||||
let key = try!(parser.expect_name());
|
||||
|
||||
try!(parser.expect(&Token::Colon));
|
||||
|
||||
let value = try!(parse_value_literal(parser, is_const));
|
||||
|
||||
Ok(Spanning::start_end(&key.start.clone(),
|
||||
&value.end.clone(),
|
||||
(key.map(|s| s.to_owned()), value)))
|
||||
Ok(Spanning::start_end(
|
||||
&key.start.clone(),
|
||||
&value.end.clone(),
|
||||
(key.map(|s| s.to_owned()), value),
|
||||
))
|
||||
}
|
||||
|
||||
fn parse_variable_literal<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, InputValue> {
|
||||
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Dollar));
|
||||
let Spanning {
|
||||
start: start_pos, ..
|
||||
} = try!(parser.expect(&Token::Dollar));
|
||||
let Spanning {
|
||||
item: name,
|
||||
end: end_pos,
|
||||
..
|
||||
} = try!(parser.expect_name());
|
||||
|
||||
Ok(Spanning::start_end(&start_pos, &end_pos, InputValue::variable(name)))
|
||||
Ok(Spanning::start_end(
|
||||
&start_pos,
|
||||
&end_pos,
|
||||
InputValue::variable(name),
|
||||
))
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
use std::fmt;
|
||||
|
||||
use ast::{InputValue, FromInputValue, Type};
|
||||
use ast::{FromInputValue, InputValue, Type};
|
||||
use types::base::TypeKind;
|
||||
|
||||
/// Scalar type metadata
|
||||
|
@ -189,12 +189,24 @@ impl<'a> MetaType<'a> {
|
|||
/// Lists, nullable wrappers, and placeholders don't have names.
|
||||
pub fn description(&self) -> Option<&String> {
|
||||
match *self {
|
||||
MetaType::Scalar(ScalarMeta { ref description, .. }) |
|
||||
MetaType::Object(ObjectMeta { ref description, .. }) |
|
||||
MetaType::Enum(EnumMeta { ref description, .. }) |
|
||||
MetaType::Interface(InterfaceMeta { ref description, .. }) |
|
||||
MetaType::Union(UnionMeta { ref description, .. }) |
|
||||
MetaType::InputObject(InputObjectMeta { ref description, .. }) => description.as_ref(),
|
||||
MetaType::Scalar(ScalarMeta {
|
||||
ref description, ..
|
||||
}) |
|
||||
MetaType::Object(ObjectMeta {
|
||||
ref description, ..
|
||||
}) |
|
||||
MetaType::Enum(EnumMeta {
|
||||
ref description, ..
|
||||
}) |
|
||||
MetaType::Interface(InterfaceMeta {
|
||||
ref description, ..
|
||||
}) |
|
||||
MetaType::Union(UnionMeta {
|
||||
ref description, ..
|
||||
}) |
|
||||
MetaType::InputObject(InputObjectMeta {
|
||||
ref description, ..
|
||||
}) => description.as_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -235,9 +247,9 @@ impl<'a> MetaType<'a> {
|
|||
/// Only input objects have input fields. This method always returns `None` for other types.
|
||||
pub fn input_field_by_name(&self, name: &str) -> Option<&Argument> {
|
||||
match *self {
|
||||
MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) => {
|
||||
input_fields.iter().find(|f| f.name == name)
|
||||
}
|
||||
MetaType::InputObject(InputObjectMeta {
|
||||
ref input_fields, ..
|
||||
}) => input_fields.iter().find(|f| f.name == name),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -254,13 +266,11 @@ impl<'a> MetaType<'a> {
|
|||
MetaType::List(ListMeta { ref of_type }) => {
|
||||
Type::NonNullList(Box::new(of_type.clone()))
|
||||
}
|
||||
MetaType::Nullable(NullableMeta { ref of_type }) => {
|
||||
match *of_type {
|
||||
Type::NonNullNamed(inner) => Type::Named(inner),
|
||||
Type::NonNullList(ref inner) => Type::List(inner.clone()),
|
||||
ref t => t.clone(),
|
||||
}
|
||||
}
|
||||
MetaType::Nullable(NullableMeta { ref of_type }) => match *of_type {
|
||||
Type::NonNullNamed(inner) => Type::Named(inner),
|
||||
Type::NonNullList(ref inner) => Type::List(inner.clone()),
|
||||
ref t => t.clone(),
|
||||
},
|
||||
MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
|
||||
}
|
||||
}
|
||||
|
@ -273,9 +283,15 @@ impl<'a> MetaType<'a> {
|
|||
/// Only scalars, enums, and input objects have parse functions.
|
||||
pub fn input_value_parse_fn(&self) -> Option<&Box<Fn(&InputValue) -> bool + Send + Sync>> {
|
||||
match *self {
|
||||
MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. }) |
|
||||
MetaType::Enum(EnumMeta { ref try_parse_fn, .. }) |
|
||||
MetaType::InputObject(InputObjectMeta { ref try_parse_fn, .. }) => Some(try_parse_fn),
|
||||
MetaType::Scalar(ScalarMeta {
|
||||
ref try_parse_fn, ..
|
||||
}) |
|
||||
MetaType::Enum(EnumMeta {
|
||||
ref try_parse_fn, ..
|
||||
}) |
|
||||
MetaType::InputObject(InputObjectMeta {
|
||||
ref try_parse_fn, ..
|
||||
}) => Some(try_parse_fn),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -285,9 +301,7 @@ impl<'a> MetaType<'a> {
|
|||
/// Objects, interfaces, and unions are composite.
|
||||
pub fn is_composite(&self) -> bool {
|
||||
match *self {
|
||||
MetaType::Object(_) |
|
||||
MetaType::Interface(_) |
|
||||
MetaType::Union(_) => true,
|
||||
MetaType::Object(_) | MetaType::Interface(_) | MetaType::Union(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -297,8 +311,7 @@ impl<'a> MetaType<'a> {
|
|||
/// Only enums and scalars are leaf types.
|
||||
pub fn is_leaf(&self) -> bool {
|
||||
match *self {
|
||||
MetaType::Enum(_) |
|
||||
MetaType::Scalar(_) => true,
|
||||
MetaType::Enum(_) | MetaType::Scalar(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -308,8 +321,7 @@ impl<'a> MetaType<'a> {
|
|||
/// Only interfaces and unions are abstract types.
|
||||
pub fn is_abstract(&self) -> bool {
|
||||
match *self {
|
||||
MetaType::Interface(_) |
|
||||
MetaType::Union(_) => true,
|
||||
MetaType::Interface(_) | MetaType::Union(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -319,9 +331,7 @@ impl<'a> MetaType<'a> {
|
|||
/// Only scalars, enums, and input objects are input types.
|
||||
pub fn is_input(&self) -> bool {
|
||||
match *self {
|
||||
MetaType::Scalar(_) |
|
||||
MetaType::Enum(_) |
|
||||
MetaType::InputObject(_) => true,
|
||||
MetaType::Scalar(_) | MetaType::Enum(_) | MetaType::InputObject(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -490,9 +500,10 @@ impl<'a> UnionMeta<'a> {
|
|||
|
||||
impl<'a> InputObjectMeta<'a> {
|
||||
/// Build a new input type with the specified name and input fields
|
||||
pub fn new<T: FromInputValue>(name: &'a str,
|
||||
input_fields: &[Argument<'a>])
|
||||
-> InputObjectMeta<'a> {
|
||||
pub fn new<T: FromInputValue>(
|
||||
name: &'a str,
|
||||
input_fields: &[Argument<'a>],
|
||||
) -> InputObjectMeta<'a> {
|
||||
InputObjectMeta {
|
||||
name: name,
|
||||
description: None,
|
||||
|
|
|
@ -2,9 +2,9 @@ use std::collections::HashMap;
|
|||
use std::fmt;
|
||||
|
||||
use types::base::GraphQLType;
|
||||
use executor::{Registry, Context};
|
||||
use executor::{Context, Registry};
|
||||
use ast::Type;
|
||||
use schema::meta::{MetaType, ObjectMeta, PlaceholderMeta, UnionMeta, InterfaceMeta, Argument};
|
||||
use schema::meta::{Argument, InterfaceMeta, MetaType, ObjectMeta, PlaceholderMeta, UnionMeta};
|
||||
|
||||
/// Root query node of a schema
|
||||
///
|
||||
|
@ -53,8 +53,9 @@ pub enum DirectiveLocation {
|
|||
}
|
||||
|
||||
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
|
||||
where QueryT: GraphQLType,
|
||||
MutationT: GraphQLType
|
||||
where
|
||||
QueryT: GraphQLType,
|
||||
MutationT: GraphQLType,
|
||||
{
|
||||
/// Construct a new root node from query and mutation nodes
|
||||
///
|
||||
|
@ -71,8 +72,9 @@ impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
|
|||
|
||||
impl<'a> SchemaType<'a> {
|
||||
pub fn new<QueryT, MutationT>() -> SchemaType<'a>
|
||||
where QueryT: GraphQLType,
|
||||
MutationT: GraphQLType
|
||||
where
|
||||
QueryT: GraphQLType,
|
||||
MutationT: GraphQLType,
|
||||
{
|
||||
let mut directives = HashMap::new();
|
||||
let query_type_name: String;
|
||||
|
@ -84,12 +86,15 @@ impl<'a> SchemaType<'a> {
|
|||
|
||||
registry.get_type::<SchemaType>();
|
||||
directives.insert("skip".to_owned(), DirectiveType::new_skip(&mut registry));
|
||||
directives.insert("include".to_owned(),
|
||||
DirectiveType::new_include(&mut registry));
|
||||
directives.insert(
|
||||
"include".to_owned(),
|
||||
DirectiveType::new_include(&mut registry),
|
||||
);
|
||||
|
||||
let mut meta_fields = vec![
|
||||
registry.field::<SchemaType>("__schema"),
|
||||
registry.field::<TypeType>("__type")
|
||||
registry
|
||||
.field::<TypeType>("__type")
|
||||
.argument(registry.arg::<String>("name")),
|
||||
];
|
||||
|
||||
|
@ -134,9 +139,11 @@ impl<'a> SchemaType<'a> {
|
|||
}
|
||||
|
||||
pub fn query_type(&self) -> TypeType {
|
||||
TypeType::Concrete(self.types
|
||||
.get(&self.query_type_name)
|
||||
.expect("Query type does not exist in schema"))
|
||||
TypeType::Concrete(
|
||||
self.types
|
||||
.get(&self.query_type_name)
|
||||
.expect("Query type does not exist in schema"),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn concrete_query_type(&self) -> &MetaType {
|
||||
|
@ -147,20 +154,20 @@ impl<'a> SchemaType<'a> {
|
|||
|
||||
pub fn mutation_type(&self) -> Option<TypeType> {
|
||||
if let Some(ref mutation_type_name) = self.mutation_type_name {
|
||||
Some(self.type_by_name(mutation_type_name)
|
||||
.expect("Mutation type does not exist in schema"))
|
||||
Some(
|
||||
self.type_by_name(mutation_type_name)
|
||||
.expect("Mutation type does not exist in schema"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn concrete_mutation_type(&self) -> Option<&MetaType> {
|
||||
self.mutation_type_name
|
||||
.as_ref()
|
||||
.map(|name| {
|
||||
self.concrete_type_by_name(name)
|
||||
.expect("Mutation type does not exist in schema")
|
||||
})
|
||||
self.mutation_type_name.as_ref().map(|name| {
|
||||
self.concrete_type_by_name(name)
|
||||
.expect("Mutation type does not exist in schema")
|
||||
})
|
||||
}
|
||||
|
||||
pub fn type_list(&self) -> Vec<TypeType> {
|
||||
|
@ -173,9 +180,9 @@ impl<'a> SchemaType<'a> {
|
|||
|
||||
pub fn make_type(&self, t: &Type) -> TypeType {
|
||||
match *t {
|
||||
Type::NonNullNamed(n) => {
|
||||
TypeType::NonNull(Box::new(self.type_by_name(n).expect("Type not found in schema")))
|
||||
}
|
||||
Type::NonNullNamed(n) => TypeType::NonNull(Box::new(
|
||||
self.type_by_name(n).expect("Type not found in schema"),
|
||||
)),
|
||||
Type::NonNullList(ref inner) => {
|
||||
TypeType::NonNull(Box::new(TypeType::List(Box::new(self.make_type(inner)))))
|
||||
}
|
||||
|
@ -198,11 +205,9 @@ impl<'a> SchemaType<'a> {
|
|||
}
|
||||
|
||||
match (t1.is_abstract(), t2.is_abstract()) {
|
||||
(true, true) => {
|
||||
self.possible_types(t1)
|
||||
.iter()
|
||||
.any(|t| self.is_possible_type(t2, t))
|
||||
}
|
||||
(true, true) => self.possible_types(t1)
|
||||
.iter()
|
||||
.any(|t| self.is_possible_type(t2, t)),
|
||||
(true, false) => self.is_possible_type(t1, t2),
|
||||
(false, true) => self.is_possible_type(t2, t1),
|
||||
(false, false) => false,
|
||||
|
@ -211,31 +216,30 @@ impl<'a> SchemaType<'a> {
|
|||
|
||||
pub fn possible_types(&self, t: &MetaType) -> Vec<&MetaType> {
|
||||
match *t {
|
||||
MetaType::Union(UnionMeta { ref of_type_names, .. }) => {
|
||||
of_type_names
|
||||
.iter()
|
||||
.flat_map(|t| self.concrete_type_by_name(t))
|
||||
.collect()
|
||||
}
|
||||
MetaType::Interface(InterfaceMeta { name, .. }) => {
|
||||
self.concrete_type_list()
|
||||
.into_iter()
|
||||
.filter(|t| match **t {
|
||||
MetaType::Object(ObjectMeta { ref interface_names, .. }) => {
|
||||
interface_names.iter().any(|iname| iname == name)
|
||||
}
|
||||
_ => false,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
MetaType::Union(UnionMeta {
|
||||
ref of_type_names, ..
|
||||
}) => of_type_names
|
||||
.iter()
|
||||
.flat_map(|t| self.concrete_type_by_name(t))
|
||||
.collect(),
|
||||
MetaType::Interface(InterfaceMeta { name, .. }) => self.concrete_type_list()
|
||||
.into_iter()
|
||||
.filter(|t| match **t {
|
||||
MetaType::Object(ObjectMeta {
|
||||
ref interface_names,
|
||||
..
|
||||
}) => interface_names.iter().any(|iname| iname == name),
|
||||
_ => false,
|
||||
})
|
||||
.collect(),
|
||||
_ => panic!("Can't retrieve possible types from non-abstract meta type"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_possible_type(&self, abstract_type: &MetaType, possible_type: &MetaType) -> bool {
|
||||
self.possible_types(abstract_type)
|
||||
.into_iter()
|
||||
.any(|t| (t as *const MetaType) == (possible_type as *const MetaType))
|
||||
self.possible_types(abstract_type).into_iter().any(|t| {
|
||||
(t as *const MetaType) == (possible_type as *const MetaType)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_subtype<'b>(&self, sub_type: &Type<'b>, super_type: &Type<'b>) -> bool {
|
||||
|
@ -263,8 +267,10 @@ impl<'a> SchemaType<'a> {
|
|||
pub fn is_named_subtype(&self, sub_type_name: &str, super_type_name: &str) -> bool {
|
||||
if sub_type_name == super_type_name {
|
||||
true
|
||||
} else if let (Some(sub_type), Some(super_type)) =
|
||||
(self.concrete_type_by_name(sub_type_name), self.concrete_type_by_name(super_type_name)) {
|
||||
} else if let (Some(sub_type), Some(super_type)) = (
|
||||
self.concrete_type_by_name(sub_type_name),
|
||||
self.concrete_type_by_name(super_type_name),
|
||||
) {
|
||||
super_type.is_abstract() && self.is_possible_type(super_type, sub_type)
|
||||
} else {
|
||||
false
|
||||
|
@ -282,10 +288,11 @@ impl<'a> TypeType<'a> {
|
|||
}
|
||||
|
||||
impl<'a> DirectiveType<'a> {
|
||||
pub fn new(name: &str,
|
||||
locations: &[DirectiveLocation],
|
||||
arguments: &[Argument<'a>])
|
||||
-> DirectiveType<'a> {
|
||||
pub fn new(
|
||||
name: &str,
|
||||
locations: &[DirectiveLocation],
|
||||
arguments: &[Argument<'a>],
|
||||
) -> DirectiveType<'a> {
|
||||
DirectiveType {
|
||||
name: name.to_owned(),
|
||||
description: None,
|
||||
|
@ -295,19 +302,27 @@ impl<'a> DirectiveType<'a> {
|
|||
}
|
||||
|
||||
fn new_skip(registry: &mut Registry<'a>) -> DirectiveType<'a> {
|
||||
Self::new("skip",
|
||||
&[DirectiveLocation::Field,
|
||||
DirectiveLocation::FragmentSpread,
|
||||
DirectiveLocation::InlineFragment],
|
||||
&[registry.arg::<bool>("if")])
|
||||
Self::new(
|
||||
"skip",
|
||||
&[
|
||||
DirectiveLocation::Field,
|
||||
DirectiveLocation::FragmentSpread,
|
||||
DirectiveLocation::InlineFragment,
|
||||
],
|
||||
&[registry.arg::<bool>("if")],
|
||||
)
|
||||
}
|
||||
|
||||
fn new_include(registry: &mut Registry<'a>) -> DirectiveType<'a> {
|
||||
Self::new("include",
|
||||
&[DirectiveLocation::Field,
|
||||
DirectiveLocation::FragmentSpread,
|
||||
DirectiveLocation::InlineFragment],
|
||||
&[registry.arg::<bool>("if")])
|
||||
Self::new(
|
||||
"include",
|
||||
&[
|
||||
DirectiveLocation::Field,
|
||||
DirectiveLocation::FragmentSpread,
|
||||
DirectiveLocation::InlineFragment,
|
||||
],
|
||||
&[registry.arg::<bool>("if")],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn description(mut self, description: &str) -> DirectiveType<'a> {
|
||||
|
@ -319,13 +334,13 @@ impl<'a> DirectiveType<'a> {
|
|||
impl fmt::Display for DirectiveLocation {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.write_str(match *self {
|
||||
DirectiveLocation::Query => "query",
|
||||
DirectiveLocation::Mutation => "mutation",
|
||||
DirectiveLocation::Field => "field",
|
||||
DirectiveLocation::FragmentDefinition => "fragment definition",
|
||||
DirectiveLocation::FragmentSpread => "fragment spread",
|
||||
DirectiveLocation::InlineFragment => "inline fragment",
|
||||
})
|
||||
DirectiveLocation::Query => "query",
|
||||
DirectiveLocation::Mutation => "mutation",
|
||||
DirectiveLocation::Field => "field",
|
||||
DirectiveLocation::FragmentDefinition => "fragment definition",
|
||||
DirectiveLocation::FragmentSpread => "fragment spread",
|
||||
DirectiveLocation::InlineFragment => "inline fragment",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
use types::base::{GraphQLType, Arguments, TypeKind};
|
||||
use executor::{Executor, Registry, ExecutionResult};
|
||||
use types::base::{Arguments, GraphQLType, TypeKind};
|
||||
use executor::{ExecutionResult, Executor, Registry};
|
||||
|
||||
use schema::meta::{MetaType, ObjectMeta, EnumMeta, InputObjectMeta, UnionMeta, InterfaceMeta,
|
||||
Field, Argument, EnumValue};
|
||||
use schema::model::{RootNode, SchemaType, TypeType, DirectiveType, DirectiveLocation};
|
||||
use schema::meta::{Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, MetaType,
|
||||
ObjectMeta, UnionMeta};
|
||||
use schema::model::{DirectiveLocation, DirectiveType, RootNode, SchemaType, TypeType};
|
||||
|
||||
impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT>
|
||||
where QueryT: GraphQLType<Context = CtxT>,
|
||||
MutationT: GraphQLType<Context = CtxT>
|
||||
where
|
||||
QueryT: GraphQLType<Context = CtxT>,
|
||||
MutationT: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -19,17 +20,16 @@ impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT
|
|||
QueryT::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_field(&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_field(
|
||||
&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>,
|
||||
) -> ExecutionResult {
|
||||
match field {
|
||||
"__schema" => {
|
||||
executor
|
||||
.replaced_context(&self.schema)
|
||||
.resolve(&self.schema)
|
||||
}
|
||||
"__schema" => executor
|
||||
.replaced_context(&self.schema)
|
||||
.resolve(&self.schema),
|
||||
"__type" => {
|
||||
let type_name: String = args.get("name").unwrap();
|
||||
executor
|
||||
|
|
|
@ -21,14 +21,31 @@ fn test_query_type_name() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("__schema", Value::object(vec![
|
||||
("queryType", Value::object(vec![
|
||||
("name", Value::string("Query")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"__schema",
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"queryType",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Query"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -44,12 +61,19 @@ fn test_specific_type_name() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("__type", Value::object(vec![
|
||||
("name", Value::string("Droid")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"__type",
|
||||
Value::object(vec![("name", Value::string("Droid"))].into_iter().collect()),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -67,13 +91,25 @@ fn test_specific_object_type_name_and_kind() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("__type", Value::object(vec![
|
||||
("name", Value::string("Droid")),
|
||||
("kind", Value::string("OBJECT")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"__type",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Droid")),
|
||||
("kind", Value::string("OBJECT")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -91,13 +127,25 @@ fn test_specific_interface_type_name_and_kind() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("__type", Value::object(vec![
|
||||
("name", Value::string("Character")),
|
||||
("kind", Value::string("INTERFACE")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"__type",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Character")),
|
||||
("kind", Value::string("INTERFACE")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -116,13 +164,29 @@ fn test_documentation() {
|
|||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((
|
||||
Value::object(vec![
|
||||
("__type", Value::object(vec![
|
||||
("name", Value::string("Droid")),
|
||||
("description", Value::string("A mechanical creature in the Star Wars universe.")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"__type",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Droid")),
|
||||
(
|
||||
"description",
|
||||
Value::string(
|
||||
"A mechanical creature in the Star Wars universe.",
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -160,19 +224,14 @@ fn test_possible_types() {
|
|||
.expect("'possibleTypes' not a list")
|
||||
.iter()
|
||||
.map(|t| {
|
||||
t.as_object_value()
|
||||
.expect("possible type not an object")
|
||||
.get("name")
|
||||
.expect("'name' not present in type")
|
||||
.as_string_value()
|
||||
.expect("'name' not a string")
|
||||
})
|
||||
t.as_object_value()
|
||||
.expect("possible type not an object")
|
||||
.get("name")
|
||||
.expect("'name' not present in type")
|
||||
.as_string_value()
|
||||
.expect("'name' not a string")
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
assert_eq!(
|
||||
possible_types,
|
||||
vec![
|
||||
"Human",
|
||||
"Droid",
|
||||
].into_iter().collect());
|
||||
assert_eq!(possible_types, vec!["Human", "Droid"].into_iter().collect());
|
||||
}
|
||||
|
|
|
@ -104,13 +104,14 @@ pub struct Database {
|
|||
}
|
||||
|
||||
impl HumanData {
|
||||
pub fn new(id: &str,
|
||||
name: &str,
|
||||
friend_ids: &[&str],
|
||||
appears_in: &[Episode],
|
||||
secret_backstory: Option<&str>,
|
||||
home_planet: Option<&str>)
|
||||
-> HumanData {
|
||||
pub fn new(
|
||||
id: &str,
|
||||
name: &str,
|
||||
friend_ids: &[&str],
|
||||
appears_in: &[Episode],
|
||||
secret_backstory: Option<&str>,
|
||||
home_planet: Option<&str>,
|
||||
) -> HumanData {
|
||||
HumanData {
|
||||
id: id.to_owned(),
|
||||
name: name.to_owned(),
|
||||
|
@ -127,13 +128,14 @@ impl HumanData {
|
|||
}
|
||||
|
||||
impl DroidData {
|
||||
pub fn new(id: &str,
|
||||
name: &str,
|
||||
friend_ids: &[&str],
|
||||
appears_in: &[Episode],
|
||||
secret_backstory: Option<&str>,
|
||||
primary_function: Option<&str>)
|
||||
-> DroidData {
|
||||
pub fn new(
|
||||
id: &str,
|
||||
name: &str,
|
||||
friend_ids: &[&str],
|
||||
appears_in: &[Episode],
|
||||
secret_backstory: Option<&str>,
|
||||
primary_function: Option<&str>,
|
||||
) -> DroidData {
|
||||
DroidData {
|
||||
id: id.to_owned(),
|
||||
name: name.to_owned(),
|
||||
|
@ -154,61 +156,89 @@ impl Database {
|
|||
let mut humans = HashMap::new();
|
||||
let mut droids = HashMap::new();
|
||||
|
||||
humans.insert("1000".to_owned(),
|
||||
HumanData::new("1000",
|
||||
"Luke Skywalker",
|
||||
&["1002", "1003", "2000", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Tatooine")));
|
||||
humans.insert(
|
||||
"1000".to_owned(),
|
||||
HumanData::new(
|
||||
"1000",
|
||||
"Luke Skywalker",
|
||||
&["1002", "1003", "2000", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Tatooine"),
|
||||
),
|
||||
);
|
||||
|
||||
humans.insert("1001".to_owned(),
|
||||
HumanData::new("1001",
|
||||
"Darth Vader",
|
||||
&["1004"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Tatooine")));
|
||||
humans.insert(
|
||||
"1001".to_owned(),
|
||||
HumanData::new(
|
||||
"1001",
|
||||
"Darth Vader",
|
||||
&["1004"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Tatooine"),
|
||||
),
|
||||
);
|
||||
|
||||
humans.insert("1002".to_owned(),
|
||||
HumanData::new("1002",
|
||||
"Han Solo",
|
||||
&["1000", "1003", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
None));
|
||||
humans.insert(
|
||||
"1002".to_owned(),
|
||||
HumanData::new(
|
||||
"1002",
|
||||
"Han Solo",
|
||||
&["1000", "1003", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
humans.insert("1003".to_owned(),
|
||||
HumanData::new("1003",
|
||||
"Leia Organa",
|
||||
&["1000", "1002", "2000", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Alderaan")));
|
||||
humans.insert(
|
||||
"1003".to_owned(),
|
||||
HumanData::new(
|
||||
"1003",
|
||||
"Leia Organa",
|
||||
&["1000", "1002", "2000", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Alderaan"),
|
||||
),
|
||||
);
|
||||
|
||||
humans.insert("1004".to_owned(),
|
||||
HumanData::new("1004",
|
||||
"Wilhuff Tarkin",
|
||||
&["1001"],
|
||||
&[Episode::NewHope],
|
||||
None,
|
||||
None));
|
||||
humans.insert(
|
||||
"1004".to_owned(),
|
||||
HumanData::new(
|
||||
"1004",
|
||||
"Wilhuff Tarkin",
|
||||
&["1001"],
|
||||
&[Episode::NewHope],
|
||||
None,
|
||||
None,
|
||||
),
|
||||
);
|
||||
|
||||
droids.insert("2000".to_owned(),
|
||||
DroidData::new("2000",
|
||||
"C-3PO",
|
||||
&["1000", "1002", "1003", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Protocol")));
|
||||
droids.insert(
|
||||
"2000".to_owned(),
|
||||
DroidData::new(
|
||||
"2000",
|
||||
"C-3PO",
|
||||
&["1000", "1002", "1003", "2001"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Protocol"),
|
||||
),
|
||||
);
|
||||
|
||||
droids.insert("2001".to_owned(),
|
||||
DroidData::new("2001",
|
||||
"R2-D2",
|
||||
&["1000", "1002", "1003"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Astromech")));
|
||||
droids.insert(
|
||||
"2001".to_owned(),
|
||||
DroidData::new(
|
||||
"2001",
|
||||
"R2-D2",
|
||||
&["1000", "1002", "1003"],
|
||||
&[Episode::NewHope, Episode::Empire, Episode::Jedi],
|
||||
None,
|
||||
Some("Astromech"),
|
||||
),
|
||||
);
|
||||
|
||||
Database {
|
||||
humans: humans,
|
||||
|
|
|
@ -18,12 +18,19 @@ fn test_hero_name() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("hero", Value::object(vec![
|
||||
("name", Value::string("R2-D2")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"hero",
|
||||
Value::object(vec![("name", Value::string("R2-D2"))].into_iter().collect()),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -43,24 +50,45 @@ fn test_hero_name_and_friends() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("hero", Value::object(vec![
|
||||
("id", Value::string("2001")),
|
||||
("name", Value::string("R2-D2")),
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"hero",
|
||||
Value::object(
|
||||
vec![
|
||||
("id", Value::string("2001")),
|
||||
("name", Value::string("R2-D2")),
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![("name", Value::string("Luke Skywalker"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("Han Solo"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("Leia Organa"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -84,78 +112,160 @@ fn test_hero_name_and_friends_and_friends_of_friends() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("hero", Value::object(vec![
|
||||
("id", Value::string("2001")),
|
||||
("name", Value::string("R2-D2")),
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
("appearsIn", Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
])),
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("C-3PO")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("R2-D2")),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
("appearsIn", Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
])),
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("R2-D2")),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
("appearsIn", Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
])),
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("C-3PO")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("R2-D2")),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"hero",
|
||||
Value::object(
|
||||
vec![
|
||||
("id", Value::string("2001")),
|
||||
("name", Value::string("R2-D2")),
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
(
|
||||
"appearsIn",
|
||||
Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"name",
|
||||
Value::string("Leia Organa"),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("C-3PO"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("R2-D2"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
(
|
||||
"appearsIn",
|
||||
Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"name",
|
||||
Value::string("Luke Skywalker"),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"name",
|
||||
Value::string("Leia Organa"),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("R2-D2"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
(
|
||||
"appearsIn",
|
||||
Value::list(vec![
|
||||
Value::string("NEW_HOPE"),
|
||||
Value::string("EMPIRE"),
|
||||
Value::string("JEDI"),
|
||||
]),
|
||||
),
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"name",
|
||||
Value::string("Luke Skywalker"),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("C-3PO"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("R2-D2"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -166,12 +276,23 @@ fn test_query_name() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("human", Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"human",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Luke Skywalker"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -182,12 +303,23 @@ fn test_query_alias_single() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("luke", Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"luke",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Luke Skywalker"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -202,15 +334,31 @@ fn test_query_alias_multiple() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("luke", Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect())),
|
||||
("leia", Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"luke",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Luke Skywalker"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
(
|
||||
"leia",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Leia Organa"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -230,17 +378,35 @@ fn test_query_alias_multiple_with_fragment() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("luke", Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
("homePlanet", Value::string("Tatooine")),
|
||||
].into_iter().collect())),
|
||||
("leia", Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
("homePlanet", Value::string("Alderaan")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"luke",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
("homePlanet", Value::string("Tatooine")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
(
|
||||
"leia",
|
||||
Value::object(
|
||||
vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
("homePlanet", Value::string("Alderaan")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -249,20 +415,29 @@ fn test_query_name_variable() {
|
|||
let database = Database::new();
|
||||
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
|
||||
|
||||
let vars = vec![
|
||||
("someId".to_owned(), InputValue::string("1000")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let vars = vec![("someId".to_owned(), InputValue::string("1000"))]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &vars, &database),
|
||||
Ok((Value::object(vec![
|
||||
("human", Value::object(vec![
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"human",
|
||||
Value::object(
|
||||
vec![("name", Value::string("Luke Skywalker"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -271,18 +446,17 @@ fn test_query_name_invalid_variable() {
|
|||
let database = Database::new();
|
||||
let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
|
||||
|
||||
let vars = vec![
|
||||
("someId".to_owned(), InputValue::string("some invalid id")),
|
||||
]
|
||||
.into_iter()
|
||||
.collect();
|
||||
let vars = vec![("someId".to_owned(), InputValue::string("some invalid id"))]
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &vars, &database),
|
||||
Ok((Value::object(vec![
|
||||
("human", Value::null()),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(vec![("human", Value::null())].into_iter().collect()),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -293,25 +467,48 @@ fn test_query_friends_names() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("human", Value::object(vec![
|
||||
("friends", Value::list(vec![
|
||||
Value::object(vec![
|
||||
("name", Value::string("Han Solo")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("Leia Organa")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("C-3PO")),
|
||||
].into_iter().collect()),
|
||||
Value::object(vec![
|
||||
("name", Value::string("R2-D2")),
|
||||
].into_iter().collect()),
|
||||
])),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"human",
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"friends",
|
||||
Value::list(vec![
|
||||
Value::object(
|
||||
vec![("name", Value::string("Han Solo"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("Leia Organa"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("C-3PO"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
Value::object(
|
||||
vec![("name", Value::string("R2-D2"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
]),
|
||||
),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -333,14 +530,26 @@ fn test_query_inline_fragments_droid() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("hero", Value::object(vec![
|
||||
("__typename", Value::string("Droid")),
|
||||
("name", Value::string("R2-D2")),
|
||||
("primaryFunction", Value::string("Astromech")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"hero",
|
||||
Value::object(
|
||||
vec![
|
||||
("__typename", Value::string("Droid")),
|
||||
("name", Value::string("R2-D2")),
|
||||
("primaryFunction", Value::string("Astromech")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -358,13 +567,25 @@ fn test_query_inline_fragments_human() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("hero", Value::object(vec![
|
||||
("__typename", Value::string("Human")),
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"hero",
|
||||
Value::object(
|
||||
vec![
|
||||
("__typename", Value::string("Human")),
|
||||
("name", Value::string("Luke Skywalker")),
|
||||
].into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -380,10 +601,21 @@ fn test_object_typename() {
|
|||
|
||||
assert_eq!(
|
||||
::execute(doc, None, &schema, &Variables::new(), &database),
|
||||
Ok((Value::object(vec![
|
||||
("human", Value::object(vec![
|
||||
("__typename", Value::string("Human")),
|
||||
].into_iter().collect())),
|
||||
].into_iter().collect()),
|
||||
vec![])));
|
||||
Ok((
|
||||
Value::object(
|
||||
vec![
|
||||
(
|
||||
"human",
|
||||
Value::object(
|
||||
vec![("__typename", Value::string("Human"))]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
),
|
||||
),
|
||||
].into_iter()
|
||||
.collect()
|
||||
),
|
||||
vec![]
|
||||
))
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use tests::model::{Character, Human, Droid, Database, Episode};
|
||||
use tests::model::{Character, Database, Droid, Episode, Human};
|
||||
use executor::Context;
|
||||
|
||||
impl Context for Database {}
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::collections::HashMap;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
use ast::{InputValue, Selection, Directive, FromInputValue};
|
||||
use ast::{Directive, FromInputValue, InputValue, Selection};
|
||||
use executor::Variables;
|
||||
use value::Value;
|
||||
|
||||
use schema::meta::{Argument, MetaType};
|
||||
use executor::{Executor, Registry, ExecutionResult};
|
||||
use executor::{ExecutionResult, Executor, Registry};
|
||||
use parser::Spanning;
|
||||
|
||||
/// GraphQL type kind
|
||||
|
@ -71,9 +71,10 @@ pub struct Arguments<'a> {
|
|||
|
||||
impl<'a> Arguments<'a> {
|
||||
#[doc(hidden)]
|
||||
pub fn new(mut args: Option<HashMap<&'a str, InputValue>>,
|
||||
meta_args: &'a Option<Vec<Argument>>)
|
||||
-> Arguments<'a> {
|
||||
pub fn new(
|
||||
mut args: Option<HashMap<&'a str, InputValue>>,
|
||||
meta_args: &'a Option<Vec<Argument>>,
|
||||
) -> Arguments<'a> {
|
||||
if meta_args.is_some() && args.is_none() {
|
||||
args = Some(HashMap::new());
|
||||
}
|
||||
|
@ -101,15 +102,14 @@ impl<'a> Arguments<'a> {
|
|||
/// Returns `Some` if the argument is present _and_ type conversion
|
||||
/// succeeeds.
|
||||
pub fn get<T>(&self, key: &str) -> Option<T>
|
||||
where T: FromInputValue
|
||||
where
|
||||
T: FromInputValue,
|
||||
{
|
||||
match self.args {
|
||||
Some(ref args) => {
|
||||
match args.get(key) {
|
||||
Some(v) => Some(v.convert().unwrap()),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
Some(ref args) => match args.get(key) {
|
||||
Some(v) => Some(v.convert().unwrap()),
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
@ -241,11 +241,12 @@ pub trait GraphQLType: Sized {
|
|||
///
|
||||
/// The default implementation panics.
|
||||
#[allow(unused_variables)]
|
||||
fn resolve_field(&self,
|
||||
field_name: &str,
|
||||
arguments: &Arguments,
|
||||
executor: &Executor<Self::Context>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_field(
|
||||
&self,
|
||||
field_name: &str,
|
||||
arguments: &Arguments,
|
||||
executor: &Executor<Self::Context>,
|
||||
) -> ExecutionResult {
|
||||
panic!("resolve_field must be implemented by object types");
|
||||
}
|
||||
|
||||
|
@ -256,11 +257,12 @@ pub trait GraphQLType: Sized {
|
|||
///
|
||||
/// The default implementation panics.
|
||||
#[allow(unused_variables)]
|
||||
fn resolve_into_type(&self,
|
||||
type_name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<Self::Context>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_into_type(
|
||||
&self,
|
||||
type_name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<Self::Context>,
|
||||
) -> ExecutionResult {
|
||||
if Self::name().unwrap() == type_name {
|
||||
Ok(self.resolve(selection_set, executor))
|
||||
} else {
|
||||
|
@ -286,10 +288,11 @@ pub trait GraphQLType: Sized {
|
|||
/// The default implementation uses `resolve_field` to resolve all fields,
|
||||
/// including those through fragment expansion, for object types. For
|
||||
/// non-object types, this method panics.
|
||||
fn resolve(&self,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<Self::Context>)
|
||||
-> Value {
|
||||
fn resolve(
|
||||
&self,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<Self::Context>,
|
||||
) -> Value {
|
||||
if let Some(selection_set) = selection_set {
|
||||
let mut result = HashMap::new();
|
||||
resolve_selection_set_into(self, selection_set, executor, &mut result);
|
||||
|
@ -300,11 +303,13 @@ pub trait GraphQLType: Sized {
|
|||
}
|
||||
}
|
||||
|
||||
fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
||||
selection_set: &[Selection],
|
||||
executor: &Executor<CtxT>,
|
||||
result: &mut HashMap<String, Value>)
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
fn resolve_selection_set_into<T, CtxT>(
|
||||
instance: &T,
|
||||
selection_set: &[Selection],
|
||||
executor: &Executor<CtxT>,
|
||||
result: &mut HashMap<String, Value>,
|
||||
) where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
let meta_type = executor
|
||||
.schema()
|
||||
|
@ -314,10 +319,10 @@ fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
|||
for selection in selection_set {
|
||||
match *selection {
|
||||
Selection::Field(Spanning {
|
||||
item: ref f,
|
||||
start: ref start_pos,
|
||||
..
|
||||
}) => {
|
||||
item: ref f,
|
||||
start: ref start_pos,
|
||||
..
|
||||
}) => {
|
||||
if is_excluded(&f.directives, executor.variables()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -325,32 +330,44 @@ fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
|||
let response_name = &f.alias.as_ref().unwrap_or(&f.name).item;
|
||||
|
||||
if f.name.item == "__typename" {
|
||||
result.insert((*response_name).to_owned(),
|
||||
Value::string(instance.concrete_type_name(executor.context())));
|
||||
result.insert(
|
||||
(*response_name).to_owned(),
|
||||
Value::string(instance.concrete_type_name(executor.context())),
|
||||
);
|
||||
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 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.sub_executor(Some(response_name),
|
||||
start_pos.clone(),
|
||||
f.selection_set.as_ref().map(|v| &v[..]));
|
||||
let sub_exec = executor.sub_executor(
|
||||
Some(response_name),
|
||||
start_pos.clone(),
|
||||
f.selection_set.as_ref().map(|v| &v[..]),
|
||||
);
|
||||
|
||||
let field_result = instance.resolve_field(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);
|
||||
let field_result = instance.resolve_field(
|
||||
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(v) => merge_key_into(result, response_name, v),
|
||||
|
@ -360,34 +377,37 @@ fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
|||
}
|
||||
}
|
||||
}
|
||||
Selection::FragmentSpread(Spanning { item: ref spread, .. }) => {
|
||||
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");
|
||||
.fragment_by_name(spread.name.item)
|
||||
.expect("Fragment could not be found");
|
||||
|
||||
resolve_selection_set_into(instance, &fragment.selection_set[..], executor, result);
|
||||
}
|
||||
Selection::InlineFragment(Spanning {
|
||||
item: ref fragment,
|
||||
start: ref start_pos,
|
||||
..
|
||||
}) => {
|
||||
item: ref fragment,
|
||||
start: ref start_pos,
|
||||
..
|
||||
}) => {
|
||||
if is_excluded(&fragment.directives, executor.variables()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let sub_exec =
|
||||
executor
|
||||
.sub_executor(None, start_pos.clone(), Some(&fragment.selection_set[..]));
|
||||
let sub_exec = executor
|
||||
.sub_executor(None, start_pos.clone(), Some(&fragment.selection_set[..]));
|
||||
|
||||
if let Some(ref type_condition) = fragment.type_condition {
|
||||
let sub_result = instance.resolve_into_type(type_condition.item,
|
||||
Some(&fragment.selection_set[..]),
|
||||
&sub_exec);
|
||||
let sub_result = instance.resolve_into_type(
|
||||
type_condition.item,
|
||||
Some(&fragment.selection_set[..]),
|
||||
&sub_exec,
|
||||
);
|
||||
|
||||
if let Ok(Value::Object(mut hash_map)) = sub_result {
|
||||
for (k, v) in hash_map.drain() {
|
||||
|
@ -397,10 +417,12 @@ fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
|||
sub_exec.push_error(e, start_pos.clone());
|
||||
}
|
||||
} else {
|
||||
resolve_selection_set_into(instance,
|
||||
&fragment.selection_set[..],
|
||||
&sub_exec,
|
||||
result);
|
||||
resolve_selection_set_into(
|
||||
instance,
|
||||
&fragment.selection_set[..],
|
||||
&sub_exec,
|
||||
result,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -409,7 +431,11 @@ fn resolve_selection_set_into<T, CtxT>(instance: &T,
|
|||
|
||||
fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables) -> bool {
|
||||
if let Some(ref directives) = *directives {
|
||||
for &Spanning { item: ref directive, .. } in directives {
|
||||
for &Spanning {
|
||||
item: ref directive,
|
||||
..
|
||||
} in directives
|
||||
{
|
||||
let condition: bool = directive
|
||||
.arguments
|
||||
.iter()
|
||||
|
@ -419,7 +445,8 @@ fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables)
|
|||
.unwrap();
|
||||
|
||||
if (directive.name.item == "skip" && condition) ||
|
||||
(directive.name.item == "include" && !condition) {
|
||||
(directive.name.item == "include" && !condition)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -429,14 +456,12 @@ fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables)
|
|||
|
||||
fn merge_key_into(result: &mut HashMap<String, Value>, response_name: &str, value: Value) {
|
||||
match result.entry(response_name.to_owned()) {
|
||||
Entry::Occupied(mut e) => {
|
||||
match (e.get_mut().as_mut_object_value(), value) {
|
||||
(Some(dest_obj), Value::Object(src_obj)) => {
|
||||
merge_maps(dest_obj, src_obj);
|
||||
}
|
||||
_ => {}
|
||||
Entry::Occupied(mut e) => match (e.get_mut().as_mut_object_value(), value) {
|
||||
(Some(dest_obj), Value::Object(src_obj)) => {
|
||||
merge_maps(dest_obj, src_obj);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(value);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ast::{InputValue, ToInputValue, FromInputValue, Selection};
|
||||
use ast::{FromInputValue, InputValue, Selection, ToInputValue};
|
||||
use value::Value;
|
||||
use schema::meta::MetaType;
|
||||
|
||||
|
@ -6,7 +6,8 @@ use executor::{Executor, Registry};
|
|||
use types::base::GraphQLType;
|
||||
|
||||
impl<T, CtxT> GraphQLType for Option<T>
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -27,23 +28,23 @@ impl<T, CtxT> GraphQLType for Option<T>
|
|||
}
|
||||
|
||||
impl<T> FromInputValue for Option<T>
|
||||
where T: FromInputValue
|
||||
where
|
||||
T: FromInputValue,
|
||||
{
|
||||
fn from(v: &InputValue) -> Option<Option<T>> {
|
||||
match v {
|
||||
&InputValue::Null => Some(None),
|
||||
v => {
|
||||
match v.convert() {
|
||||
Some(x) => Some(Some(x)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
v => match v.convert() {
|
||||
Some(x) => Some(Some(x)),
|
||||
None => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToInputValue for Option<T>
|
||||
where T: ToInputValue
|
||||
where
|
||||
T: ToInputValue,
|
||||
{
|
||||
fn to(&self) -> InputValue {
|
||||
match *self {
|
||||
|
@ -54,7 +55,8 @@ impl<T> ToInputValue for Option<T>
|
|||
}
|
||||
|
||||
impl<T, CtxT> GraphQLType for Vec<T>
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -67,35 +69,41 @@ impl<T, CtxT> GraphQLType for Vec<T>
|
|||
}
|
||||
|
||||
fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value {
|
||||
Value::list(self.iter()
|
||||
.map(|e| executor.resolve_into_value(e))
|
||||
.collect())
|
||||
Value::list(
|
||||
self.iter()
|
||||
.map(|e| executor.resolve_into_value(e))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> FromInputValue for Vec<T>
|
||||
where T: FromInputValue
|
||||
where
|
||||
T: FromInputValue,
|
||||
{
|
||||
fn from(v: &InputValue) -> Option<Vec<T>> {
|
||||
match *v {
|
||||
InputValue::List(ref ls) => {
|
||||
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
|
||||
|
||||
if v.len() == ls.len() { Some(v) } else { None }
|
||||
}
|
||||
ref other => {
|
||||
if let Some(e) = other.convert() {
|
||||
Some(vec![ e ])
|
||||
if v.len() == ls.len() {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ref other => if let Some(e) = other.convert() {
|
||||
Some(vec![e])
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ToInputValue for Vec<T>
|
||||
where T: ToInputValue
|
||||
where
|
||||
T: ToInputValue,
|
||||
{
|
||||
fn to(&self) -> InputValue {
|
||||
InputValue::list(self.iter().map(|v| v.to()).collect())
|
||||
|
@ -103,7 +111,8 @@ impl<T> ToInputValue for Vec<T>
|
|||
}
|
||||
|
||||
impl<'a, T, CtxT> GraphQLType for &'a [T]
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -116,14 +125,17 @@ impl<'a, T, CtxT> GraphQLType for &'a [T]
|
|||
}
|
||||
|
||||
fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value {
|
||||
Value::list(self.iter()
|
||||
.map(|e| executor.resolve_into_value(e))
|
||||
.collect())
|
||||
Value::list(
|
||||
self.iter()
|
||||
.map(|e| executor.resolve_into_value(e))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ToInputValue for &'a [T]
|
||||
where T: ToInputValue
|
||||
where
|
||||
T: ToInputValue,
|
||||
{
|
||||
fn to(&self) -> InputValue {
|
||||
InputValue::list(self.iter().map(|v| v.to()).collect())
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use ast::{Selection, InputValue, ToInputValue, FromInputValue};
|
||||
use ast::{FromInputValue, InputValue, Selection, ToInputValue};
|
||||
use value::Value;
|
||||
|
||||
use schema::meta::MetaType;
|
||||
use executor::{Executor, Registry, ExecutionResult};
|
||||
use executor::{ExecutionResult, Executor, Registry};
|
||||
use types::base::{Arguments, GraphQLType};
|
||||
|
||||
impl<T, CtxT> GraphQLType for Box<T>
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -18,19 +19,21 @@ impl<T, CtxT> GraphQLType for Box<T>
|
|||
T::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_into_type(&self,
|
||||
name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<CtxT>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_into_type(
|
||||
&self,
|
||||
name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<CtxT>,
|
||||
) -> ExecutionResult {
|
||||
(**self).resolve_into_type(name, selection_set, executor)
|
||||
}
|
||||
|
||||
fn resolve_field(&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_field(
|
||||
&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>,
|
||||
) -> ExecutionResult {
|
||||
(**self).resolve_field(field, args, executor)
|
||||
}
|
||||
|
||||
|
@ -40,7 +43,8 @@ impl<T, CtxT> GraphQLType for Box<T>
|
|||
}
|
||||
|
||||
impl<T> FromInputValue for Box<T>
|
||||
where T: FromInputValue
|
||||
where
|
||||
T: FromInputValue,
|
||||
{
|
||||
fn from(v: &InputValue) -> Option<Box<T>> {
|
||||
match <T as FromInputValue>::from(v) {
|
||||
|
@ -51,7 +55,8 @@ impl<T> FromInputValue for Box<T>
|
|||
}
|
||||
|
||||
impl<T> ToInputValue for Box<T>
|
||||
where T: ToInputValue
|
||||
where
|
||||
T: ToInputValue,
|
||||
{
|
||||
fn to(&self) -> InputValue {
|
||||
(**self).to()
|
||||
|
@ -59,7 +64,8 @@ impl<T> ToInputValue for Box<T>
|
|||
}
|
||||
|
||||
impl<'a, T, CtxT> GraphQLType for &'a T
|
||||
where T: GraphQLType<Context = CtxT>
|
||||
where
|
||||
T: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
type Context = CtxT;
|
||||
|
||||
|
@ -71,19 +77,21 @@ impl<'a, T, CtxT> GraphQLType for &'a T
|
|||
T::meta(registry)
|
||||
}
|
||||
|
||||
fn resolve_into_type(&self,
|
||||
name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<CtxT>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_into_type(
|
||||
&self,
|
||||
name: &str,
|
||||
selection_set: Option<&[Selection]>,
|
||||
executor: &Executor<CtxT>,
|
||||
) -> ExecutionResult {
|
||||
(**self).resolve_into_type(name, selection_set, executor)
|
||||
}
|
||||
|
||||
fn resolve_field(&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>)
|
||||
-> ExecutionResult {
|
||||
fn resolve_field(
|
||||
&self,
|
||||
field: &str,
|
||||
args: &Arguments,
|
||||
executor: &Executor<CtxT>,
|
||||
) -> ExecutionResult {
|
||||
(**self).resolve_field(field, args, executor)
|
||||
}
|
||||
|
||||
|
@ -93,7 +101,8 @@ impl<'a, T, CtxT> GraphQLType for &'a T
|
|||
}
|
||||
|
||||
impl<'a, T> ToInputValue for &'a T
|
||||
where T: ToInputValue
|
||||
where
|
||||
T: ToInputValue,
|
||||
{
|
||||
fn to(&self) -> InputValue {
|
||||
(**self).to()
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::convert::From;
|
|||
use std::marker::PhantomData;
|
||||
use std::ops::Deref;
|
||||
|
||||
use ast::{InputValue, Selection, FromInputValue, ToInputValue};
|
||||
use ast::{FromInputValue, InputValue, Selection, ToInputValue};
|
||||
use value::Value;
|
||||
|
||||
use schema::meta::MetaType;
|
||||
|
@ -156,7 +156,9 @@ pub struct EmptyMutation<T> {
|
|||
impl<T> EmptyMutation<T> {
|
||||
/// Construct a new empty mutation
|
||||
pub fn new() -> EmptyMutation<T> {
|
||||
EmptyMutation { phantom: PhantomData }
|
||||
EmptyMutation {
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,84 +1,77 @@
|
|||
use std::collections::HashSet;
|
||||
use ast::InputValue;
|
||||
use schema::model::{SchemaType, TypeType};
|
||||
use schema::meta::{MetaType, InputObjectMeta, EnumMeta};
|
||||
use schema::meta::{EnumMeta, InputObjectMeta, MetaType};
|
||||
|
||||
pub fn is_valid_literal_value(schema: &SchemaType,
|
||||
arg_type: &TypeType,
|
||||
arg_value: &InputValue)
|
||||
-> bool {
|
||||
pub fn is_valid_literal_value(
|
||||
schema: &SchemaType,
|
||||
arg_type: &TypeType,
|
||||
arg_value: &InputValue,
|
||||
) -> bool {
|
||||
match *arg_type {
|
||||
TypeType::NonNull(ref inner) => {
|
||||
if arg_value.is_null() {
|
||||
false
|
||||
} else {
|
||||
is_valid_literal_value(schema, inner, arg_value)
|
||||
}
|
||||
}
|
||||
TypeType::List(ref inner) => {
|
||||
match *arg_value {
|
||||
InputValue::List(ref items) => {
|
||||
items
|
||||
.iter()
|
||||
.all(|i| is_valid_literal_value(schema, inner, &i.item))
|
||||
}
|
||||
ref v => is_valid_literal_value(schema, inner, v),
|
||||
}
|
||||
}
|
||||
TypeType::NonNull(ref inner) => if arg_value.is_null() {
|
||||
false
|
||||
} else {
|
||||
is_valid_literal_value(schema, inner, arg_value)
|
||||
},
|
||||
TypeType::List(ref inner) => match *arg_value {
|
||||
InputValue::List(ref items) => items
|
||||
.iter()
|
||||
.all(|i| is_valid_literal_value(schema, inner, &i.item)),
|
||||
ref v => is_valid_literal_value(schema, inner, v),
|
||||
},
|
||||
TypeType::Concrete(t) => {
|
||||
// Even though InputValue::String can be parsed into an enum, they
|
||||
// are not valid as enum *literals* in a GraphQL query.
|
||||
if let (&InputValue::String(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
|
||||
(arg_value, arg_type.to_concrete()) {
|
||||
(arg_value, arg_type.to_concrete())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
match *arg_value {
|
||||
InputValue::Null |
|
||||
InputValue::Variable(_) => true,
|
||||
InputValue::Null | InputValue::Variable(_) => true,
|
||||
ref v @ InputValue::Int(_) |
|
||||
ref v @ InputValue::Float(_) |
|
||||
ref v @ InputValue::String(_) |
|
||||
ref v @ InputValue::Boolean(_) |
|
||||
ref v @ InputValue::Enum(_) => {
|
||||
if let Some(parse_fn) = t.input_value_parse_fn() {
|
||||
parse_fn(v)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
ref v @ InputValue::Enum(_) => if let Some(parse_fn) = t.input_value_parse_fn() {
|
||||
parse_fn(v)
|
||||
} else {
|
||||
false
|
||||
},
|
||||
InputValue::List(_) => false,
|
||||
InputValue::Object(ref obj) => {
|
||||
if let MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) = *t {
|
||||
let mut remaining_required_fields = input_fields
|
||||
InputValue::Object(ref obj) => if let MetaType::InputObject(InputObjectMeta {
|
||||
ref input_fields, ..
|
||||
}) = *t
|
||||
{
|
||||
let mut remaining_required_fields = input_fields
|
||||
.iter()
|
||||
.filter_map(|f| if f.arg_type.is_non_null() {
|
||||
Some(&f.name)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
|
||||
let all_types_ok = obj.iter().all(|&(ref key, ref value)| {
|
||||
remaining_required_fields.remove(&key.item);
|
||||
if let Some(ref arg_type) = input_fields
|
||||
.iter()
|
||||
.filter_map(|f| if f.arg_type.is_non_null() {
|
||||
Some(&f.name)
|
||||
} else {
|
||||
None
|
||||
})
|
||||
.collect::<HashSet<_>>();
|
||||
.filter(|f| f.name == key.item)
|
||||
.map(|f| schema.make_type(&f.arg_type))
|
||||
.next()
|
||||
{
|
||||
is_valid_literal_value(schema, arg_type, &value.item)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
let all_types_ok = obj.iter()
|
||||
.all(|&(ref key, ref value)| {
|
||||
remaining_required_fields.remove(&key.item);
|
||||
if let Some(ref arg_type) =
|
||||
input_fields
|
||||
.iter()
|
||||
.filter(|f| f.name == key.item)
|
||||
.map(|f| schema.make_type(&f.arg_type))
|
||||
.next() {
|
||||
is_valid_literal_value(schema, arg_type, &value.item)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
|
||||
all_types_ok && remaining_required_fields.is_empty()
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
all_types_ok && remaining_required_fields.is_empty()
|
||||
} else {
|
||||
false
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::HashSet;
|
||||
|
||||
use ast::{Document, Definition, Type};
|
||||
use ast::{Definition, Document, Type};
|
||||
|
||||
use schema::meta::MetaType;
|
||||
use schema::model::SchemaType;
|
||||
|
@ -63,9 +63,9 @@ impl<'a> ValidatorContext<'a> {
|
|||
fragment_names: document
|
||||
.iter()
|
||||
.filter_map(|def| match *def {
|
||||
Definition::Fragment(ref frag) => Some(frag.item.name.item),
|
||||
_ => None,
|
||||
})
|
||||
Definition::Fragment(ref frag) => Some(frag.item.name.item),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
@ -88,7 +88,8 @@ impl<'a> ValidatorContext<'a> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn with_pushed_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) -> R
|
||||
where F: FnOnce(&mut ValidatorContext<'a>) -> R
|
||||
where
|
||||
F: FnOnce(&mut ValidatorContext<'a>) -> R,
|
||||
{
|
||||
if let Some(t) = t {
|
||||
self.type_stack
|
||||
|
@ -109,7 +110,8 @@ impl<'a> ValidatorContext<'a> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn with_pushed_parent_type<F, R>(&mut self, f: F) -> R
|
||||
where F: FnOnce(&mut ValidatorContext<'a>) -> R
|
||||
where
|
||||
F: FnOnce(&mut ValidatorContext<'a>) -> R,
|
||||
{
|
||||
self.parent_type_stack
|
||||
.push(*self.type_stack.last().unwrap_or(&None));
|
||||
|
@ -121,7 +123,8 @@ impl<'a> ValidatorContext<'a> {
|
|||
|
||||
#[doc(hidden)]
|
||||
pub fn with_pushed_input_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) -> R
|
||||
where F: FnOnce(&mut ValidatorContext<'a>) -> R
|
||||
where
|
||||
F: FnOnce(&mut ValidatorContext<'a>) -> R,
|
||||
{
|
||||
if let Some(t) = t {
|
||||
self.input_type_stack
|
||||
|
|
|
@ -2,11 +2,11 @@ use std::collections::HashSet;
|
|||
use std::fmt;
|
||||
|
||||
use parser::SourcePosition;
|
||||
use ast::{InputValue, Document, Definition, VariableDefinitions};
|
||||
use ast::{Definition, Document, InputValue, VariableDefinitions};
|
||||
use executor::Variables;
|
||||
use validation::RuleError;
|
||||
use schema::model::{SchemaType, TypeType};
|
||||
use schema::meta::{MetaType, ScalarMeta, InputObjectMeta, EnumMeta};
|
||||
use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Path<'a> {
|
||||
|
@ -15,10 +15,11 @@ enum Path<'a> {
|
|||
ObjectField(&'a str, &'a Path<'a>),
|
||||
}
|
||||
|
||||
pub fn validate_input_values(values: &Variables,
|
||||
document: &Document,
|
||||
schema: &SchemaType)
|
||||
-> Vec<RuleError> {
|
||||
pub fn validate_input_values(
|
||||
values: &Variables,
|
||||
document: &Document,
|
||||
schema: &SchemaType,
|
||||
) -> Vec<RuleError> {
|
||||
let mut errs = vec![];
|
||||
|
||||
for def in document {
|
||||
|
@ -33,10 +34,12 @@ pub fn validate_input_values(values: &Variables,
|
|||
errs
|
||||
}
|
||||
|
||||
fn validate_var_defs(values: &Variables,
|
||||
var_defs: &VariableDefinitions,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>) {
|
||||
fn validate_var_defs(
|
||||
values: &Variables,
|
||||
var_defs: &VariableDefinitions,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>,
|
||||
) {
|
||||
for &(ref name, ref def) in var_defs.iter() {
|
||||
let raw_type_name = def.var_type.item.innermost_name();
|
||||
match schema.concrete_type_by_name(raw_type_name) {
|
||||
|
@ -44,11 +47,14 @@ fn validate_var_defs(values: &Variables,
|
|||
let ct = schema.make_type(&def.var_type.item);
|
||||
|
||||
if def.var_type.item.is_non_null() && is_absent_or_null(values.get(name.item)) {
|
||||
errors.push(RuleError::new(&format!(
|
||||
errors.push(RuleError::new(
|
||||
&format!(
|
||||
r#"Variable "${}" of required type "{}" was not provided."#,
|
||||
name.item, def.var_type.item,
|
||||
name.item,
|
||||
def.var_type.item,
|
||||
),
|
||||
&[name.start.clone()]));
|
||||
&[name.start.clone()],
|
||||
));
|
||||
} else if let Some(v) = values.get(name.item) {
|
||||
unify_value(name.item, &name.start, v, &ct, schema, errors, Path::Root);
|
||||
}
|
||||
|
@ -64,25 +70,27 @@ fn validate_var_defs(values: &Variables,
|
|||
}
|
||||
}
|
||||
|
||||
fn unify_value<'a>(var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta_type: &TypeType<'a>,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: Path<'a>) {
|
||||
fn unify_value<'a>(
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta_type: &TypeType<'a>,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: Path<'a>,
|
||||
) {
|
||||
match *meta_type {
|
||||
TypeType::NonNull(ref inner) => {
|
||||
if value.is_null() {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
&path,
|
||||
&format!(r#"Expected "{}", found null"#, meta_type));
|
||||
} else {
|
||||
unify_value(var_name, var_pos, value, inner, schema, errors, path);
|
||||
}
|
||||
}
|
||||
TypeType::NonNull(ref inner) => if value.is_null() {
|
||||
push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
&path,
|
||||
&format!(r#"Expected "{}", found null"#, meta_type),
|
||||
);
|
||||
} else {
|
||||
unify_value(var_name, var_pos, value, inner, schema, errors, path);
|
||||
},
|
||||
|
||||
TypeType::List(ref inner) => {
|
||||
if value.is_null() {
|
||||
|
@ -90,17 +98,17 @@ fn unify_value<'a>(var_name: &str,
|
|||
}
|
||||
|
||||
match value.to_list_value() {
|
||||
Some(l) => {
|
||||
for (i, v) in l.iter().enumerate() {
|
||||
unify_value(var_name,
|
||||
var_pos,
|
||||
v,
|
||||
inner,
|
||||
schema,
|
||||
errors,
|
||||
Path::ArrayElement(i, &path));
|
||||
}
|
||||
}
|
||||
Some(l) => for (i, v) in l.iter().enumerate() {
|
||||
unify_value(
|
||||
var_name,
|
||||
var_pos,
|
||||
v,
|
||||
inner,
|
||||
schema,
|
||||
errors,
|
||||
Path::ArrayElement(i, &path),
|
||||
);
|
||||
},
|
||||
_ => unify_value(var_name, var_pos, value, inner, schema, errors, path),
|
||||
}
|
||||
}
|
||||
|
@ -124,55 +132,62 @@ fn unify_value<'a>(var_name: &str,
|
|||
}
|
||||
}
|
||||
|
||||
fn unify_scalar<'a>(var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &ScalarMeta,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>) {
|
||||
fn unify_scalar<'a>(
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &ScalarMeta,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>,
|
||||
) {
|
||||
if !(meta.try_parse_fn)(value) {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}""#, meta.name));
|
||||
push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}""#, meta.name),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match *value {
|
||||
InputValue::List(_) => {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found list"#, meta.name))
|
||||
}
|
||||
InputValue::Object(_) => {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found object"#, meta.name))
|
||||
}
|
||||
InputValue::List(_) => push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found list"#, meta.name),
|
||||
),
|
||||
InputValue::Object(_) => push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found object"#, meta.name),
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn unify_enum<'a>(var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &EnumMeta,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>) {
|
||||
fn unify_enum<'a>(
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &EnumMeta,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>,
|
||||
) {
|
||||
match *value {
|
||||
InputValue::String(ref name) |
|
||||
InputValue::Enum(ref name) => {
|
||||
InputValue::String(ref name) | InputValue::Enum(ref name) => {
|
||||
if !meta.values.iter().any(|ev| &ev.name == name) {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Invalid value for enum "{}""#, meta.name))
|
||||
push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Invalid value for enum "{}""#, meta.name),
|
||||
)
|
||||
}
|
||||
}
|
||||
_ => push_unification_error(
|
||||
|
@ -185,13 +200,15 @@ fn unify_enum<'a>(var_name: &str,
|
|||
}
|
||||
}
|
||||
|
||||
fn unify_input_object<'a>(var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &InputObjectMeta,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>) {
|
||||
fn unify_input_object<'a>(
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
value: &InputValue,
|
||||
meta: &InputObjectMeta,
|
||||
schema: &SchemaType,
|
||||
errors: &mut Vec<RuleError>,
|
||||
path: &Path<'a>,
|
||||
) {
|
||||
if let Some(ref obj) = value.to_object_value() {
|
||||
let mut keys = obj.keys().collect::<HashSet<&&str>>();
|
||||
|
||||
|
@ -203,13 +220,15 @@ fn unify_input_object<'a>(var_name: &str,
|
|||
if !value.is_null() {
|
||||
has_value = true;
|
||||
|
||||
unify_value(var_name,
|
||||
var_pos,
|
||||
value,
|
||||
&schema.make_type(&input_field.arg_type),
|
||||
schema,
|
||||
errors,
|
||||
Path::ObjectField(&input_field.name, path));
|
||||
unify_value(
|
||||
var_name,
|
||||
var_pos,
|
||||
value,
|
||||
&schema.make_type(&input_field.arg_type),
|
||||
schema,
|
||||
errors,
|
||||
Path::ObjectField(&input_field.name, path),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,18 +244,22 @@ fn unify_input_object<'a>(var_name: &str,
|
|||
}
|
||||
|
||||
for key in keys {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
&Path::ObjectField(key, path),
|
||||
"Unknown field");
|
||||
push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
&Path::ObjectField(key, path),
|
||||
"Unknown field",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
push_unification_error(errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found not an object"#, meta.name));
|
||||
push_unification_error(
|
||||
errors,
|
||||
var_name,
|
||||
var_pos,
|
||||
path,
|
||||
&format!(r#"Expected "{}", found not an object"#, meta.name),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -244,16 +267,22 @@ fn is_absent_or_null(v: Option<&InputValue>) -> bool {
|
|||
v.map_or(true, InputValue::is_null)
|
||||
}
|
||||
|
||||
fn push_unification_error<'a>(errors: &mut Vec<RuleError>,
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
path: &Path<'a>,
|
||||
message: &str) {
|
||||
errors.push(RuleError::new(&format!(
|
||||
fn push_unification_error<'a>(
|
||||
errors: &mut Vec<RuleError>,
|
||||
var_name: &str,
|
||||
var_pos: &SourcePosition,
|
||||
path: &Path<'a>,
|
||||
message: &str,
|
||||
) {
|
||||
errors.push(RuleError::new(
|
||||
&format!(
|
||||
r#"Variable "${}" got invalid value. {}{}."#,
|
||||
var_name, path, message,
|
||||
var_name,
|
||||
path,
|
||||
message,
|
||||
),
|
||||
&[var_pos.clone()]));
|
||||
&[var_pos.clone()],
|
||||
));
|
||||
}
|
||||
|
||||
impl<'a> fmt::Display for Path<'a> {
|
||||
|
|
|
@ -18,5 +18,5 @@ pub use self::multi_visitor::{MultiVisitor, MultiVisitorNil};
|
|||
pub use self::input_value::validate_input_values;
|
||||
|
||||
#[cfg(test)]
|
||||
pub use self::test_harness::{expect_passes_rule, expect_fails_rule,
|
||||
expect_passes_rule_with_schema, expect_fails_rule_with_schema};
|
||||
pub use self::test_harness::{expect_fails_rule, expect_fails_rule_with_schema, expect_passes_rule,
|
||||
expect_passes_rule_with_schema};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
|
||||
Field, FragmentSpread, InlineFragment};
|
||||
use ast::{Directive, Document, Field, Fragment, FragmentSpread, InlineFragment, InputValue,
|
||||
Operation, Selection, VariableDefinition};
|
||||
use parser::Spanning;
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
||||
|
@ -8,7 +8,8 @@ pub trait MultiVisitor<'a> {
|
|||
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, f: F);
|
||||
|
||||
fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self>
|
||||
where Self: Sized
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
MultiVisitorCons(visitor, self)
|
||||
}
|
||||
|
@ -32,7 +33,8 @@ impl<'a, A: Visitor<'a>, B: MultiVisitor<'a>> MultiVisitor<'a> for MultiVisitorC
|
|||
}
|
||||
|
||||
impl<'a, M> Visitor<'a> for M
|
||||
where M: MultiVisitor<'a>
|
||||
where
|
||||
M: MultiVisitor<'a>,
|
||||
{
|
||||
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
|
||||
self.visit_all(|v| v.enter_document(ctx, doc));
|
||||
|
@ -42,36 +44,48 @@ impl<'a, M> Visitor<'a> for M
|
|||
self.visit_all(|v| v.exit_document(ctx, doc));
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_operation_definition(ctx, op));
|
||||
}
|
||||
fn exit_operation_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn exit_operation_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_operation_definition(ctx, op));
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_fragment_definition(ctx, f));
|
||||
}
|
||||
fn exit_fragment_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn exit_fragment_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_fragment_definition(ctx, f));
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
self.visit_all(|v| v.enter_variable_definition(ctx, def));
|
||||
}
|
||||
fn exit_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn exit_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
self.visit_all(|v| v.exit_variable_definition(ctx, def));
|
||||
}
|
||||
|
||||
|
@ -82,14 +96,18 @@ impl<'a, M> Visitor<'a> for M
|
|||
self.visit_all(|v| v.exit_directive(ctx, d));
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
arg: &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
self.visit_all(|v| v.enter_argument(ctx, arg));
|
||||
}
|
||||
fn exit_argument(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn exit_argument(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
arg: &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
self.visit_all(|v| v.exit_argument(ctx, arg));
|
||||
}
|
||||
|
||||
|
@ -107,25 +125,33 @@ impl<'a, M> Visitor<'a> for M
|
|||
self.visit_all(|v| v.exit_field(ctx, f));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
s: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
s: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_fragment_spread(ctx, s));
|
||||
}
|
||||
fn exit_fragment_spread(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
s: &'a Spanning<FragmentSpread>) {
|
||||
fn exit_fragment_spread(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
s: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_fragment_spread(ctx, s));
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_inline_fragment(ctx, f));
|
||||
}
|
||||
fn exit_inline_fragment(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>) {
|
||||
fn exit_inline_fragment(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_inline_fragment(ctx, f));
|
||||
}
|
||||
|
||||
|
@ -178,36 +204,48 @@ impl<'a, M> Visitor<'a> for M
|
|||
self.visit_all(|v| v.exit_variable_value(ctx, s.clone()));
|
||||
}
|
||||
|
||||
fn enter_list_value(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
l: Spanning<&'a Vec<Spanning<InputValue>>>) {
|
||||
fn enter_list_value(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
l: Spanning<&'a Vec<Spanning<InputValue>>>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_list_value(ctx, l.clone()));
|
||||
}
|
||||
fn exit_list_value(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
l: Spanning<&'a Vec<Spanning<InputValue>>>) {
|
||||
fn exit_list_value(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
l: Spanning<&'a Vec<Spanning<InputValue>>>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_list_value(ctx, l.clone()));
|
||||
}
|
||||
|
||||
fn enter_object_value(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn enter_object_value(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
self.visit_all(|v| v.enter_object_value(ctx, o.clone()));
|
||||
}
|
||||
fn exit_object_value(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn exit_object_value(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
self.visit_all(|v| v.exit_object_value(ctx, o.clone()));
|
||||
}
|
||||
|
||||
fn enter_object_field(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a (Spanning<String>, Spanning<InputValue>)) {
|
||||
fn enter_object_field(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a (Spanning<String>, Spanning<InputValue>),
|
||||
) {
|
||||
self.visit_all(|v| v.enter_object_field(ctx, f));
|
||||
}
|
||||
fn exit_object_field(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a (Spanning<String>, Spanning<InputValue>)) {
|
||||
fn exit_object_field(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
f: &'a (Spanning<String>, Spanning<InputValue>),
|
||||
) {
|
||||
self.visit_all(|v| v.exit_object_field(ctx, f));
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,7 @@
|
|||
use ast::VariableDefinition;
|
||||
use types::utilities::is_valid_literal_value;
|
||||
use parser::Spanning;
|
||||
use validation::{Visitor, ValidatorContext};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
||||
pub struct DefaultValuesOfCorrectType {}
|
||||
|
||||
|
@ -10,26 +10,30 @@ pub fn factory() -> DefaultValuesOfCorrectType {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
||||
fn enter_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, ref var_def): &'a (Spanning<&'a str>,
|
||||
VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
if let Some(Spanning {
|
||||
item: ref var_value,
|
||||
ref start,
|
||||
..
|
||||
}) = var_def.default_value {
|
||||
item: ref var_value,
|
||||
ref start,
|
||||
..
|
||||
}) = var_def.default_value
|
||||
{
|
||||
if var_def.var_type.item.is_non_null() {
|
||||
ctx.report_error(&non_null_error_message(var_name.item,
|
||||
&format!("{}", var_def.var_type.item)),
|
||||
&[start.clone()])
|
||||
ctx.report_error(
|
||||
&non_null_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
|
||||
&[start.clone()],
|
||||
)
|
||||
} else {
|
||||
let meta_type = ctx.schema.make_type(&var_def.var_type.item);
|
||||
|
||||
if !is_valid_literal_value(ctx.schema, &meta_type, var_value) {
|
||||
ctx.report_error(&type_error_message(var_name.item,
|
||||
&format!("{}", var_def.var_type.item)),
|
||||
&[start.clone()]);
|
||||
ctx.report_error(
|
||||
&type_error_message(var_name.item, &format!("{}", var_def.var_type.item)),
|
||||
&[start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,46 +43,55 @@ impl<'a> Visitor<'a> for DefaultValuesOfCorrectType {
|
|||
fn type_error_message(arg_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
"Invalid default value for argument \"{}\", expected type \"{}\"",
|
||||
arg_name, type_name)
|
||||
arg_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
fn non_null_error_message(arg_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
"Argument \"{}\" has type \"{}\" and is not nullable, so it't can't have a default value",
|
||||
arg_name, type_name)
|
||||
arg_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{type_error_message, non_null_error_message, factory};
|
||||
use super::{factory, non_null_error_message, type_error_message};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn variables_with_no_default_values() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query NullableValues($a: Int, $b: String, $c: ComplexInput) {
|
||||
dog { name }
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn required_variables_without_default_values() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query RequiredValues($a: Int!, $b: String!) {
|
||||
dog { name }
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_with_valid_default_values() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query WithDefaultValues(
|
||||
$a: Int = 1,
|
||||
$b: String = "ok",
|
||||
|
@ -86,27 +99,37 @@ mod tests {
|
|||
) {
|
||||
dog { name }
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_required_variables_with_default_values() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
|
||||
dog { name }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&non_null_error_message("a", "Int!"),
|
||||
&[SourcePosition::new(53, 1, 52)]),
|
||||
RuleError::new(&non_null_error_message("b", "String!"),
|
||||
&[SourcePosition::new(70, 1, 69)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&non_null_error_message("a", "Int!"),
|
||||
&[SourcePosition::new(53, 1, 52)],
|
||||
),
|
||||
RuleError::new(
|
||||
&non_null_error_message("b", "String!"),
|
||||
&[SourcePosition::new(70, 1, 69)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_with_invalid_default_values() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query InvalidDefaultValues(
|
||||
$a: Int = "one",
|
||||
$b: String = 4,
|
||||
|
@ -115,36 +138,57 @@ mod tests {
|
|||
dog { name }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&type_error_message("a", "Int"),
|
||||
&[SourcePosition::new(61, 2, 22)]),
|
||||
RuleError::new(&type_error_message("b", "String"),
|
||||
&[SourcePosition::new(93, 3, 25)]),
|
||||
RuleError::new(&type_error_message("c", "ComplexInput"),
|
||||
&[SourcePosition::new(127, 4, 31)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&type_error_message("a", "Int"),
|
||||
&[SourcePosition::new(61, 2, 22)],
|
||||
),
|
||||
RuleError::new(
|
||||
&type_error_message("b", "String"),
|
||||
&[SourcePosition::new(93, 3, 25)],
|
||||
),
|
||||
RuleError::new(
|
||||
&type_error_message("c", "ComplexInput"),
|
||||
&[SourcePosition::new(127, 4, 31)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_variables_missing_required_field() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query MissingRequiredField($a: ComplexInput = {intField: 3}) {
|
||||
dog { name }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&type_error_message("a", "ComplexInput"),
|
||||
&[SourcePosition::new(57, 1, 56)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&type_error_message("a", "ComplexInput"),
|
||||
&[SourcePosition::new(57, 1, 56)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn list_variables_with_invalid_item() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query InvalidItem($a: [String] = ["one", 2]) {
|
||||
dog { name }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&type_error_message("a", "[String]"),
|
||||
&[SourcePosition::new(44, 1, 43)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&type_error_message("a", "[String]"),
|
||||
&[SourcePosition::new(44, 1, 43)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::Field;
|
||||
use validation::{Visitor, ValidatorContext};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
|
||||
pub struct FieldsOnCorrectType {}
|
||||
|
@ -16,8 +16,10 @@ impl<'a> Visitor<'a> for FieldsOnCorrectType {
|
|||
let type_name = parent_type.name().unwrap_or("<unknown>");
|
||||
|
||||
if parent_type.field_by_name(field_name.item).is_none() {
|
||||
context.report_error(&error_message(field_name.item, type_name),
|
||||
&[field_name.start.clone()]);
|
||||
context.report_error(
|
||||
&error_message(field_name.item, type_name),
|
||||
&[field_name.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,75 +35,88 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn selection_on_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectFieldSelection on Dog {
|
||||
__typename
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliased_selection_on_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment aliasedObjectFieldSelection on Dog {
|
||||
tn : __typename
|
||||
otherName : name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn selection_on_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceFieldSelection on Pet {
|
||||
__typename
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn aliased_selection_on_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceFieldSelection on Pet {
|
||||
otherName : name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lying_alias_selection() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment lyingAliasSelection on Dog {
|
||||
name : nickname
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_unknown_type() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment unknownSelection on UnknownType {
|
||||
unknownField
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn nested_unknown_fields() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment typeKnownAgain on Pet {
|
||||
unknown_pet_field {
|
||||
... on Cat {
|
||||
|
@ -110,140 +125,204 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("unknown_pet_field", "Pet"),
|
||||
&[SourcePosition::new(56, 2, 12)]),
|
||||
RuleError::new(&error_message("unknown_cat_field", "Cat"),
|
||||
&[SourcePosition::new(119, 4, 16)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("unknown_pet_field", "Pet"),
|
||||
&[SourcePosition::new(56, 2, 12)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("unknown_cat_field", "Cat"),
|
||||
&[SourcePosition::new(119, 4, 16)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_field_on_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fieldNotDefined on Dog {
|
||||
meowVolume
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("meowVolume", "Dog"),
|
||||
&[SourcePosition::new(57, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("meowVolume", "Dog"),
|
||||
&[SourcePosition::new(57, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_deeply_unknown_field() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment deepFieldNotDefined on Dog {
|
||||
unknown_field {
|
||||
deeper_unknown_field
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("unknown_field", "Dog"),
|
||||
&[SourcePosition::new(61, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("unknown_field", "Dog"),
|
||||
&[SourcePosition::new(61, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_subfield() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment subFieldNotDefined on Human {
|
||||
pets {
|
||||
unknown_field
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("unknown_field", "Pet"),
|
||||
&[SourcePosition::new(83, 3, 14)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("unknown_field", "Pet"),
|
||||
&[SourcePosition::new(83, 3, 14)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_field_on_inline_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fieldNotDefined on Pet {
|
||||
... on Dog {
|
||||
meowVolume
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("meowVolume", "Dog"),
|
||||
&[SourcePosition::new(84, 3, 14)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("meowVolume", "Dog"),
|
||||
&[SourcePosition::new(84, 3, 14)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_aliased_target() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment aliasedFieldTargetNotDefined on Dog {
|
||||
volume : mooVolume
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("mooVolume", "Dog"),
|
||||
&[SourcePosition::new(79, 2, 21)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("mooVolume", "Dog"),
|
||||
&[SourcePosition::new(79, 2, 21)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_aliased_lying_field_target() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment aliasedLyingFieldTargetNotDefined on Dog {
|
||||
barkVolume : kawVolume
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("kawVolume", "Dog"),
|
||||
&[SourcePosition::new(88, 2, 25)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("kawVolume", "Dog"),
|
||||
&[SourcePosition::new(88, 2, 25)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_defined_on_interface() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment notDefinedOnInterface on Pet {
|
||||
tailLength
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("tailLength", "Pet"),
|
||||
&[SourcePosition::new(63, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("tailLength", "Pet"),
|
||||
&[SourcePosition::new(63, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn defined_in_concrete_types_but_not_interface() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment definedOnImplementorsButNotInterface on Pet {
|
||||
nickname
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("nickname", "Pet"),
|
||||
&[SourcePosition::new(78, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("nickname", "Pet"),
|
||||
&[SourcePosition::new(78, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn meta_field_on_union() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment definedOnImplementorsButNotInterface on Pet {
|
||||
__typename
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fields_on_union() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment definedOnImplementorsQueriedOnUnion on CatOrDog {
|
||||
name
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("name", "CatOrDog"),
|
||||
&[SourcePosition::new(82, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("name", "CatOrDog"),
|
||||
&[SourcePosition::new(82, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_field_in_inline_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectFieldSelection on Pet {
|
||||
... on Dog {
|
||||
name
|
||||
|
@ -252,7 +331,8 @@ mod tests {
|
|||
name
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use ast::{Fragment, InlineFragment};
|
||||
use parser::Spanning;
|
||||
use validation::{Visitor, ValidatorContext};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
||||
pub struct FragmentsOnCompositeTypes {}
|
||||
|
||||
|
@ -9,25 +9,31 @@ pub fn factory() -> FragmentsOnCompositeTypes {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for FragmentsOnCompositeTypes {
|
||||
fn enter_fragment_definition(&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
{
|
||||
if let Some(current_type) = context.current_type() {
|
||||
if !current_type.is_composite() {
|
||||
let type_name = current_type.name().unwrap_or("<unknown>");
|
||||
let type_cond = &f.item.type_condition;
|
||||
|
||||
context.report_error(&error_message(Some(f.item.name.item), type_name),
|
||||
&[type_cond.start.clone()]);
|
||||
context.report_error(
|
||||
&error_message(Some(f.item.name.item), type_name),
|
||||
&[type_cond.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
{
|
||||
if let Some(ref type_cond) = f.item.type_condition {
|
||||
let invalid_type_name = context
|
||||
|
@ -49,11 +55,14 @@ fn error_message(fragment_name: Option<&str>, on_type: &str) -> String {
|
|||
if let Some(name) = fragment_name {
|
||||
format!(
|
||||
r#"Fragment "{}" cannot condition non composite type "{}"#,
|
||||
name, on_type)
|
||||
name,
|
||||
on_type
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
r#"Fragment cannot condition on non composite type "{}""#,
|
||||
on_type)
|
||||
on_type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,109 +71,143 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn on_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment validFragment on Dog {
|
||||
barks
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment validFragment on Pet {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_object_inline() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment validFragment on Pet {
|
||||
... on Dog {
|
||||
barks
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_inline_without_type_cond() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment validFragment on Pet {
|
||||
... {
|
||||
name
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn on_union() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment validFragment on CatOrDog {
|
||||
__typename
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_on_scalar() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarFragment on Boolean {
|
||||
bad
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("scalarFragment"), "Boolean"),
|
||||
&[SourcePosition::new(38, 1, 37)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("scalarFragment"), "Boolean"),
|
||||
&[SourcePosition::new(38, 1, 37)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_on_enum() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarFragment on FurColor {
|
||||
bad
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("scalarFragment"), "FurColor"),
|
||||
&[SourcePosition::new(38, 1, 37)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("scalarFragment"), "FurColor"),
|
||||
&[SourcePosition::new(38, 1, 37)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_on_input_object() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment inputFragment on ComplexInput {
|
||||
stringField
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("inputFragment"), "ComplexInput"),
|
||||
&[SourcePosition::new(37, 1, 36)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("inputFragment"), "ComplexInput"),
|
||||
&[SourcePosition::new(37, 1, 36)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_on_scalar_inline() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidFragment on Pet {
|
||||
... on String {
|
||||
barks
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(None, "String"),
|
||||
&[SourcePosition::new(64, 2, 19)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(None, "String"),
|
||||
&[SourcePosition::new(64, 2, 19)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ast::{Field, InputValue, Directive};
|
||||
use ast::{Directive, Field, InputValue};
|
||||
use schema::meta::Argument;
|
||||
use parser::Spanning;
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
@ -18,13 +18,19 @@ pub fn factory<'a>() -> KnownArgumentNames<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
|
||||
fn enter_directive(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>) {
|
||||
self.current_args =
|
||||
ctx.schema
|
||||
.directive_by_name(directive.item.name.item)
|
||||
.map(|d| (ArgumentPosition::Directive(directive.item.name.item), &d.arguments));
|
||||
fn enter_directive(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>,
|
||||
) {
|
||||
self.current_args = ctx.schema.directive_by_name(directive.item.name.item).map(
|
||||
|d| {
|
||||
(
|
||||
ArgumentPosition::Directive(directive.item.name.item),
|
||||
&d.arguments,
|
||||
)
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {
|
||||
|
@ -36,22 +42,28 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
|
|||
.and_then(|t| t.field_by_name(field.item.name.item))
|
||||
.and_then(|f| f.arguments.as_ref())
|
||||
.map(|args| {
|
||||
(ArgumentPosition::Field(field.item.name.item,
|
||||
ctx.parent_type()
|
||||
.expect("Parent type should exist")
|
||||
.name()
|
||||
.expect("Parent type should be named")),
|
||||
args)
|
||||
});
|
||||
(
|
||||
ArgumentPosition::Field(
|
||||
field.item.name.item,
|
||||
ctx.parent_type()
|
||||
.expect("Parent type should exist")
|
||||
.name()
|
||||
.expect("Parent type should be named"),
|
||||
),
|
||||
args,
|
||||
)
|
||||
});
|
||||
}
|
||||
|
||||
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {
|
||||
self.current_args = None;
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
if let Some((ref pos, args)) = self.current_args {
|
||||
if args.iter().find(|a| a.name == arg_name.item).is_none() {
|
||||
let message = match *pos {
|
||||
|
@ -72,76 +84,92 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
|
|||
fn field_error_message(arg_name: &str, field_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
r#"Unknown argument "{}" on field "{}" of type "{}""#,
|
||||
arg_name, field_name, type_name)
|
||||
arg_name,
|
||||
field_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
fn directive_error_message(arg_name: &str, directive_name: &str) -> String {
|
||||
format!(
|
||||
r#"Unknown argument "{}" on directive "{}""#,
|
||||
arg_name, directive_name)
|
||||
arg_name,
|
||||
directive_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{field_error_message, directive_error_message, factory};
|
||||
use super::{directive_error_message, factory, field_error_message};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn single_arg_is_known() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment argOnRequiredArg on Dog {
|
||||
doesKnowCommand(dogCommand: SIT)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_args_are_known() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment multipleArgs on ComplicatedArgs {
|
||||
multipleReqs(req1: 1, req2: 2)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_args_of_unknown_fields() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment argOnUnknownField on Dog {
|
||||
unknownField(unknownArg: SIT)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_args_in_reverse_order_are_known() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment multipleArgsReverseOrder on ComplicatedArgs {
|
||||
multipleReqs(req2: 2, req1: 1)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_args_on_optional_arg() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment noArgOnOptionalArg on Dog {
|
||||
isHousetrained
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn args_are_known_deeply() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog {
|
||||
doesKnowCommand(dogCommand: SIT)
|
||||
|
@ -154,67 +182,85 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn directive_args_are_known() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @skip(if: true)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undirective_args_are_invalid() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @skip(unless: true)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&directive_error_message("unless", "skip"),
|
||||
&[SourcePosition::new(35, 2, 22)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&directive_error_message("unless", "skip"),
|
||||
&[SourcePosition::new(35, 2, 22)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_arg_name() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidArgName on Dog {
|
||||
doesKnowCommand(unknown: true)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("unknown",
|
||||
"doesKnowCommand",
|
||||
"Dog"),
|
||||
&[SourcePosition::new(72, 2, 28)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("unknown", "doesKnowCommand", "Dog"),
|
||||
&[SourcePosition::new(72, 2, 28)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_args_amongst_known_args() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment oneGoodArgOneInvalidArg on Dog {
|
||||
doesKnowCommand(whoknows: 1, dogCommand: SIT, unknown: true)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("whoknows",
|
||||
"doesKnowCommand",
|
||||
"Dog"),
|
||||
&[SourcePosition::new(81, 2, 28)]),
|
||||
RuleError::new(&field_error_message("unknown",
|
||||
"doesKnowCommand",
|
||||
"Dog"),
|
||||
&[SourcePosition::new(111, 2, 58)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("whoknows", "doesKnowCommand", "Dog"),
|
||||
&[SourcePosition::new(81, 2, 28)],
|
||||
),
|
||||
RuleError::new(
|
||||
&field_error_message("unknown", "doesKnowCommand", "Dog"),
|
||||
&[SourcePosition::new(111, 2, 58)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_args_deeply() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog {
|
||||
doesKnowCommand(unknown: true)
|
||||
|
@ -228,13 +274,16 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("unknown",
|
||||
"doesKnowCommand",
|
||||
"Dog"),
|
||||
&[SourcePosition::new(61, 3, 30)]),
|
||||
RuleError::new(&field_error_message("unknown",
|
||||
"doesKnowCommand",
|
||||
"Dog"),
|
||||
&[SourcePosition::new(193, 8, 34)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("unknown", "doesKnowCommand", "Dog"),
|
||||
&[SourcePosition::new(61, 3, 30)],
|
||||
),
|
||||
RuleError::new(
|
||||
&field_error_message("unknown", "doesKnowCommand", "Dog"),
|
||||
&[SourcePosition::new(193, 8, 34)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ast::{Directive, Operation, OperationType, Fragment, FragmentSpread, Field, InlineFragment};
|
||||
use ast::{Directive, Field, Fragment, FragmentSpread, InlineFragment, Operation, OperationType};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
use schema::model::DirectiveLocation;
|
||||
use parser::Spanning;
|
||||
|
@ -8,23 +8,28 @@ pub struct KnownDirectives {
|
|||
}
|
||||
|
||||
pub fn factory() -> KnownDirectives {
|
||||
KnownDirectives { location_stack: Vec::new() }
|
||||
KnownDirectives {
|
||||
location_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for KnownDirectives {
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
self.location_stack
|
||||
.push(match op.item.operation_type {
|
||||
OperationType::Query => DirectiveLocation::Query,
|
||||
OperationType::Mutation => DirectiveLocation::Mutation,
|
||||
});
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
self.location_stack.push(match op.item.operation_type {
|
||||
OperationType::Query => DirectiveLocation::Query,
|
||||
OperationType::Mutation => DirectiveLocation::Mutation,
|
||||
});
|
||||
}
|
||||
|
||||
fn exit_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>) {
|
||||
fn exit_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>,
|
||||
) {
|
||||
let top = self.location_stack.pop();
|
||||
assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation));
|
||||
}
|
||||
|
@ -38,65 +43,84 @@ impl<'a> Visitor<'a> for KnownDirectives {
|
|||
assert_eq!(top, Some(DirectiveLocation::Field));
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.location_stack
|
||||
.push(DirectiveLocation::FragmentDefinition);
|
||||
}
|
||||
|
||||
fn exit_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>) {
|
||||
fn exit_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>,
|
||||
) {
|
||||
let top = self.location_stack.pop();
|
||||
assert_eq!(top, Some(DirectiveLocation::FragmentDefinition));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
self.location_stack.push(DirectiveLocation::FragmentSpread);
|
||||
}
|
||||
|
||||
fn exit_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>) {
|
||||
fn exit_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
let top = self.location_stack.pop();
|
||||
assert_eq!(top, Some(DirectiveLocation::FragmentSpread));
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
self.location_stack.push(DirectiveLocation::InlineFragment);
|
||||
}
|
||||
|
||||
fn exit_inline_fragment(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>) {
|
||||
fn exit_inline_fragment(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
let top = self.location_stack.pop();
|
||||
assert_eq!(top, Some(DirectiveLocation::InlineFragment));
|
||||
}
|
||||
|
||||
fn enter_directive(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>) {
|
||||
fn enter_directive(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>,
|
||||
) {
|
||||
let directive_name = &directive.item.name.item;
|
||||
|
||||
if let Some(directive_type) = ctx.schema.directive_by_name(directive_name) {
|
||||
if let Some(current_location) = self.location_stack.last() {
|
||||
if directive_type
|
||||
.locations
|
||||
.iter()
|
||||
.find(|l| l == ¤t_location)
|
||||
.is_none() {
|
||||
ctx.report_error(&misplaced_error_message(directive_name, current_location),
|
||||
&[directive.start.clone()]);
|
||||
.locations
|
||||
.iter()
|
||||
.find(|l| l == ¤t_location)
|
||||
.is_none()
|
||||
{
|
||||
ctx.report_error(
|
||||
&misplaced_error_message(directive_name, current_location),
|
||||
&[directive.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ctx.report_error(&unknown_error_message(directive_name),
|
||||
&[directive.start.clone()]);
|
||||
ctx.report_error(
|
||||
&unknown_error_message(directive_name),
|
||||
&[directive.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -106,21 +130,26 @@ fn unknown_error_message(directive_name: &str) -> String {
|
|||
}
|
||||
|
||||
fn misplaced_error_message(directive_name: &str, location: &DirectiveLocation) -> String {
|
||||
format!(r#"Directive "{}" may not be used on {}"#, directive_name, location)
|
||||
format!(
|
||||
r#"Directive "{}" may not be used on {}"#,
|
||||
directive_name,
|
||||
location
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{unknown_error_message, misplaced_error_message, factory};
|
||||
use super::{factory, misplaced_error_message, unknown_error_message};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
use schema::model::DirectiveLocation;
|
||||
|
||||
#[test]
|
||||
fn with_no_directives() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
name
|
||||
...Frag
|
||||
|
@ -129,13 +158,15 @@ mod tests {
|
|||
fragment Frag on Dog {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_known_directives() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @include(if: true) {
|
||||
name
|
||||
|
@ -144,27 +175,35 @@ mod tests {
|
|||
name
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_unknown_directive() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @unknown(directive: "value") {
|
||||
name
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(29, 2, 16)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(29, 2, 16)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_many_unknown_directives() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @unknown(directive: "value") {
|
||||
name
|
||||
|
@ -177,18 +216,28 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(29, 2, 16)]),
|
||||
RuleError::new(&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(111, 5, 18)]),
|
||||
RuleError::new(&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(180, 7, 19)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(29, 2, 16)],
|
||||
),
|
||||
RuleError::new(
|
||||
&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(111, 5, 18)],
|
||||
),
|
||||
RuleError::new(
|
||||
&unknown_error_message("unknown"),
|
||||
&[SourcePosition::new(180, 7, 19)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_well_placed_directives() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo @onQuery {
|
||||
name @include(if: true)
|
||||
...Frag @include(if: true)
|
||||
|
@ -199,12 +248,15 @@ mod tests {
|
|||
mutation Bar @onMutation {
|
||||
someField
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_misplaced_directives() {
|
||||
expect_fails_rule(factory, r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo @include(if: true) {
|
||||
name @onQuery
|
||||
...Frag @onQuery
|
||||
|
@ -215,18 +267,23 @@ mod tests {
|
|||
}
|
||||
"#,
|
||||
&[
|
||||
RuleError::new(&misplaced_error_message("include", &DirectiveLocation::Query), &[
|
||||
SourcePosition::new(21, 1, 20),
|
||||
]),
|
||||
RuleError::new(&misplaced_error_message("onQuery", &DirectiveLocation::Field), &[
|
||||
SourcePosition::new(59, 2, 17),
|
||||
]),
|
||||
RuleError::new(&misplaced_error_message("onQuery", &DirectiveLocation::FragmentSpread), &[
|
||||
SourcePosition::new(88, 3, 20),
|
||||
]),
|
||||
RuleError::new(&misplaced_error_message("onQuery", &DirectiveLocation::Mutation), &[
|
||||
SourcePosition::new(133, 6, 23),
|
||||
]),
|
||||
]);
|
||||
RuleError::new(
|
||||
&misplaced_error_message("include", &DirectiveLocation::Query),
|
||||
&[SourcePosition::new(21, 1, 20)],
|
||||
),
|
||||
RuleError::new(
|
||||
&misplaced_error_message("onQuery", &DirectiveLocation::Field),
|
||||
&[SourcePosition::new(59, 2, 17)],
|
||||
),
|
||||
RuleError::new(
|
||||
&misplaced_error_message("onQuery", &DirectiveLocation::FragmentSpread),
|
||||
&[SourcePosition::new(88, 3, 20)],
|
||||
),
|
||||
RuleError::new(
|
||||
&misplaced_error_message("onQuery", &DirectiveLocation::Mutation),
|
||||
&[SourcePosition::new(133, 6, 23)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,13 +9,17 @@ pub fn factory() -> KnownFragmentNames {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for KnownFragmentNames {
|
||||
fn enter_fragment_spread(&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
let spread_name = &spread.item.name;
|
||||
if !context.is_known_fragment(spread_name.item) {
|
||||
context.report_error(&error_message(spread_name.item),
|
||||
&[spread_name.start.clone()]);
|
||||
context.report_error(
|
||||
&error_message(spread_name.item),
|
||||
&[spread_name.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -29,12 +33,13 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn known() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
human(id: 4) {
|
||||
...HumanFields1
|
||||
|
@ -56,13 +61,15 @@ mod tests {
|
|||
fragment HumanFields3 on Human {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
human(id: 4) {
|
||||
...UnknownFragment1
|
||||
|
@ -76,11 +83,20 @@ mod tests {
|
|||
...UnknownFragment3
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("UnknownFragment1"),
|
||||
&[SourcePosition::new(57, 3, 17)]),
|
||||
RuleError::new(&error_message("UnknownFragment2"),
|
||||
&[SourcePosition::new(122, 5, 19)]),
|
||||
RuleError::new(&error_message("UnknownFragment3"),
|
||||
&[SourcePosition::new(255, 11, 15)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("UnknownFragment1"),
|
||||
&[SourcePosition::new(57, 3, 17)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("UnknownFragment2"),
|
||||
&[SourcePosition::new(122, 5, 19)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("UnknownFragment3"),
|
||||
&[SourcePosition::new(255, 11, 15)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,24 +9,30 @@ pub fn factory() -> KnownTypeNames {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for KnownTypeNames {
|
||||
fn enter_inline_fragment(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<InlineFragment>) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
if let Some(ref type_cond) = fragment.item.type_condition {
|
||||
validate_type(ctx, type_cond.item, &type_cond.start);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>,
|
||||
) {
|
||||
let type_cond = &fragment.item.type_condition;
|
||||
validate_type(ctx, type_cond.item, &type_cond.start);
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(_, ref var_def): &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(_, ref var_def): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
let type_name = var_def.var_type.item.innermost_name();
|
||||
validate_type(ctx, type_name, &var_def.var_type.start);
|
||||
}
|
||||
|
@ -47,12 +53,13 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn known_type_names_are_valid() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($var: String, $required: [String!]!) {
|
||||
user(id: 4) {
|
||||
pets { ... on Pet { name }, ...PetFields, ... { name } }
|
||||
|
@ -61,13 +68,15 @@ mod tests {
|
|||
fragment PetFields on Pet {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unknown_type_names_are_invalid() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($var: JumbledUpLetters) {
|
||||
user(id: 4) {
|
||||
name
|
||||
|
@ -78,11 +87,14 @@ mod tests {
|
|||
name
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("JumbledUpLetters"),
|
||||
&[SourcePosition::new(27, 1, 26)]),
|
||||
RuleError::new(&error_message("Badger"),
|
||||
&[SourcePosition::new(120, 4, 28)]),
|
||||
RuleError::new(&error_message("Peettt"),
|
||||
&[SourcePosition::new(210, 7, 32)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("JumbledUpLetters"),
|
||||
&[SourcePosition::new(27, 1, 26)],
|
||||
),
|
||||
RuleError::new(&error_message("Badger"), &[SourcePosition::new(120, 4, 28)]),
|
||||
RuleError::new(&error_message("Peettt"), &[SourcePosition::new(210, 7, 32)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,22 +7,28 @@ pub struct LoneAnonymousOperation {
|
|||
}
|
||||
|
||||
pub fn factory() -> LoneAnonymousOperation {
|
||||
LoneAnonymousOperation { operation_count: None }
|
||||
LoneAnonymousOperation {
|
||||
operation_count: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for LoneAnonymousOperation {
|
||||
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, doc: &'a Document) {
|
||||
self.operation_count = Some(doc.iter()
|
||||
.filter(|d| match **d {
|
||||
Definition::Operation(_) => true,
|
||||
Definition::Fragment(_) => false,
|
||||
})
|
||||
.count());
|
||||
self.operation_count = Some(
|
||||
doc.iter()
|
||||
.filter(|d| match **d {
|
||||
Definition::Operation(_) => true,
|
||||
Definition::Fragment(_) => false,
|
||||
})
|
||||
.count(),
|
||||
);
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
if let Some(operation_count) = self.operation_count {
|
||||
if operation_count > 1 && op.item.name.is_none() {
|
||||
ctx.report_error(error_message(), &[op.start.clone()]);
|
||||
|
@ -40,32 +46,37 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn no_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_anon_operation() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_named_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
field
|
||||
}
|
||||
|
@ -73,26 +84,30 @@ mod tests {
|
|||
query Bar {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anon_operation_with_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...Foo
|
||||
}
|
||||
fragment Foo on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_anon_operations() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
fieldA
|
||||
}
|
||||
|
@ -100,14 +115,18 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(error_message(), &[SourcePosition::new(54, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(error_message(), &[SourcePosition::new(54, 4, 10)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn anon_operation_with_a_mutation() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
fieldA
|
||||
}
|
||||
|
@ -115,6 +134,9 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ mod variables_are_input_types;
|
|||
mod variables_in_allowed_position;
|
||||
|
||||
use ast::Document;
|
||||
use validation::{ValidatorContext, MultiVisitor, MultiVisitorNil, visit};
|
||||
use validation::{visit, MultiVisitor, MultiVisitorNil, ValidatorContext};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn visit_all_rules<'a>(ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use ast::{Fragment, FragmentSpread, Document};
|
||||
use validation::{ValidatorContext, Visitor, RuleError};
|
||||
use ast::{Document, Fragment, FragmentSpread};
|
||||
use validation::{RuleError, ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
|
||||
pub struct NoFragmentCycles<'a> {
|
||||
|
@ -46,9 +46,11 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
|
|||
ctx.append_errors(detector.errors);
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>,
|
||||
) {
|
||||
assert!(self.current_fragment.is_none());
|
||||
|
||||
let fragment_name = &fragment.item.name.item;
|
||||
|
@ -56,23 +58,29 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
|
|||
self.fragment_order.push(fragment_name);
|
||||
}
|
||||
|
||||
fn exit_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>) {
|
||||
fn exit_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>,
|
||||
) {
|
||||
assert_eq!(Some(fragment.item.name.item), self.current_fragment);
|
||||
self.current_fragment = None;
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let Some(current_fragment) = self.current_fragment {
|
||||
self.spreads
|
||||
.entry(current_fragment)
|
||||
.or_insert_with(Vec::new)
|
||||
.push(Spanning::start_end(&spread.start.clone(),
|
||||
&spread.end.clone(),
|
||||
spread.item.name.item));
|
||||
.push(Spanning::start_end(
|
||||
&spread.start.clone(),
|
||||
&spread.end.clone(),
|
||||
spread.item.name.item,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,8 +106,10 @@ impl<'a> CycleDetector<'a> {
|
|||
node
|
||||
};
|
||||
|
||||
self.errors
|
||||
.push(RuleError::new(&error_message(name), &[err_pos.start.clone()]));
|
||||
self.errors.push(RuleError::new(
|
||||
&error_message(name),
|
||||
&[err_pos.start.clone()],
|
||||
));
|
||||
} else if !self.visited.contains(name) {
|
||||
path.push(node);
|
||||
self.detect_from(name, path);
|
||||
|
@ -120,40 +130,47 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn single_reference_is_valid() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB }
|
||||
fragment fragB on Dog { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spreading_twice_is_not_circular() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB, ...fragB }
|
||||
fragment fragB on Dog { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spreading_twice_indirectly_is_not_circular() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB, ...fragC }
|
||||
fragment fragB on Dog { ...fragC }
|
||||
fragment fragC on Dog { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn double_spread_within_abstract_types() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment nameFragment on Pet {
|
||||
... on Dog { name }
|
||||
... on Cat { name }
|
||||
|
@ -163,79 +180,98 @@ mod tests {
|
|||
... on Dog { ...nameFragment }
|
||||
... on Cat { ...nameFragment }
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn does_not_false_positive_on_unknown_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment nameFragment on Pet {
|
||||
...UnknownFragment
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn spreading_recursively_within_field_fails() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Human { relatives { ...fragA } },
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(49, 1, 48)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(49, 1, 48)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_directly() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragA }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_directly_within_inline_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Pet {
|
||||
... on Dog {
|
||||
...fragA
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(74, 3, 14)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(74, 3, 14)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_indirectly() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB }
|
||||
fragment fragB on Dog { ...fragA }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_indirectly_reports_opposite_order() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragB on Dog { ...fragA }
|
||||
fragment fragA on Dog { ...fragB }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragB"),
|
||||
&[SourcePosition::new(35, 1, 34)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragB"), &[SourcePosition::new(35, 1, 34)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_indirectly_within_inline_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Pet {
|
||||
... on Dog {
|
||||
...fragB
|
||||
|
@ -247,14 +283,17 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(74, 3, 14)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(74, 3, 14)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_deeply() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB }
|
||||
fragment fragB on Dog { ...fragC }
|
||||
fragment fragC on Dog { ...fragO }
|
||||
|
@ -264,53 +303,59 @@ mod tests {
|
|||
fragment fragO on Dog { ...fragP }
|
||||
fragment fragP on Dog { ...fragA, ...fragX }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragO"),
|
||||
&[SourcePosition::new(305, 7, 34)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragO"), &[SourcePosition::new(305, 7, 34)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_deeply_two_paths() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB, ...fragC }
|
||||
fragment fragB on Dog { ...fragA }
|
||||
fragment fragC on Dog { ...fragA }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(45, 1, 44)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(45, 1, 44)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_deeply_two_paths_alt_traversal_order() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragC }
|
||||
fragment fragB on Dog { ...fragC }
|
||||
fragment fragC on Dog { ...fragA, ...fragB }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragC"),
|
||||
&[SourcePosition::new(135, 3, 44)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragC"), &[SourcePosition::new(135, 3, 44)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_spreading_itself_deeply_and_immediately() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Dog { ...fragB }
|
||||
fragment fragB on Dog { ...fragB, ...fragC }
|
||||
fragment fragC on Dog { ...fragA, ...fragB }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("fragA"),
|
||||
&[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragB"),
|
||||
&[SourcePosition::new(80, 2, 34)]),
|
||||
RuleError::new(&error_message("fragB"),
|
||||
&[SourcePosition::new(90, 2, 44)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("fragA"), &[SourcePosition::new(35, 1, 34)]),
|
||||
RuleError::new(&error_message("fragB"), &[SourcePosition::new(80, 2, 34)]),
|
||||
RuleError::new(&error_message("fragB"), &[SourcePosition::new(90, 2, 44)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{HashSet, HashMap};
|
||||
use ast::{Document, Fragment, FragmentSpread, VariableDefinition, Operation, InputValue};
|
||||
use validation::{ValidatorContext, Visitor, RuleError};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use ast::{Document, Fragment, FragmentSpread, InputValue, Operation, VariableDefinition};
|
||||
use validation::{RuleError, ValidatorContext, Visitor};
|
||||
use parser::{SourcePosition, Spanning};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -26,11 +26,13 @@ pub fn factory<'a>() -> NoUndefinedVariables<'a> {
|
|||
}
|
||||
|
||||
impl<'a> NoUndefinedVariables<'a> {
|
||||
fn find_undef_vars(&'a self,
|
||||
scope: &Scope<'a>,
|
||||
defined: &HashSet<&'a str>,
|
||||
unused: &mut Vec<&'a Spanning<&'a str>>,
|
||||
visited: &mut HashSet<Scope<'a>>) {
|
||||
fn find_undef_vars(
|
||||
&'a self,
|
||||
scope: &Scope<'a>,
|
||||
defined: &HashSet<&'a str>,
|
||||
unused: &mut Vec<&'a Spanning<&'a str>>,
|
||||
visited: &mut HashSet<Scope<'a>>,
|
||||
) {
|
||||
if visited.contains(scope) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,39 +60,51 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
|
|||
for (op_name, &(ref pos, ref def_vars)) in &self.defined_variables {
|
||||
let mut unused = Vec::new();
|
||||
let mut visited = HashSet::new();
|
||||
self.find_undef_vars(&Scope::Operation(*op_name),
|
||||
def_vars,
|
||||
&mut unused,
|
||||
&mut visited);
|
||||
self.find_undef_vars(
|
||||
&Scope::Operation(*op_name),
|
||||
def_vars,
|
||||
&mut unused,
|
||||
&mut visited,
|
||||
);
|
||||
|
||||
ctx.append_errors(unused
|
||||
.into_iter()
|
||||
.map(|var| {
|
||||
RuleError::new(&error_message(var.item, *op_name),
|
||||
&[var.start.clone(), pos.clone()])
|
||||
})
|
||||
.collect());
|
||||
ctx.append_errors(
|
||||
unused
|
||||
.into_iter()
|
||||
.map(|var| {
|
||||
RuleError::new(
|
||||
&error_message(var.item, *op_name),
|
||||
&[var.start.clone(), pos.clone()],
|
||||
)
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
let op_name = op.item.name.as_ref().map(|s| s.item);
|
||||
self.current_scope = Some(Scope::Operation(op_name));
|
||||
self.defined_variables
|
||||
.insert(op_name, (op.start.clone(), HashSet::new()));
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.current_scope = Some(Scope::Fragment(f.item.name.item));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.spreads
|
||||
.entry(scope.clone())
|
||||
|
@ -99,9 +113,11 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
if let Some(Scope::Operation(ref name)) = self.current_scope {
|
||||
if let Some(&mut (_, ref mut vars)) = self.defined_variables.get_mut(name) {
|
||||
vars.insert(var_name.item);
|
||||
|
@ -109,30 +125,34 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.used_variables
|
||||
.entry(scope.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.append(&mut value
|
||||
.item
|
||||
.referenced_variables()
|
||||
.iter()
|
||||
.map(|&var_name| {
|
||||
Spanning::start_end(&value.start.clone(),
|
||||
&value.end.clone(),
|
||||
var_name)
|
||||
})
|
||||
.collect());
|
||||
.item
|
||||
.referenced_variables()
|
||||
.iter()
|
||||
.map(|&var_name| {
|
||||
Spanning::start_end(&value.start.clone(), &value.end.clone(), var_name)
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error_message(var_name: &str, op_name: Option<&str>) -> String {
|
||||
if let Some(op_name) = op_name {
|
||||
format!(r#"Variable "${}" is not defined by operation "{}""#, var_name, op_name)
|
||||
format!(
|
||||
r#"Variable "${}" is not defined by operation "{}""#,
|
||||
var_name,
|
||||
op_name
|
||||
)
|
||||
} else {
|
||||
format!(r#"Variable "${}" is not defined"#, var_name)
|
||||
}
|
||||
|
@ -143,22 +163,25 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn all_variables_defined() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
field(a: $a, b: $b, c: $c)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variables_deeply_defined() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
field(a: $a) {
|
||||
field(b: $b) {
|
||||
|
@ -166,13 +189,15 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variables_deeply_defined_in_inline_fragments_defined() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
... on Type {
|
||||
field(a: $a) {
|
||||
|
@ -184,13 +209,15 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_variables_in_fragments_deeply_defined() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -207,13 +234,15 @@ mod tests {
|
|||
fragment FragC on Type {
|
||||
field(c: $c)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_within_single_fragment_defined_in_multiple_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -223,13 +252,15 @@ mod tests {
|
|||
fragment FragA on Type {
|
||||
field(a: $a)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_within_fragments_defined_in_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -242,13 +273,15 @@ mod tests {
|
|||
fragment FragB on Type {
|
||||
field(b: $b)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_within_recursive_fragment_defined() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -257,55 +290,85 @@ mod tests {
|
|||
...FragA
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_defined() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
field(a: $a, b: $b, c: $c, d: $d)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("d", Some("Foo")),
|
||||
&[SourcePosition::new(101, 2, 42),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("d", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(101, 2, 42),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_defined_by_unnamed_query() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(a: $a)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", None),
|
||||
&[SourcePosition::new(34, 2, 21),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", None),
|
||||
&[
|
||||
SourcePosition::new(34, 2, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_variables_not_defined() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
field(a: $a, b: $b, c: $c)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(56, 2, 21),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(70, 2, 35),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(56, 2, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(70, 2, 35),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_in_fragment_not_defined_by_unnamed_query() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...FragA
|
||||
}
|
||||
|
@ -313,15 +376,23 @@ mod tests {
|
|||
field(a: $a)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", None),
|
||||
&[SourcePosition::new(102, 5, 21),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", None),
|
||||
&[
|
||||
SourcePosition::new(102, 5, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_in_fragment_not_defined_by_operation() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -339,15 +410,23 @@ mod tests {
|
|||
field(c: $c)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(358, 15, 21),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(358, 15, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_variables_in_fragments_not_defined() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -365,18 +444,30 @@ mod tests {
|
|||
field(c: $c)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(124, 5, 21),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(346, 15, 21),
|
||||
SourcePosition::new(11, 1, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(124, 5, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(346, 15, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_variable_in_fragment_not_defined_by_multiple_operations() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragAB
|
||||
}
|
||||
|
@ -387,18 +478,30 @@ mod tests {
|
|||
field(a: $a, b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("b", Some("Foo")),
|
||||
&[SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("b", Some("Bar")),
|
||||
&[SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(79, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(79, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variables_in_fragment_not_defined_by_multiple_operations() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragAB
|
||||
}
|
||||
|
@ -409,18 +512,30 @@ mod tests {
|
|||
field(a: $a, b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(194, 8, 21),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("b", Some("Bar")),
|
||||
&[SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(79, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(194, 8, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(201, 8, 28),
|
||||
SourcePosition::new(79, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_in_fragment_used_by_other_operation() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -434,18 +549,30 @@ mod tests {
|
|||
field(b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(191, 8, 21),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("b", Some("Bar")),
|
||||
&[SourcePosition::new(263, 11, 21),
|
||||
SourcePosition::new(78, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(191, 8, 21),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(263, 11, 21),
|
||||
SourcePosition::new(78, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_undefined_variables_produce_multiple_errors() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragAB
|
||||
}
|
||||
|
@ -461,23 +588,50 @@ mod tests {
|
|||
field2(c: $c)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(195, 8, 22),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("b", Some("Bar")),
|
||||
&[SourcePosition::new(202, 8, 29),
|
||||
SourcePosition::new(79, 4, 10)]),
|
||||
RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(249, 10, 22),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("b", Some("Bar")),
|
||||
&[SourcePosition::new(256, 10, 29),
|
||||
SourcePosition::new(79, 4, 10)]),
|
||||
RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(329, 13, 22),
|
||||
SourcePosition::new(11, 1, 10)]),
|
||||
RuleError::new(&error_message("c", Some("Bar")),
|
||||
&[SourcePosition::new(329, 13, 22),
|
||||
SourcePosition::new(79, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(195, 8, 22),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(202, 8, 29),
|
||||
SourcePosition::new(79, 4, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(249, 10, 22),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(256, 10, 29),
|
||||
SourcePosition::new(79, 4, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[
|
||||
SourcePosition::new(329, 13, 22),
|
||||
SourcePosition::new(11, 1, 10),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Bar")),
|
||||
&[
|
||||
SourcePosition::new(329, 13, 22),
|
||||
SourcePosition::new(79, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{HashSet, HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use ast::{Document, Definition, Operation, Fragment, FragmentSpread};
|
||||
use ast::{Definition, Document, Fragment, FragmentSpread, Operation};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
|
||||
|
@ -47,7 +47,11 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
|
|||
let mut reachable = HashSet::new();
|
||||
|
||||
for def in defs {
|
||||
if let Definition::Operation(Spanning { item: Operation { ref name, .. }, .. }) = *def {
|
||||
if let Definition::Operation(Spanning {
|
||||
item: Operation { ref name, .. },
|
||||
..
|
||||
}) = *def
|
||||
{
|
||||
let op_name = name.as_ref().map(|s| s.item);
|
||||
self.find_reachable_fragments(&Scope::Operation(op_name), &mut reachable);
|
||||
}
|
||||
|
@ -60,24 +64,30 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
let op_name = op.item.name.as_ref().map(|s| s.item.as_ref());
|
||||
self.current_scope = Some(Scope::Operation(op_name));
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.current_scope = Some(Scope::Fragment(f.item.name.item));
|
||||
self.defined_fragments
|
||||
.insert(Spanning::start_end(&f.start, &f.end, f.item.name.item));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.spreads
|
||||
.entry(scope.clone())
|
||||
|
@ -96,12 +106,13 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn all_fragment_names_are_used() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
human(id: 4) {
|
||||
...HumanFields1
|
||||
|
@ -120,13 +131,15 @@ mod tests {
|
|||
fragment HumanFields3 on Human {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_fragment_names_are_used_by_multiple_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
human(id: 4) {
|
||||
...HumanFields1
|
||||
|
@ -147,13 +160,15 @@ mod tests {
|
|||
fragment HumanFields3 on Human {
|
||||
name
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contains_unknown_fragments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
human(id: 4) {
|
||||
...HumanFields1
|
||||
|
@ -181,16 +196,24 @@ mod tests {
|
|||
name
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("Unused1"),
|
||||
&[SourcePosition::new(465, 21, 10)]),
|
||||
RuleError::new(&error_message("Unused2"),
|
||||
&[SourcePosition::new(532, 24, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("Unused1"),
|
||||
&[SourcePosition::new(465, 21, 10)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("Unused2"),
|
||||
&[SourcePosition::new(532, 24, 10)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contains_unknown_fragments_with_ref_cycle() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
human(id: 4) {
|
||||
...HumanFields1
|
||||
|
@ -220,16 +243,24 @@ mod tests {
|
|||
...Unused1
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("Unused1"),
|
||||
&[SourcePosition::new(465, 21, 10)]),
|
||||
RuleError::new(&error_message("Unused2"),
|
||||
&[SourcePosition::new(555, 25, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("Unused1"),
|
||||
&[SourcePosition::new(465, 21, 10)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("Unused2"),
|
||||
&[SourcePosition::new(555, 25, 10)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contains_unknown_and_undef_fragments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
human(id: 4) {
|
||||
...bar
|
||||
|
@ -239,7 +270,9 @@ mod tests {
|
|||
name
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("foo"),
|
||||
&[SourcePosition::new(107, 6, 10)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("foo"), &[SourcePosition::new(107, 6, 10)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{HashSet, HashMap};
|
||||
use ast::{Document, Fragment, FragmentSpread, VariableDefinition, Operation, InputValue};
|
||||
use validation::{ValidatorContext, Visitor, RuleError};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use ast::{Document, Fragment, FragmentSpread, InputValue, Operation, VariableDefinition};
|
||||
use validation::{RuleError, ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -26,11 +26,13 @@ pub fn factory<'a>() -> NoUnusedVariables<'a> {
|
|||
}
|
||||
|
||||
impl<'a> NoUnusedVariables<'a> {
|
||||
fn find_used_vars(&self,
|
||||
from: &Scope<'a>,
|
||||
defined: &HashSet<&'a str>,
|
||||
used: &mut HashSet<&'a str>,
|
||||
visited: &mut HashSet<Scope<'a>>) {
|
||||
fn find_used_vars(
|
||||
&self,
|
||||
from: &Scope<'a>,
|
||||
defined: &HashSet<&'a str>,
|
||||
used: &mut HashSet<&'a str>,
|
||||
visited: &mut HashSet<Scope<'a>>,
|
||||
) {
|
||||
if visited.contains(from) {
|
||||
return;
|
||||
}
|
||||
|
@ -58,39 +60,48 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
|
|||
for (op_name, def_vars) in &self.defined_variables {
|
||||
let mut used = HashSet::new();
|
||||
let mut visited = HashSet::new();
|
||||
self.find_used_vars(&Scope::Operation(*op_name),
|
||||
&def_vars.iter().map(|def| def.item).collect(),
|
||||
&mut used,
|
||||
&mut visited);
|
||||
self.find_used_vars(
|
||||
&Scope::Operation(*op_name),
|
||||
&def_vars.iter().map(|def| def.item).collect(),
|
||||
&mut used,
|
||||
&mut visited,
|
||||
);
|
||||
|
||||
ctx.append_errors(def_vars
|
||||
.iter()
|
||||
.filter(|var| !used.contains(var.item))
|
||||
.map(|var| {
|
||||
RuleError::new(&error_message(var.item, *op_name),
|
||||
&[var.start.clone()])
|
||||
})
|
||||
.collect());
|
||||
ctx.append_errors(
|
||||
def_vars
|
||||
.iter()
|
||||
.filter(|var| !used.contains(var.item))
|
||||
.map(|var| {
|
||||
RuleError::new(&error_message(var.item, *op_name), &[var.start.clone()])
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
let op_name = op.item.name.as_ref().map(|s| s.item);
|
||||
self.current_scope = Some(Scope::Operation(op_name));
|
||||
self.defined_variables.insert(op_name, HashSet::new());
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.current_scope = Some(Scope::Fragment(f.item.name.item));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.spreads
|
||||
.entry(scope.clone())
|
||||
|
@ -99,9 +110,11 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
if let Some(Scope::Operation(ref name)) = self.current_scope {
|
||||
if let Some(vars) = self.defined_variables.get_mut(name) {
|
||||
vars.insert(var_name);
|
||||
|
@ -109,9 +122,11 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.used_variables
|
||||
.entry(scope.clone())
|
||||
|
@ -123,7 +138,11 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
|
|||
|
||||
fn error_message(var_name: &str, op_name: Option<&str>) -> String {
|
||||
if let Some(op_name) = op_name {
|
||||
format!(r#"Variable "${}" is not defined by operation "{}""#, var_name, op_name)
|
||||
format!(
|
||||
r#"Variable "${}" is not defined by operation "{}""#,
|
||||
var_name,
|
||||
op_name
|
||||
)
|
||||
} else {
|
||||
format!(r#"Variable "${}" is not defined"#, var_name)
|
||||
}
|
||||
|
@ -134,22 +153,25 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn uses_all_variables() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query ($a: String, $b: String, $c: String) {
|
||||
field(a: $a, b: $b, c: $c)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_all_variables_deeply() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
field(a: $a) {
|
||||
field(b: $b) {
|
||||
|
@ -157,13 +179,15 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_all_variables_deeply_in_inline_fragments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
... on Type {
|
||||
field(a: $a) {
|
||||
|
@ -175,13 +199,15 @@ mod tests {
|
|||
}
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uses_all_variables_in_fragments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -198,13 +224,15 @@ mod tests {
|
|||
fragment FragC on Type {
|
||||
field(c: $c)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_used_by_fragment_in_multiple_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -217,13 +245,15 @@ mod tests {
|
|||
fragment FragB on Type {
|
||||
field(b: $b)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_used_by_recursive_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -232,39 +262,52 @@ mod tests {
|
|||
...FragA
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_used() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query ($a: String, $b: String, $c: String) {
|
||||
field(a: $a, b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("c", None),
|
||||
&[SourcePosition::new(42, 1, 41)])]);
|
||||
&[
|
||||
RuleError::new(&error_message("c", None), &[SourcePosition::new(42, 1, 41)]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_variables_not_used_1() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
field(b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)]),
|
||||
RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_used_in_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -282,14 +325,20 @@ mod tests {
|
|||
field
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_variables_not_used_2() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: String, $c: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -307,16 +356,24 @@ mod tests {
|
|||
field
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)]),
|
||||
RuleError::new(&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", Some("Foo")),
|
||||
&[SourcePosition::new(45, 1, 44)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_used_by_unreferenced_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -327,14 +384,20 @@ mod tests {
|
|||
field(b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("b", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn variable_not_used_by_fragment_used_by_other_operation() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($b: String) {
|
||||
...FragA
|
||||
}
|
||||
|
@ -348,9 +411,16 @@ mod tests {
|
|||
field(b: $b)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("b", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)]),
|
||||
RuleError::new(&error_message("a", Some("Bar")),
|
||||
&[SourcePosition::new(88, 4, 20)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("b", Some("Foo")),
|
||||
&[SourcePosition::new(21, 1, 20)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("a", Some("Bar")),
|
||||
&[SourcePosition::new(88, 4, 20)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,5 +1,5 @@
|
|||
use std::collections::HashMap;
|
||||
use ast::{Document, Definition, InlineFragment, FragmentSpread};
|
||||
use ast::{Definition, Document, FragmentSpread, InlineFragment};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
use schema::meta::MetaType;
|
||||
|
@ -9,7 +9,9 @@ pub struct PossibleFragmentSpreads<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> PossibleFragmentSpreads<'a> {
|
||||
PossibleFragmentSpreads { fragment_types: HashMap::new() }
|
||||
PossibleFragmentSpreads {
|
||||
fragment_types: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
|
||||
|
@ -23,34 +25,49 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
frag: &'a Spanning<InlineFragment>) {
|
||||
if let (Some(parent_type), Some(frag_type)) =
|
||||
(ctx.parent_type(),
|
||||
frag.item
|
||||
.type_condition
|
||||
.as_ref()
|
||||
.and_then(|s| ctx.schema.concrete_type_by_name(s.item))) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
frag: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
if let (Some(parent_type), Some(frag_type)) = (
|
||||
ctx.parent_type(),
|
||||
frag.item
|
||||
.type_condition
|
||||
.as_ref()
|
||||
.and_then(|s| ctx.schema.concrete_type_by_name(s.item)),
|
||||
) {
|
||||
if !ctx.schema.type_overlap(parent_type, frag_type) {
|
||||
ctx.report_error(&error_message(None,
|
||||
parent_type.name().unwrap_or("<unknown>"),
|
||||
frag_type.name().unwrap_or("<unknown>")),
|
||||
&[frag.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(
|
||||
None,
|
||||
parent_type.name().unwrap_or("<unknown>"),
|
||||
frag_type.name().unwrap_or("<unknown>"),
|
||||
),
|
||||
&[frag.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
if let (Some(parent_type), Some(frag_type)) =
|
||||
(ctx.parent_type(), self.fragment_types.get(spread.item.name.item)) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let (Some(parent_type), Some(frag_type)) = (
|
||||
ctx.parent_type(),
|
||||
self.fragment_types.get(spread.item.name.item),
|
||||
) {
|
||||
if !ctx.schema.type_overlap(parent_type, frag_type) {
|
||||
ctx.report_error(&error_message(Some(spread.item.name.item),
|
||||
parent_type.name().unwrap_or("<unknown>"),
|
||||
frag_type.name().unwrap_or("<unknown>")),
|
||||
&[spread.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(
|
||||
Some(spread.item.name.item),
|
||||
parent_type.name().unwrap_or("<unknown>"),
|
||||
frag_type.name().unwrap_or("<unknown>"),
|
||||
),
|
||||
&[spread.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,13 +77,18 @@ fn error_message(frag_name: Option<&str>, parent_type_name: &str, frag_type: &st
|
|||
if let Some(frag_name) = frag_name {
|
||||
format!(
|
||||
"Fragment \"{}\" cannot be spread here as objects of type \
|
||||
\"{}\" can never be of type \"{}\"",
|
||||
frag_name, parent_type_name, frag_type)
|
||||
\"{}\" can never be of type \"{}\"",
|
||||
frag_name,
|
||||
parent_type_name,
|
||||
frag_type
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"Fragment cannot be spread here as objects of type \"{}\" \
|
||||
can never be of type \"{}\"",
|
||||
parent_type_name, frag_type)
|
||||
can never be of type \"{}\"",
|
||||
parent_type_name,
|
||||
frag_type
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -75,242 +97,316 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn of_the_same_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectWithinObject on Dog { ...dogFragment }
|
||||
fragment dogFragment on Dog { barkVolume }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn of_the_same_object_with_inline_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_into_an_implemented_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectWithinInterface on Pet { ...dogFragment }
|
||||
fragment dogFragment on Dog { barkVolume }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_into_containing_union() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment objectWithinUnion on CatOrDog { ...dogFragment }
|
||||
fragment dogFragment on Dog { barkVolume }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_contained_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment unionWithinObject on Dog { ...catOrDogFragment }
|
||||
fragment catOrDogFragment on CatOrDog { __typename }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_overlapping_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment unionWithinInterface on Pet { ...catOrDogFragment }
|
||||
fragment catOrDogFragment on CatOrDog { __typename }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_overlapping_union() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
|
||||
fragment catOrDogFragment on CatOrDog { __typename }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_implemented_object() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceWithinObject on Dog { ...petFragment }
|
||||
fragment petFragment on Pet { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_overlapping_interface() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceWithinInterface on Pet { ...beingFragment }
|
||||
fragment beingFragment on Being { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_overlapping_interface_in_inline_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceWithinInterface on Pet { ... on Being { name } }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_overlapping_union() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment interfaceWithinUnion on CatOrDog { ...petFragment }
|
||||
fragment petFragment on Pet { name }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_object_into_object() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidObjectWithinObject on Cat { ...dogFragment }
|
||||
fragment dogFragment on Dog { barkVolume }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("dogFragment"), "Cat", "Dog"),
|
||||
&[SourcePosition::new(55, 1, 54)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("dogFragment"), "Cat", "Dog"),
|
||||
&[SourcePosition::new(55, 1, 54)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn different_object_into_object_in_inline_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidObjectWithinObjectAnon on Cat {
|
||||
... on Dog { barkVolume }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(None, "Cat", "Dog"),
|
||||
&[SourcePosition::new(71, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(None, "Cat", "Dog"),
|
||||
&[SourcePosition::new(71, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_into_not_implementing_interface() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidObjectWithinInterface on Pet { ...humanFragment }
|
||||
fragment humanFragment on Human { pets { name } }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("humanFragment"), "Pet", "Human"),
|
||||
&[SourcePosition::new(58, 1, 57)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("humanFragment"), "Pet", "Human"),
|
||||
&[SourcePosition::new(58, 1, 57)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_into_not_containing_union() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
|
||||
fragment humanFragment on Human { pets { name } }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("humanFragment"),
|
||||
"CatOrDog",
|
||||
"Human"),
|
||||
&[SourcePosition::new(59, 1, 58)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("humanFragment"), "CatOrDog", "Human"),
|
||||
&[SourcePosition::new(59, 1, 58)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_not_contained_object() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
|
||||
fragment catOrDogFragment on CatOrDog { __typename }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("catOrDogFragment"),
|
||||
"Human",
|
||||
"CatOrDog"),
|
||||
&[SourcePosition::new(56, 1, 55)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("catOrDogFragment"), "Human", "CatOrDog"),
|
||||
&[SourcePosition::new(56, 1, 55)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_non_overlapping_interface() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
|
||||
fragment humanOrAlienFragment on HumanOrAlien { __typename }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("humanOrAlienFragment"),
|
||||
"Pet",
|
||||
"HumanOrAlien"),
|
||||
&[SourcePosition::new(57, 1, 56)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("humanOrAlienFragment"), "Pet", "HumanOrAlien"),
|
||||
&[SourcePosition::new(57, 1, 56)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn union_into_non_overlapping_union() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
|
||||
fragment humanOrAlienFragment on HumanOrAlien { __typename }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("humanOrAlienFragment"),
|
||||
"CatOrDog",
|
||||
"HumanOrAlien"),
|
||||
&[SourcePosition::new(58, 1, 57)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("humanOrAlienFragment"), "CatOrDog", "HumanOrAlien"),
|
||||
&[SourcePosition::new(58, 1, 57)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_non_implementing_object() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
|
||||
fragment intelligentFragment on Intelligent { iq }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("intelligentFragment"),
|
||||
"Cat",
|
||||
"Intelligent"),
|
||||
&[SourcePosition::new(58, 1, 57)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("intelligentFragment"), "Cat", "Intelligent"),
|
||||
&[SourcePosition::new(58, 1, 57)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_non_overlapping_interface() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidInterfaceWithinInterface on Pet {
|
||||
...intelligentFragment
|
||||
}
|
||||
fragment intelligentFragment on Intelligent { iq }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("intelligentFragment"),
|
||||
"Pet",
|
||||
"Intelligent"),
|
||||
&[SourcePosition::new(73, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("intelligentFragment"), "Pet", "Intelligent"),
|
||||
&[SourcePosition::new(73, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_non_overlapping_interface_in_inline_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidInterfaceWithinInterfaceAnon on Pet {
|
||||
...on Intelligent { iq }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message(None, "Pet", "Intelligent"),
|
||||
&[SourcePosition::new(77, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(None, "Pet", "Intelligent"),
|
||||
&[SourcePosition::new(77, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_into_non_overlapping_union() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
|
||||
fragment petFragment on Pet { name }
|
||||
"#,
|
||||
&[RuleError::new(&error_message(Some("petFragment"),
|
||||
"HumanOrAlien",
|
||||
"Pet"),
|
||||
&[SourcePosition::new(66, 1, 65)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message(Some("petFragment"), "HumanOrAlien", "Pet"),
|
||||
&[SourcePosition::new(66, 1, 65)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use ast::{Field, Directive};
|
||||
use ast::{Directive, Field};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
use schema::meta::Field as FieldType;
|
||||
|
@ -14,44 +14,62 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
|||
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) {
|
||||
let field_name = &field.item.name.item;
|
||||
|
||||
if let Some(&FieldType { arguments: Some(ref meta_args), .. }) =
|
||||
ctx.parent_type().and_then(|t| t.field_by_name(field_name)) {
|
||||
if let Some(&FieldType {
|
||||
arguments: Some(ref meta_args),
|
||||
..
|
||||
}) = ctx.parent_type().and_then(|t| t.field_by_name(field_name))
|
||||
{
|
||||
for meta_arg in meta_args {
|
||||
if meta_arg.arg_type.is_non_null() &&
|
||||
field
|
||||
.item
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.item.get(&meta_arg.name))
|
||||
.is_none() {
|
||||
ctx.report_error(&field_error_message(field_name,
|
||||
&meta_arg.name,
|
||||
&format!("{}", meta_arg.arg_type)),
|
||||
&[field.start.clone()]);
|
||||
field
|
||||
.item
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.item.get(&meta_arg.name))
|
||||
.is_none()
|
||||
{
|
||||
ctx.report_error(
|
||||
&field_error_message(
|
||||
field_name,
|
||||
&meta_arg.name,
|
||||
&format!("{}", meta_arg.arg_type),
|
||||
),
|
||||
&[field.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn enter_directive(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>) {
|
||||
fn enter_directive(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directive: &'a Spanning<Directive>,
|
||||
) {
|
||||
let directive_name = &directive.item.name.item;
|
||||
|
||||
if let Some(&DirectiveType { arguments: ref meta_args, .. }) =
|
||||
ctx.schema.directive_by_name(directive_name) {
|
||||
if let Some(&DirectiveType {
|
||||
arguments: ref meta_args,
|
||||
..
|
||||
}) = ctx.schema.directive_by_name(directive_name)
|
||||
{
|
||||
for meta_arg in meta_args {
|
||||
if meta_arg.arg_type.is_non_null() &&
|
||||
directive
|
||||
.item
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.item.get(&meta_arg.name))
|
||||
.is_none() {
|
||||
ctx.report_error(&directive_error_message(directive_name,
|
||||
&meta_arg.name,
|
||||
&format!("{}", meta_arg.arg_type)),
|
||||
&[directive.start.clone()]);
|
||||
directive
|
||||
.item
|
||||
.arguments
|
||||
.as_ref()
|
||||
.and_then(|args| args.item.get(&meta_arg.name))
|
||||
.is_none()
|
||||
{
|
||||
ctx.report_error(
|
||||
&directive_error_message(
|
||||
directive_name,
|
||||
&meta_arg.name,
|
||||
&format!("{}", meta_arg.arg_type),
|
||||
),
|
||||
&[directive.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,212 +79,263 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
|
|||
fn field_error_message(field_name: &str, arg_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
r#"Field "{}" argument "{}" of type "{}" is required but not provided"#,
|
||||
field_name, arg_name, type_name)
|
||||
field_name,
|
||||
arg_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
fn directive_error_message(directive_name: &str, arg_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
r#"Directive "@{}" argument "{}" of type "{}" is required but not provided"#,
|
||||
directive_name, arg_name, type_name)
|
||||
directive_name,
|
||||
arg_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{field_error_message, directive_error_message, factory};
|
||||
use super::{directive_error_message, factory, field_error_message};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn ignores_unknown_arguments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog {
|
||||
isHousetrained(unknownArgument: true)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arg_on_optional_arg() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog {
|
||||
isHousetrained(atOtherHomes: true)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_arg_on_optional_arg() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog {
|
||||
isHousetrained
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_args() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleReqs(req1: 1, req2: 2)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_args_reverse_order() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleReqs(req2: 2, req1: 1)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_args_on_multiple_optional() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOpts
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_arg_on_multiple_optional() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOpts(opt1: 1)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn second_arg_on_multiple_optional() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOpts(opt2: 1)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn muliple_reqs_on_mixed_list() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOptAndReq(req1: 3, req2: 4)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_reqs_and_one_opt_on_mixed_list() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn all_reqs_on_opts_on_mixed_list() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_one_non_nullable_argument() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleReqs(req2: 2)
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("multipleReqs", "req1", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_multiple_non_nullable_arguments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleReqs
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)]),
|
||||
RuleError::new(&field_error_message("multipleReqs", "req2", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("multipleReqs", "req1", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)],
|
||||
),
|
||||
RuleError::new(
|
||||
&field_error_message("multipleReqs", "req2", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn incorrect_value_and_missing_argument() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
complicatedArgs {
|
||||
multipleReqs(req1: "one")
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&field_error_message("multipleReqs", "req2", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&field_error_message("multipleReqs", "req2", "Int!"),
|
||||
&[SourcePosition::new(63, 3, 16)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_unknown_directives() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @unknown
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_directives_of_valid_types() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @include(if: true) {
|
||||
name
|
||||
|
@ -275,22 +344,31 @@ mod tests {
|
|||
name
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn with_directive_with_missing_types() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
dog @include {
|
||||
name @skip
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&directive_error_message("include", "if", "Boolean!"),
|
||||
&[SourcePosition::new(33, 2, 18)]),
|
||||
RuleError::new(&directive_error_message("skip", "if", "Boolean!"),
|
||||
&[SourcePosition::new(65, 3, 21)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&directive_error_message("include", "if", "Boolean!"),
|
||||
&[SourcePosition::new(33, 2, 18)],
|
||||
),
|
||||
RuleError::new(
|
||||
&directive_error_message("skip", "if", "Boolean!"),
|
||||
&[SourcePosition::new(65, 3, 21)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::Field;
|
||||
use validation::{ValidatorContext, Visitor, RuleError};
|
||||
use validation::{RuleError, ValidatorContext, Visitor};
|
||||
use parser::Spanning;
|
||||
|
||||
pub struct ScalarLeafs {}
|
||||
|
@ -13,14 +13,17 @@ impl<'a> Visitor<'a> for ScalarLeafs {
|
|||
let field_name = &field.item.name.item;
|
||||
|
||||
let error = if let (Some(field_type), Some(field_type_literal)) =
|
||||
(ctx.current_type(), ctx.current_type_literal()) {
|
||||
(ctx.current_type(), ctx.current_type_literal())
|
||||
{
|
||||
match (field_type.is_leaf(), &field.item.selection_set) {
|
||||
(true, &Some(_)) => Some(RuleError::new(
|
||||
&no_allowed_error_message(field_name, &format!("{}", field_type_literal)),
|
||||
&[field.start.clone()])),
|
||||
&[field.start.clone()],
|
||||
)),
|
||||
(false, &None) => Some(RuleError::new(
|
||||
&required_error_message(field_name, &format!("{}", field_type_literal)),
|
||||
&[field.start.clone()])),
|
||||
&[field.start.clone()],
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
|
@ -36,7 +39,9 @@ impl<'a> Visitor<'a> for ScalarLeafs {
|
|||
fn no_allowed_error_message(field_name: &str, type_name: &str) -> String {
|
||||
format!(
|
||||
r#"Field "{}" must not have a selection since type {} has no subfields"#,
|
||||
field_name, type_name)
|
||||
field_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
fn required_error_message(field_name: &str, type_name: &str) -> String {
|
||||
|
@ -47,115 +52,159 @@ fn required_error_message(field_name: &str, type_name: &str) -> String {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{no_allowed_error_message, required_error_message, factory};
|
||||
use super::{factory, no_allowed_error_message, required_error_message};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn valid_scalar_selection() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelection on Dog {
|
||||
barks
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_type_missing_selection() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query directQueryOnObjectWithoutSubFields {
|
||||
human
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&required_error_message("human", "Human"),
|
||||
&[SourcePosition::new(67, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&required_error_message("human", "Human"),
|
||||
&[SourcePosition::new(67, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interface_type_missing_selection() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
human { pets }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&required_error_message("pets", "[Pet]"),
|
||||
&[SourcePosition::new(33, 2, 20)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&required_error_message("pets", "[Pet]"),
|
||||
&[SourcePosition::new(33, 2, 20)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_scalar_selection_with_args() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionWithArgs on Dog {
|
||||
doesKnowCommand(dogCommand: SIT)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_selection_not_allowed_on_boolean() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionsNotAllowedOnBoolean on Dog {
|
||||
barks { sinceWhen }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&no_allowed_error_message("barks", "Boolean"),
|
||||
&[SourcePosition::new(77, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&no_allowed_error_message("barks", "Boolean"),
|
||||
&[SourcePosition::new(77, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_selection_not_allowed_on_enum() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionsNotAllowedOnEnum on Cat {
|
||||
furColor { inHexdec }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&no_allowed_error_message("furColor", "FurColor"),
|
||||
&[SourcePosition::new(74, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&no_allowed_error_message("furColor", "FurColor"),
|
||||
&[SourcePosition::new(74, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_selection_not_allowed_with_args() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionsNotAllowedWithArgs on Dog {
|
||||
doesKnowCommand(dogCommand: SIT) { sinceWhen }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&no_allowed_error_message("doesKnowCommand",
|
||||
"Boolean"),
|
||||
&[SourcePosition::new(76, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&no_allowed_error_message("doesKnowCommand", "Boolean"),
|
||||
&[SourcePosition::new(76, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_selection_not_allowed_with_directives() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionsNotAllowedWithDirectives on Dog {
|
||||
name @include(if: true) { isAlsoHumanName }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&no_allowed_error_message("name", "String"),
|
||||
&[SourcePosition::new(82, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&no_allowed_error_message("name", "String"),
|
||||
&[SourcePosition::new(82, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scalar_selection_not_allowed_with_directives_and_args() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
|
||||
doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&no_allowed_error_message("doesKnowCommand",
|
||||
"Boolean"),
|
||||
&[SourcePosition::new(89, 2, 12)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&no_allowed_error_message("doesKnowCommand", "Boolean"),
|
||||
&[SourcePosition::new(89, 2, 12)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::hash_map::{HashMap, Entry};
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
|
||||
use ast::{Directive, Field, InputValue};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
@ -9,7 +9,9 @@ pub struct UniqueArgumentNames<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> UniqueArgumentNames<'a> {
|
||||
UniqueArgumentNames { known_names: HashMap::new() }
|
||||
UniqueArgumentNames {
|
||||
known_names: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueArgumentNames<'a> {
|
||||
|
@ -21,13 +23,17 @@ impl<'a> Visitor<'a> for UniqueArgumentNames<'a> {
|
|||
self.known_names = HashMap::new();
|
||||
}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
match self.known_names.entry(arg_name.item) {
|
||||
Entry::Occupied(e) => {
|
||||
ctx.report_error(&error_message(arg_name.item),
|
||||
&[e.get().clone(), arg_name.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(arg_name.item),
|
||||
&[e.get().clone(), arg_name.start.clone()],
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(arg_name.start.clone());
|
||||
|
@ -45,155 +51,213 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn no_arguments_on_field() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_arguments_on_directive() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_on_field() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn argument_on_directive() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive(arg: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_argument_on_two_fields() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
one: field(arg: "value")
|
||||
two: field(arg: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_argument_on_field_and_directive() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: "value") @directive(arg: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_argument_on_two_directives() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive1(arg: "value") @directive2(arg: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_field_arguments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg1: "value", arg2: "value", arg3: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_directive_arguments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive(arg1: "value", arg2: "value", arg3: "value")
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_field_arguments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg1: "value", arg1: "value")
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(46, 2, 33)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(46, 2, 33),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_duplicate_field_arguments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg1: "value", arg1: "value", arg1: "value")
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(46, 2, 33)]),
|
||||
RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(61, 2, 48)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(46, 2, 33),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(31, 2, 18),
|
||||
SourcePosition::new(61, 2, 48),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_directive_arguments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive(arg1: "value", arg1: "value")
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(57, 2, 44)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(57, 2, 44),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_duplicate_directive_arguments() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field @directive(arg1: "value", arg1: "value", arg1: "value")
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(57, 2, 44)]),
|
||||
RuleError::new(&error_message("arg1"),
|
||||
&[SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(72, 2, 59)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(57, 2, 44),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("arg1"),
|
||||
&[
|
||||
SourcePosition::new(42, 2, 29),
|
||||
SourcePosition::new(72, 2, 59),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::hash_map::{HashMap, Entry};
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
|
||||
use ast::Fragment;
|
||||
use parser::{SourcePosition, Spanning};
|
||||
|
@ -9,17 +9,23 @@ pub struct UniqueFragmentNames<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> UniqueFragmentNames<'a> {
|
||||
UniqueFragmentNames { names: HashMap::new() }
|
||||
UniqueFragmentNames {
|
||||
names: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueFragmentNames<'a> {
|
||||
fn enter_fragment_definition(&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
context: &mut ValidatorContext<'a>,
|
||||
f: &'a Spanning<Fragment>,
|
||||
) {
|
||||
match self.names.entry(f.item.name.item) {
|
||||
Entry::Occupied(e) => {
|
||||
context.report_error(&duplicate_message(f.item.name.item),
|
||||
&[e.get().clone(), f.item.name.start.clone()]);
|
||||
context.report_error(
|
||||
&duplicate_message(f.item.name.item),
|
||||
&[e.get().clone(), f.item.name.start.clone()],
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(f.item.name.start.clone());
|
||||
|
@ -37,22 +43,25 @@ mod tests {
|
|||
use super::{duplicate_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn no_fragments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...fragA
|
||||
}
|
||||
|
@ -60,13 +69,15 @@ mod tests {
|
|||
fragment fragA on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_fragments() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...fragA
|
||||
...fragB
|
||||
|
@ -81,13 +92,15 @@ mod tests {
|
|||
fragment fragC on Type {
|
||||
fieldC
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn inline_fragments_always_unique() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...on Type {
|
||||
fieldA
|
||||
|
@ -96,26 +109,30 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragment_and_operation_named_the_same() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
...Foo
|
||||
}
|
||||
fragment Foo on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragments_named_the_same() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
...fragA
|
||||
}
|
||||
|
@ -126,15 +143,23 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&duplicate_message("fragA"),
|
||||
&[SourcePosition::new(65, 4, 19),
|
||||
SourcePosition::new(131, 7, 19)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&duplicate_message("fragA"),
|
||||
&[
|
||||
SourcePosition::new(65, 4, 19),
|
||||
SourcePosition::new(131, 7, 19),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragments_named_the_same_no_reference() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Type {
|
||||
fieldA
|
||||
}
|
||||
|
@ -142,8 +167,15 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&duplicate_message("fragA"),
|
||||
&[SourcePosition::new(20, 1, 19),
|
||||
SourcePosition::new(86, 4, 19)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&duplicate_message("fragA"),
|
||||
&[
|
||||
SourcePosition::new(20, 1, 19),
|
||||
SourcePosition::new(86, 4, 19),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::hash_map::{HashMap, Entry};
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
|
||||
use ast::InputValue;
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
@ -9,30 +9,40 @@ pub struct UniqueInputFieldNames<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> UniqueInputFieldNames<'a> {
|
||||
UniqueInputFieldNames { known_name_stack: Vec::new() }
|
||||
UniqueInputFieldNames {
|
||||
known_name_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueInputFieldNames<'a> {
|
||||
fn enter_object_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn enter_object_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
self.known_name_stack.push(HashMap::new());
|
||||
}
|
||||
|
||||
fn exit_object_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn exit_object_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
self.known_name_stack.pop();
|
||||
}
|
||||
|
||||
fn enter_object_field(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue>)) {
|
||||
fn enter_object_field(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue>),
|
||||
) {
|
||||
if let Some(ref mut known_names) = self.known_name_stack.last_mut() {
|
||||
match known_names.entry(&field_name.item) {
|
||||
Entry::Occupied(e) => {
|
||||
ctx.report_error(&error_message(&field_name.item),
|
||||
&[e.get().clone(), field_name.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(&field_name.item),
|
||||
&[e.get().clone(), field_name.start.clone()],
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(field_name.start.clone());
|
||||
|
@ -51,42 +61,49 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn input_object_with_fields() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: { f: true })
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn same_input_object_within_two_args() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg1: { f: true }, arg2: { f: true })
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_input_object_fields() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: { f1: "value", f2: "value", f3: "value" })
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_for_nested_input_objects_with_similar_fields() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: {
|
||||
deep: {
|
||||
|
@ -98,36 +115,57 @@ mod tests {
|
|||
id: 1
|
||||
})
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_input_object_fields() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: { f1: "value", f1: "value" })
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("f1"),
|
||||
&[SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(51, 2, 38)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("f1"),
|
||||
&[
|
||||
SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(51, 2, 38),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn many_duplicate_input_object_fields() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field(arg: { f1: "value", f1: "value", f1: "value" })
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("f1"),
|
||||
&[SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(51, 2, 38)]),
|
||||
RuleError::new(&error_message("f1"),
|
||||
&[SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(64, 2, 51)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("f1"),
|
||||
&[
|
||||
SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(51, 2, 38),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("f1"),
|
||||
&[
|
||||
SourcePosition::new(38, 2, 25),
|
||||
SourcePosition::new(64, 2, 51),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::hash_map::{HashMap, Entry};
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
|
||||
use ast::Operation;
|
||||
use parser::{SourcePosition, Spanning};
|
||||
|
@ -9,18 +9,24 @@ pub struct UniqueOperationNames<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> UniqueOperationNames<'a> {
|
||||
UniqueOperationNames { names: HashMap::new() }
|
||||
UniqueOperationNames {
|
||||
names: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueOperationNames<'a> {
|
||||
fn enter_operation_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
if let Some(ref op_name) = op.item.name {
|
||||
match self.names.entry(op_name.item) {
|
||||
Entry::Occupied(e) => {
|
||||
ctx.report_error(&error_message(op_name.item),
|
||||
&[e.get().clone(), op.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(op_name.item),
|
||||
&[e.get().clone(), op.start.clone()],
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(op.start.clone());
|
||||
|
@ -39,42 +45,49 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn no_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment fragA on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_anon_operation() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
{
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_named_operation() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_operations() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
field
|
||||
}
|
||||
|
@ -82,13 +95,15 @@ mod tests {
|
|||
query Bar {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_operations_of_different_types() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
field
|
||||
}
|
||||
|
@ -96,26 +111,30 @@ mod tests {
|
|||
mutation Bar {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fragment_and_operation_named_the_same() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
...Foo
|
||||
}
|
||||
fragment Foo on Type {
|
||||
field
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_operations_of_same_name() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
fieldA
|
||||
}
|
||||
|
@ -123,15 +142,23 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("Foo"),
|
||||
&[SourcePosition::new(11, 1, 10),
|
||||
SourcePosition::new(64, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("Foo"),
|
||||
&[
|
||||
SourcePosition::new(11, 1, 10),
|
||||
SourcePosition::new(64, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multiple_ops_of_same_name_of_different_types() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo {
|
||||
fieldA
|
||||
}
|
||||
|
@ -139,8 +166,15 @@ mod tests {
|
|||
fieldB
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("Foo"),
|
||||
&[SourcePosition::new(11, 1, 10),
|
||||
SourcePosition::new(64, 4, 10)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("Foo"),
|
||||
&[
|
||||
SourcePosition::new(11, 1, 10),
|
||||
SourcePosition::new(64, 4, 10),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::collections::hash_map::{HashMap, Entry};
|
||||
use std::collections::hash_map::{Entry, HashMap};
|
||||
|
||||
use ast::{Operation, VariableDefinition};
|
||||
use parser::{SourcePosition, Spanning};
|
||||
|
@ -9,23 +9,31 @@ pub struct UniqueVariableNames<'a> {
|
|||
}
|
||||
|
||||
pub fn factory<'a>() -> UniqueVariableNames<'a> {
|
||||
UniqueVariableNames { names: HashMap::new() }
|
||||
UniqueVariableNames {
|
||||
names: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueVariableNames<'a> {
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>,
|
||||
) {
|
||||
self.names = HashMap::new();
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
match self.names.entry(var_name.item) {
|
||||
Entry::Occupied(e) => {
|
||||
ctx.report_error(&error_message(var_name.item),
|
||||
&[e.get().clone(), var_name.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(var_name.item),
|
||||
&[e.get().clone(), var_name.start.clone()],
|
||||
);
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
e.insert(var_name.start.clone());
|
||||
|
@ -43,36 +51,58 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn unique_variable_names() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query A($x: Int, $y: String) { __typename }
|
||||
query B($x: String, $y: Int) { __typename }
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn duplicate_variable_names() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query A($x: Int, $x: Int, $x: String) { __typename }
|
||||
query B($x: String, $x: Int) { __typename }
|
||||
query C($x: Int, $x: Int) { __typename }
|
||||
"#,
|
||||
&[RuleError::new(&error_message("x"),
|
||||
&[SourcePosition::new(19, 1, 18),
|
||||
SourcePosition::new(28, 1, 27)]),
|
||||
RuleError::new(&error_message("x"),
|
||||
&[SourcePosition::new(19, 1, 18),
|
||||
SourcePosition::new(37, 1, 36)]),
|
||||
RuleError::new(&error_message("x"),
|
||||
&[SourcePosition::new(82, 2, 18),
|
||||
SourcePosition::new(94, 2, 30)]),
|
||||
RuleError::new(&error_message("x"),
|
||||
&[SourcePosition::new(136, 3, 18),
|
||||
SourcePosition::new(145, 3, 27)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("x"),
|
||||
&[
|
||||
SourcePosition::new(19, 1, 18),
|
||||
SourcePosition::new(28, 1, 27),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("x"),
|
||||
&[
|
||||
SourcePosition::new(19, 1, 18),
|
||||
SourcePosition::new(37, 1, 36),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("x"),
|
||||
&[
|
||||
SourcePosition::new(82, 2, 18),
|
||||
SourcePosition::new(94, 2, 30),
|
||||
],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("x"),
|
||||
&[
|
||||
SourcePosition::new(136, 3, 18),
|
||||
SourcePosition::new(145, 3, 27),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,23 +9,30 @@ pub fn factory() -> UniqueVariableNames {
|
|||
}
|
||||
|
||||
impl<'a> Visitor<'a> for UniqueVariableNames {
|
||||
fn enter_variable_definition(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, ref var_def): &'a (Spanning<&'a str>,
|
||||
VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
&(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
if let Some(var_type) = ctx.schema
|
||||
.concrete_type_by_name(var_def.var_type.item.innermost_name()) {
|
||||
.concrete_type_by_name(var_def.var_type.item.innermost_name())
|
||||
{
|
||||
if !var_type.is_input() {
|
||||
ctx.report_error(&error_message(var_name.item,
|
||||
&format!("{}", var_def.var_type.item)),
|
||||
&[var_def.var_type.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(var_name.item, &format!("{}", var_def.var_type.item)),
|
||||
&[var_def.var_type.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn error_message(var_name: &str, type_name: &str) -> String {
|
||||
format!("Variable \"{}\" cannot be of non-input type \"{}\"", var_name, type_name)
|
||||
format!(
|
||||
"Variable \"{}\" cannot be of non-input type \"{}\"",
|
||||
var_name,
|
||||
type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -33,31 +40,43 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn input_types_are_valid() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
|
||||
field(a: $a, b: $b, c: $c)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn output_types_are_invalid() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {
|
||||
field(a: $a, b: $b, c: $c)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("a", "Dog"),
|
||||
&[SourcePosition::new(25, 1, 24)]),
|
||||
RuleError::new(&error_message("b", "[[CatOrDog!]]!"),
|
||||
&[SourcePosition::new(34, 1, 33)]),
|
||||
RuleError::new(&error_message("c", "Pet"),
|
||||
&[SourcePosition::new(54, 1, 53)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("a", "Dog"),
|
||||
&[SourcePosition::new(25, 1, 24)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("b", "[[CatOrDog!]]!"),
|
||||
&[SourcePosition::new(34, 1, 33)],
|
||||
),
|
||||
RuleError::new(
|
||||
&error_message("c", "Pet"),
|
||||
&[SourcePosition::new(54, 1, 53)],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{HashSet, HashMap};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use ast::{Type, VariableDefinition, Document, Fragment, Operation, FragmentSpread};
|
||||
use ast::{Document, Fragment, FragmentSpread, Operation, Type, VariableDefinition};
|
||||
use parser::Spanning;
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
||||
|
@ -27,11 +27,13 @@ pub fn factory<'a>() -> VariableInAllowedPosition<'a> {
|
|||
}
|
||||
|
||||
impl<'a> VariableInAllowedPosition<'a> {
|
||||
fn collect_incorrect_usages(&self,
|
||||
from: &Scope<'a>,
|
||||
var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition)>,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
visited: &mut HashSet<Scope<'a>>) {
|
||||
fn collect_incorrect_usages(
|
||||
&self,
|
||||
from: &Scope<'a>,
|
||||
var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition)>,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
visited: &mut HashSet<Scope<'a>>,
|
||||
) {
|
||||
if visited.contains(from) {
|
||||
return;
|
||||
}
|
||||
|
@ -40,10 +42,10 @@ impl<'a> VariableInAllowedPosition<'a> {
|
|||
|
||||
if let Some(usages) = self.variable_usages.get(from) {
|
||||
for &(ref var_name, ref var_type) in usages {
|
||||
if let Some(&&(ref var_def_name, ref var_def)) =
|
||||
var_defs
|
||||
.iter()
|
||||
.find(|&&&(ref n, _)| &n.item == var_name.item) {
|
||||
if let Some(&&(ref var_def_name, ref var_def)) = var_defs
|
||||
.iter()
|
||||
.find(|&&&(ref n, _)| &n.item == var_name.item)
|
||||
{
|
||||
let expected_type = match (&var_def.default_value, &var_def.var_type.item) {
|
||||
(&Some(_), &Type::List(ref inner)) => Type::NonNullList(inner.clone()),
|
||||
(&Some(_), &Type::Named(inner)) => Type::NonNullNamed(inner),
|
||||
|
@ -51,10 +53,14 @@ impl<'a> VariableInAllowedPosition<'a> {
|
|||
};
|
||||
|
||||
if !ctx.schema.is_subtype(&expected_type, var_type) {
|
||||
ctx.report_error(&error_message(var_name.item,
|
||||
&format!("{}", expected_type),
|
||||
&format!("{}", var_type)),
|
||||
&[var_def_name.start.clone(), var_name.start.clone()]);
|
||||
ctx.report_error(
|
||||
&error_message(
|
||||
var_name.item,
|
||||
&format!("{}", expected_type),
|
||||
&format!("{}", var_type),
|
||||
),
|
||||
&[var_def_name.start.clone(), var_name.start.clone()],
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,21 +81,27 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<Fragment>,
|
||||
) {
|
||||
self.current_scope = Some(Scope::Fragment(fragment.item.name.item));
|
||||
}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
op: &'a Spanning<Operation>,
|
||||
) {
|
||||
self.current_scope = Some(Scope::Operation(op.item.name.as_ref().map(|s| s.item)));
|
||||
}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.spreads
|
||||
.entry(scope.clone())
|
||||
|
@ -98,9 +110,11 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
def: &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
if let Some(ref scope) = self.current_scope {
|
||||
self.variable_defs
|
||||
.entry(scope.clone())
|
||||
|
@ -109,16 +123,21 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_variable_value(&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
var_name: Spanning<&'a String>) {
|
||||
fn enter_variable_value(
|
||||
&mut self,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
var_name: Spanning<&'a String>,
|
||||
) {
|
||||
if let (&Some(ref scope), Some(input_type)) =
|
||||
(&self.current_scope, ctx.current_input_type_literal()) {
|
||||
(&self.current_scope, ctx.current_input_type_literal())
|
||||
{
|
||||
self.variable_usages
|
||||
.entry(scope.clone())
|
||||
.or_insert_with(Vec::new)
|
||||
.push((Spanning::start_end(&var_name.start, &var_name.end, var_name.item),
|
||||
input_type.clone()));
|
||||
.push((
|
||||
Spanning::start_end(&var_name.start, &var_name.end, var_name.item),
|
||||
input_type.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -126,7 +145,10 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
|
|||
fn error_message(var_name: &str, type_name: &str, expected_type_name: &str) -> String {
|
||||
format!(
|
||||
"Variable \"{}\" of type \"{}\" used in position expecting type \"{}\"",
|
||||
var_name, type_name, expected_type_name)
|
||||
var_name,
|
||||
type_name,
|
||||
expected_type_name
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -134,25 +156,28 @@ mod tests {
|
|||
use super::{error_message, factory};
|
||||
|
||||
use parser::SourcePosition;
|
||||
use validation::{RuleError, expect_passes_rule, expect_fails_rule};
|
||||
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
|
||||
|
||||
#[test]
|
||||
fn boolean_into_boolean() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($booleanArg: Boolean)
|
||||
{
|
||||
complicatedArgs {
|
||||
booleanArgField(booleanArg: $booleanArg)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_into_boolean_within_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment booleanArgFrag on ComplicatedArgs {
|
||||
booleanArgField(booleanArg: $booleanArg)
|
||||
}
|
||||
|
@ -162,10 +187,12 @@ mod tests {
|
|||
...booleanArgFrag
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($booleanArg: Boolean)
|
||||
{
|
||||
complicatedArgs {
|
||||
|
@ -175,26 +202,30 @@ mod tests {
|
|||
fragment booleanArgFrag on ComplicatedArgs {
|
||||
booleanArgField(booleanArg: $booleanArg)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_null_boolean_into_boolean() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($nonNullBooleanArg: Boolean!)
|
||||
{
|
||||
complicatedArgs {
|
||||
booleanArgField(booleanArg: $nonNullBooleanArg)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_null_boolean_into_boolean_within_fragment() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment booleanArgFrag on ComplicatedArgs {
|
||||
booleanArgField(booleanArg: $nonNullBooleanArg)
|
||||
}
|
||||
|
@ -205,141 +236,169 @@ mod tests {
|
|||
...booleanArgFrag
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_into_non_null_int_with_default() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($intArg: Int = 1)
|
||||
{
|
||||
complicatedArgs {
|
||||
nonNullIntArgField(nonNullIntArg: $intArg)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_list_into_string_list() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringListVar: [String])
|
||||
{
|
||||
complicatedArgs {
|
||||
stringListArgField(stringListArg: $stringListVar)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_null_string_list_into_string_list() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringListVar: [String!])
|
||||
{
|
||||
complicatedArgs {
|
||||
stringListArgField(stringListArg: $stringListVar)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_into_string_list_in_item_position() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringVar: String)
|
||||
{
|
||||
complicatedArgs {
|
||||
stringListArgField(stringListArg: [$stringVar])
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_null_string_into_string_list_in_item_position() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringVar: String!)
|
||||
{
|
||||
complicatedArgs {
|
||||
stringListArgField(stringListArg: [$stringVar])
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_input_into_complex_input() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($complexVar: ComplexInput)
|
||||
{
|
||||
complicatedArgs {
|
||||
complexArgField(complexArg: $complexVar)
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complex_input_into_complex_input_in_field_position() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($boolVar: Boolean = false)
|
||||
{
|
||||
complicatedArgs {
|
||||
complexArgField(complexArg: {requiredArg: $boolVar})
|
||||
}
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn non_null_boolean_into_non_null_boolean_in_directive() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($boolVar: Boolean!)
|
||||
{
|
||||
dog @include(if: $boolVar)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_in_non_null_in_directive_with_default() {
|
||||
expect_passes_rule(factory,
|
||||
r#"
|
||||
expect_passes_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($boolVar: Boolean = false)
|
||||
{
|
||||
dog @include(if: $boolVar)
|
||||
}
|
||||
"#);
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_into_non_null_int() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($intArg: Int) {
|
||||
complicatedArgs {
|
||||
nonNullIntArgField(nonNullIntArg: $intArg)
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("intArg", "Int", "Int!"),
|
||||
&[SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(117, 3, 48)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("intArg", "Int", "Int!"),
|
||||
&[
|
||||
SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(117, 3, 48),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_into_non_null_int_within_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment nonNullIntArgFieldFrag on ComplicatedArgs {
|
||||
nonNullIntArgField(nonNullIntArg: $intArg)
|
||||
}
|
||||
|
@ -350,15 +409,23 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("intArg", "Int", "Int!"),
|
||||
&[SourcePosition::new(154, 5, 22),
|
||||
SourcePosition::new(110, 2, 46)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("intArg", "Int", "Int!"),
|
||||
&[
|
||||
SourcePosition::new(154, 5, 22),
|
||||
SourcePosition::new(110, 2, 46),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn int_into_non_null_int_within_nested_fragment() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
fragment outerFrag on ComplicatedArgs {
|
||||
...nonNullIntArgFieldFrag
|
||||
}
|
||||
|
@ -373,64 +440,103 @@ mod tests {
|
|||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("intArg", "Int", "Int!"),
|
||||
&[SourcePosition::new(255, 9, 22),
|
||||
SourcePosition::new(211, 6, 46)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("intArg", "Int", "Int!"),
|
||||
&[
|
||||
SourcePosition::new(255, 9, 22),
|
||||
SourcePosition::new(211, 6, 46),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_over_boolean() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringVar: String) {
|
||||
complicatedArgs {
|
||||
booleanArgField(booleanArg: $stringVar)
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("stringVar", "String", "Boolean"),
|
||||
&[SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(117, 3, 42)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("stringVar", "String", "Boolean"),
|
||||
&[
|
||||
SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(117, 3, 42),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_into_string_list() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringVar: String) {
|
||||
complicatedArgs {
|
||||
stringListArgField(stringListArg: $stringVar)
|
||||
}
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("stringVar", "String", "[String]"),
|
||||
&[SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(123, 3, 48)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("stringVar", "String", "[String]"),
|
||||
&[
|
||||
SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(123, 3, 48),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_into_non_null_boolean_in_directive() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($boolVar: Boolean) {
|
||||
dog @include(if: $boolVar)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("boolVar", "Boolean", "Boolean!"),
|
||||
&[SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(73, 2, 29)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("boolVar", "Boolean", "Boolean!"),
|
||||
&[
|
||||
SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(73, 2, 29),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn string_into_non_null_boolean_in_directive() {
|
||||
expect_fails_rule(factory,
|
||||
r#"
|
||||
expect_fails_rule(
|
||||
factory,
|
||||
r#"
|
||||
query Query($stringVar: String) {
|
||||
dog @include(if: $stringVar)
|
||||
}
|
||||
"#,
|
||||
&[RuleError::new(&error_message("stringVar", "String", "Boolean!"),
|
||||
&[SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(74, 2, 29)])]);
|
||||
&[
|
||||
RuleError::new(
|
||||
&error_message("stringVar", "String", "Boolean!"),
|
||||
&[
|
||||
SourcePosition::new(23, 1, 22),
|
||||
SourcePosition::new(74, 2, 29),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,9 @@ use ast::{FromInputValue, InputValue};
|
|||
use types::base::GraphQLType;
|
||||
use executor::Registry;
|
||||
use types::scalars::{EmptyMutation, ID};
|
||||
use schema::model::{DirectiveType, DirectiveLocation, RootNode};
|
||||
use schema::model::{DirectiveLocation, DirectiveType, RootNode};
|
||||
use schema::meta::{EnumValue, MetaType};
|
||||
use validation::{Visitor, RuleError, ValidatorContext, MultiVisitor, MultiVisitorNil, visit};
|
||||
use validation::{visit, MultiVisitor, MultiVisitorNil, RuleError, ValidatorContext, Visitor};
|
||||
|
||||
struct Being;
|
||||
struct Pet;
|
||||
|
@ -59,9 +59,11 @@ impl GraphQLType for Being {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname"))];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
];
|
||||
|
||||
registry.build_interface_type::<Self>(fields).into_meta()
|
||||
}
|
||||
|
@ -75,9 +77,11 @@ impl GraphQLType for Pet {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname"))];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
];
|
||||
|
||||
registry.build_interface_type::<Self>(fields).into_meta()
|
||||
}
|
||||
|
@ -91,9 +95,11 @@ impl GraphQLType for Canine {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname"))];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
];
|
||||
|
||||
registry.build_interface_type::<Self>(fields).into_meta()
|
||||
}
|
||||
|
@ -108,9 +114,11 @@ impl GraphQLType for DogCommand {
|
|||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
registry
|
||||
.build_enum_type::<Self>(&[EnumValue::new("SIT"),
|
||||
EnumValue::new("HEEL"),
|
||||
EnumValue::new("DOWN")])
|
||||
.build_enum_type::<Self>(&[
|
||||
EnumValue::new("SIT"),
|
||||
EnumValue::new("HEEL"),
|
||||
EnumValue::new("DOWN"),
|
||||
])
|
||||
.into_meta()
|
||||
}
|
||||
}
|
||||
|
@ -134,28 +142,32 @@ impl GraphQLType for Dog {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<String>>("nickname"),
|
||||
registry.field::<Option<i32>>("barkVolume"),
|
||||
registry.field::<Option<bool>>("barks"),
|
||||
registry
|
||||
.field::<Option<bool>>("doesKnowCommand")
|
||||
.argument(registry.arg::<Option<DogCommand>>("dogCommand")),
|
||||
registry
|
||||
.field::<Option<bool>>("isHousetrained")
|
||||
.argument(registry.arg_with_default("atOtherHomes", &true)),
|
||||
registry
|
||||
.field::<Option<bool>>("isAtLocation")
|
||||
.argument(registry.arg::<Option<i32>>("x"))
|
||||
.argument(registry.arg::<Option<i32>>("y"))];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<String>>("nickname"),
|
||||
registry.field::<Option<i32>>("barkVolume"),
|
||||
registry.field::<Option<bool>>("barks"),
|
||||
registry
|
||||
.field::<Option<bool>>("doesKnowCommand")
|
||||
.argument(registry.arg::<Option<DogCommand>>("dogCommand")),
|
||||
registry
|
||||
.field::<Option<bool>>("isHousetrained")
|
||||
.argument(registry.arg_with_default("atOtherHomes", &true)),
|
||||
registry
|
||||
.field::<Option<bool>>("isAtLocation")
|
||||
.argument(registry.arg::<Option<i32>>("x"))
|
||||
.argument(registry.arg::<Option<i32>>("y")),
|
||||
];
|
||||
|
||||
registry
|
||||
.build_object_type::<Self>(fields)
|
||||
.interfaces(&[registry.get_type::<Being>(),
|
||||
registry.get_type::<Pet>(),
|
||||
registry.get_type::<Canine>()])
|
||||
.interfaces(&[
|
||||
registry.get_type::<Being>(),
|
||||
registry.get_type::<Pet>(),
|
||||
registry.get_type::<Canine>(),
|
||||
])
|
||||
.into_meta()
|
||||
}
|
||||
}
|
||||
|
@ -169,10 +181,12 @@ impl GraphQLType for FurColor {
|
|||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
registry
|
||||
.build_enum_type::<Self>(&[EnumValue::new("BROWN"),
|
||||
EnumValue::new("BLACK"),
|
||||
EnumValue::new("TAN"),
|
||||
EnumValue::new("SPOTTED")])
|
||||
.build_enum_type::<Self>(&[
|
||||
EnumValue::new("BROWN"),
|
||||
EnumValue::new("BLACK"),
|
||||
EnumValue::new("TAN"),
|
||||
EnumValue::new("SPOTTED"),
|
||||
])
|
||||
.into_meta()
|
||||
}
|
||||
}
|
||||
|
@ -197,13 +211,15 @@ impl GraphQLType for Cat {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<String>>("nickname"),
|
||||
registry.field::<Option<bool>>("meows"),
|
||||
registry.field::<Option<i32>>("meowVolume"),
|
||||
registry.field::<Option<FurColor>>("furColor")];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<String>>("nickname"),
|
||||
registry.field::<Option<bool>>("meows"),
|
||||
registry.field::<Option<i32>>("meowVolume"),
|
||||
registry.field::<Option<FurColor>>("furColor"),
|
||||
];
|
||||
|
||||
registry
|
||||
.build_object_type::<Self>(fields)
|
||||
|
@ -248,16 +264,20 @@ impl GraphQLType for Human {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<Vec<Option<Pet>>>>("pets"),
|
||||
registry.field::<Option<Vec<Human>>>("relatives"),
|
||||
registry.field::<Option<i32>>("iq")];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<Vec<Option<Pet>>>>("pets"),
|
||||
registry.field::<Option<Vec<Human>>>("relatives"),
|
||||
registry.field::<Option<i32>>("iq"),
|
||||
];
|
||||
registry
|
||||
.build_object_type::<Self>(fields)
|
||||
.interfaces(&[registry.get_type::<Being>(),
|
||||
registry.get_type::<Intelligent>()])
|
||||
.interfaces(&[
|
||||
registry.get_type::<Being>(),
|
||||
registry.get_type::<Intelligent>(),
|
||||
])
|
||||
.into_meta()
|
||||
}
|
||||
}
|
||||
|
@ -270,16 +290,20 @@ impl GraphQLType for Alien {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<i32>>("iq"),
|
||||
registry.field::<Option<i32>>("numEyes")];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("name")
|
||||
.argument(registry.arg::<Option<bool>>("surname")),
|
||||
registry.field::<Option<i32>>("iq"),
|
||||
registry.field::<Option<i32>>("numEyes"),
|
||||
];
|
||||
|
||||
registry
|
||||
.build_object_type::<Self>(fields)
|
||||
.interfaces(&[registry.get_type::<Being>(),
|
||||
registry.get_type::<Intelligent>()])
|
||||
.interfaces(&[
|
||||
registry.get_type::<Being>(),
|
||||
registry.get_type::<Intelligent>(),
|
||||
])
|
||||
.into_meta()
|
||||
}
|
||||
}
|
||||
|
@ -320,11 +344,13 @@ impl GraphQLType for ComplexInput {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry.arg::<bool>("requiredField"),
|
||||
registry.arg::<Option<i32>>("intField"),
|
||||
registry.arg::<Option<String>>("stringField"),
|
||||
registry.arg::<Option<bool>>("booleanField"),
|
||||
registry.arg::<Option<Vec<Option<String>>>>("stringListField")];
|
||||
let fields = &[
|
||||
registry.arg::<bool>("requiredField"),
|
||||
registry.arg::<Option<i32>>("intField"),
|
||||
registry.arg::<Option<String>>("stringField"),
|
||||
registry.arg::<Option<bool>>("booleanField"),
|
||||
registry.arg::<Option<Vec<Option<String>>>>("stringListField"),
|
||||
];
|
||||
|
||||
registry.build_input_object_type::<Self>(fields).into_meta()
|
||||
}
|
||||
|
@ -338,15 +364,15 @@ impl FromInputValue for ComplexInput {
|
|||
};
|
||||
|
||||
Some(ComplexInput {
|
||||
required_field: match obj.get("requiredField").and_then(|v| v.convert()) {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
},
|
||||
int_field: obj.get("intField").and_then(|v| v.convert()),
|
||||
string_field: obj.get("stringField").and_then(|v| v.convert()),
|
||||
boolean_field: obj.get("booleanField").and_then(|v| v.convert()),
|
||||
string_list_field: obj.get("stringListField").and_then(|v| v.convert()),
|
||||
})
|
||||
required_field: match obj.get("requiredField").and_then(|v| v.convert()) {
|
||||
Some(f) => f,
|
||||
None => return None,
|
||||
},
|
||||
int_field: obj.get("intField").and_then(|v| v.convert()),
|
||||
string_field: obj.get("stringField").and_then(|v| v.convert()),
|
||||
boolean_field: obj.get("booleanField").and_then(|v| v.convert()),
|
||||
string_list_field: obj.get("stringListField").and_then(|v| v.convert()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -358,48 +384,49 @@ impl GraphQLType for ComplicatedArgs {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields =
|
||||
&[registry
|
||||
.field::<Option<String>>("intArgField")
|
||||
.argument(registry.arg::<Option<i32>>("intArg")),
|
||||
registry
|
||||
.field::<Option<String>>("nonNullIntArgField")
|
||||
.argument(registry.arg::<i32>("nonNullIntArg")),
|
||||
registry
|
||||
.field::<Option<String>>("stringArgField")
|
||||
.argument(registry.arg::<Option<String>>("stringArg")),
|
||||
registry
|
||||
.field::<Option<String>>("booleanArgField")
|
||||
.argument(registry.arg::<Option<bool>>("booleanArg")),
|
||||
registry
|
||||
.field::<Option<String>>("enumArgField")
|
||||
.argument(registry.arg::<Option<FurColor>>("enumArg")),
|
||||
registry
|
||||
.field::<Option<String>>("floatArgField")
|
||||
.argument(registry.arg::<Option<f64>>("floatArg")),
|
||||
registry
|
||||
.field::<Option<String>>("idArgField")
|
||||
.argument(registry.arg::<Option<ID>>("idArg")),
|
||||
registry
|
||||
.field::<Option<String>>("stringListArgField")
|
||||
.argument(registry.arg::<Option<Vec<Option<String>>>>("stringListArg")),
|
||||
registry
|
||||
.field::<Option<String>>("complexArgField")
|
||||
.argument(registry.arg::<Option<ComplexInput>>("complexArg")),
|
||||
registry
|
||||
.field::<Option<String>>("multipleReqs")
|
||||
.argument(registry.arg::<i32>("req1"))
|
||||
.argument(registry.arg::<i32>("req2")),
|
||||
registry
|
||||
.field::<Option<String>>("multipleOpts")
|
||||
.argument(registry.arg_with_default("opt1", &0i32))
|
||||
.argument(registry.arg_with_default("opt2", &0i32)),
|
||||
registry
|
||||
.field::<Option<String>>("multipleOptAndReq")
|
||||
.argument(registry.arg::<i32>("req1"))
|
||||
.argument(registry.arg::<i32>("req2"))
|
||||
.argument(registry.arg_with_default("opt1", &0i32))
|
||||
.argument(registry.arg_with_default("opt2", &0i32))];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<String>>("intArgField")
|
||||
.argument(registry.arg::<Option<i32>>("intArg")),
|
||||
registry
|
||||
.field::<Option<String>>("nonNullIntArgField")
|
||||
.argument(registry.arg::<i32>("nonNullIntArg")),
|
||||
registry
|
||||
.field::<Option<String>>("stringArgField")
|
||||
.argument(registry.arg::<Option<String>>("stringArg")),
|
||||
registry
|
||||
.field::<Option<String>>("booleanArgField")
|
||||
.argument(registry.arg::<Option<bool>>("booleanArg")),
|
||||
registry
|
||||
.field::<Option<String>>("enumArgField")
|
||||
.argument(registry.arg::<Option<FurColor>>("enumArg")),
|
||||
registry
|
||||
.field::<Option<String>>("floatArgField")
|
||||
.argument(registry.arg::<Option<f64>>("floatArg")),
|
||||
registry
|
||||
.field::<Option<String>>("idArgField")
|
||||
.argument(registry.arg::<Option<ID>>("idArg")),
|
||||
registry
|
||||
.field::<Option<String>>("stringListArgField")
|
||||
.argument(registry.arg::<Option<Vec<Option<String>>>>("stringListArg")),
|
||||
registry
|
||||
.field::<Option<String>>("complexArgField")
|
||||
.argument(registry.arg::<Option<ComplexInput>>("complexArg")),
|
||||
registry
|
||||
.field::<Option<String>>("multipleReqs")
|
||||
.argument(registry.arg::<i32>("req1"))
|
||||
.argument(registry.arg::<i32>("req2")),
|
||||
registry
|
||||
.field::<Option<String>>("multipleOpts")
|
||||
.argument(registry.arg_with_default("opt1", &0i32))
|
||||
.argument(registry.arg_with_default("opt2", &0i32)),
|
||||
registry
|
||||
.field::<Option<String>>("multipleOptAndReq")
|
||||
.argument(registry.arg::<i32>("req1"))
|
||||
.argument(registry.arg::<i32>("req2"))
|
||||
.argument(registry.arg_with_default("opt1", &0i32))
|
||||
.argument(registry.arg_with_default("opt2", &0i32)),
|
||||
];
|
||||
|
||||
registry.build_object_type::<Self>(fields).into_meta()
|
||||
}
|
||||
|
@ -413,47 +440,62 @@ impl GraphQLType for QueryRoot {
|
|||
}
|
||||
|
||||
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
|
||||
let fields = &[registry
|
||||
.field::<Option<Human>>("human")
|
||||
.argument(registry.arg::<Option<ID>>("id")),
|
||||
registry.field::<Option<Alien>>("alien"),
|
||||
registry.field::<Option<Dog>>("dog"),
|
||||
registry.field::<Option<Cat>>("cat"),
|
||||
registry.field::<Option<Pet>>("pet"),
|
||||
registry.field::<Option<CatOrDog>>("catOrDog"),
|
||||
registry.field::<Option<DogOrHuman>>("dorOrHuman"),
|
||||
registry.field::<Option<HumanOrAlien>>("humanOrAlien"),
|
||||
registry.field::<Option<ComplicatedArgs>>("complicatedArgs")];
|
||||
let fields = &[
|
||||
registry
|
||||
.field::<Option<Human>>("human")
|
||||
.argument(registry.arg::<Option<ID>>("id")),
|
||||
registry.field::<Option<Alien>>("alien"),
|
||||
registry.field::<Option<Dog>>("dog"),
|
||||
registry.field::<Option<Cat>>("cat"),
|
||||
registry.field::<Option<Pet>>("pet"),
|
||||
registry.field::<Option<CatOrDog>>("catOrDog"),
|
||||
registry.field::<Option<DogOrHuman>>("dorOrHuman"),
|
||||
registry.field::<Option<HumanOrAlien>>("humanOrAlien"),
|
||||
registry.field::<Option<ComplicatedArgs>>("complicatedArgs"),
|
||||
];
|
||||
|
||||
registry.build_object_type::<Self>(fields).into_meta()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn validate<'a, R, V, F>(r: R, q: &'a str, factory: F) -> Vec<RuleError>
|
||||
where R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V
|
||||
where
|
||||
R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V,
|
||||
{
|
||||
let mut root = RootNode::new(r, EmptyMutation::<()>::new());
|
||||
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onQuery", &[DirectiveLocation::Query], &[]));
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onMutation", &[DirectiveLocation::Mutation], &[]));
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onField", &[DirectiveLocation::Field], &[]));
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onFragmentDefinition",
|
||||
&[DirectiveLocation::FragmentDefinition],
|
||||
&[]));
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onFragmentSpread",
|
||||
&[DirectiveLocation::FragmentSpread],
|
||||
&[]));
|
||||
root.schema
|
||||
.add_directive(DirectiveType::new("onInlineFragment",
|
||||
&[DirectiveLocation::InlineFragment],
|
||||
&[]));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onQuery",
|
||||
&[DirectiveLocation::Query],
|
||||
&[],
|
||||
));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onMutation",
|
||||
&[DirectiveLocation::Mutation],
|
||||
&[],
|
||||
));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onField",
|
||||
&[DirectiveLocation::Field],
|
||||
&[],
|
||||
));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onFragmentDefinition",
|
||||
&[DirectiveLocation::FragmentDefinition],
|
||||
&[],
|
||||
));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onFragmentSpread",
|
||||
&[DirectiveLocation::FragmentSpread],
|
||||
&[],
|
||||
));
|
||||
root.schema.add_directive(DirectiveType::new(
|
||||
"onInlineFragment",
|
||||
&[DirectiveLocation::InlineFragment],
|
||||
&[],
|
||||
));
|
||||
|
||||
let doc = parse_document_source(q).expect(&format!("Parse error on input {:#?}", q));
|
||||
let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc);
|
||||
|
@ -465,16 +507,18 @@ pub fn validate<'a, R, V, F>(r: R, q: &'a str, factory: F) -> Vec<RuleError>
|
|||
}
|
||||
|
||||
pub fn expect_passes_rule<'a, V, F>(factory: F, q: &'a str)
|
||||
where V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V
|
||||
where
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V,
|
||||
{
|
||||
expect_passes_rule_with_schema(QueryRoot, factory, q);
|
||||
}
|
||||
|
||||
pub fn expect_passes_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &'a str)
|
||||
where R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V
|
||||
where
|
||||
R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V,
|
||||
{
|
||||
let errs = validate(r, q, factory);
|
||||
|
||||
|
@ -485,19 +529,22 @@ pub fn expect_passes_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &'a str)
|
|||
}
|
||||
|
||||
pub fn expect_fails_rule<'a, V, F>(factory: F, q: &'a str, expected_errors: &[RuleError])
|
||||
where V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V
|
||||
where
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V,
|
||||
{
|
||||
expect_fails_rule_with_schema(QueryRoot, factory, q, expected_errors);
|
||||
}
|
||||
|
||||
pub fn expect_fails_rule_with_schema<'a, R, V, F>(r: R,
|
||||
factory: F,
|
||||
q: &'a str,
|
||||
expected_errors: &[RuleError])
|
||||
where R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V
|
||||
pub fn expect_fails_rule_with_schema<'a, R, V, F>(
|
||||
r: R,
|
||||
factory: F,
|
||||
q: &'a str,
|
||||
expected_errors: &[RuleError],
|
||||
) where
|
||||
R: GraphQLType,
|
||||
V: Visitor<'a> + 'a,
|
||||
F: Fn() -> V,
|
||||
{
|
||||
let errs = validate(r, q, factory);
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
|
||||
Field, FragmentSpread, InlineFragment};
|
||||
use ast::{Directive, Document, Field, Fragment, FragmentSpread, InlineFragment, InputValue,
|
||||
Operation, Selection, VariableDefinition};
|
||||
use parser::Spanning;
|
||||
use validation::ValidatorContext;
|
||||
|
||||
|
@ -9,43 +9,59 @@ pub trait Visitor<'a> {
|
|||
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {}
|
||||
fn exit_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {}
|
||||
|
||||
fn enter_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>) {
|
||||
fn enter_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>,
|
||||
) {
|
||||
}
|
||||
fn exit_operation_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>) {
|
||||
fn exit_operation_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Operation>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>) {
|
||||
fn enter_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>,
|
||||
) {
|
||||
}
|
||||
fn exit_fragment_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>) {
|
||||
fn exit_fragment_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<Fragment>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_variable_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn enter_variable_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
}
|
||||
fn exit_variable_definition(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, VariableDefinition)) {
|
||||
fn exit_variable_definition(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, VariableDefinition),
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {}
|
||||
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {}
|
||||
|
||||
fn enter_argument(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn enter_argument(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
}
|
||||
fn exit_argument(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
|
||||
fn exit_argument(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<&'a str>, Spanning<InputValue>),
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {}
|
||||
|
@ -54,22 +70,30 @@ pub trait Visitor<'a> {
|
|||
fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {}
|
||||
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {}
|
||||
|
||||
fn enter_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>) {
|
||||
fn enter_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
}
|
||||
fn exit_fragment_spread(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>) {
|
||||
fn exit_fragment_spread(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_inline_fragment(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>) {
|
||||
fn enter_inline_fragment(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
}
|
||||
fn exit_inline_fragment(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>) {
|
||||
fn exit_inline_fragment(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {}
|
||||
|
@ -93,30 +117,42 @@ pub trait Visitor<'a> {
|
|||
fn enter_variable_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
|
||||
fn exit_variable_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
|
||||
|
||||
fn enter_list_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<Spanning<InputValue>>>) {
|
||||
fn enter_list_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<Spanning<InputValue>>>,
|
||||
) {
|
||||
}
|
||||
fn exit_list_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<Spanning<InputValue>>>) {
|
||||
fn exit_list_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<Spanning<InputValue>>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_object_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn enter_object_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
}
|
||||
fn exit_object_value(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
|
||||
fn exit_object_value(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn enter_object_field(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<String>, Spanning<InputValue>)) {
|
||||
fn enter_object_field(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<String>, Spanning<InputValue>),
|
||||
) {
|
||||
}
|
||||
fn exit_object_field(&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<String>, Spanning<InputValue>)) {
|
||||
fn exit_object_field(
|
||||
&mut self,
|
||||
_: &mut ValidatorContext<'a>,
|
||||
_: &'a (Spanning<String>, Spanning<InputValue>),
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use ast::{Definition, Document, Fragment, VariableDefinitions, Type, InputValue, Directive,
|
||||
Arguments, Selection, Field, FragmentSpread, InlineFragment, Operation, OperationType};
|
||||
use ast::{Arguments, Definition, Directive, Document, Field, Fragment, FragmentSpread,
|
||||
InlineFragment, InputValue, Operation, OperationType, Selection, Type,
|
||||
VariableDefinitions};
|
||||
use schema::meta::Argument;
|
||||
use parser::Spanning;
|
||||
use validation::{Visitor, ValidatorContext};
|
||||
use validation::{ValidatorContext, Visitor};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, d: &'a Document) {
|
||||
|
@ -11,32 +12,39 @@ pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, d: &
|
|||
v.exit_document(ctx, d);
|
||||
}
|
||||
|
||||
fn visit_definitions<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
d: &'a Vec<Definition>) {
|
||||
fn visit_definitions<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
d: &'a Vec<Definition>,
|
||||
) {
|
||||
for def in d {
|
||||
let def_type =
|
||||
match *def {
|
||||
Definition::Fragment(Spanning {
|
||||
item: Fragment {
|
||||
type_condition: Spanning { item: name, .. }, ..
|
||||
},
|
||||
..
|
||||
}) => Some(Type::NonNullNamed(name)),
|
||||
Definition::Operation(Spanning {
|
||||
item: Operation { operation_type: OperationType::Query, .. }, .. }) =>
|
||||
Some(Type::NonNullNamed(ctx.schema.concrete_query_type().name().unwrap())),
|
||||
Definition::Operation(Spanning {
|
||||
item: Operation {
|
||||
operation_type: OperationType::Mutation, ..
|
||||
},
|
||||
..
|
||||
}) => {
|
||||
ctx.schema
|
||||
.concrete_mutation_type()
|
||||
.map(|t| Type::NonNullNamed(t.name().unwrap()))
|
||||
}
|
||||
};
|
||||
let def_type = match *def {
|
||||
Definition::Fragment(Spanning {
|
||||
item: Fragment {
|
||||
type_condition: Spanning { item: name, .. },
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => Some(Type::NonNullNamed(name)),
|
||||
Definition::Operation(Spanning {
|
||||
item: Operation {
|
||||
operation_type: OperationType::Query,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => Some(Type::NonNullNamed(
|
||||
ctx.schema.concrete_query_type().name().unwrap(),
|
||||
)),
|
||||
Definition::Operation(Spanning {
|
||||
item: Operation {
|
||||
operation_type: OperationType::Mutation,
|
||||
..
|
||||
},
|
||||
..
|
||||
}) => ctx.schema
|
||||
.concrete_mutation_type()
|
||||
.map(|t| Type::NonNullNamed(t.name().unwrap())),
|
||||
};
|
||||
|
||||
ctx.with_pushed_type(def_type.as_ref(), |ctx| {
|
||||
enter_definition(v, ctx, def);
|
||||
|
@ -46,27 +54,33 @@ fn visit_definitions<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn enter_definition<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition) {
|
||||
fn enter_definition<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition,
|
||||
) {
|
||||
match *def {
|
||||
Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
|
||||
Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f),
|
||||
}
|
||||
}
|
||||
|
||||
fn exit_definition<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition) {
|
||||
fn exit_definition<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition,
|
||||
) {
|
||||
match *def {
|
||||
Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
|
||||
Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_definition<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition) {
|
||||
fn visit_definition<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
def: &'a Definition,
|
||||
) {
|
||||
match *def {
|
||||
Definition::Operation(ref op) => {
|
||||
visit_variable_definitions(v, ctx, &op.item.variable_definitions);
|
||||
|
@ -80,9 +94,11 @@ fn visit_definition<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_variable_definitions<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
defs: &'a Option<Spanning<VariableDefinitions>>) {
|
||||
fn visit_variable_definitions<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
defs: &'a Option<Spanning<VariableDefinitions>>,
|
||||
) {
|
||||
if let Some(ref defs) = *defs {
|
||||
for def in defs.item.iter() {
|
||||
let var_type = def.1.var_type.item.clone();
|
||||
|
@ -100,9 +116,11 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_directives<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directives: &'a Option<Vec<Spanning<Directive>>>) {
|
||||
fn visit_directives<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
directives: &'a Option<Vec<Spanning<Directive>>>,
|
||||
) {
|
||||
if let Some(ref directives) = *directives {
|
||||
for directive in directives {
|
||||
let directive_arguments = ctx.schema
|
||||
|
@ -116,10 +134,12 @@ fn visit_directives<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_arguments<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
meta_args: &Option<&Vec<Argument<'a>>>,
|
||||
arguments: &'a Option<Spanning<Arguments>>) {
|
||||
fn visit_arguments<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
meta_args: &Option<&Vec<Argument<'a>>>,
|
||||
arguments: &'a Option<Spanning<Arguments>>,
|
||||
) {
|
||||
if let Some(ref arguments) = *arguments {
|
||||
for argument in arguments.item.iter() {
|
||||
let arg_type = meta_args
|
||||
|
@ -137,9 +157,11 @@ fn visit_arguments<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_selection_set<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
selection_set: &'a Vec<Selection>) {
|
||||
fn visit_selection_set<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
selection_set: &'a Vec<Selection>,
|
||||
) {
|
||||
ctx.with_pushed_parent_type(|ctx| {
|
||||
v.enter_selection_set(ctx, selection_set);
|
||||
|
||||
|
@ -151,9 +173,11 @@ fn visit_selection_set<'a, V: Visitor<'a>>(v: &mut V,
|
|||
});
|
||||
}
|
||||
|
||||
fn visit_selection<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
selection: &'a Selection) {
|
||||
fn visit_selection<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
selection: &'a Selection,
|
||||
) {
|
||||
match *selection {
|
||||
Selection::Field(ref field) => visit_field(v, ctx, field),
|
||||
Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread),
|
||||
|
@ -161,9 +185,11 @@ fn visit_selection<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn visit_field<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
field: &'a Spanning<Field>) {
|
||||
fn visit_field<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
field: &'a Spanning<Field>,
|
||||
) {
|
||||
let meta_field = ctx.parent_type()
|
||||
.and_then(|t| t.field_by_name(field.item.name.item));
|
||||
|
||||
|
@ -184,9 +210,11 @@ fn visit_field<'a, V: Visitor<'a>>(v: &mut V,
|
|||
});
|
||||
}
|
||||
|
||||
fn visit_fragment_spread<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>) {
|
||||
fn visit_fragment_spread<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
spread: &'a Spanning<FragmentSpread>,
|
||||
) {
|
||||
v.enter_fragment_spread(ctx, spread);
|
||||
|
||||
visit_directives(v, ctx, &spread.item.directives);
|
||||
|
@ -194,9 +222,11 @@ fn visit_fragment_spread<'a, V: Visitor<'a>>(v: &mut V,
|
|||
v.exit_fragment_spread(ctx, spread);
|
||||
}
|
||||
|
||||
fn visit_inline_fragment<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<InlineFragment>) {
|
||||
fn visit_inline_fragment<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
fragment: &'a Spanning<InlineFragment>,
|
||||
) {
|
||||
let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| {
|
||||
v.enter_inline_fragment(ctx, fragment);
|
||||
|
||||
|
@ -206,44 +236,48 @@ fn visit_inline_fragment<'a, V: Visitor<'a>>(v: &mut V,
|
|||
v.exit_inline_fragment(ctx, fragment);
|
||||
};
|
||||
|
||||
if let Some(Spanning { item: type_name, .. }) = fragment.item.type_condition {
|
||||
if let Some(Spanning {
|
||||
item: type_name, ..
|
||||
}) = fragment.item.type_condition
|
||||
{
|
||||
ctx.with_pushed_type(Some(&Type::NonNullNamed(type_name)), visit_fn);
|
||||
} else {
|
||||
visit_fn(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>) {
|
||||
fn visit_input_value<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>,
|
||||
) {
|
||||
enter_input_value(v, ctx, input_value);
|
||||
|
||||
match input_value.item {
|
||||
InputValue::Object(ref fields) => {
|
||||
for field in fields {
|
||||
let inner_type = ctx.current_input_type_literal()
|
||||
.and_then(|t| match *t {
|
||||
Type::NonNullNamed(name) |
|
||||
Type::Named(name) => ctx.schema.concrete_type_by_name(name),
|
||||
_ => None,
|
||||
})
|
||||
.and_then(|ct| ct.input_field_by_name(&field.0.item))
|
||||
.map(|f| &f.arg_type);
|
||||
|
||||
ctx.with_pushed_input_type(inner_type, |ctx| {
|
||||
v.enter_object_field(ctx, field);
|
||||
visit_input_value(v, ctx, &field.1);
|
||||
v.exit_object_field(ctx, field);
|
||||
})
|
||||
}
|
||||
}
|
||||
InputValue::List(ref ls) => {
|
||||
InputValue::Object(ref fields) => for field in fields {
|
||||
let inner_type = ctx.current_input_type_literal()
|
||||
.and_then(|t| match *t {
|
||||
Type::List(ref inner) |
|
||||
Type::NonNullList(ref inner) => Some(inner.as_ref().clone()),
|
||||
_ => None,
|
||||
});
|
||||
Type::NonNullNamed(name) | Type::Named(name) => {
|
||||
ctx.schema.concrete_type_by_name(name)
|
||||
}
|
||||
_ => None,
|
||||
})
|
||||
.and_then(|ct| ct.input_field_by_name(&field.0.item))
|
||||
.map(|f| &f.arg_type);
|
||||
|
||||
ctx.with_pushed_input_type(inner_type, |ctx| {
|
||||
v.enter_object_field(ctx, field);
|
||||
visit_input_value(v, ctx, &field.1);
|
||||
v.exit_object_field(ctx, field);
|
||||
})
|
||||
},
|
||||
InputValue::List(ref ls) => {
|
||||
let inner_type = ctx.current_input_type_literal().and_then(|t| match *t {
|
||||
Type::List(ref inner) | Type::NonNullList(ref inner) => {
|
||||
Some(inner.as_ref().clone())
|
||||
}
|
||||
_ => None,
|
||||
});
|
||||
|
||||
ctx.with_pushed_input_type(inner_type.as_ref(), |ctx| for value in ls {
|
||||
visit_input_value(v, ctx, value);
|
||||
|
@ -255,9 +289,11 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V,
|
|||
exit_input_value(v, ctx, input_value);
|
||||
}
|
||||
|
||||
fn enter_input_value<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>) {
|
||||
fn enter_input_value<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>,
|
||||
) {
|
||||
use InputValue::*;
|
||||
|
||||
let start = &input_value.start;
|
||||
|
@ -276,9 +312,11 @@ fn enter_input_value<'a, V: Visitor<'a>>(v: &mut V,
|
|||
}
|
||||
}
|
||||
|
||||
fn exit_input_value<'a, V: Visitor<'a>>(v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>) {
|
||||
fn exit_input_value<'a, V: Visitor<'a>>(
|
||||
v: &mut V,
|
||||
ctx: &mut ValidatorContext<'a>,
|
||||
input_value: &'a Spanning<InputValue>,
|
||||
) {
|
||||
use InputValue::*;
|
||||
|
||||
let start = &input_value.start;
|
||||
|
|
|
@ -60,7 +60,8 @@ impl Value {
|
|||
|
||||
/// Construct an object value.
|
||||
pub fn object<K>(o: HashMap<K, Value>) -> Value
|
||||
where K: Into<String> + Eq + Hash
|
||||
where
|
||||
K: Into<String> + Eq + Hash,
|
||||
{
|
||||
Value::Object(o.into_iter().map(|(k, v)| (k.into(), v)).collect())
|
||||
}
|
||||
|
@ -119,14 +120,13 @@ impl ToInputValue for Value {
|
|||
Value::List(ref l) => {
|
||||
InputValue::List(l.iter().map(|x| Spanning::unlocated(x.to())).collect())
|
||||
}
|
||||
Value::Object(ref o) => {
|
||||
InputValue::Object(o.iter()
|
||||
.map(|(k, v)| {
|
||||
(Spanning::unlocated(k.clone()),
|
||||
Spanning::unlocated(v.to()))
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
Value::Object(ref o) => InputValue::Object(
|
||||
o.iter()
|
||||
.map(|(k, v)| {
|
||||
(Spanning::unlocated(k.clone()), Spanning::unlocated(v.to()))
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use syn;
|
|||
use syn::*;
|
||||
use quote::Tokens;
|
||||
|
||||
use ::util::*;
|
||||
use util::*;
|
||||
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -28,7 +28,8 @@ impl EnumAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -63,7 +64,8 @@ impl EnumVariantAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -76,7 +78,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> Tokens {
|
|||
Body::Enum(ref var) => var,
|
||||
Body::Struct(_) => {
|
||||
panic!("#[derive(GraphlQLEnum)] may only be applied to enums, not to structs");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Parse attributes.
|
||||
|
@ -98,13 +100,16 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> Tokens {
|
|||
if variant.data != VariantData::Unit {
|
||||
panic!(format!(
|
||||
"Invalid enum variant {}.\nGraphQL enums may only contain unit variants.",
|
||||
variant.ident));
|
||||
variant.ident
|
||||
));
|
||||
}
|
||||
let var_attrs = EnumVariantAttrs::from_input(variant);
|
||||
let var_ident = &variant.ident;
|
||||
|
||||
// Build value.
|
||||
let name = var_attrs.name.unwrap_or(variant.ident.as_ref().to_uppercase());
|
||||
let name = var_attrs
|
||||
.name
|
||||
.unwrap_or(variant.ident.as_ref().to_uppercase());
|
||||
let descr = match var_attrs.description {
|
||||
Some(s) => quote!{ Some(#s.to_string()) },
|
||||
None => quote!{ None },
|
||||
|
@ -140,7 +145,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> Tokens {
|
|||
::juniper::InputValue::string(#name.to_string()),
|
||||
};
|
||||
to_inputs.push(to_input);
|
||||
};
|
||||
}
|
||||
|
||||
quote! {
|
||||
impl ::juniper::GraphQLType for #ident {
|
||||
|
|
|
@ -2,7 +2,7 @@ use syn;
|
|||
use syn::*;
|
||||
use quote::Tokens;
|
||||
|
||||
use ::util::*;
|
||||
use util::*;
|
||||
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -28,7 +28,8 @@ impl ObjAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLInputObject)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -63,7 +64,8 @@ impl ObjFieldAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLInputObject)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -72,17 +74,17 @@ impl ObjFieldAttrs {
|
|||
|
||||
pub fn impl_input_object(ast: &syn::DeriveInput) -> Tokens {
|
||||
let fields = match ast.body {
|
||||
Body::Struct(ref data) => {
|
||||
match data {
|
||||
&VariantData::Struct(ref fields) => fields,
|
||||
_ => {
|
||||
panic!("#[derive(GraphQLInputObject)] may only be used on regular structs with fields");
|
||||
},
|
||||
Body::Struct(ref data) => match data {
|
||||
&VariantData::Struct(ref fields) => fields,
|
||||
_ => {
|
||||
panic!(
|
||||
"#[derive(GraphQLInputObject)] may only be used on regular structs with fields"
|
||||
);
|
||||
}
|
||||
},
|
||||
Body::Enum(_) => {
|
||||
panic!("#[derive(GraphlQLInputObject)] may only be applied to structs, not to enums");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Parse attributes.
|
||||
|
@ -109,11 +111,11 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> Tokens {
|
|||
Some(ref name) => {
|
||||
// Custom name specified.
|
||||
name.to_string()
|
||||
},
|
||||
}
|
||||
None => {
|
||||
// Note: auto camel casing when no custom name specified.
|
||||
::util::to_camel_case(field_ident.as_ref())
|
||||
},
|
||||
}
|
||||
};
|
||||
let field_description = match field_attrs.description {
|
||||
Some(s) => quote!{ let field = field.description(#s); },
|
||||
|
@ -121,12 +123,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> Tokens {
|
|||
};
|
||||
|
||||
let default = match field_attrs.default {
|
||||
Some(ref def) => {
|
||||
match syn::parse_token_trees(def) {
|
||||
Ok(t) => Some(quote!{ #(#t)* }),
|
||||
Err(_) => {
|
||||
panic!("#graphql(default = ?) must be a valid Rust expression inside a string");
|
||||
},
|
||||
Some(ref def) => match syn::parse_token_trees(def) {
|
||||
Ok(t) => Some(quote!{ #(#t)* }),
|
||||
Err(_) => {
|
||||
panic!("#graphql(default = ?) must be a valid Rust expression inside a string");
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
|
@ -137,7 +137,7 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> Tokens {
|
|||
quote!{
|
||||
let field = registry.arg_with_default::<#field_ty>( #name, &#def);
|
||||
}
|
||||
},
|
||||
}
|
||||
None => {
|
||||
quote!{
|
||||
let field = registry.arg::<#field_ty>(#name);
|
||||
|
@ -160,7 +160,7 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> Tokens {
|
|||
quote!{
|
||||
Some(&&::juniper::InputValue::Null) | None if true => #def,
|
||||
}
|
||||
},
|
||||
}
|
||||
None => quote!{},
|
||||
};
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ use syn;
|
|||
use syn::*;
|
||||
use quote::Tokens;
|
||||
|
||||
use ::util::*;
|
||||
use util::*;
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct ObjAttrs {
|
||||
|
@ -27,7 +27,8 @@ impl ObjAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLObject)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -62,7 +63,8 @@ impl ObjFieldAttrs {
|
|||
}
|
||||
panic!(format!(
|
||||
"Unknown attribute for #[derive(GraphQLObject)]: {:?}",
|
||||
item));
|
||||
item
|
||||
));
|
||||
}
|
||||
}
|
||||
res
|
||||
|
@ -71,17 +73,15 @@ impl ObjFieldAttrs {
|
|||
|
||||
pub fn impl_object(ast: &syn::DeriveInput) -> Tokens {
|
||||
let fields = match ast.body {
|
||||
Body::Struct(ref data) => {
|
||||
match data {
|
||||
&VariantData::Struct(ref fields) => fields,
|
||||
_ => {
|
||||
panic!("#[derive(GraphQLObject)] may only be used on regular structs with fields");
|
||||
},
|
||||
Body::Struct(ref data) => match data {
|
||||
&VariantData::Struct(ref fields) => fields,
|
||||
_ => {
|
||||
panic!("#[derive(GraphQLObject)] may only be used on regular structs with fields");
|
||||
}
|
||||
},
|
||||
Body::Enum(_) => {
|
||||
panic!("#[derive(GraphlQLObject)] may only be applied to structs, not to enums");
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// Parse attributes.
|
||||
|
@ -107,11 +107,11 @@ pub fn impl_object(ast: &syn::DeriveInput) -> Tokens {
|
|||
Some(ref name) => {
|
||||
// Custom name specified.
|
||||
name.to_string()
|
||||
},
|
||||
}
|
||||
None => {
|
||||
// Note: auto camel casing when no custom name specified.
|
||||
::util::to_camel_case(field_ident.as_ref())
|
||||
},
|
||||
}
|
||||
};
|
||||
let build_description = match field_attrs.description {
|
||||
Some(s) => quote!{ field.description(#s) },
|
||||
|
|
|
@ -3,46 +3,41 @@ use syn::*;
|
|||
pub fn get_graphl_attr(attrs: &Vec<Attribute>) -> Option<&Vec<NestedMetaItem>> {
|
||||
for attr in attrs {
|
||||
match attr.value {
|
||||
MetaItem::List(ref attr_name, ref items) => {
|
||||
if attr_name == "graphql" {
|
||||
return Some(items);
|
||||
}
|
||||
MetaItem::List(ref attr_name, ref items) => if attr_name == "graphql" {
|
||||
return Some(items);
|
||||
},
|
||||
_ => {},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn keyed_item_value(item: &NestedMetaItem, name: &str, must_be_string: bool)
|
||||
-> Option<String>
|
||||
{
|
||||
pub fn keyed_item_value(item: &NestedMetaItem, name: &str, must_be_string: bool) -> Option<String> {
|
||||
let item = match item {
|
||||
&NestedMetaItem::MetaItem(ref item) => item,
|
||||
_ => { return None; }
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
let lit = match item {
|
||||
&MetaItem::NameValue(ref ident, ref lit) => {
|
||||
if ident == name {
|
||||
lit
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
_ => { return None; },
|
||||
};
|
||||
match lit {
|
||||
&Lit::Str(ref val, _) => {
|
||||
Some(val.clone())
|
||||
&MetaItem::NameValue(ref ident, ref lit) => if ident == name {
|
||||
lit
|
||||
} else {
|
||||
return None;
|
||||
},
|
||||
_ => {
|
||||
if must_be_string {
|
||||
panic!(format!(
|
||||
"Invalid format for attribute \"{:?}\": expected a string",
|
||||
item));
|
||||
} else {
|
||||
None
|
||||
}
|
||||
return None;
|
||||
}
|
||||
};
|
||||
match lit {
|
||||
&Lit::Str(ref val, _) => Some(val.clone()),
|
||||
_ => if must_be_string {
|
||||
panic!(format!(
|
||||
"Invalid format for attribute \"{:?}\": expected a string",
|
||||
item
|
||||
));
|
||||
} else {
|
||||
None
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -55,15 +50,17 @@ pub fn to_camel_case(s: &str) -> String {
|
|||
for (i, part) in s.split('_').enumerate() {
|
||||
if i > 0 && part.len() == 1 {
|
||||
dest.push_str(&part.to_uppercase());
|
||||
}
|
||||
else if i > 0 && part.len() > 1 {
|
||||
let first = part.chars().next().unwrap().to_uppercase().collect::<String>();
|
||||
} else if i > 0 && part.len() > 1 {
|
||||
let first = part.chars()
|
||||
.next()
|
||||
.unwrap()
|
||||
.to_uppercase()
|
||||
.collect::<String>();
|
||||
let second = &part[1..];
|
||||
|
||||
dest.push_str(&first);
|
||||
dest.push_str(second);
|
||||
}
|
||||
else if i == 0 {
|
||||
} else if i == 0 {
|
||||
dest.push_str(part);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -96,15 +96,17 @@ There's also a built-in [GraphiQL][4] handler included.
|
|||
extern crate serde_json;
|
||||
extern crate juniper;
|
||||
extern crate urlencoded;
|
||||
#[macro_use] extern crate iron;
|
||||
#[cfg(test)] extern crate iron_test;
|
||||
#[macro_use]
|
||||
extern crate iron;
|
||||
#[cfg(test)]
|
||||
extern crate iron_test;
|
||||
|
||||
use iron::prelude::*;
|
||||
use iron::middleware::Handler;
|
||||
use iron::mime::Mime;
|
||||
use iron::status;
|
||||
use iron::method;
|
||||
use urlencoded::{UrlEncodedQuery, UrlDecodingError};
|
||||
use urlencoded::{UrlDecodingError, UrlEncodedQuery};
|
||||
|
||||
use std::io::Read;
|
||||
use std::error::Error;
|
||||
|
@ -112,7 +114,7 @@ use std::fmt;
|
|||
|
||||
use serde_json::error::Error as SerdeError;
|
||||
|
||||
use juniper::{InputValue, GraphQLType, RootNode};
|
||||
use juniper::{GraphQLType, InputValue, RootNode};
|
||||
use juniper::http;
|
||||
|
||||
/// Handler that executes GraphQL queries in the given schema
|
||||
|
@ -126,10 +128,11 @@ use juniper::http;
|
|||
/// The variables should be a JSON object containing the variable to value
|
||||
/// mapping.
|
||||
pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static,
|
||||
where
|
||||
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
{
|
||||
context_factory: CtxFactory,
|
||||
root_node: RootNode<'a, Query, Mutation>,
|
||||
|
@ -144,39 +147,39 @@ pub struct GraphiQLHandler {
|
|||
fn get_single_value<T>(mut values: Vec<T>) -> IronResult<T> {
|
||||
if values.len() == 1 {
|
||||
Ok(values.remove(0))
|
||||
} else {
|
||||
Err(
|
||||
GraphQLIronError::InvalidData("Duplicate URL query parameter").into(),
|
||||
)
|
||||
}
|
||||
else {
|
||||
Err(GraphQLIronError::InvalidData("Duplicate URL query parameter").into())
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_url_param(params: Option<Vec<String>>) -> IronResult<Option<String>> {
|
||||
if let Some(values) = params {
|
||||
get_single_value(values).map(Some)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputValue>> {
|
||||
if let Some(values) = params {
|
||||
Ok(serde_json::from_str::<InputValue>(get_single_value(values)?.as_ref())
|
||||
.map(Some)
|
||||
Ok(serde_json::from_str::<InputValue>(
|
||||
get_single_value(values)?.as_ref(),
|
||||
).map(Some)
|
||||
.map_err(GraphQLIronError::Serde)?)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static,
|
||||
impl<'a, CtxFactory, Query, Mutation, CtxT> GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
where
|
||||
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
{
|
||||
/// Build a new GraphQL handler
|
||||
///
|
||||
|
@ -201,25 +204,31 @@ GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
|||
let operation_name = parse_url_param(url_query_string.remove("operationName"))?;
|
||||
let variables = parse_variable_param(url_query_string.remove("variables"))?;
|
||||
|
||||
Ok(http::GraphQLRequest::new(input_query, operation_name, variables))
|
||||
Ok(http::GraphQLRequest::new(
|
||||
input_query,
|
||||
operation_name,
|
||||
variables,
|
||||
))
|
||||
}
|
||||
|
||||
fn handle_post(&self, req: &mut Request) -> IronResult<http::GraphQLRequest> {
|
||||
let mut request_payload = String::new();
|
||||
itry!(req.body.read_to_string(&mut request_payload));
|
||||
|
||||
Ok(serde_json::from_str::<http::GraphQLRequest>(request_payload.as_str())
|
||||
.map_err(|err| GraphQLIronError::Serde(err))?)
|
||||
Ok(serde_json::from_str::<http::GraphQLRequest>(
|
||||
request_payload.as_str(),
|
||||
).map_err(|err| GraphQLIronError::Serde(err))?)
|
||||
}
|
||||
|
||||
fn execute(&self, context: &CtxT, request: http::GraphQLRequest) -> IronResult<Response> {
|
||||
let response = request.execute(
|
||||
&self.root_node,
|
||||
context,
|
||||
);
|
||||
let response = request.execute(&self.root_node, context);
|
||||
let content_type = "application/json".parse::<Mime>().unwrap();
|
||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
||||
let status = if response.is_ok() { status::Ok } else { status::BadRequest };
|
||||
let status = if response.is_ok() {
|
||||
status::Ok
|
||||
} else {
|
||||
status::BadRequest
|
||||
};
|
||||
Ok(Response::with((content_type, status, json)))
|
||||
}
|
||||
}
|
||||
|
@ -236,13 +245,14 @@ impl GraphiQLHandler {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
Handler
|
||||
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context=CtxT> + Send + Sync + 'static, 'a: 'static,
|
||||
impl<'a, CtxFactory, Query, Mutation, CtxT> Handler
|
||||
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
||||
where
|
||||
CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
|
||||
CtxT: 'static,
|
||||
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static,
|
||||
'a: 'static,
|
||||
{
|
||||
fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
|
||||
let context = (self.context_factory)(req);
|
||||
|
@ -250,7 +260,7 @@ for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
|
|||
let graphql_request = match req.method {
|
||||
method::Get => self.handle_get(&mut req)?,
|
||||
method::Post => self.handle_post(&mut req)?,
|
||||
_ => return Ok(Response::with((status::MethodNotAllowed)))
|
||||
_ => return Ok(Response::with((status::MethodNotAllowed))),
|
||||
};
|
||||
|
||||
self.execute(&context, graphql_request)
|
||||
|
@ -318,15 +328,14 @@ mod tests {
|
|||
use iron::{Handler, Headers};
|
||||
|
||||
use juniper::tests::model::Database;
|
||||
use ::http::tests as http_tests;
|
||||
use http::tests as http_tests;
|
||||
use juniper::EmptyMutation;
|
||||
|
||||
use super::GraphQLHandler;
|
||||
|
||||
struct TestIronIntegration;
|
||||
|
||||
impl http_tests::HTTPIntegration for TestIronIntegration
|
||||
{
|
||||
impl http_tests::HTTPIntegration for TestIronIntegration {
|
||||
fn get(&self, url: &str) -> http_tests::TestResponse {
|
||||
make_test_response(request::get(
|
||||
&("http://localhost:3000".to_owned() + url),
|
||||
|
@ -358,11 +367,17 @@ mod tests {
|
|||
|
||||
fn make_test_response(response: IronResult<Response>) -> http_tests::TestResponse {
|
||||
let response = response.expect("Error response from GraphQL handler");
|
||||
let status_code = response.status.expect("No status code returned from handler").to_u16() as i32;
|
||||
let status_code = response
|
||||
.status
|
||||
.expect("No status code returned from handler")
|
||||
.to_u16() as i32;
|
||||
let content_type = String::from_utf8(
|
||||
response.headers.get_raw("content-type")
|
||||
.expect("No content type header from handler")[0].clone())
|
||||
.expect("Content-type header invalid UTF-8");
|
||||
response
|
||||
.headers
|
||||
.get_raw("content-type")
|
||||
.expect("No content type header from handler")[0]
|
||||
.clone(),
|
||||
).expect("Content-type header invalid UTF-8");
|
||||
let body = response::extract_body_to_string(response);
|
||||
|
||||
http_tests::TestResponse {
|
||||
|
|
|
@ -27,7 +27,7 @@ fn get_graphql_handler(
|
|||
request.execute(&schema, &context)
|
||||
}
|
||||
|
||||
#[post("/graphql", data="<request>")]
|
||||
#[post("/graphql", data = "<request>")]
|
||||
fn post_graphql_handler(
|
||||
context: State<Database>,
|
||||
request: juniper_rocket::GraphQLRequest,
|
||||
|
@ -39,7 +39,13 @@ fn post_graphql_handler(
|
|||
fn main() {
|
||||
rocket::ignite()
|
||||
.manage(Database::new())
|
||||
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
|
||||
.mount("/", routes![graphiql, get_graphql_handler, post_graphql_handler])
|
||||
.manage(Schema::new(
|
||||
Database::new(),
|
||||
EmptyMutation::<Database>::new(),
|
||||
))
|
||||
.mount(
|
||||
"/",
|
||||
routes![graphiql, get_graphql_handler, post_graphql_handler],
|
||||
)
|
||||
.launch();
|
||||
}
|
||||
|
|
|
@ -9,12 +9,12 @@ use std::io::{Cursor, Read};
|
|||
use std::error::Error;
|
||||
|
||||
use rocket::Request;
|
||||
use rocket::request::{FromForm, FormItems};
|
||||
use rocket::request::{FormItems, FromForm};
|
||||
use rocket::data::{FromData, Outcome as FromDataOutcome};
|
||||
use rocket::response::{Responder, Response, content};
|
||||
use rocket::response::{content, Responder, Response};
|
||||
use rocket::http::{ContentType, Status};
|
||||
use rocket::Data;
|
||||
use rocket::Outcome::{Forward, Failure, Success};
|
||||
use rocket::Outcome::{Failure, Forward, Success};
|
||||
|
||||
use juniper::InputValue;
|
||||
use juniper::http;
|
||||
|
@ -43,13 +43,17 @@ impl GraphQLRequest {
|
|||
&self,
|
||||
root_node: &RootNode<QueryT, MutationT>,
|
||||
context: &CtxT,
|
||||
)
|
||||
-> GraphQLResponse
|
||||
where QueryT: GraphQLType<Context=CtxT>,
|
||||
MutationT: GraphQLType<Context=CtxT>,
|
||||
) -> GraphQLResponse
|
||||
where
|
||||
QueryT: GraphQLType<Context = CtxT>,
|
||||
MutationT: GraphQLType<Context = CtxT>,
|
||||
{
|
||||
let response = self.0.execute(root_node, context);
|
||||
let status = if response.is_ok() { Status::Ok } else { Status::BadRequest };
|
||||
let status = if response.is_ok() {
|
||||
Status::Ok
|
||||
} else {
|
||||
Status::BadRequest
|
||||
};
|
||||
let json = serde_json::to_string_pretty(&response).unwrap();
|
||||
|
||||
GraphQLResponse(status, json)
|
||||
|
@ -59,57 +63,44 @@ impl GraphQLRequest {
|
|||
impl<'f> FromForm<'f> for GraphQLRequest {
|
||||
type Error = String;
|
||||
|
||||
fn from_form(
|
||||
form_items: &mut FormItems<'f>,
|
||||
strict: bool
|
||||
) -> Result<Self, String> {
|
||||
fn from_form(form_items: &mut FormItems<'f>, strict: bool) -> Result<Self, String> {
|
||||
let mut query = None;
|
||||
let mut operation_name = None;
|
||||
let mut variables = None;
|
||||
|
||||
for (key, value) in form_items {
|
||||
match key.as_str() {
|
||||
"query" => {
|
||||
if query.is_some() {
|
||||
return Err("Query parameter must not occur more than once".to_owned());
|
||||
}
|
||||
else {
|
||||
query = Some(value.as_str().to_string());
|
||||
}
|
||||
}
|
||||
"operation_name" => {
|
||||
if operation_name.is_some() {
|
||||
return Err("Operation name parameter must not occur more than once".to_owned());
|
||||
}
|
||||
else {
|
||||
operation_name = Some(value.as_str().to_string());
|
||||
}
|
||||
}
|
||||
"variables" => {
|
||||
if variables.is_some() {
|
||||
return Err("Variables parameter must not occur more than once".to_owned());
|
||||
}
|
||||
else {
|
||||
variables = Some(serde_json::from_str::<InputValue>(value.as_str())
|
||||
.map_err(|err| err.description().to_owned())?);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
if strict {
|
||||
return Err(format!("Prohibited extra field '{}'", key).to_owned());
|
||||
}
|
||||
}
|
||||
"query" => if query.is_some() {
|
||||
return Err("Query parameter must not occur more than once".to_owned());
|
||||
} else {
|
||||
query = Some(value.as_str().to_string());
|
||||
},
|
||||
"operation_name" => if operation_name.is_some() {
|
||||
return Err(
|
||||
"Operation name parameter must not occur more than once".to_owned(),
|
||||
);
|
||||
} else {
|
||||
operation_name = Some(value.as_str().to_string());
|
||||
},
|
||||
"variables" => if variables.is_some() {
|
||||
return Err(
|
||||
"Variables parameter must not occur more than once".to_owned(),
|
||||
);
|
||||
} else {
|
||||
variables = Some(serde_json::from_str::<InputValue>(value.as_str())
|
||||
.map_err(|err| err.description().to_owned())?);
|
||||
},
|
||||
_ => if strict {
|
||||
return Err(format!("Prohibited extra field '{}'", key).to_owned());
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(query) = query {
|
||||
Ok(GraphQLRequest(http::GraphQLRequest::new(
|
||||
query,
|
||||
operation_name,
|
||||
variables
|
||||
)))
|
||||
}
|
||||
else {
|
||||
Ok(GraphQLRequest(
|
||||
http::GraphQLRequest::new(query, operation_name, variables),
|
||||
))
|
||||
} else {
|
||||
Err("Query parameter missing".to_owned())
|
||||
}
|
||||
}
|
||||
|
@ -130,9 +121,7 @@ impl FromData for GraphQLRequest {
|
|||
|
||||
match serde_json::from_str(&body) {
|
||||
Ok(value) => Success(GraphQLRequest(value)),
|
||||
Err(failure) => return Failure(
|
||||
(Status::BadRequest, format!("{}", failure)),
|
||||
),
|
||||
Err(failure) => return Failure((Status::BadRequest, format!("{}", failure))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -141,11 +130,13 @@ impl<'r> Responder<'r> for GraphQLResponse {
|
|||
fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
|
||||
let GraphQLResponse(status, body) = self;
|
||||
|
||||
Ok(Response::build()
|
||||
.header(ContentType::new("application", "json"))
|
||||
.status(status)
|
||||
.sized_body(Cursor::new(body))
|
||||
.finalize())
|
||||
Ok(
|
||||
Response::build()
|
||||
.header(ContentType::new("application", "json"))
|
||||
.status(status)
|
||||
.sized_body(Cursor::new(body))
|
||||
.finalize(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,7 +165,7 @@ mod tests {
|
|||
request.execute(&schema, &context)
|
||||
}
|
||||
|
||||
#[post("/", data="<request>")]
|
||||
#[post("/", data = "<request>")]
|
||||
fn post_graphql_handler(
|
||||
context: State<Database>,
|
||||
request: super::GraphQLRequest,
|
||||
|
|
|
@ -1,49 +1,39 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use juniper::{self, InputValue, ToInputValue, GraphQLType, FromInputValue};
|
||||
use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue};
|
||||
|
||||
#[derive(GraphQLEnum, Debug, PartialEq)]
|
||||
#[graphql(name="Some", description="enum descr")]
|
||||
#[graphql(name = "Some", description = "enum descr")]
|
||||
enum SomeEnum {
|
||||
Regular,
|
||||
Regular,
|
||||
|
||||
#[graphql(
|
||||
name="FULL",
|
||||
description="field descr",
|
||||
deprecated="depr"
|
||||
)]
|
||||
Full,
|
||||
#[graphql(name = "FULL", description = "field descr", deprecated = "depr")]
|
||||
Full,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derived_enum() {
|
||||
// Ensure that rename works.
|
||||
assert_eq!(SomeEnum::name(), Some("Some"));
|
||||
// Ensure that rename works.
|
||||
assert_eq!(SomeEnum::name(), Some("Some"));
|
||||
|
||||
// Ensure validity of meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = SomeEnum::meta(&mut registry);
|
||||
// Ensure validity of meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = SomeEnum::meta(&mut registry);
|
||||
|
||||
assert_eq!(meta.name(), Some("Some"));
|
||||
assert_eq!(meta.description(), Some(&"enum descr".to_string()));
|
||||
assert_eq!(meta.name(), Some("Some"));
|
||||
assert_eq!(meta.description(), Some(&"enum descr".to_string()));
|
||||
|
||||
// Test Regular variant.
|
||||
assert_eq!(
|
||||
SomeEnum::Regular.to(),
|
||||
InputValue::String("REGULAR".into())
|
||||
);
|
||||
assert_eq!(
|
||||
FromInputValue::from(&InputValue::String("REGULAR".into())),
|
||||
Some(SomeEnum::Regular)
|
||||
);
|
||||
// Test Regular variant.
|
||||
assert_eq!(SomeEnum::Regular.to(), InputValue::String("REGULAR".into()));
|
||||
assert_eq!(
|
||||
FromInputValue::from(&InputValue::String("REGULAR".into())),
|
||||
Some(SomeEnum::Regular)
|
||||
);
|
||||
|
||||
// Test FULL variant.
|
||||
assert_eq!(
|
||||
SomeEnum::Full.to(),
|
||||
InputValue::String("FULL".into())
|
||||
);
|
||||
assert_eq!(
|
||||
FromInputValue::from(&InputValue::String("FULL".into())),
|
||||
Some(SomeEnum::Full)
|
||||
);
|
||||
// Test FULL variant.
|
||||
assert_eq!(SomeEnum::Full.to(), InputValue::String("FULL".into()));
|
||||
assert_eq!(
|
||||
FromInputValue::from(&InputValue::String("FULL".into())),
|
||||
Some(SomeEnum::Full)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,29 +1,29 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use juniper::{self, ToInputValue, GraphQLType, FromInputValue};
|
||||
use juniper::{self, FromInputValue, GraphQLType, ToInputValue};
|
||||
|
||||
#[derive(GraphQLInputObject, Debug, PartialEq)]
|
||||
#[graphql(name="MyInput", description="input descr")]
|
||||
#[graphql(name = "MyInput", description = "input descr")]
|
||||
struct Input {
|
||||
regular_field: String,
|
||||
#[graphql(name="haha", default="33", description="haha descr")]
|
||||
c: i32,
|
||||
regular_field: String,
|
||||
#[graphql(name = "haha", default = "33", description = "haha descr")]
|
||||
c: i32,
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_derived_input_object() {
|
||||
assert_eq!(Input::name(), Some("MyInput"));
|
||||
assert_eq!(Input::name(), Some("MyInput"));
|
||||
|
||||
// Validate meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = Input::meta(&mut registry);
|
||||
assert_eq!(meta.name(), Some("MyInput"));
|
||||
assert_eq!(meta.description(), Some(&"input descr".to_string()));
|
||||
// Validate meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = Input::meta(&mut registry);
|
||||
assert_eq!(meta.name(), Some("MyInput"));
|
||||
assert_eq!(meta.description(), Some(&"input descr".to_string()));
|
||||
|
||||
let obj = Input {
|
||||
regular_field: "a".to_string(),
|
||||
c: 33,
|
||||
};
|
||||
let restored: Input = FromInputValue::from(&obj.to()).unwrap();
|
||||
assert_eq!(obj, restored);
|
||||
let obj = Input {
|
||||
regular_field: "a".to_string(),
|
||||
c: 33,
|
||||
};
|
||||
let restored: Input = FromInputValue::from(&obj.to()).unwrap();
|
||||
assert_eq!(obj, restored);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use juniper::{self, execute, GraphQLType, Value, Variables, EmptyMutation, RootNode};
|
||||
use juniper::{self, execute, EmptyMutation, GraphQLType, RootNode, Value, Variables};
|
||||
|
||||
#[derive(GraphQLObject, Debug, PartialEq)]
|
||||
#[graphql(name="MyObj", description="obj descr")]
|
||||
#[graphql(name = "MyObj", description = "obj descr")]
|
||||
struct Obj {
|
||||
regular_field: bool,
|
||||
#[graphql(name="renamedField", description="descr", deprecation="field descr")]
|
||||
c: i32,
|
||||
regular_field: bool,
|
||||
#[graphql(name = "renamedField", description = "descr", deprecation = "field descr")]
|
||||
c: i32,
|
||||
}
|
||||
|
||||
struct Query;
|
||||
|
@ -23,16 +23,16 @@ graphql_object!(Query: () |&self| {
|
|||
|
||||
#[test]
|
||||
fn test_derived_object() {
|
||||
assert_eq!(Obj::name(), Some("MyObj"));
|
||||
assert_eq!(Obj::name(), Some("MyObj"));
|
||||
|
||||
// Verify meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = Obj::meta(&mut registry);
|
||||
// Verify meta info.
|
||||
let mut registry = juniper::Registry::new(HashMap::new());
|
||||
let meta = Obj::meta(&mut registry);
|
||||
|
||||
assert_eq!(meta.name(), Some("MyObj"));
|
||||
assert_eq!(meta.description(), Some(&"obj descr".to_string()));
|
||||
assert_eq!(meta.name(), Some("MyObj"));
|
||||
assert_eq!(meta.description(), Some(&"obj descr".to_string()));
|
||||
|
||||
let doc = r#"
|
||||
let doc = r#"
|
||||
{
|
||||
obj {
|
||||
regularField
|
||||
|
@ -40,9 +40,9 @@ fn test_derived_object() {
|
|||
}
|
||||
}"#;
|
||||
|
||||
let schema = RootNode::new(Query, EmptyMutation::<()>::new());
|
||||
let schema = RootNode::new(Query, EmptyMutation::<()>::new());
|
||||
|
||||
assert_eq!(
|
||||
assert_eq!(
|
||||
execute(doc, None, &schema, &Variables::new(), &()),
|
||||
Ok((Value::object(vec![
|
||||
("obj", Value::object(vec![
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#[macro_use] extern crate juniper;
|
||||
#[macro_use] extern crate juniper_codegen;
|
||||
#[macro_use]
|
||||
extern crate juniper;
|
||||
#[macro_use]
|
||||
extern crate juniper_codegen;
|
||||
extern crate serde_json;
|
||||
|
||||
mod codegen;
|
||||
|
|
Loading…
Reference in a new issue