Rust fmt the whole codebase

This commit is contained in:
theduke 2017-08-06 20:48:46 +02:00 committed by theduke
parent 81ce7780fa
commit 0c7e39f14e
85 changed files with 7205 additions and 4502 deletions

View file

@ -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) {

View file

@ -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}");
}

View file

@ -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()");

View file

@ -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]

View file

@ -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();

View file

@ -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, []);

View file

@ -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 {

View file

@ -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, []);

View file

@ -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();

View file

@ -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")
);
}
}

View file

@ -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(),

View file

@ -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));

View file

@ -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()

View file

@ -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, []);

View file

@ -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()

View file

@ -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);

View file

@ -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");

View file

@ -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");

View file

@ -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, []);

View file

@ -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");

View file

@ -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 {

View file

@ -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))
}
})
}
}

View file

@ -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};

View file

@ -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)),
}
}

View file

@ -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)
)
);
}

View file

@ -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\""
);

View file

@ -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"),
),
),
]),
),
),
])
)
);
}

View file

@ -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);

View file

@ -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),
))
}

View file

@ -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,

View file

@ -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",
})
}
}

View file

@ -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

View file

@ -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());
}

View file

@ -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,

View file

@ -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![]
))
);
}

View file

@ -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 {}

View file

@ -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);
}

View file

@ -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())

View file

@ -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()

View file

@ -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,
}
}
}

View file

@ -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
},
}
}
}

View file

@ -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

View file

@ -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> {

View file

@ -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};

View file

@ -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

View file

@ -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)],
),
],
);
}
}

View file

@ -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
}
}
"#);
"#,
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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 == &current_location)
.is_none() {
ctx.report_error(&misplaced_error_message(directive_name, current_location),
&[directive.start.clone()]);
.locations
.iter()
.find(|l| l == &current_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)],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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)]),
],
);
}
}

View file

@ -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)]),
],
);
}
}

View file

@ -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) {

View file

@ -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)]),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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)]),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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)],
),
],
);
}
}

View file

@ -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),
],
),
],
);
}
}

View file

@ -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);

View file

@ -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>),
) {
}
}

View file

@ -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;

View file

@ -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(),
),
}
}
}

View file

@ -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 {

View file

@ -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!{},
};

View file

@ -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) },

View file

@ -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);
}
}

View file

@ -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 {

View file

@ -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();
}

View file

@ -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,

View file

@ -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)
);
}

View file

@ -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);
}

View file

@ -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![

View file

@ -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;