Merge pull request #73 from theduke/extract-iron

Extract Iron + Rocket

* Added juniper_iron crate
* Fixed up juniper_rocket crate
* Updated juniper/Cargo.toml
* Updated docs (readme, module docs)
* Export http integrator tests with export-test-schema feature
* Update CI tests (Use cargo-make )
* Format parts of the code base
* Update rocket to 0.3
This commit is contained in:
theduke 2017-08-06 19:27:05 +02:00 committed by GitHub
commit 27048ae7cd
89 changed files with 4642 additions and 4297 deletions

View file

@ -10,53 +10,13 @@ rust:
- 1.17.0 - 1.17.0
- 1.18.0 - 1.18.0
matrix:
allow_failures:
- rust: nightly
env: env:
global: global:
- secure: "SsepHEYRmW9ee3RhxPpqGuPigZINFfA/yOwUJFseQt4t+Zs90r1xdl3Q8eDfPlnvBsL7Rd0QQrFDO7JUaimVLlgQkUnrl62o0CYzkodp+qtocyAHS00W6WTqi8Y6E6KBxPshCl03dRLaySUfx5TqTLTIHkJ0G6vDW35k7hRrA3221lRphs5rrpvAZ21pqsDsNLH3HVo792L6A0kOtBa3ocw1pgHLxnBbArIViu2htUuFvY/TgsmVbAdlow0efw/xkcJ/p0/r5q7igLek6Iqk8udfRc7CktvoiFQ2vUnhtNtQu/zYll3Q7OUx5d+w5lhbzz2QINmsezBEisH9k1haL7dMviLPp0pn4WZed60KovO0Iqfgjy1utTaKvJVfNWYsgkfU8c9a/z2rcZOKwXNKQW2ptBrtVjaB9dk7eMoyuFCDZwNtKqvG+ZKmvMpun+R8mmx+buOmN8Vlf5ygIoGxz3nbEtlLYGVTXHfdXXqRkFIwtiYVJEO7SLRKT9pbx1E++ARsi2+y8bXJT4e4z0osYMq9EsiFUpw3J2gcshrgseqkB7UgCZ3SXuitJnJNfDAU3a3nwwS/JiAunZMNnC4rKUBbl7WbTB4Cpw7EgVOlCqcyyzlkNl3xabLzTFzLOfSHLTVX5FmGNsD21vBoS5/8ejftx9wuV3rGHxuO3i3+A3k=" - secure: "SsepHEYRmW9ee3RhxPpqGuPigZINFfA/yOwUJFseQt4t+Zs90r1xdl3Q8eDfPlnvBsL7Rd0QQrFDO7JUaimVLlgQkUnrl62o0CYzkodp+qtocyAHS00W6WTqi8Y6E6KBxPshCl03dRLaySUfx5TqTLTIHkJ0G6vDW35k7hRrA3221lRphs5rrpvAZ21pqsDsNLH3HVo792L6A0kOtBa3ocw1pgHLxnBbArIViu2htUuFvY/TgsmVbAdlow0efw/xkcJ/p0/r5q7igLek6Iqk8udfRc7CktvoiFQ2vUnhtNtQu/zYll3Q7OUx5d+w5lhbzz2QINmsezBEisH9k1haL7dMviLPp0pn4WZed60KovO0Iqfgjy1utTaKvJVfNWYsgkfU8c9a/z2rcZOKwXNKQW2ptBrtVjaB9dk7eMoyuFCDZwNtKqvG+ZKmvMpun+R8mmx+buOmN8Vlf5ygIoGxz3nbEtlLYGVTXHfdXXqRkFIwtiYVJEO7SLRKT9pbx1E++ARsi2+y8bXJT4e4z0osYMq9EsiFUpw3J2gcshrgseqkB7UgCZ3SXuitJnJNfDAU3a3nwwS/JiAunZMNnC4rKUBbl7WbTB4Cpw7EgVOlCqcyyzlkNl3xabLzTFzLOfSHLTVX5FmGNsD21vBoS5/8ejftx9wuV3rGHxuO3i3+A3k="
script: script:
# Build and run tests for main juniper crate with certain features. - cargo install -f --debug cargo-make
- cd juniper - cargo make ci-flow
- cargo build --verbose
- cargo build --verbose --features iron-handlers
- |
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
cargo build --verbose --features rocket-handlers
fi
# Build example binaries; first Iron, then Rocket examples
- cargo build --verbose --example server --features "iron-handlers expose-test-schema"
- |
if [ "TRAVIS_RUST_VERSION" = "nightly" ]; then
cargo build --verbose --example rocket-server --features "rocket-handlers expose-test-schema"
fi
# Run all tests for the base library and available integrations
- export TEST_FEATURES="iron-handlers expose-test-schema"
- |
if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then
export TEST_FEATURES="$TEST_FEATURES rocket-handlers rocket/testing"
fi
- cargo test --verbose --features "$TEST_FEATURES"
- cd ..
# Build juniper_codegen and run tests.
- cd juniper_codegen
- cargo build --verbose
- cargo test
- cd ..
# Build and run integration tests.
- cd juniper_tests
- cargo build --verbose
- cargo test
before_deploy: before_deploy:
- rm -rf target/package/ - rm -rf target/package/

View file

@ -40,7 +40,7 @@ Tiny release to fix broken crate metadata on crates.io.
* A new `rocket-handlers` feature now includes some tools to use the * A new `rocket-handlers` feature now includes some tools to use the
[Rocket](https://rocket.rs) framework. [A simple [Rocket](https://rocket.rs) framework. [A simple
example](examples/rocket-server.rs) has been added to the examples folder. example](juniper_rocket/examples/rocket-server.rs) has been added to the examples folder.
## Bugfixes ## Bugfixes

View file

@ -3,4 +3,6 @@ members = [
"juniper", "juniper",
"juniper_codegen", "juniper_codegen",
"juniper_tests", "juniper_tests",
"juniper_iron",
"juniper_rocket",
] ]

View file

@ -30,20 +30,22 @@ Add Juniper to your Cargo.toml:
juniper = "0.8.1" juniper = "0.8.1"
``` ```
If you want the Iron integration enabled, you need to enable the `iron-handlers` If you want Iron integration, you need to depend on the `juniper_iron` crate.
feature flag: feature flag:
```toml ```toml
[dependencies] [dependencies]
juniper = { version = "0.8.1", features = ["iron-handlers"] } juniper = { version = "0.8.1" }
juniper_iron = { git = "https://github.com/mhallin/juniper" }
``` ```
If you want the Rocket integration enabled, you need to use the nightly Rust If you want Rocket integration, you need to depend on the `juniper_rocket` crate.
compiler and enable the `rocket-handlers` feature flag:
```toml ```toml
[dependencies] [dependencies]
juniper = { version = "0.8.1", features = ["rocket-handlers"] } juniper = { version = "0.8.1" }
juniper_rocket = { git = "https://github.com/mhallin/juniper" }
``` ```
## Building schemas ## Building schemas
@ -160,5 +162,5 @@ as well.
[graphql_spec]: http://facebook.github.io/graphql [graphql_spec]: http://facebook.github.io/graphql
[test_schema_rs]: src/tests/schema.rs [test_schema_rs]: src/tests/schema.rs
[tokio]: https://github.com/tokio-rs/tokio [tokio]: https://github.com/tokio-rs/tokio
[examples]: examples/ [examples]: juniper_rocket/examples/
[Rocket]: https://rocket.rs [Rocket]: https://rocket.rs

View file

@ -32,7 +32,7 @@ environment:
- TARGET: x86_64-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc
CHANNEL: nightly CHANNEL: nightly
# Install Rust and Cargo # Install Rust ,Cargo and cargo-make.
# (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml) # (Based on from https://github.com/rust-lang/libc/blob/master/appveyor.yml)
install: install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs - curl -sSf -o rustup-init.exe https://win.rustup.rs
@ -40,6 +40,7 @@ install:
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv - rustc -Vv
- cargo -V - cargo -V
- cargo install --debug cargo-make
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
@ -48,30 +49,9 @@ install:
build: false build: false
test_script: test_script:
# Build and test main juniper crate with certain features. - cd juniper && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper - cd juniper_codegen && cargo build --verbose && cargo test --verbose && cd ..
- cargo build --verbose --features iron-handlers - cd juniper_iron && cargo build --verbose && cargo test --verbose && cd ..
- IF "%CHANNEL%"=="nightly" (cargo build --verbose --features rocket-handlers) - cd juniper_tests && cargo build --verbose && cargo test --verbose && cd ..
- IF NOT %TARGET% == %TARGET:msvc=% ( IF %CHANNEL% == "nightly" ( cd juniper_rocket && cargo test --verbose && cargo build --verbose && cd .. ) )
# Build example binaries; first Iron, then Rocket examples
- cargo build --verbose --example server --features "iron-handlers expose-test-schema"
- IF "%CHANNEL%"=="nightly" (cargo build --verbose --example rocket-server --features "rocket-handlers expose-test-schema")
# Run all tests for the base library and available integrations
- set TEST_FEATURES=iron-handlers expose-test-schema
- IF "%CHANNEL%"=="nightly" (set TEST_FEATURES=%TEST_FEATURES% rocket-handlers rocket/testing)
- cargo test --verbose --features "%TEST_FEATURES%"
- cd ..
# Build juniper_codegen and run tests.
- cd juniper_codegen
- cargo build --verbose
- cargo test
- cd ..
# Build and run integration tests.
- cd juniper_tests
- cargo build --verbose
- cargo test

View file

@ -7,12 +7,9 @@ license = "BSD-2-Clause"
documentation = "https://docs.rs/juniper/0.8.1/juniper/" documentation = "https://docs.rs/juniper/0.8.1/juniper/"
repository = "https://github.com/mhallin/juniper" repository = "https://github.com/mhallin/juniper"
readme = "README.md" readme = "README.md"
keywords = ["graphql", "server", "iron", "web", "rocket"] keywords = ["graphql", "server", "web", "rocket"]
categories = ["web-programming"] categories = ["web-programming"]
[package.metadata.docs.rs]
features = [ "iron-handlers" ]
[badges] [badges]
travis-ci = { repository = "mhallin/juniper" } travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" } appveyor = { repository = "mhallin/juniper" }
@ -22,36 +19,15 @@ name = "bench"
harness = false harness = false
path = "benches/bench.rs" path = "benches/bench.rs"
[[example]]
name = "server"
required-features = ["iron-handlers", "expose-test-schema"]
[[example]]
name = "rocket-server"
required-features = ["rocket-handlers", "expose-test-schema"]
[features] [features]
nightly = [] nightly = []
iron-handlers = ["iron", "serde_json", "urlencoded"]
rocket-handlers = ["rocket", "rocket_codegen", "serde_json"]
expose-test-schema = [] expose-test-schema = []
[dependencies] [dependencies]
serde = { version = "^1.0.8" } serde = { version = "^1.0.8" }
serde_derive = {version="^1.0.8" } serde_derive = {version="^1.0.8" }
serde_json = { version="^1.0.2", optional = true } serde_json = { version="^1.0.2", optional = true }
iron = { version = "^0.5.1", optional = true }
urlencoded = { version = "^0.5.0", optional = true }
rocket = { version = "^0.2.8", optional = true }
rocket_codegen = { version = "^0.2.8", optional = true }
[dev-dependencies] [dev-dependencies]
iron = "^0.5.1"
router = "^0.5.0"
mount = "^0.3.0"
logger = "^0.3.0"
iron-test = "^0.5.0"
bencher = "^0.1.2" bencher = "^0.1.2"
serde_json = { version = "^1.0.2" }

View file

@ -1,4 +1,5 @@
#[macro_use] extern crate bencher; #[macro_use]
extern crate bencher;
extern crate juniper; extern crate juniper;
use bencher::Bencher; use bencher::Bencher;

View file

@ -168,8 +168,9 @@ impl<'a> Type<'a> {
/// Only applies to named types; lists will return `None`. /// Only applies to named types; lists will return `None`.
pub fn name(&self) -> Option<&str> { pub fn name(&self) -> Option<&str> {
match *self { match *self {
Type::Named(n) | Type::NonNullNamed(n) => Some(n), Type::Named(n) |
_ => None Type::NonNullNamed(n) => Some(n),
_ => None,
} }
} }
@ -178,15 +179,18 @@ impl<'a> Type<'a> {
/// All type literals contain exactly one named type. /// All type literals contain exactly one named type.
pub fn innermost_name(&self) -> &str { pub fn innermost_name(&self) -> &str {
match *self { match *self {
Type::Named(n) | Type::NonNullNamed(n) => n, Type::Named(n) |
Type::List(ref l) | Type::NonNullList(ref l) => l.innermost_name(), 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. /// Determines if a type only can represent non-null values.
pub fn is_non_null(&self) -> bool { pub fn is_non_null(&self) -> bool {
match *self { match *self {
Type::NonNullNamed(_) | Type::NonNullList(_) => true, Type::NonNullNamed(_) |
Type::NonNullList(_) => true,
_ => false, _ => false,
} }
} }
@ -205,16 +209,24 @@ impl<'a> fmt::Display for Type<'a> {
impl InputValue { impl InputValue {
/// Construct a null value. /// Construct a null value.
pub fn null() -> InputValue { InputValue::Null } pub fn null() -> InputValue {
InputValue::Null
}
/// Construct an integer value. /// Construct an integer value.
pub fn int(i: i32) -> InputValue { InputValue::Int(i) } pub fn int(i: i32) -> InputValue {
InputValue::Int(i)
}
/// Construct a floating point value. /// Construct a floating point value.
pub fn float(f: f64) -> InputValue { InputValue::Float(f) } pub fn float(f: f64) -> InputValue {
InputValue::Float(f)
}
/// Construct a boolean value. /// Construct a boolean value.
pub fn boolean(b: bool) -> InputValue { InputValue::Boolean(b) } pub fn boolean(b: bool) -> InputValue {
InputValue::Boolean(b)
}
/// Construct a string value. /// Construct a string value.
pub fn string<T: AsRef<str>>(s: T) -> InputValue { pub fn string<T: AsRef<str>>(s: T) -> InputValue {
@ -252,12 +264,12 @@ impl InputValue {
pub fn object<K>(o: HashMap<K, InputValue>) -> InputValue pub fn object<K>(o: HashMap<K, InputValue>) -> InputValue
where K: AsRef<str> + Eq + Hash where K: AsRef<str> + Eq + Hash
{ {
InputValue::Object( InputValue::Object(o.into_iter()
o.into_iter() .map(|(k, v)| {
.map(|(k, v)| (Spanning::unlocated(k.as_ref().to_owned()),
(Spanning::unlocated(k.as_ref().to_owned()), Spanning::unlocated(v))) Spanning::unlocated(v))
.collect() })
) .collect())
} }
/// Construct a located object. /// Construct a located object.
@ -268,20 +280,25 @@ impl InputValue {
/// Resolve all variables to their values. /// Resolve all variables to their values.
pub fn into_const(self, vars: &Variables) -> InputValue { pub fn into_const(self, vars: &Variables) -> InputValue {
match self { match self {
InputValue::Variable(v) => vars.get(&v) InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone),
.map_or_else(InputValue::null, Clone::clone), InputValue::List(l) => {
InputValue::List(l) => InputValue::List( InputValue::List(l.into_iter()
l.into_iter().map(|s| s.map(|v| v.into_const(vars))).collect() .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::Object(o) => {
), InputValue::Object(o.into_iter()
.map(|(sk, sv)| (sk, sv.map(|v| v.into_const(vars))))
.collect())
}
v => v, v => v,
} }
} }
/// Shorthand form of invoking `FromInputValue::from()`. /// Shorthand form of invoking `FromInputValue::from()`.
pub fn convert<T>(&self) -> Option<T> where T: FromInputValue { pub fn convert<T>(&self) -> Option<T>
where T: FromInputValue
{
<T as FromInputValue>::from(self) <T as FromInputValue>::from(self)
} }
@ -331,8 +348,11 @@ impl InputValue {
/// and values in `self`. /// and values in `self`.
pub fn to_object_value(&self) -> Option<HashMap<&str, &InputValue>> { pub fn to_object_value(&self) -> Option<HashMap<&str, &InputValue>> {
match *self { match *self {
InputValue::Object(ref o) => Some( InputValue::Object(ref o) => {
o.iter().map(|&(ref sk, ref sv)| (sk.item.as_str(), &sv.item)).collect()), Some(o.iter()
.map(|&(ref sk, ref sv)| (sk.item.as_str(), &sv.item))
.collect())
}
_ => None, _ => None,
} }
} }
@ -352,8 +372,16 @@ impl InputValue {
pub fn referenced_variables(&self) -> Vec<&str> { pub fn referenced_variables(&self) -> Vec<&str> {
match *self { match *self {
InputValue::Variable(ref name) => vec![name], InputValue::Variable(ref name) => vec![name],
InputValue::List(ref l) => l.iter().flat_map(|v| v.item.referenced_variables()).collect(), InputValue::List(ref l) => {
InputValue::Object(ref obj) => obj.iter().flat_map(|&(_, ref v)| v.item.referenced_variables()).collect(), 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![], _ => vec![],
} }
} }
@ -370,14 +398,22 @@ impl InputValue {
(&Enum(ref s1), &Enum(ref s2)) | (&Enum(ref s1), &Enum(ref s2)) |
(&Variable(ref s1), &Variable(ref s2)) => s1 == s2, (&Variable(ref s1), &Variable(ref s2)) => s1 == s2,
(&Boolean(b1), &Boolean(b2)) => b1 == b2, (&Boolean(b1), &Boolean(b2)) => b1 == b2,
(&List(ref l1), &List(ref l2)) => (&List(ref l1), &List(ref l2)) => {
l1.iter().zip(l2.iter()).all(|(v1, v2)| v1.item.unlocated_eq(&v2.item)), l1.iter()
(&Object(ref o1), &Object(ref o2)) => .zip(l2.iter())
o1.len() == o2.len() .all(|(v1, v2)| v1.item.unlocated_eq(&v2.item))
&& o1.iter() }
.all(|&(ref sk1, ref sv1)| o2.iter().any( (&Object(ref o1), &Object(ref o2)) => {
|&(ref sk2, ref sv2)| sk1.item == sk2.item && sv1.item.unlocated_eq(&sv2.item))), o1.len() == o2.len() &&
_ => false 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,
} }
} }
} }
@ -397,18 +433,22 @@ impl fmt::Display for InputValue {
for (i, spanning) in v.iter().enumerate() { for (i, spanning) in v.iter().enumerate() {
try!(spanning.item.fmt(f)); try!(spanning.item.fmt(f));
if i < v.len() - 1 { try!(write!(f, ", ")); } if i < v.len() - 1 {
try!(write!(f, ", "));
}
} }
write!(f, "]") write!(f, "]")
}, }
InputValue::Object(ref o) => { InputValue::Object(ref o) => {
try!(write!(f, "{{")); try!(write!(f, "{{"));
for (i, &(ref k, ref v)) in o.iter().enumerate() { for (i, &(ref k, ref v)) in o.iter().enumerate() {
try!(write!(f, "{}: ", k.item)); try!(write!(f, "{}: ", k.item));
try!(v.item.fmt(f)); try!(v.item.fmt(f));
if i < o.len() - 1 { try!(write!(f, ", ")); } if i < o.len() - 1 {
try!(write!(f, ", "));
}
} }
write!(f, "}}") write!(f, "}}")
@ -481,10 +521,9 @@ mod tests {
let value = InputValue::list(list); let value = InputValue::list(list);
assert_eq!(format!("{}", value), "[1, 2]"); assert_eq!(format!("{}", value), "[1, 2]");
let object = vec![ let object =
(Spanning::unlocated("foo".to_owned()), Spanning::unlocated(InputValue::int(1))), vec![(Spanning::unlocated("foo".to_owned()), Spanning::unlocated(InputValue::int(1))),
(Spanning::unlocated("bar".to_owned()), Spanning::unlocated(InputValue::int(2))), (Spanning::unlocated("bar".to_owned()), Spanning::unlocated(InputValue::int(2)))];
];
let value = InputValue::parsed_object(object); let value = InputValue::parsed_object(object);
assert_eq!(format!("{}", value), "{foo: 1, bar: 2}"); assert_eq!(format!("{}", value), "{foo: 1, bar: 2}");
} }

View file

@ -1,14 +1,14 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::RwLock; use std::sync::RwLock;
use ::GraphQLError; use GraphQLError;
use ast::{InputValue, ToInputValue, Document, Selection, Fragment, Definition, Type, FromInputValue, OperationType}; use ast::{InputValue, ToInputValue, Document, Selection, Fragment, Definition, Type,
FromInputValue, OperationType};
use value::Value; use value::Value;
use parser::SourcePosition; use parser::SourcePosition;
use schema::meta::{MetaType, ScalarMeta, ListMeta, NullableMeta, use schema::meta::{MetaType, ScalarMeta, ListMeta, NullableMeta, ObjectMeta, EnumMeta,
ObjectMeta, EnumMeta, InterfaceMeta, UnionMeta, InterfaceMeta, UnionMeta, InputObjectMeta, PlaceholderMeta, Field, Argument,
InputObjectMeta, PlaceholderMeta, Field, Argument,
EnumValue}; EnumValue};
use schema::model::{RootNode, SchemaType}; use schema::model::{RootNode, SchemaType};
@ -34,7 +34,9 @@ pub enum FieldPath<'a> {
/// ///
/// The executor helps drive the query execution in a schema. It keeps track /// The executor helps drive the query execution in a schema. It keeps track
/// of the current field stack, context, variables, and errors. /// of the current field stack, context, variables, and errors.
pub struct Executor<'a, CtxT> where CtxT: 'a { pub struct Executor<'a, CtxT>
where CtxT: 'a
{
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>, fragments: &'a HashMap<&'a str, &'a Fragment<'a>>,
variables: &'a Variables, variables: &'a Variables,
current_selection_set: Option<&'a [Selection<'a>]>, current_selection_set: Option<&'a [Selection<'a>]>,
@ -70,13 +72,17 @@ pub trait IntoResolvable<'a, T: GraphQLType, C>: Sized {
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>>; fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>>;
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T where T::Context: FromContext<C> { impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T
where T::Context: FromContext<C>
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
Ok(Some((FromContext::from(ctx), self))) Ok(Some((FromContext::from(ctx), self)))
} }
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<T> where T::Context: FromContext<C> { impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<T>
where T::Context: FromContext<C>
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
self.map(|v| Some((FromContext::from(ctx), v))) self.map(|v| Some((FromContext::from(ctx), v)))
} }
@ -135,7 +141,9 @@ impl<T> FromContext<T> for () {
} }
} }
impl<T> FromContext<T> for T where T: Context { impl<T> FromContext<T> for T
where T: Context
{
fn from(value: &T) -> &Self { fn from(value: &T) -> &Self {
value value
} }
@ -143,10 +151,10 @@ impl<T> FromContext<T> for T where T: Context {
impl<'a, CtxT> Executor<'a, CtxT> { impl<'a, CtxT> Executor<'a, CtxT> {
/// Resolve a single arbitrary value, mapping the context to a new type /// Resolve a single arbitrary value, mapping the context to a new type
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context=NewCtxT>>( pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>(&self,
&self, value: &T value: &T)
) -> ExecutionResult -> ExecutionResult
where NewCtxT: FromContext<CtxT>, where NewCtxT: FromContext<CtxT>
{ {
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context)) self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
.resolve(value) .resolve(value)
@ -167,7 +175,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
let position = self.field_path.location().clone(); let position = self.field_path.location().clone();
self.push_error(e, position); self.push_error(e, position);
Value::null() Value::null()
}, }
} }
} }
@ -188,14 +196,11 @@ impl<'a, CtxT> Executor<'a, CtxT> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn sub_executor( pub fn sub_executor(&self,
&self,
field_name: Option<&'a str>, field_name: Option<&'a str>,
location: SourcePosition, location: SourcePosition,
selection_set: Option<&'a [Selection]>, selection_set: Option<&'a [Selection]>)
) -> Executor<CtxT> {
-> Executor<CtxT>
{
Executor { Executor {
fragments: self.fragments, fragments: self.fragments,
variables: self.variables, variables: self.variables,
@ -262,7 +267,7 @@ impl<'a> FieldPath<'a> {
fn location(&self) -> &SourcePosition { fn location(&self) -> &SourcePosition {
match *self { match *self {
FieldPath::Root(ref pos) | FieldPath::Root(ref pos) |
FieldPath::Field(_, ref pos, _) => pos FieldPath::Field(_, ref pos, _) => pos,
} }
} }
} }
@ -293,13 +298,12 @@ impl ExecutionError {
} }
} }
pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>( pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>
document: Document, (document: Document,
operation_name: Option<&str>, operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>, root_node: &RootNode<QueryT, MutationT>,
variables: &Variables, variables: &Variables,
context: &CtxT context: &CtxT)
)
-> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
where QueryT: GraphQLType<Context = CtxT>, where QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT> MutationT: GraphQLType<Context = CtxT>
@ -314,8 +318,8 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
return Err(GraphQLError::MultipleOperationsProvided); return Err(GraphQLError::MultipleOperationsProvided);
} }
let move_op = operation_name.is_none() 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 { if move_op {
operation = Some(op); operation = Some(op);
@ -330,11 +334,19 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
None => return Err(GraphQLError::UnknownOperationName), None => return Err(GraphQLError::UnknownOperationName),
}; };
let default_variable_values = op.item.variable_definitions let default_variable_values = op.item
.map(|defs| defs.item.items.iter().filter_map( .variable_definitions
|&(ref name, ref def)| def.default_value.as_ref().map( .map(|defs| {
|i| (name.item.to_owned(), i.item.clone()))) defs.item
.collect::<HashMap<String, InputValue>>()); .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 errors = RwLock::new(Vec::new());
let value; let value;
@ -354,7 +366,10 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
} }
let executor = Executor { let executor = Executor {
fragments: &fragments.iter().map(|f| (f.item.name.item, &f.item)).collect(), fragments: &fragments
.iter()
.map(|f| (f.item.name.item, &f.item))
.collect(),
variables: final_vars, variables: final_vars,
current_selection_set: Some(&op.item.selection_set[..]), current_selection_set: Some(&op.item.selection_set[..]),
schema: &root_node.schema, schema: &root_node.schema,
@ -378,16 +393,16 @@ pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
impl<'r> Registry<'r> { impl<'r> Registry<'r> {
/// Construct a new registry /// Construct a new registry
pub fn new(types: HashMap<String, MetaType<'r>>) -> Registry<'r> { pub fn new(types: HashMap<String, MetaType<'r>>) -> Registry<'r> {
Registry { Registry { types: types }
types: types,
}
} }
/// Get the `Type` instance for a given GraphQL type /// Get the `Type` instance for a given GraphQL type
/// ///
/// If the registry hasn't seen a type with this name before, it will /// If the registry hasn't seen a type with this name before, it will
/// construct its metadata and store it. /// construct its metadata and store it.
pub fn get_type<T>(&mut self) -> Type<'r> where T: GraphQLType { pub fn get_type<T>(&mut self) -> Type<'r>
where T: GraphQLType
{
if let Some(name) = T::name() { if let Some(name) = T::name() {
if !self.types.contains_key(name) { if !self.types.contains_key(name) {
self.insert_placeholder(name, Type::NonNullNamed(name)); self.insert_placeholder(name, Type::NonNullNamed(name));
@ -395,14 +410,15 @@ impl<'r> Registry<'r> {
self.types.insert(name.to_owned(), meta); self.types.insert(name.to_owned(), meta);
} }
self.types[name].as_type() self.types[name].as_type()
} } else {
else {
T::meta(self).as_type() T::meta(self).as_type()
} }
} }
/// Create a field with the provided name /// Create a field with the provided name
pub fn field<T>(&mut self, name: &str) -> Field<'r> where T: GraphQLType { pub fn field<T>(&mut self, name: &str) -> Field<'r>
where T: GraphQLType
{
Field { Field {
name: name.to_owned(), name: name.to_owned(),
description: None, description: None,
@ -426,7 +442,9 @@ impl<'r> Registry<'r> {
} }
/// Create an argument with the provided name /// Create an argument with the provided name
pub fn arg<T>(&mut self, name: &str) -> Argument<'r> where T: GraphQLType + FromInputValue { pub fn arg<T>(&mut self, name: &str) -> Argument<'r>
where T: GraphQLType + FromInputValue
{
Argument::new(name, self.get_type::<T>()) Argument::new(name, self.get_type::<T>())
} }
@ -434,22 +452,16 @@ impl<'r> Registry<'r> {
/// ///
/// When called with type `T`, the actual argument will be given the type /// When called with type `T`, the actual argument will be given the type
/// `Option<T>`. /// `Option<T>`.
pub fn arg_with_default<T>( pub fn arg_with_default<T>(&mut self, name: &str, value: &T) -> Argument<'r>
&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>>()) Argument::new(name, self.get_type::<Option<T>>()).default_value(value.to())
.default_value(value.to())
} }
fn insert_placeholder(&mut self, name: &str, of_type: Type<'r>) { fn insert_placeholder(&mut self, name: &str, of_type: Type<'r>) {
if !self.types.contains_key(name) { if !self.types.contains_key(name) {
self.types.insert( self.types
name.to_owned(), .insert(name.to_owned(),
MetaType::Placeholder(PlaceholderMeta { of_type: of_type })); MetaType::Placeholder(PlaceholderMeta { of_type: of_type }));
} }
} }

View file

@ -22,8 +22,7 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let (result, errs) = ::execute(query, None, &schema, &vars, &()) let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -42,9 +41,7 @@ fn run_query<F>(query: &str, f: F)
#[test] #[test]
fn scalar_include_true() { fn scalar_include_true() {
run_query( run_query("{ a, b @include(if: true) }", |result| {
"{ a, b @include(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -52,9 +49,7 @@ fn scalar_include_true() {
#[test] #[test]
fn scalar_include_false() { fn scalar_include_false() {
run_query( run_query("{ a, b @include(if: false) }", |result| {
"{ a, b @include(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -62,9 +57,7 @@ fn scalar_include_false() {
#[test] #[test]
fn scalar_skip_false() { fn scalar_skip_false() {
run_query( run_query("{ a, b @skip(if: false) }", |result| {
"{ a, b @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -72,9 +65,7 @@ fn scalar_skip_false() {
#[test] #[test]
fn scalar_skip_true() { fn scalar_skip_true() {
run_query( run_query("{ a, b @skip(if: true) }", |result| {
"{ a, b @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -85,8 +76,7 @@ fn scalar_skip_true() {
#[test] #[test]
fn fragment_spread_include_true() { fn fragment_spread_include_true() {
run_query( run_query("{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
"{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
@ -95,8 +85,7 @@ fn fragment_spread_include_true() {
#[test] #[test]
fn fragment_spread_include_false() { fn fragment_spread_include_false() {
run_query( run_query("{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
"{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
@ -105,8 +94,7 @@ fn fragment_spread_include_false() {
#[test] #[test]
fn fragment_spread_skip_false() { fn fragment_spread_skip_false() {
run_query( run_query("{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
"{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
@ -115,8 +103,7 @@ fn fragment_spread_skip_false() {
#[test] #[test]
fn fragment_spread_skip_true() { fn fragment_spread_skip_true() {
run_query( run_query("{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
"{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
@ -127,8 +114,7 @@ fn fragment_spread_skip_true() {
#[test] #[test]
fn inline_fragment_include_true() { fn inline_fragment_include_true() {
run_query( run_query("{ a, ... on TestType @include(if: true) { b } }",
"{ a, ... on TestType @include(if: true) { b } }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
@ -137,8 +123,7 @@ fn inline_fragment_include_true() {
#[test] #[test]
fn inline_fragment_include_false() { fn inline_fragment_include_false() {
run_query( run_query("{ a, ... on TestType @include(if: false) { b } }",
"{ a, ... on TestType @include(if: false) { b } }",
|result| { |result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
@ -147,9 +132,7 @@ fn inline_fragment_include_false() {
#[test] #[test]
fn inline_fragment_skip_false() { fn inline_fragment_skip_false() {
run_query( run_query("{ a, ... on TestType @skip(if: false) { b } }", |result| {
"{ a, ... on TestType @skip(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -157,9 +140,7 @@ fn inline_fragment_skip_false() {
#[test] #[test]
fn inline_fragment_skip_true() { fn inline_fragment_skip_true() {
run_query( run_query("{ a, ... on TestType @skip(if: true) { b } }", |result| {
"{ a, ... on TestType @skip(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -170,9 +151,7 @@ fn inline_fragment_skip_true() {
#[test] #[test]
fn anonymous_inline_fragment_include_true() { fn anonymous_inline_fragment_include_true() {
run_query( run_query("{ a, ... @include(if: true) { b } }", |result| {
"{ a, ... @include(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -180,9 +159,7 @@ fn anonymous_inline_fragment_include_true() {
#[test] #[test]
fn anonymous_inline_fragment_include_false() { fn anonymous_inline_fragment_include_false() {
run_query( run_query("{ a, ... @include(if: false) { b } }", |result| {
"{ a, ... @include(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -190,9 +167,7 @@ fn anonymous_inline_fragment_include_false() {
#[test] #[test]
fn anonymous_inline_fragment_skip_false() { fn anonymous_inline_fragment_skip_false() {
run_query( run_query("{ a, ... @skip(if: false) { b } }", |result| {
"{ a, ... @skip(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -200,9 +175,7 @@ fn anonymous_inline_fragment_skip_false() {
#[test] #[test]
fn anonymous_inline_fragment_skip_true() { fn anonymous_inline_fragment_skip_true() {
run_query( run_query("{ a, ... @skip(if: true) { b } }", |result| {
"{ a, ... @skip(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -213,9 +186,7 @@ fn anonymous_inline_fragment_skip_true() {
#[test] #[test]
fn scalar_include_true_skip_true() { fn scalar_include_true_skip_true() {
run_query( run_query("{ a, b @include(if: true) @skip(if: true) }", |result| {
"{ a, b @include(if: true) @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -223,9 +194,7 @@ fn scalar_include_true_skip_true() {
#[test] #[test]
fn scalar_include_true_skip_false() { fn scalar_include_true_skip_false() {
run_query( run_query("{ a, b @include(if: true) @skip(if: false) }", |result| {
"{ a, b @include(if: true) @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b"))); assert_eq!(result.get("b"), Some(&Value::string("b")));
}); });
@ -233,9 +202,7 @@ fn scalar_include_true_skip_false() {
#[test] #[test]
fn scalar_include_false_skip_true() { fn scalar_include_false_skip_true() {
run_query( run_query("{ a, b @include(if: false) @skip(if: true) }", |result| {
"{ a, b @include(if: false) @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });
@ -243,9 +210,7 @@ fn scalar_include_false_skip_true() {
#[test] #[test]
fn scalar_include_false_skip_false() { fn scalar_include_false_skip_false() {
run_query( run_query("{ a, b @include(if: false) @skip(if: false) }", |result| {
"{ a, b @include(if: false) @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a"))); assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None); assert_eq!(result.get("b"), None);
}); });

View file

@ -4,13 +4,17 @@ use value::Value;
use ast::InputValue; use ast::InputValue;
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use ::GraphQLError::ValidationError; use GraphQLError::ValidationError;
use validation::RuleError; use validation::RuleError;
use parser::SourcePosition; use parser::SourcePosition;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
#[derive(Debug)] #[derive(Debug)]
enum Color { Red, Green, Blue } enum Color {
Red,
Green,
Blue,
}
struct TestType; struct TestType;
graphql_enum!(Color { graphql_enum!(Color {
@ -34,8 +38,7 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let (result, errs) = ::execute(query, None, &schema, &vars, &()) let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -54,9 +57,7 @@ fn run_query<F>(query: &str, f: F)
#[test] #[test]
fn accepts_enum_literal() { fn accepts_enum_literal() {
run_query( run_query("{ toString(color: RED) }", |result| {
"{ toString(color: RED) }",
|result| {
assert_eq!( assert_eq!(
result.get("toString"), result.get("toString"),
Some(&Value::string("Color::Red"))); Some(&Value::string("Color::Red")));
@ -65,9 +66,7 @@ fn accepts_enum_literal() {
#[test] #[test]
fn serializes_as_output() { fn serializes_as_output() {
run_query( run_query("{ aColor }", |result| {
"{ aColor }",
|result| {
assert_eq!( assert_eq!(
result.get("aColor"), result.get("aColor"),
Some(&Value::string("RED"))); Some(&Value::string("RED")));
@ -79,11 +78,9 @@ fn does_not_accept_string_literals() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"{ toString(color: "RED") }"#; let query = r#"{ toString(color: "RED") }"#;
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -95,11 +92,12 @@ fn does_not_accept_string_literals() {
#[test] #[test]
fn accepts_strings_in_variables() { fn accepts_strings_in_variables() {
run_variable_query( run_variable_query("query q($color: Color!) { toString(color: $color) }",
"query q($color: Color!) { toString(color: $color) }",
vec![ vec![
("color".to_owned(), InputValue::string("RED")), ("color".to_owned(), InputValue::string("RED")),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("toString"), result.get("toString"),
@ -114,10 +112,11 @@ fn does_not_accept_incorrect_enum_name_in_variables() {
let query = r#"query q($color: Color!) { toString(color: $color) }"#; let query = r#"query q($color: Color!) { toString(color: $color) }"#;
let vars = vec![ let vars = vec![
("color".to_owned(), InputValue::string("BLURPLE")), ("color".to_owned(), InputValue::string("BLURPLE")),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -134,10 +133,11 @@ fn does_not_accept_incorrect_type_in_variables() {
let query = r#"query q($color: Color!) { toString(color: $color) }"#; let query = r#"query q($color: Color!) { toString(color: $color) }"#;
let vars = vec![ let vars = vec![
("color".to_owned(), InputValue::int(123)), ("color".to_owned(), InputValue::int(123)),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(

View file

@ -63,10 +63,11 @@ mod field_execution {
let vars = vec![ let vars = vec![
("size".to_owned(), InputValue::int(100)) ("size".to_owned(), InputValue::int(100))
].into_iter().collect(); ]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -137,8 +138,7 @@ mod merge_parallel_fragments {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -187,7 +187,10 @@ mod threads_context_correctly {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, let (result, errs) = ::execute(doc,
None,
&schema,
&vars,
&TestContext { value: "Context value".to_owned() }) &TestContext { value: "Context value".to_owned() })
.expect("Execution failed"); .expect("Execution failed");
@ -269,11 +272,12 @@ mod dynamic_context_switching {
items: vec![ items: vec![
(0, InnerContext { value: "First value".to_owned() }), (0, InnerContext { value: "First value".to_owned() }),
(1, InnerContext { value: "Second 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) let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -305,11 +309,12 @@ mod dynamic_context_switching {
items: vec![ items: vec![
(0, InnerContext { value: "First value".to_owned() }), (0, InnerContext { value: "First value".to_owned() }),
(1, InnerContext { value: "Second 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) let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, vec![ assert_eq!(errs, vec![
ExecutionError::new( ExecutionError::new(
@ -348,11 +353,12 @@ mod dynamic_context_switching {
items: vec![ items: vec![
(0, InnerContext { value: "First value".to_owned() }), (0, InnerContext { value: "First value".to_owned() }),
(1, InnerContext { value: "Second 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) let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, [ assert_eq!(errs, [
ExecutionError::new( ExecutionError::new(
@ -386,11 +392,12 @@ mod dynamic_context_switching {
items: vec![ items: vec![
(0, InnerContext { value: "First value".to_owned() }), (0, InnerContext { value: "First value".to_owned() }),
(1, InnerContext { value: "Second 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) let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -427,8 +434,7 @@ mod nulls_out_errors {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
println!("Result: {:?}", result); println!("Result: {:?}", result);
@ -455,7 +461,7 @@ mod named_operations {
use value::Value; use value::Value;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use ::GraphQLError; use GraphQLError;
struct Schema; struct Schema;
@ -470,8 +476,7 @@ mod named_operations {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -489,8 +494,7 @@ mod named_operations {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -527,8 +531,7 @@ mod named_operations {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let err = ::execute(doc, None, &schema, &vars, &()) let err = ::execute(doc, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(err, GraphQLError::MultipleOperationsProvided); assert_eq!(err, GraphQLError::MultipleOperationsProvided);
} }
@ -540,8 +543,7 @@ mod named_operations {
let vars = vec![].into_iter().collect(); let vars = vec![].into_iter().collect();
let err = ::execute(doc, Some("UnknownExample"), &schema, &vars, &()) let err = ::execute(doc, Some("UnknownExample"), &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(err, GraphQLError::UnknownOperationName); assert_eq!(err, GraphQLError::UnknownOperationName);
} }

View file

@ -6,8 +6,12 @@ mod interface {
trait Pet { trait Pet {
fn name(&self) -> &str; fn name(&self) -> &str;
fn as_dog(&self) -> Option<&Dog> { None } fn as_dog(&self) -> Option<&Dog> {
fn as_cat(&self) -> Option<&Cat> { None } None
}
fn as_cat(&self) -> Option<&Cat> {
None
}
} }
graphql_interface!(<'a> &'a Pet: () as "Pet" |&self| { graphql_interface!(<'a> &'a Pet: () as "Pet" |&self| {
@ -25,8 +29,12 @@ mod interface {
} }
impl Pet for Dog { impl Pet for Dog {
fn name(&self) -> &str { &self.name } fn name(&self) -> &str {
fn as_dog(&self) -> Option<&Dog> { Some(self) } &self.name
}
fn as_dog(&self) -> Option<&Dog> {
Some(self)
}
} }
graphql_object!(Dog: () |&self| { graphql_object!(Dog: () |&self| {
@ -42,8 +50,12 @@ mod interface {
} }
impl Pet for Cat { impl Pet for Cat {
fn name(&self) -> &str { &self.name } fn name(&self) -> &str {
fn as_cat(&self) -> Option<&Cat> { Some(self) } &self.name
}
fn as_cat(&self) -> Option<&Cat> {
Some(self)
}
} }
graphql_object!(Cat: () |&self| { graphql_object!(Cat: () |&self| {
@ -65,8 +77,7 @@ mod interface {
#[test] #[test]
fn test() { fn test() {
let schema = RootNode::new( let schema = RootNode::new(Schema {
Schema {
pets: vec![ pets: vec![
Box::new(Dog { name: "Odie".to_owned(), woofs: true }), Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
Box::new(Cat { name: "Garfield".to_owned(), meows: false }), Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
@ -86,11 +97,9 @@ mod interface {
} }
}"; }";
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -122,8 +131,12 @@ mod union {
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
trait Pet { trait Pet {
fn as_dog(&self) -> Option<&Dog> { None } fn as_dog(&self) -> Option<&Dog> {
fn as_cat(&self) -> Option<&Cat> { None } None
}
fn as_cat(&self) -> Option<&Cat> {
None
}
} }
graphql_union!(<'a> &'a Pet: () as "Pet" |&self| { graphql_union!(<'a> &'a Pet: () as "Pet" |&self| {
@ -139,7 +152,9 @@ mod union {
} }
impl Pet for Dog { impl Pet for Dog {
fn as_dog(&self) -> Option<&Dog> { Some(self) } fn as_dog(&self) -> Option<&Dog> {
Some(self)
}
} }
graphql_object!(Dog: () |&self| { graphql_object!(Dog: () |&self| {
@ -153,7 +168,9 @@ mod union {
} }
impl Pet for Cat { impl Pet for Cat {
fn as_cat(&self) -> Option<&Cat> { Some(self) } fn as_cat(&self) -> Option<&Cat> {
Some(self)
}
} }
graphql_object!(Cat: () |&self| { graphql_object!(Cat: () |&self| {
@ -173,8 +190,7 @@ mod union {
#[test] #[test]
fn test() { fn test() {
let schema = RootNode::new( let schema = RootNode::new(Schema {
Schema {
pets: vec![ pets: vec![
Box::new(Dog { name: "Odie".to_owned(), woofs: true }), Box::new(Dog { name: "Odie".to_owned(), woofs: true }),
Box::new(Cat { name: "Garfield".to_owned(), meows: false }), Box::new(Cat { name: "Garfield".to_owned(), meows: false }),
@ -195,11 +211,9 @@ mod union {
} }
}"; }";
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);

View file

@ -114,9 +114,12 @@ fn enum_introspection() {
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum"))); assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum")));
assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM"))); assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM")));
@ -127,8 +130,10 @@ fn enum_introspection() {
assert_eq!(type_info.get("ofType"), Some(&Value::null())); assert_eq!(type_info.get("ofType"), Some(&Value::null()));
let values = type_info let values = type_info
.get("enumValues").expect("enumValues field missing") .get("enumValues")
.as_list_value().expect("enumValues not a list"); .expect("enumValues field missing")
.as_list_value()
.expect("enumValues not a list");
assert_eq!(values.len(), 2); assert_eq!(values.len(), 2);
@ -192,9 +197,12 @@ fn interface_introspection() {
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(type_info.get("name"), Some(&Value::string("SampleInterface"))); assert_eq!(type_info.get("name"), Some(&Value::string("SampleInterface")));
assert_eq!(type_info.get("kind"), Some(&Value::string("INTERFACE"))); assert_eq!(type_info.get("kind"), Some(&Value::string("INTERFACE")));
@ -205,8 +213,10 @@ fn interface_introspection() {
assert_eq!(type_info.get("ofType"), Some(&Value::null())); assert_eq!(type_info.get("ofType"), Some(&Value::null()));
let possible_types = type_info let possible_types = type_info
.get("possibleTypes").expect("possibleTypes field missing") .get("possibleTypes")
.as_list_value().expect("possibleTypes not a list"); .expect("possibleTypes field missing")
.as_list_value()
.expect("possibleTypes not a list");
assert_eq!(possible_types.len(), 1); assert_eq!(possible_types.len(), 1);
@ -215,8 +225,10 @@ fn interface_introspection() {
].into_iter().collect()))); ].into_iter().collect())));
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields field not an object value"); .expect("fields field missing")
.as_list_value()
.expect("fields field not an object value");
assert_eq!(fields.len(), 1); assert_eq!(fields.len(), 1);
@ -293,9 +305,12 @@ fn object_introspection() {
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(type_info.get("name"), Some(&Value::string("Root"))); assert_eq!(type_info.get("name"), Some(&Value::string("Root")));
assert_eq!(type_info.get("kind"), Some(&Value::string("OBJECT"))); assert_eq!(type_info.get("kind"), Some(&Value::string("OBJECT")));
@ -313,8 +328,10 @@ fn object_introspection() {
assert_eq!(type_info.get("possibleTypes"), Some(&Value::null())); assert_eq!(type_info.get("possibleTypes"), Some(&Value::null()));
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields field not an object value"); .expect("fields field missing")
.as_list_value()
.expect("fields field not an object value");
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
@ -405,8 +422,10 @@ fn scalar_introspection() {
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing"); .expect("Result is not an object")
.get("__type")
.expect("__type field missing");
assert_eq!(type_info, &Value::object(vec![ assert_eq!(type_info, &Value::object(vec![
("name", Value::string("SampleScalar")), ("name", Value::string("SampleScalar")),

View file

@ -4,7 +4,7 @@ use value::Value;
use ast::InputValue; use ast::InputValue;
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use ::GraphQLError::ValidationError; use GraphQLError::ValidationError;
use validation::RuleError; use validation::RuleError;
use parser::SourcePosition; use parser::SourcePosition;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
@ -123,8 +123,7 @@ fn run_variable_query<F>(query: &str, vars: Variables, f: F)
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let (result, errs) = ::execute(query, None, &schema, &vars, &()) let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
@ -238,10 +237,11 @@ fn variable_error_on_nested_non_null() {
("b", InputValue::string("bar")), ("b", InputValue::string("bar")),
("c", InputValue::null()), ("c", InputValue::null()),
].into_iter().collect())) ].into_iter().collect()))
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -258,10 +258,11 @@ fn variable_error_on_incorrect_type() {
let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#;
let vars = vec![ let vars = vec![
("input".to_owned(), InputValue::string("foo bar")), ("input".to_owned(), InputValue::string("foo bar")),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -281,10 +282,11 @@ fn variable_error_on_omit_non_null() {
("a", InputValue::string("foo")), ("a", InputValue::string("foo")),
("b", InputValue::string("bar")), ("b", InputValue::string("bar")),
].into_iter().collect())) ].into_iter().collect()))
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -298,17 +300,19 @@ fn variable_error_on_omit_non_null() {
fn variable_multiple_errors_with_nesting() { fn variable_multiple_errors_with_nesting() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($input: TestNestedInputObject) { fieldWithNestedObjectInput(input: $input) }"#; let query =
r#"query q($input: TestNestedInputObject) { fieldWithNestedObjectInput(input: $input) }"#;
let vars = vec![ let vars = vec![
("input".to_owned(), InputValue::object(vec![ ("input".to_owned(), InputValue::object(vec![
("na", InputValue::object(vec![ ("na", InputValue::object(vec![
("a", InputValue::string("foo")), ("a", InputValue::string("foo")),
].into_iter().collect())), ].into_iter().collect())),
].into_iter().collect())) ].into_iter().collect()))
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -334,10 +338,11 @@ fn variable_error_on_additional_field() {
("c", InputValue::string("baz")), ("c", InputValue::string("baz")),
("extra", InputValue::string("dog")), ("extra", InputValue::string("dog")),
].into_iter().collect())) ].into_iter().collect()))
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -349,9 +354,7 @@ fn variable_error_on_additional_field() {
#[test] #[test]
fn allow_nullable_inputs_to_be_omitted() { fn allow_nullable_inputs_to_be_omitted() {
run_query( run_query(r#"{ fieldWithNullableStringInput }"#, |result| {
r#"{ fieldWithNullableStringInput }"#,
|result| {
assert_eq!( assert_eq!(
result.get("fieldWithNullableStringInput"), result.get("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))); Some(&Value::string(r#"None"#)));
@ -360,8 +363,7 @@ fn allow_nullable_inputs_to_be_omitted() {
#[test] #[test]
fn allow_nullable_inputs_to_be_omitted_in_variable() { fn allow_nullable_inputs_to_be_omitted_in_variable() {
run_query( run_query(r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| { |result| {
assert_eq!( assert_eq!(
result.get("fieldWithNullableStringInput"), result.get("fieldWithNullableStringInput"),
@ -371,8 +373,7 @@ fn allow_nullable_inputs_to_be_omitted_in_variable() {
#[test] #[test]
fn allow_nullable_inputs_to_be_explicitly_null() { fn allow_nullable_inputs_to_be_explicitly_null() {
run_query( run_query(r#"{ fieldWithNullableStringInput(input: null) }"#,
r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| { |result| {
assert_eq!( assert_eq!(
result.get("fieldWithNullableStringInput"), result.get("fieldWithNullableStringInput"),
@ -410,8 +411,7 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
#[test] #[test]
fn allow_nullable_inputs_to_be_set_to_value_directly() { fn allow_nullable_inputs_to_be_set_to_value_directly() {
run_query( run_query(r#"{ fieldWithNullableStringInput(input: "a") }"#,
r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| { |result| {
assert_eq!( assert_eq!(
result.get("fieldWithNullableStringInput"), result.get("fieldWithNullableStringInput"),
@ -424,11 +424,9 @@ fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#;
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -445,10 +443,11 @@ 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 query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#;
let vars = vec![ let vars = vec![
("value".to_owned(), InputValue::null()), ("value".to_owned(), InputValue::null()),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -474,8 +473,7 @@ fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
#[test] #[test]
fn allow_non_nullable_inputs_to_be_set_to_value_directly() { fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
run_query( run_query(r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| { |result| {
assert_eq!( assert_eq!(
result.get("fieldWithNonNullableStringInput"), result.get("fieldWithNonNullableStringInput"),
@ -485,11 +483,12 @@ fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
#[test] #[test]
fn allow_lists_to_be_null() { fn allow_lists_to_be_null() {
run_variable_query( run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
r#"query q($input: [String]) { list(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::null()), ("input".to_owned(), InputValue::null()),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("list"), result.get("list"),
@ -499,13 +498,14 @@ fn allow_lists_to_be_null() {
#[test] #[test]
fn allow_lists_to_contain_values() { fn allow_lists_to_contain_values() {
run_variable_query( run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
r#"query q($input: [String]) { list(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("list"), result.get("list"),
@ -515,15 +515,16 @@ fn allow_lists_to_contain_values() {
#[test] #[test]
fn allow_lists_to_contain_null() { fn allow_lists_to_contain_null() {
run_variable_query( run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
r#"query q($input: [String]) { list(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("list"), result.get("list"),
@ -538,10 +539,11 @@ fn does_not_allow_non_null_lists_to_be_null() {
let query = r#"query q($input: [String]!) { nnList(input: $input) }"#; let query = r#"query q($input: [String]!) { nnList(input: $input) }"#;
let vars = vec![ let vars = vec![
("input".to_owned(), InputValue::null()), ("input".to_owned(), InputValue::null()),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -553,13 +555,14 @@ fn does_not_allow_non_null_lists_to_be_null() {
#[test] #[test]
fn allow_non_null_lists_to_contain_values() { fn allow_non_null_lists_to_contain_values() {
run_variable_query( run_variable_query(r#"query q($input: [String]!) { nnList(input: $input) }"#,
r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("nnList"), result.get("nnList"),
@ -568,15 +571,16 @@ fn allow_non_null_lists_to_contain_values() {
} }
#[test] #[test]
fn allow_non_null_lists_to_contain_null() { fn allow_non_null_lists_to_contain_null() {
run_variable_query( run_variable_query(r#"query q($input: [String]!) { nnList(input: $input) }"#,
r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("nnList"), result.get("nnList"),
@ -586,11 +590,12 @@ fn allow_non_null_lists_to_contain_null() {
#[test] #[test]
fn allow_lists_of_non_null_to_be_null() { fn allow_lists_of_non_null_to_be_null() {
run_variable_query( run_variable_query(r#"query q($input: [String!]) { listNn(input: $input) }"#,
r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::null()), ("input".to_owned(), InputValue::null()),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("listNn"), result.get("listNn"),
@ -600,13 +605,14 @@ fn allow_lists_of_non_null_to_be_null() {
#[test] #[test]
fn allow_lists_of_non_null_to_contain_values() { fn allow_lists_of_non_null_to_contain_values() {
run_variable_query( run_variable_query(r#"query q($input: [String!]) { listNn(input: $input) }"#,
r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("listNn"), result.get("listNn"),
@ -625,10 +631,11 @@ fn does_not_allow_lists_of_non_null_to_contain_null() {
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -649,10 +656,11 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() {
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -669,10 +677,11 @@ fn does_not_allow_non_null_lists_of_non_null_to_be_null() {
let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#;
let vars = vec![ let vars = vec![
("value".to_owned(), InputValue::null()), ("value".to_owned(), InputValue::null()),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -684,13 +693,14 @@ fn does_not_allow_non_null_lists_of_non_null_to_be_null() {
#[test] #[test]
fn allow_non_null_lists_of_non_null_to_contain_values() { fn allow_non_null_lists_of_non_null_to_contain_values() {
run_variable_query( run_variable_query(r#"query q($input: [String!]!) { nnListNn(input: $input) }"#,
r#"query q($input: [String!]!) { nnListNn(input: $input) }"#,
vec![ vec![
("input".to_owned(), InputValue::list(vec![ ("input".to_owned(), InputValue::list(vec![
InputValue::string("A"), InputValue::string("A"),
])), ])),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("nnListNn"), result.get("nnListNn"),
@ -708,10 +718,11 @@ fn does_not_allow_invalid_types_to_be_used_as_values() {
InputValue::string("A"), InputValue::string("A"),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -731,10 +742,11 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
InputValue::string("A"), InputValue::string("A"),
InputValue::string("B"), InputValue::string("B"),
])), ])),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -746,9 +758,7 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
#[test] #[test]
fn default_argument_when_not_provided() { fn default_argument_when_not_provided() {
run_query( run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| {
r#"{ fieldWithDefaultArgumentValue }"#,
|result| {
assert_eq!( assert_eq!(
result.get("fieldWithDefaultArgumentValue"), result.get("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#))); Some(&Value::string(r#""Hello World""#)));
@ -757,8 +767,7 @@ fn default_argument_when_not_provided() {
#[test] #[test]
fn default_argument_when_nullable_variable_not_provided() { fn default_argument_when_nullable_variable_not_provided() {
run_query( run_query(r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#,
r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#,
|result| { |result| {
assert_eq!( assert_eq!(
result.get("fieldWithDefaultArgumentValue"), result.get("fieldWithDefaultArgumentValue"),
@ -782,17 +791,13 @@ fn default_argument_when_nullable_variable_set_to_null() {
#[test] #[test]
fn nullable_input_object_arguments_successful_without_variables() { fn nullable_input_object_arguments_successful_without_variables() {
run_query( run_query(r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#, |result| {
r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#,
|result| {
assert_eq!( assert_eq!(
result.get("exampleInput"), result.get("exampleInput"),
Some(&Value::string(r#"a: Some("abc"), b: 123"#))); Some(&Value::string(r#"a: Some("abc"), b: 123"#)));
}); });
run_query( run_query(r#"{ exampleInput(arg: {a: null, b: 1}) }"#, |result| {
r#"{ exampleInput(arg: {a: null, b: 1}) }"#,
|result| {
assert_eq!( assert_eq!(
result.get("exampleInput"), result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#))); Some(&Value::string(r#"a: None, b: 1"#)));
@ -801,32 +806,32 @@ fn nullable_input_object_arguments_successful_without_variables() {
#[test] #[test]
fn nullable_input_object_arguments_successful_with_variables() { fn nullable_input_object_arguments_successful_with_variables() {
run_variable_query( run_variable_query(r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#,
r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(123)), ("var".to_owned(), InputValue::int(123)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("exampleInput"), result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 123"#))); Some(&Value::string(r#"a: None, b: 123"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
vec![ vec![
("var".to_owned(), InputValue::null()), ("var".to_owned(), InputValue::null()),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("exampleInput"), result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#))); Some(&Value::string(r#"a: None, b: 1"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#, vec![].into_iter().collect(),
vec![
].into_iter().collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("exampleInput"), result.get("exampleInput"),
@ -839,11 +844,9 @@ fn does_not_allow_missing_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"{ exampleInput(arg: {a: "abc"}) }"#; let query = r#"{ exampleInput(arg: {a: "abc"}) }"#;
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -858,11 +861,9 @@ fn does_not_allow_null_in_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"{ exampleInput(arg: {a: "abc", b: null}) }"#; let query = r#"{ exampleInput(arg: {a: "abc", b: null}) }"#;
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -877,11 +878,9 @@ fn does_not_allow_missing_variable_for_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#;
let vars = vec![ let vars = vec![].into_iter().collect();
].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -898,10 +897,11 @@ fn does_not_allow_null_variable_for_required_field() {
let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#;
let vars = vec![ let vars = vec![
("var".to_owned(), InputValue::null()), ("var".to_owned(), InputValue::null()),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -913,40 +913,38 @@ fn does_not_allow_null_variable_for_required_field() {
#[test] #[test]
fn input_object_with_default_values() { fn input_object_with_default_values() {
run_query( run_query(r#"{ inputWithDefaults(arg: {a: 1}) }"#, |result| {
r#"{ inputWithDefaults(arg: {a: 1}) }"#,
|result| {
assert_eq!( assert_eq!(
result.get("inputWithDefaults"), result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))); Some(&Value::string(r#"a: 1"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(1)), ("var".to_owned(), InputValue::int(1)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("inputWithDefaults"), result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))); Some(&Value::string(r#"a: 1"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#, vec![].into_iter().collect(),
vec![
].into_iter().collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("inputWithDefaults"), result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))); Some(&Value::string(r#"a: 1"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(2)), ("var".to_owned(), InputValue::int(2)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("inputWithDefaults"), result.get("inputWithDefaults"),
@ -960,22 +958,24 @@ mod integers {
#[test] #[test]
fn positive_and_negative_should_work() { fn positive_and_negative_should_work() {
run_variable_query( run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(1)), ("var".to_owned(), InputValue::int(1)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("integerInput"), result.get("integerInput"),
Some(&Value::string(r#"value: 1"#))); Some(&Value::string(r#"value: 1"#)));
}); });
run_variable_query( run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(-1)), ("var".to_owned(), InputValue::int(-1)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("integerInput"), result.get("integerInput"),
@ -990,10 +990,11 @@ mod integers {
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![ let vars = vec![
("var".to_owned(), InputValue::float(10.0)), ("var".to_owned(), InputValue::float(10.0)),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -1010,10 +1011,11 @@ mod integers {
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![ let vars = vec![
("var".to_owned(), InputValue::string("10")), ("var".to_owned(), InputValue::string("10")),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(
@ -1030,11 +1032,12 @@ mod floats {
#[test] #[test]
fn float_values_should_work() { fn float_values_should_work() {
run_variable_query( run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![ vec![
("var".to_owned(), InputValue::float(10.0)), ("var".to_owned(), InputValue::float(10.0)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("floatInput"), result.get("floatInput"),
@ -1044,11 +1047,12 @@ mod floats {
#[test] #[test]
fn coercion_from_integers_should_work() { fn coercion_from_integers_should_work() {
run_variable_query( run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![ vec![
("var".to_owned(), InputValue::int(-1)), ("var".to_owned(), InputValue::int(-1)),
].into_iter().collect(), ]
.into_iter()
.collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get("floatInput"), result.get("floatInput"),
@ -1063,10 +1067,11 @@ mod floats {
let query = r#"query q($var: Float!) { floatInput(value: $var) }"#; let query = r#"query q($var: Float!) { floatInput(value: $var) }"#;
let vars = vec![ let vars = vec![
("var".to_owned(), InputValue::string("10")), ("var".to_owned(), InputValue::string("10")),
].into_iter().collect(); ]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()) let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
.unwrap_err();
assert_eq!(error, ValidationError(vec![ assert_eq!(error, ValidationError(vec![
RuleError::new( RuleError::new(

View file

@ -3,7 +3,7 @@
use serde::ser; use serde::ser;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
use ::{GraphQLError, Value, Variables, GraphQLType, RootNode}; use {GraphQLError, Value, Variables, GraphQLType, RootNode};
use ast::InputValue; use ast::InputValue;
use executor::ExecutionError; use executor::ExecutionError;
@ -19,7 +19,7 @@ pub struct GraphQLRequest {
query: String, query: String,
#[serde(rename = "operationName")] #[serde(rename = "operationName")]
operation_name: Option<String>, operation_name: Option<String>,
variables: Option<InputValue> variables: Option<InputValue>,
} }
impl GraphQLRequest { impl GraphQLRequest {
@ -28,15 +28,24 @@ impl GraphQLRequest {
} }
fn variables(&self) -> Variables { fn variables(&self) -> Variables {
self.variables.as_ref().and_then(|iv| { self.variables
iv.to_object_value().map(|o| { .as_ref()
o.into_iter().map(|(k, v)| (k.to_owned(), v.clone())).collect() .and_then(|iv| {
iv.to_object_value()
.map(|o| {
o.into_iter()
.map(|(k, v)| (k.to_owned(), v.clone()))
.collect()
}) })
}).unwrap_or_default() })
.unwrap_or_default()
} }
/// Construct a new GraphQL request from parts /// 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 { GraphQLRequest {
query: query, query: query,
operation_name: operation_name, operation_name: operation_name,
@ -48,22 +57,18 @@ impl GraphQLRequest {
/// ///
/// This is a simple wrapper around the `execute` function exposed at the /// This is a simple wrapper around the `execute` function exposed at the
/// top level of this crate. /// top level of this crate.
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, CtxT, QueryT, MutationT>(&'a self,
&'a self,
root_node: &RootNode<QueryT, MutationT>, root_node: &RootNode<QueryT, MutationT>,
context: &CtxT, context: &CtxT)
)
-> GraphQLResponse<'a> -> GraphQLResponse<'a>
where QueryT: GraphQLType<Context = CtxT>, where QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context=CtxT>, MutationT: GraphQLType<Context = CtxT>
{ {
GraphQLResponse(::execute( GraphQLResponse(::execute(&self.query,
&self.query,
self.operation_name(), self.operation_name(),
root_node, root_node,
&self.variables(), &self.variables(),
context, context))
))
} }
} }
@ -86,7 +91,7 @@ impl<'a> GraphQLResponse<'a> {
impl<'a> ser::Serialize for GraphQLResponse<'a> { impl<'a> ser::Serialize for GraphQLResponse<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer, where S: ser::Serializer
{ {
match self.0 { match self.0 {
Ok((ref res, ref err)) => { Ok((ref res, ref err)) => {
@ -101,18 +106,18 @@ impl<'a> ser::Serialize for GraphQLResponse<'a> {
} }
map.end() map.end()
}, }
Err(ref err) => { Err(ref err) => {
let mut map = try!(serializer.serialize_map(Some(1))); let mut map = try!(serializer.serialize_map(Some(1)));
try!(map.serialize_key("errors")); try!(map.serialize_key("errors"));
try!(map.serialize_value(err)); try!(map.serialize_value(err));
map.end() map.end()
}, }
} }
} }
} }
#[cfg(all(test, any(feature="iron-handlers", feature="rocket-handlers")))] #[cfg(any(test, feature="expose-test-schema"))]
pub mod tests { pub mod tests {
use serde_json::Value as Json; use serde_json::Value as Json;
use serde_json; use serde_json;
@ -145,9 +150,11 @@ pub mod tests {
} }
fn unwrap_json_response(response: &TestResponse) -> Json { fn unwrap_json_response(response: &TestResponse) -> Json {
serde_json::from_str::<Json>( serde_json::from_str::<Json>(response
response.body.as_ref().expect("No data returned from request") .body
).expect("Could not parse JSON object") .as_ref()
.expect("No data returned from request"))
.expect("Could not parse JSON object")
} }
fn test_simple_get<T: HTTPIntegration>(integration: &T) { fn test_simple_get<T: HTTPIntegration>(integration: &T) {
@ -215,9 +222,7 @@ pub mod tests {
} }
fn test_simple_post<T: HTTPIntegration>(integration: &T) { fn test_simple_post<T: HTTPIntegration>(integration: &T) {
let response = integration.post( let response = integration.post("/", r#"{"query": "{hero{name}}"}"#);
"/",
r#"{"query": "{hero{name}}"}"#);
assert_eq!(response.status_code, 200); assert_eq!(response.status_code, 200);
assert_eq!(response.content_type, "application/json"); assert_eq!(response.content_type, "application/json");

View file

@ -1,3 +1 @@
#[cfg(feature="iron-handlers")] pub mod iron_handlers;
#[cfg(feature="rocket-handlers")] pub mod rocket_handlers;
pub mod serde; pub mod serde;

View file

@ -3,7 +3,7 @@ use serde::ser::SerializeMap;
use std::fmt; use std::fmt;
use std::collections::HashMap; use std::collections::HashMap;
use ::{GraphQLError, Value}; use {GraphQLError, Value};
use ast::InputValue; use ast::InputValue;
use executor::ExecutionError; use executor::ExecutionError;
use parser::{ParseError, Spanning, SourcePosition}; use parser::{ParseError, Spanning, SourcePosition};
@ -12,7 +12,7 @@ use validation::RuleError;
impl ser::Serialize for ExecutionError { impl ser::Serialize for ExecutionError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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))); let mut map = try!(serializer.serialize_map(Some(3)));
@ -32,27 +32,25 @@ impl ser::Serialize for ExecutionError {
impl<'a> ser::Serialize for GraphQLError<'a> { impl<'a> ser::Serialize for GraphQLError<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer, where S: ser::Serializer
{ {
match *self { match *self {
GraphQLError::ParseError(ref err) => vec![err].serialize(serializer), GraphQLError::ParseError(ref err) => vec![err].serialize(serializer),
GraphQLError::ValidationError(ref errs) => errs.serialize(serializer), GraphQLError::ValidationError(ref errs) => errs.serialize(serializer),
GraphQLError::NoOperationProvided => { GraphQLError::NoOperationProvided => {
serializer.serialize_str("Must provide an operation") serializer.serialize_str("Must provide an operation")
}, }
GraphQLError::MultipleOperationsProvided => { GraphQLError::MultipleOperationsProvided => {
serializer.serialize_str("Must provide operation name if query contains multiple operations") serializer.serialize_str("Must provide operation name if query contains multiple operations")
}, }
GraphQLError::UnknownOperationName => { GraphQLError::UnknownOperationName => serializer.serialize_str("Unknown operation"),
serializer.serialize_str("Unknown operation")
},
} }
} }
} }
impl<'de> de::Deserialize<'de> for InputValue { impl<'de> de::Deserialize<'de> for InputValue {
fn deserialize<D>(deserializer: D) -> Result<InputValue, D::Error> fn deserialize<D>(deserializer: D) -> Result<InputValue, D::Error>
where D: de::Deserializer<'de>, where D: de::Deserializer<'de>
{ {
struct InputValueVisitor; struct InputValueVisitor;
@ -68,23 +66,21 @@ impl<'de> de::Deserialize<'de> for InputValue {
} }
fn visit_i64<E>(self, value: i64) -> Result<InputValue, E> 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 { if value >= i32::min_value() as i64 && value <= i32::max_value() as i64 {
Ok(InputValue::int(value as i32)) Ok(InputValue::int(value as i32))
} } else {
else {
Err(E::custom(format!("integer out of range"))) Err(E::custom(format!("integer out of range")))
} }
} }
fn visit_u64<E>(self, value: u64) -> Result<InputValue, E> 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 { if value <= i32::max_value() as u64 {
self.visit_i64(value as i64) self.visit_i64(value as i64)
} } else {
else {
Err(E::custom(format!("integer out of range"))) Err(E::custom(format!("integer out of range")))
} }
} }
@ -94,7 +90,7 @@ impl<'de> de::Deserialize<'de> for InputValue {
} }
fn visit_str<E>(self, value: &str) -> Result<InputValue, E> fn visit_str<E>(self, value: &str) -> Result<InputValue, E>
where E: de::Error, where E: de::Error
{ {
self.visit_string(value.into()) self.visit_string(value.into())
} }
@ -112,7 +108,7 @@ impl<'de> de::Deserialize<'de> for InputValue {
} }
fn visit_seq<V>(self, mut visitor: V) -> Result<InputValue, V::Error> 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(); let mut values = Vec::new();
@ -124,7 +120,7 @@ impl<'de> de::Deserialize<'de> for InputValue {
} }
fn visit_map<V>(self, mut visitor: V) -> Result<InputValue, V::Error> 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(); let mut values: HashMap<String, InputValue> = HashMap::new();
@ -142,30 +138,35 @@ impl<'de> de::Deserialize<'de> for InputValue {
impl ser::Serialize for InputValue { impl ser::Serialize for InputValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer, where S: ser::Serializer
{ {
match *self { 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::Int(v) => serializer.serialize_i64(v as i64),
InputValue::Float(v) => serializer.serialize_f64(v), 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::Boolean(v) => serializer.serialize_bool(v),
InputValue::List(ref v) => { InputValue::List(ref v) => {
v.iter().map(|x| x.item.clone()).collect::<Vec<_>>().serialize(serializer) v.iter()
}, .map(|x| x.item.clone())
.collect::<Vec<_>>()
.serialize(serializer)
}
InputValue::Object(ref v) => { InputValue::Object(ref v) => {
v.iter() v.iter()
.map(|&(ref k, ref v)| (k.item.clone(), v.item.clone())) .map(|&(ref k, ref v)| (k.item.clone(), v.item.clone()))
.collect::<HashMap<_, _>>() .collect::<HashMap<_, _>>()
.serialize(serializer) .serialize(serializer)
}, }
} }
} }
} }
impl ser::Serialize for RuleError { impl ser::Serialize for RuleError {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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))); let mut map = try!(serializer.serialize_map(Some(2)));
@ -181,7 +182,7 @@ impl ser::Serialize for RuleError {
impl ser::Serialize for SourcePosition { impl ser::Serialize for SourcePosition {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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))); let mut map = try!(serializer.serialize_map(Some(2)));
@ -199,7 +200,7 @@ impl ser::Serialize for SourcePosition {
impl<'a> ser::Serialize for Spanning<ParseError<'a>> { impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 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))); let mut map = try!(serializer.serialize_map(Some(2)));
@ -222,7 +223,7 @@ impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
impl ser::Serialize for Value { impl ser::Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: ser::Serializer, where S: ser::Serializer
{ {
match *self { match *self {
Value::Null => serializer.serialize_unit(), Value::Null => serializer.serialize_unit(),

View file

@ -10,14 +10,12 @@ statically verify their queries against a server without actually executing
them. them.
This library provides data types and traits to expose Rust types in a GraphQL This library provides data types and traits to expose Rust types in a GraphQL
schema, as well as an optional integration into the [Iron framework][2] and schema, as well as an optional integration into the [Iron framework][Iron] and
[Rocket]. It tries to keep the number of dynamic operations to a minimum, and [Rocket]. It tries to keep the number of dynamic operations to a minimum, and
give you as the schema developer the control of the query execution path. give you as the schema developer the control of the query execution path.
Juniper only depends on `serde` and `serde_derive` by default, making it Juniper only depends on `serde` and `serde_derive` by default, making it
lightweight and easy to drop into any project. If you enable any of the lightweight and easy to drop into any project.
optional framework integrations, it will naturally depend on those frameworks
too.
## Exposing data types ## Exposing data types
@ -96,93 +94,18 @@ Adding per type, field, and argument documentation is possible directly from
this macro. For more in-depth information on how to expose fields and types, see this macro. For more in-depth information on how to expose fields and types, see
the [`graphql_object!`][3] macro. the [`graphql_object!`][3] macro.
## Integrating with Iron
## Integrating with web servers
The most obvious usecase is to expose the GraphQL schema over an HTTP endpoint. The most obvious usecase is to expose the GraphQL schema over an HTTP endpoint.
To support this, the library provides optional and customizable handlers for To support this, Juniper offers additional crates that integrate with popular web frameworks.
both Iron and Rocket.
For example, continuing from the schema created above and using Iron to expose * [juniper_iron][juniper_iron]: Handlers for [Iron][Iron]
the schema on an HTTP endpoint supporting both GET and POST requests: * [juniper_rocket][juniper_rocket]: Handlers for [Rocket][Rocket]
```rust,no_run
extern crate iron;
# #[macro_use] extern crate juniper;
# use std::collections::HashMap;
use iron::prelude::*;
use juniper::iron_handlers::GraphQLHandler;
use juniper::{Context, EmptyMutation};
# use juniper::FieldResult;
#
# struct User { id: String, name: String, friend_ids: Vec<String> }
# struct QueryRoot;
# struct Database { users: HashMap<String, User> }
#
# graphql_object!(User: Database |&self| {
# field id() -> FieldResult<&String> {
# Ok(&self.id)
# }
#
# field name() -> FieldResult<&String> {
# Ok(&self.name)
# }
#
# field friends(&executor) -> FieldResult<Vec<&User>> {
# Ok(self.friend_ids.iter()
# .filter_map(|id| executor.context().users.get(id))
# .collect())
# }
# });
#
# graphql_object!(QueryRoot: Database |&self| {
# field user(&executor, id: String) -> FieldResult<Option<&User>> {
# Ok(executor.context().users.get(&id))
# }
# });
// This function is executed for every request. Here, we would realistically
// provide a database connection or similar. For this example, we'll be
// creating the database from scratch.
fn context_factory(_: &mut Request) -> Database {
Database {
users: vec![
( "1000".to_owned(), User {
id: "1000".to_owned(), name: "Robin".to_owned(),
friend_ids: vec!["1001".to_owned()] } ),
( "1001".to_owned(), User {
id: "1001".to_owned(), name: "Max".to_owned(),
friend_ids: vec!["1000".to_owned()] } ),
].into_iter().collect()
}
}
impl Context for Database {}
fn main() {
// GraphQLHandler takes a context factory function, the root object,
// and the mutation object. If we don't have any mutations to expose, we
// can use the empty tuple () to indicate absence.
let graphql_endpoint = GraphQLHandler::new(
context_factory, QueryRoot, EmptyMutation::<Database>::new());
// Start serving the schema at the root on port 8080.
Iron::new(graphql_endpoint).http("localhost:8080").unwrap();
}
```
See the [`iron_handlers`][4] module and the [`GraphQLHandler`][5] documentation
for more information on what request methods are supported. There's also a
built-in [GraphiQL][6] handler included.
[1]: http://graphql.org [1]: http://graphql.org
[2]: http://ironframework.io
[3]: macro.graphql_object!.html [3]: macro.graphql_object!.html
[4]: iron_handlers/index.html [Iron]: http://ironframework.io
[5]: iron_handlers/struct.GraphQLHandler.html
[6]: https://github.com/graphql/graphiql
[Rocket]: https://rocket.rs [Rocket]: https://rocket.rs
*/ */
@ -190,22 +113,19 @@ built-in [GraphiQL][6] handler included.
#![cfg_attr(feature="nightly", feature(test))] #![cfg_attr(feature="nightly", feature(test))]
#![warn(missing_docs)] #![warn(missing_docs)]
#![cfg_attr(feature="rocket-handlers", feature(plugin))] #[cfg(feature="nightly")]
#![cfg_attr(feature="rocket-handlers", plugin(rocket_codegen))] extern crate test;
#[cfg(feature="rocket-handlers")] extern crate rocket;
#[cfg(feature="nightly")] extern crate test;
#[cfg(feature="iron-handlers")] #[macro_use(itry)] extern crate iron;
#[cfg(feature="iron-handlers")] extern crate urlencoded;
#[cfg(test)] extern crate iron_test;
extern crate serde; extern crate serde;
#[macro_use] extern crate serde_derive; #[macro_use]
extern crate serde_derive;
#[cfg(any(feature="iron-handlers", feature="rocket-handlers"))] extern crate serde_json; #[cfg(any(test, feature="expose-test-schema"))]
extern crate serde_json;
use std::borrow::Cow; use std::borrow::Cow;
#[macro_use] mod macros; #[macro_use]
mod macros;
mod ast; mod ast;
pub mod parser; pub mod parser;
mod value; mod value;
@ -216,12 +136,16 @@ mod executor;
mod integrations; mod integrations;
pub mod graphiql; pub mod graphiql;
pub mod http; pub mod http;
#[macro_use] mod result_ext; #[macro_use]
mod result_ext;
#[cfg(all(test, not(feature="expose-test-schema")))] mod tests; #[cfg(all(test, not(feature="expose-test-schema")))]
#[cfg(feature="expose-test-schema")] pub mod tests; mod tests;
#[cfg(feature="expose-test-schema")]
pub mod tests;
#[cfg(test)] mod executor_tests; #[cfg(test)]
mod executor_tests;
use parser::{parse_document_source, ParseError, Spanning}; use parser::{parse_document_source, ParseError, Spanning};
use validation::{ValidatorContext, visit_all_rules, validate_input_values}; use validation::{ValidatorContext, visit_all_rules, validate_input_values};
@ -230,11 +154,8 @@ use executor::execute_validated_query;
pub use ast::{ToInputValue, FromInputValue, InputValue, Type, Selection}; pub use ast::{ToInputValue, FromInputValue, InputValue, Type, Selection};
pub use value::Value; pub use value::Value;
pub use types::base::{Arguments, GraphQLType, TypeKind}; pub use types::base::{Arguments, GraphQLType, TypeKind};
pub use executor::{ pub use executor::{Executor, ExecutionError, Registry, Context, FromContext, IntoResolvable,
Executor, ExecutionError, Registry, FieldResult, ExecutionResult, Variables};
Context, FromContext, IntoResolvable,
FieldResult, ExecutionResult, Variables,
};
pub use validation::RuleError; pub use validation::RuleError;
pub use types::scalars::{EmptyMutation, ID}; pub use types::scalars::{EmptyMutation, ID};
pub use schema::model::RootNode; pub use schema::model::RootNode;
@ -242,9 +163,6 @@ pub use result_ext::ResultExt;
pub use schema::meta; pub use schema::meta;
#[cfg(feature="iron-handlers")] pub use integrations::iron_handlers;
#[cfg(feature="rocket-handlers")] pub use integrations::rocket_handlers;
/// An error that prevented query execution /// An error that prevented query execution
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
@ -257,16 +175,15 @@ pub enum GraphQLError<'a> {
} }
/// Execute a query in a provided schema /// Execute a query in a provided schema
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, CtxT, QueryT, MutationT>
document_source: &'a str, (document_source: &'a str,
operation_name: Option<&str>, operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>, root_node: &RootNode<QueryT, MutationT>,
variables: &Variables, variables: &Variables,
context: &CtxT, context: &CtxT)
)
-> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
where QueryT: GraphQLType<Context = CtxT>, where QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context=CtxT>, MutationT: GraphQLType<Context = CtxT>
{ {
let document = try!(parse_document_source(document_source)); let document = try!(parse_document_source(document_source));
@ -304,15 +221,17 @@ pub fn to_camel_case<'a>(s: &'a str) -> Cow<'a, str> {
for (i, part) in s.split('_').enumerate() { for (i, part) in s.split('_').enumerate() {
if i > 0 && part.len() == 1 { if i > 0 && part.len() == 1 {
dest += Cow::Owned(part.to_uppercase()); dest += Cow::Owned(part.to_uppercase());
} } else if i > 0 && part.len() > 1 {
else if i > 0 && part.len() > 1 { let first = part.chars()
let first = part.chars().next().unwrap().to_uppercase().collect::<String>(); .next()
.unwrap()
.to_uppercase()
.collect::<String>();
let second = &part[1..]; let second = &part[1..];
dest += Cow::Owned(first); dest += Cow::Owned(first);
dest += second; dest += second;
} } else if i == 0 {
else if i == 0 {
dest = Cow::Borrowed(part); dest = Cow::Borrowed(part);
} }
} }

View file

@ -1,10 +1,19 @@
#[macro_use] mod enums; #[macro_use]
#[macro_use] mod object; mod enums;
#[macro_use] mod interface; #[macro_use]
#[macro_use] mod scalar; mod object;
#[macro_use] mod args; #[macro_use]
#[macro_use] mod field; mod interface;
#[macro_use] mod input_object; #[macro_use]
#[macro_use] mod union; mod scalar;
#[macro_use]
mod args;
#[macro_use]
mod field;
#[macro_use]
mod input_object;
#[macro_use]
mod union;
#[cfg(test)] mod tests; #[cfg(test)]
mod tests;

View file

@ -110,28 +110,41 @@ fn run_args_info_query<F>(field_name: &str, f: F)
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields not a list"); .expect("fields field missing")
.as_list_value()
.expect("fields not a list");
let field = fields let field = fields
.into_iter().filter( .into_iter()
|f| f.as_object_value().expect("Field not an object") .filter(|f| {
.get("name").expect("name field missing from field") f.as_object_value()
.as_string_value().expect("name is not a string") .expect("Field not an object")
== field_name) .get("name")
.next().expect("Field not found") .expect("name field missing from field")
.as_object_value().expect("Field is not an object"); .as_string_value()
.expect("name is not a string") == field_name
})
.next()
.expect("Field not found")
.as_object_value()
.expect("Field is not an object");
println!("Field: {:?}", field); println!("Field: {:?}", field);
let args = field let args = field
.get("args").expect("args missing from field") .get("args")
.as_list_value().expect("args is not a list"); .expect("args missing from field")
.as_list_value()
.expect("args is not a list");
println!("Args: {:?}", args); println!("Args: {:?}", args);

View file

@ -6,12 +6,30 @@ use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
enum DefaultName { Foo, Bar } enum DefaultName {
enum Named { Foo, Bar } Foo,
enum NoTrailingComma { Foo, Bar } Bar,
enum EnumDescription { Foo, Bar } }
enum EnumValueDescription { Foo, Bar } enum Named {
enum EnumDeprecation { Foo, Bar } Foo,
Bar,
}
enum NoTrailingComma {
Foo,
Bar,
}
enum EnumDescription {
Foo,
Bar,
}
enum EnumValueDescription {
Foo,
Bar,
}
enum EnumDeprecation {
Foo,
Bar,
}
struct Root; struct Root;
@ -68,7 +86,9 @@ graphql_object!(Root: () |&self| {
field enum_deprecation() -> EnumDeprecation { EnumDeprecation::Foo } field enum_deprecation() -> EnumDeprecation { EnumDeprecation::Foo }
}); });
fn run_type_info_query<F>(doc: &str, f: F) where F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> () { fn run_type_info_query<F>(doc: &str, f: F)
where F: Fn((&HashMap<String, Value>, &Vec<Value>)) -> ()
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()) let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
@ -79,13 +99,18 @@ fn run_type_info_query<F>(doc: &str, f: F) where F: Fn((&HashMap<String, Value>,
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let values = type_info let values = type_info
.get("enumValues").expect("enumValues field missing") .get("enumValues")
.as_list_value().expect("enumValues not a list"); .expect("enumValues field missing")
.as_list_value()
.expect("enumValues not a list");
f((type_info, values)); f((type_info, values));
} }

View file

@ -75,32 +75,44 @@ fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![ let vars = vec![
("typeName".to_owned(), InputValue::string(type_name)), ("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect(); ]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields not a list"); .expect("fields field missing")
.as_list_value()
.expect("fields not a list");
let field = fields let field = fields
.into_iter().filter( .into_iter()
|f| f.as_object_value().expect("Field not an object") .filter(|f| {
.get("name").expect("name field missing from field") f.as_object_value()
.as_string_value().expect("name is not a string") .expect("Field not an object")
== field_name) .get("name")
.next().expect("Field not found") .expect("name field missing from field")
.as_object_value().expect("Field is not an object"); .as_string_value()
.expect("name is not a string") == field_name
})
.next()
.expect("Field not found")
.as_object_value()
.expect("Field is not an object");
println!("Field: {:?}", field); println!("Field: {:?}", field);

View file

@ -103,7 +103,9 @@ graphql_object!(Root: () |&self| {
} }
}); });
fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> () { fn run_type_info_query<F>(doc: &str, f: F)
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()) let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
@ -114,13 +116,18 @@ fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>,
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info let fields = type_info
.get("inputFields").expect("inputFields field missing") .get("inputFields")
.as_list_value().expect("inputFields not a list"); .expect("inputFields field missing")
.as_list_value()
.expect("inputFields not a list");
f(type_info, fields); f(type_info, fields);
} }
@ -181,7 +188,9 @@ fn default_name_input_value() {
let iv = InputValue::object(vec![ let iv = InputValue::object(vec![
("fieldOne", InputValue::string("number one")), ("fieldOne", InputValue::string("number one")),
("fieldTwo", InputValue::string("number two")), ("fieldTwo", InputValue::string("number two")),
].into_iter().collect()); ]
.into_iter()
.collect());
let dv: Option<DefaultName> = FromInputValue::from(&iv); let dv: Option<DefaultName> = FromInputValue::from(&iv);

View file

@ -23,10 +23,14 @@ struct Concrete;
struct CustomName; struct CustomName;
#[allow(dead_code)] #[allow(dead_code)]
struct WithLifetime<'a> { data: PhantomData<&'a i32> } struct WithLifetime<'a> {
data: PhantomData<&'a i32>,
}
#[allow(dead_code)] #[allow(dead_code)]
struct WithGenerics<T> { data: T } struct WithGenerics<T> {
data: T,
}
struct DescriptionFirst; struct DescriptionFirst;
struct FieldsFirst; struct FieldsFirst;
@ -146,23 +150,29 @@ fn run_type_info_query<F>(type_name: &str, f: F)
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![ let vars = vec![
("typeName".to_owned(), InputValue::string(type_name)), ("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect(); ]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields field not a list value"); .expect("fields field missing")
.as_list_value()
.expect("fields field not a list value");
f(type_info, fields); f(type_info, fields);
} }

View file

@ -1,6 +1,7 @@
mod enums; mod enums;
mod scalar; mod scalar;
#[allow(dead_code)] mod input_object; #[allow(dead_code)]
mod input_object;
mod args; mod args;
mod field; mod field;
mod object; mod object;

View file

@ -22,10 +22,14 @@ struct Interface;
struct CustomName; struct CustomName;
#[allow(dead_code)] #[allow(dead_code)]
struct WithLifetime<'a> { data: PhantomData<&'a i32> } struct WithLifetime<'a> {
data: PhantomData<&'a i32>,
}
#[allow(dead_code)] #[allow(dead_code)]
struct WithGenerics<T> { data: T } struct WithGenerics<T> {
data: T,
}
struct DescriptionFirst; struct DescriptionFirst;
struct FieldsFirst; struct FieldsFirst;
@ -134,23 +138,29 @@ fn run_type_info_query<F>(type_name: &str, f: F)
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![ let vars = vec![
("typeName".to_owned(), InputValue::string(type_name)), ("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect(); ]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info let fields = type_info
.get("fields").expect("fields field missing") .get("fields")
.as_list_value().expect("fields field not a list value"); .expect("fields field missing")
.as_list_value()
.expect("fields field not a list value");
f(type_info, fields); f(type_info, fields);
} }

View file

@ -70,7 +70,9 @@ graphql_object!(Root: () |&self| {
field scalar_description() -> ScalarDescription { ScalarDescription(0) } field scalar_description() -> ScalarDescription { ScalarDescription(0) }
}); });
fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>) -> () { fn run_type_info_query<F>(doc: &str, f: F)
where F: Fn(&HashMap<String, Value>) -> ()
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()) let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &())
@ -81,9 +83,12 @@ fn run_type_info_query<F>(doc: &str, f: F) where F: Fn(&HashMap<String, Value>)
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
f(type_info); f(type_info);
} }

View file

@ -20,16 +20,30 @@ Syntax to validate:
struct Concrete; struct Concrete;
enum CustomName { Concrete(Concrete) } enum CustomName {
Concrete(Concrete),
}
enum WithLifetime<'a> { Int(PhantomData<&'a i32>) } enum WithLifetime<'a> {
enum WithGenerics<T> { Generic(T) } Int(PhantomData<&'a i32>),
}
enum WithGenerics<T> {
Generic(T),
}
enum DescriptionFirst { Concrete(Concrete) } enum DescriptionFirst {
enum ResolversFirst { Concrete(Concrete) } Concrete(Concrete),
}
enum ResolversFirst {
Concrete(Concrete),
}
enum CommasWithTrailing { Concrete(Concrete) } enum CommasWithTrailing {
enum ResolversWithTrailingComma { Concrete(Concrete) } Concrete(Concrete),
}
enum ResolversWithTrailingComma {
Concrete(Concrete),
}
struct Root; struct Root;
@ -113,23 +127,29 @@ fn run_type_info_query<F>(type_name: &str, f: F)
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![ let vars = vec![
("typeName".to_owned(), InputValue::string(type_name)), ("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect(); ]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &()) let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
.expect("Execution failed");
assert_eq!(errs, []); assert_eq!(errs, []);
println!("Result: {:?}", result); println!("Result: {:?}", result);
let type_info = result let type_info = result
.as_object_value().expect("Result is not an object") .as_object_value()
.get("__type").expect("__type field missing") .expect("Result is not an object")
.as_object_value().expect("__type field not an object value"); .get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let possible_types = type_info let possible_types = type_info
.get("possibleTypes").expect("possibleTypes field missing") .get("possibleTypes")
.as_list_value().expect("possibleTypes field not a list value"); .expect("possibleTypes field missing")
.as_list_value()
.expect("possibleTypes field not a list value");
f(type_info, possible_types); f(type_info, possible_types);
} }

View file

@ -1,9 +1,9 @@
use ast::{Definition, Document, OperationType, use ast::{Definition, Document, OperationType, VariableDefinitions, VariableDefinition,
VariableDefinitions, VariableDefinition, InputValue, InputValue, Operation, Fragment, Selection, Directive, Field, Arguments, FragmentSpread,
Operation, Fragment, Selection, Directive, Field, Arguments, InlineFragment, Type};
FragmentSpread, InlineFragment, Type};
use parser::{Lexer, Parser, Spanning, UnlocatedParseResult, OptionParseResult, ParseResult, ParseError, Token}; use parser::{Lexer, Parser, Spanning, UnlocatedParseResult, OptionParseResult, ParseResult,
ParseError, Token};
use parser::value::parse_value_literal; use parser::value::parse_value_literal;
#[doc(hidden)] #[doc(hidden)]
@ -27,10 +27,14 @@ fn parse_document<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Docum
fn parse_definition<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Definition<'a>> { fn parse_definition<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Definition<'a>> {
match parser.peek().item { match parser.peek().item {
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Token::CurlyOpen |
Ok(Definition::Operation(try!(parse_operation_definition(parser)))), Token::Name("query") |
Token::Name("fragment") => Token::Name("mutation") => {
Ok(Definition::Fragment(try!(parse_fragment_definition(parser)))), 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)), _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
@ -39,8 +43,7 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
if parser.peek().item == Token::CurlyOpen { if parser.peek().item == Token::CurlyOpen {
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end( Ok(Spanning::start_end(&selection_set.start,
&selection_set.start,
&selection_set.end, &selection_set.end,
Operation { Operation {
operation_type: OperationType::Query, operation_type: OperationType::Query,
@ -49,20 +52,18 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
directives: None, directives: None,
selection_set: selection_set.item, selection_set: selection_set.item,
})) }))
} } else {
else {
let start_pos = parser.peek().start.clone(); let start_pos = parser.peek().start.clone();
let operation_type = try!(parse_operation_type(parser)); let operation_type = try!(parse_operation_type(parser));
let name = match parser.peek().item { let name = match parser.peek().item {
Token::Name(_) => Some(try!(parser.expect_name())), Token::Name(_) => Some(try!(parser.expect_name())),
_ => None _ => None,
}; };
let variable_definitions = try!(parse_variable_definitions(parser)); let variable_definitions = try!(parse_variable_definitions(parser));
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos,
&start_pos,
&selection_set.end, &selection_set.end,
Operation { Operation {
operation_type: operation_type.item, operation_type: operation_type.item,
@ -77,12 +78,13 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fragment<'a>> { 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() { let name = match parser.expect_name() {
Ok(n) => if n.item == "on" { Ok(n) => {
if n.item == "on" {
return Err(n.map(|_| ParseError::UnexpectedToken(Token::Name("on")))); return Err(n.map(|_| ParseError::UnexpectedToken(Token::Name("on"))));
} } else {
else {
n n
}, }
}
Err(e) => return Err(e), Err(e) => return Err(e),
}; };
@ -91,8 +93,7 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos,
&start_pos,
&selection_set.end, &selection_set.end,
Fragment { Fragment {
name: name, name: name,
@ -102,20 +103,17 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
})) }))
} }
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 { if parser.peek().item == Token::CurlyOpen {
Ok(Some(try!(parse_selection_set(parser)))) Ok(Some(try!(parse_selection_set(parser))))
} } else {
else {
Ok(None) Ok(None)
} }
} }
fn parse_selection_set<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Vec<Selection<'a>>> { fn parse_selection_set<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Vec<Selection<'a>>> {
parser.unlocated_delimited_nonempty_list( parser.unlocated_delimited_nonempty_list(&Token::CurlyOpen, parse_selection, &Token::CurlyClose)
&Token::CurlyOpen,
parse_selection,
&Token::CurlyClose)
} }
fn parse_selection<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selection<'a>> { fn parse_selection<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selection<'a>> {
@ -135,56 +133,58 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment( Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
Spanning::start_end(
&start_pos.clone(),
&selection_set.end, &selection_set.end,
InlineFragment { InlineFragment {
type_condition: Some(name), type_condition: Some(name),
directives: directives.map(|s| s.item), directives: directives.map(|s| {
s.item
}),
selection_set: selection_set.item, selection_set: selection_set.item,
}))) })))
}, }
Token::CurlyOpen => { Token::CurlyOpen => {
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment( Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
Spanning::start_end(
&start_pos.clone(),
&selection_set.end, &selection_set.end,
InlineFragment { InlineFragment {
type_condition: None, type_condition: None,
directives: None, directives: None,
selection_set: selection_set.item, selection_set: selection_set.item,
}))) })))
}, }
Token::Name(_) => { Token::Name(_) => {
let frag_name = try!(parser.expect_name()); let frag_name = try!(parser.expect_name());
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
Ok(Selection::FragmentSpread( Ok(Selection::FragmentSpread(Spanning::start_end(&start_pos.clone(),
Spanning::start_end( &directives
&start_pos.clone(), .as_ref()
&directives.as_ref().map_or(&frag_name.end, |s| &s.end).clone(), .map_or(&frag_name.end,
|s| &s.end)
.clone(),
FragmentSpread { FragmentSpread {
name: frag_name, name: frag_name,
directives: directives.map(|s| s.item), directives: directives.map(|s| {
s.item
}),
}))) })))
}, }
Token::At => { Token::At => {
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser)); let selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment( Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
Spanning::start_end(
&start_pos.clone(),
&selection_set.end, &selection_set.end,
InlineFragment { InlineFragment {
type_condition: None, type_condition: None,
directives: directives.map(|s| s.item), directives: directives.map(|s| {
s.item
}),
selection_set: selection_set.item, selection_set: selection_set.item,
}))) })))
}, }
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)), _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
@ -194,8 +194,7 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
let name = if try!(parser.skip(&Token::Colon)).is_some() { let name = if try!(parser.skip(&Token::Colon)).is_some() {
try!(parser.expect_name()) try!(parser.expect_name())
} } else {
else {
alias.take().unwrap() alias.take().unwrap()
}; };
@ -203,9 +202,10 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
let directives = try!(parse_directives(parser)); let directives = try!(parse_directives(parser));
let selection_set = try!(parse_optional_selection_set(parser)); let selection_set = try!(parse_optional_selection_set(parser));
Ok(Spanning::start_end( Ok(Spanning::start_end(&alias.as_ref().unwrap_or(&name).start.clone(),
&alias.as_ref().unwrap_or(&name).start.clone(), &selection_set
&selection_set.as_ref().map(|s| &s.end) .as_ref()
.map(|s| &s.end)
.or_else(|| directives.as_ref().map(|s| &s.end)) .or_else(|| directives.as_ref().map(|s| &s.end))
.or_else(|| arguments.as_ref().map(|s| &s.end)) .or_else(|| arguments.as_ref().map(|s| &s.end))
.unwrap_or(&name.end) .unwrap_or(&name.end)
@ -227,43 +227,51 @@ fn parse_arguments<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Argumen
&Token::ParenOpen, &Token::ParenOpen,
parse_argument, parse_argument,
&Token::ParenClose &Token::ParenClose
)).map(|args| Arguments { items: args.into_iter().map(|s| s.item).collect() }))) ))
.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()); let name = try!(parser.expect_name());
try!(parser.expect(&Token::Colon)); try!(parser.expect(&Token::Colon));
let value = try!(parse_value_literal(parser, false)); let value = try!(parse_value_literal(parser, false));
Ok(Spanning::start_end( Ok(Spanning::start_end(&name.start.clone(), &value.end.clone(), (name, value)))
&name.start.clone(),
&value.end.clone(),
(name, value)))
} }
fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, OperationType> { fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, OperationType> {
match parser.peek().item { match parser.peek().item {
Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)), Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)),
Token::Name("mutation") => Ok(parser.next()?.map(|_| OperationType::Mutation)), Token::Name("mutation") => Ok(parser.next()?.map(|_| OperationType::Mutation)),
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)) _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
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 { if parser.peek().item != Token::ParenOpen {
Ok(None) Ok(None)
} } else {
else {
Ok(Some(try!(parser.delimited_nonempty_list( Ok(Some(try!(parser.delimited_nonempty_list(
&Token::ParenOpen, &Token::ParenOpen,
parse_variable_definition, parse_variable_definition,
&Token::ParenClose &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>)> { 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 Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Dollar));
let var_name = try!(parser.expect_name()); let var_name = try!(parser.expect_name());
try!(parser.expect(&Token::Colon)); try!(parser.expect(&Token::Colon));
@ -271,32 +279,27 @@ fn parse_variable_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, (Sp
let default_value = if try!(parser.skip(&Token::Equals)).is_some() { let default_value = if try!(parser.skip(&Token::Equals)).is_some() {
Some(try!(parse_value_literal(parser, true))) Some(try!(parse_value_literal(parser, true)))
} } else {
else {
None None
}; };
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos,
&start_pos, &default_value
&default_value.as_ref().map_or(&var_type.end, |s| &s.end).clone(), .as_ref()
( .map_or(&var_type.end, |s| &s.end)
Spanning::start_end( .clone(),
&start_pos, (Spanning::start_end(&start_pos, &var_name.end, var_name.item),
&var_name.end,
var_name.item,
),
VariableDefinition { VariableDefinition {
var_type: var_type, var_type: var_type,
default_value: default_value, 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 { if parser.peek().item != Token::At {
Ok(None) Ok(None)
} } else {
else {
let mut items = Vec::new(); let mut items = Vec::new();
while parser.peek().item == Token::At { while parser.peek().item == Token::At {
items.push(try!(parse_directive(parser))); items.push(try!(parse_directive(parser)));
@ -311,8 +314,7 @@ fn parse_directive<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Directive<'a>
let name = try!(parser.expect_name()); let name = try!(parser.expect_name());
let arguments = try!(parse_arguments(parser)); let arguments = try!(parse_arguments(parser));
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos,
&start_pos,
&arguments.as_ref().map_or(&name.end, |s| &s.end).clone(), &arguments.as_ref().map_or(&name.end, |s| &s.end).clone(),
Directive { Directive {
name: name, name: name,
@ -321,26 +323,26 @@ fn parse_directive<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Directive<'a>
} }
pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> { 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 inner_type = try!(parse_type(parser));
let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::BracketClose)); let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::BracketClose));
Spanning::start_end( Spanning::start_end(&start_pos, &end_pos, Type::List(Box::new(inner_type.item)))
&start_pos, } else {
&end_pos,
Type::List(Box::new(inner_type.item)))
}
else {
try!(parser.expect_name()).map(Type::Named) try!(parser.expect_name()).map(Type::Named)
}; };
Ok(match *parser.peek() { Ok(match *parser.peek() {
Spanning { item: Token::ExclamationMark, .. } => Spanning { item: Token::ExclamationMark, .. } => {
try!(wrap_non_null(parser, parsed_type)), try!(wrap_non_null(parser, parsed_type))
_ => 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 Spanning { end: end_pos, .. } = try!(parser.expect(&Token::ExclamationMark));
let wrapped = match inner.item { let wrapped = match inner.item {

View file

@ -114,8 +114,7 @@ impl<'a> Lexer<'a> {
if let Some((_, ch)) = next { if let Some((_, ch)) = next {
if ch == '\n' { if ch == '\n' {
self.position.advance_line(); self.position.advance_line();
} } else {
else {
self.position.advance_col(); self.position.advance_col();
} }
} }
@ -138,24 +137,20 @@ impl<'a> Lexer<'a> {
while let Some((_, ch)) = self.peek_char() { while let Some((_, ch)) = self.peek_char() {
if ch == '\t' || ch == ' ' || ch == '\n' || ch == '\r' || ch == ',' { if ch == '\t' || ch == ' ' || ch == '\n' || ch == '\r' || ch == ',' {
self.next_char(); self.next_char();
} } else if ch == '#' {
else if ch == '#' {
self.next_char(); self.next_char();
while let Some((_, ch)) = self.peek_char() { while let Some((_, ch)) = self.peek_char() {
if is_source_char(ch) && (ch == '\n' || ch == '\r') { if is_source_char(ch) && (ch == '\n' || ch == '\r') {
self.next_char(); self.next_char();
break; break;
} } else if is_source_char(ch) {
else if is_source_char(ch) {
self.next_char(); self.next_char();
} } else {
else {
break; break;
} }
} }
} } else {
else {
break; break;
} }
} }
@ -176,7 +171,8 @@ impl<'a> Lexer<'a> {
fn scan_name(&mut self) -> LexerResult<'a> { fn scan_name(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone(); let start_pos = self.position.clone();
let (start_idx, start_ch) = try!(self.next_char().ok_or( let (start_idx, start_ch) =
try!(self.next_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile))); Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
assert!(is_name_start(start_ch)); assert!(is_name_start(start_ch));
@ -186,21 +182,20 @@ impl<'a> Lexer<'a> {
if is_name_cont(ch) { if is_name_cont(ch) {
self.next_char(); self.next_char();
end_idx = idx; end_idx = idx;
} } else {
else {
break; break;
} }
} }
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos,
&start_pos,
&self.position, &self.position,
Token::Name(&self.source[start_idx..end_idx + 1]))) Token::Name(&self.source[start_idx..end_idx + 1])))
} }
fn scan_string(&mut self) -> LexerResult<'a> { fn scan_string(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone(); let start_pos = self.position.clone();
let (_, start_ch) = try!(self.next_char().ok_or( let (_, start_ch) =
try!(self.next_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile))); Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
assert!(start_ch == '"'); assert!(start_ch == '"');
@ -209,64 +204,72 @@ impl<'a> Lexer<'a> {
while let Some((_, ch)) = self.peek_char() { while let Some((_, ch)) = self.peek_char() {
if ch == '"' { if ch == '"' {
self.next_char(); self.next_char();
return Ok(Spanning::start_end( return Ok(Spanning::start_end(&start_pos, &self.position, Token::String(acc)));
&start_pos, } else if ch == '\\' {
&self.position,
Token::String(acc)));
}
else if ch == '\\' {
self.next_char(); self.next_char();
match self.peek_char() { match self.peek_char() {
Some((_, '"')) => { self.next_char(); acc.push('"'); }, Some((_, '"')) => {
Some((_, '\\')) => { self.next_char(); acc.push('\\'); }, self.next_char();
Some((_, '/')) => { self.next_char(); acc.push('/'); }, acc.push('"');
Some((_, 'b')) => { self.next_char(); acc.push('\u{0008}'); }, }
Some((_, 'f')) => { self.next_char(); acc.push('\u{000c}'); }, Some((_, '\\')) => {
Some((_, 'n')) => { self.next_char(); acc.push('\n'); }, self.next_char();
Some((_, 'r')) => { self.next_char(); acc.push('\r'); }, acc.push('\\');
Some((_, 't')) => { self.next_char(); acc.push('\t'); }, }
Some((_, '/')) => {
self.next_char();
acc.push('/');
}
Some((_, 'b')) => {
self.next_char();
acc.push('\u{0008}');
}
Some((_, 'f')) => {
self.next_char();
acc.push('\u{000c}');
}
Some((_, 'n')) => {
self.next_char();
acc.push('\n');
}
Some((_, 'r')) => {
self.next_char();
acc.push('\r');
}
Some((_, 't')) => {
self.next_char();
acc.push('\t');
}
Some((_, 'u')) => { Some((_, 'u')) => {
let start_pos = self.position.clone(); let start_pos = self.position.clone();
self.next_char(); self.next_char();
acc.push(try!(self.scan_escaped_unicode(&start_pos))); acc.push(try!(self.scan_escaped_unicode(&start_pos)));
}, }
Some((_, ch)) => { Some((_, ch)) => {
let mut s = String::from("\\"); let mut s = String::from("\\");
s.push(ch); s.push(ch);
return Err(Spanning::zero_width( return Err(Spanning::zero_width(&self.position,
&self.position,
LexerError::UnknownEscapeSequence(s))); LexerError::UnknownEscapeSequence(s)));
}, }
None => { None => {
return Err(Spanning::zero_width( return Err(Spanning::zero_width(&self.position,
&self.position,
LexerError::UnterminatedString)); LexerError::UnterminatedString));
}, }
} }
if let Some((_, ch)) = self.peek_char() { if let Some((_, ch)) = self.peek_char() {
if ch == 'n' { if ch == 'n' {}
} else {
} return Err(Spanning::zero_width(&self.position,
}
else {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnterminatedString)); LexerError::UnterminatedString));
} }
} } else if ch == '\n' || ch == '\r' {
else if ch == '\n' || ch == '\r' { return Err(Spanning::zero_width(&self.position, LexerError::UnterminatedString));
return Err(Spanning::zero_width( } else if !is_source_char(ch) {
&self.position, 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))); LexerError::UnknownCharacterInString(ch)));
} } else {
else {
self.next_char(); self.next_char();
acc.push(ch); acc.push(ch);
} }
@ -275,14 +278,18 @@ impl<'a> Lexer<'a> {
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>> { fn scan_escaped_unicode(&mut self,
let (start_idx, _) = try!(self.peek_char().ok_or( start_pos: &SourcePosition)
-> Result<char, Spanning<LexerError>> {
let (start_idx, _) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnterminatedString))); Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
let mut end_idx = start_idx; let mut end_idx = start_idx;
let mut len = 0; let mut len = 0;
for _ in 0..4 { for _ in 0..4 {
let (idx, ch) = try!(self.next_char().ok_or( let (idx, ch) =
try!(self.next_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnterminatedString))); Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
if !ch.is_alphanumeric() { if !ch.is_alphanumeric() {
@ -296,9 +303,9 @@ impl<'a> Lexer<'a> {
let escape = &self.source[start_idx..end_idx + 1]; let escape = &self.source[start_idx..end_idx + 1];
if len != 4 { if len != 4 {
return Err(Spanning::zero_width( return Err(Spanning::zero_width(start_pos,
start_pos, LexerError::UnknownEscapeSequence("\\u".to_owned() +
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))); 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(|_|
@ -306,10 +313,10 @@ impl<'a> Lexer<'a> {
start_pos, start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape)))); LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))));
char::from_u32(code_point).ok_or_else(|| char::from_u32(code_point).ok_or_else(|| {
Spanning::zero_width( Spanning::zero_width(start_pos,
start_pos, LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))) })
} }
fn scan_number(&mut self) -> LexerResult<'a> { fn scan_number(&mut self) -> LexerResult<'a> {
@ -334,8 +341,7 @@ impl<'a> Lexer<'a> {
if ch == '-' { if ch == '-' {
self.next_char(); self.next_char();
is_negative = true; is_negative = true;
} } else if ch == '+' {
else if ch == '+' {
self.next_char(); self.next_char();
} }
} }
@ -343,20 +349,18 @@ impl<'a> Lexer<'a> {
} }
} }
let mantissa = frac_part.map(|f| f as f64).map(|frac| let mantissa = frac_part
if frac > 0f64 { .map(|f| f as f64)
.map(|frac| if frac > 0f64 {
frac / 10f64.powf(frac.log10().floor() + 1f64) frac / 10f64.powf(frac.log10().floor() + 1f64)
} } else {
else {
0f64 0f64
}).map(|m| if int_part < 0 { -m } else { m }); })
.map(|m| if int_part < 0 { -m } else { m });
let exp = exp_part.map(|e| e as f64).map(|e| 10f64.powf(e)); let exp = exp_part.map(|e| e as f64).map(|e| 10f64.powf(e));
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos, &self.position, match (mantissa, exp) {
&start_pos,
&self.position,
match (mantissa, exp) {
(None, None) => Token::Int(int_part), (None, None) => Token::Int(int_part),
(None, Some(exp)) => Token::Float((int_part as f64) * exp), (None, Some(exp)) => Token::Float((int_part as f64) * exp),
(Some(mantissa), None) => Token::Float((int_part as f64) + mantissa), (Some(mantissa), None) => Token::Float((int_part as f64) + mantissa),
@ -366,14 +370,14 @@ impl<'a> Lexer<'a> {
fn scan_integer_part(&mut self) -> Result<i32, Spanning<LexerError>> { fn scan_integer_part(&mut self) -> Result<i32, Spanning<LexerError>> {
let is_negative = { let is_negative = {
let (_, init_ch) = try!(self.peek_char().ok_or( let (_, init_ch) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile))); Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
if init_ch == '-' { if init_ch == '-' {
self.next_char(); self.next_char();
true true
} } else {
else {
false false
} }
}; };
@ -385,18 +389,20 @@ impl<'a> Lexer<'a> {
self.next_char(); self.next_char();
match self.peek_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), _ => Ok(0),
} }
} } else {
else {
Ok(try!(self.scan_digits()) * if is_negative { -1 } else { 1 }) Ok(try!(self.scan_digits()) * if is_negative { -1 } else { 1 })
} }
} }
fn scan_digits(&mut self) -> Result<i32, Spanning<LexerError>> { fn scan_digits(&mut self) -> Result<i32, Spanning<LexerError>> {
let start_pos = self.position.clone(); let start_pos = self.position.clone();
let (start_idx, ch) = try!(self.peek_char().ok_or( let (start_idx, ch) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile))); Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
let mut end_idx = start_idx; let mut end_idx = start_idx;
@ -407,8 +413,7 @@ impl<'a> Lexer<'a> {
while let Some((idx, ch)) = self.peek_char() { while let Some((idx, ch)) = self.peek_char() {
if !ch.is_digit(10) { if !ch.is_digit(10) {
break; break;
} } else {
else {
self.next_char(); self.next_char();
end_idx = idx; end_idx = idx;
} }
@ -449,19 +454,16 @@ impl<'a> Iterator for Lexer<'a> {
Some(ch) => { Some(ch) => {
if is_number_start(ch) { if is_number_start(ch) {
self.scan_number() self.scan_number()
} } else if is_name_start(ch) {
else if is_name_start(ch) {
self.scan_name() self.scan_name()
} } else {
else {
Err(Spanning::zero_width(&self.position, LexerError::UnknownCharacter(ch))) Err(Spanning::zero_width(&self.position, LexerError::UnknownCharacter(ch)))
} }
}, }
None => { None => {
self.has_reached_eof = true; self.has_reached_eof = true;
Ok(Spanning::zero_width( Ok(Spanning::zero_width(&self.position, Token::EndOfFile))
&self.position, Token::EndOfFile)) }
},
}) })
} }
} }
@ -472,7 +474,9 @@ impl<'a> fmt::Display for Token<'a> {
Token::Name(name) => write!(f, "{}", name), Token::Name(name) => write!(f, "{}", name),
Token::Int(i) => write!(f, "{}", i), Token::Int(i) => write!(f, "{}", i),
Token::Float(v) => write!(f, "{}", v), Token::Float(v) => write!(f, "{}", v),
Token::String(ref s) => write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")), Token::String(ref s) => {
write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
}
Token::ExclamationMark => write!(f, "!"), Token::ExclamationMark => write!(f, "!"),
Token::Dollar => write!(f, "$"), Token::Dollar => write!(f, "$"),
Token::ParenOpen => write!(f, "("), Token::ParenOpen => write!(f, "("),
@ -512,8 +516,12 @@ impl fmt::Display for LexerError {
match *self { match *self {
LexerError::UnknownCharacter(c) => write!(f, "Unknown character \"{}\"", c), LexerError::UnknownCharacter(c) => write!(f, "Unknown character \"{}\"", c),
LexerError::UnterminatedString => write!(f, "Unterminated string literal"), LexerError::UnterminatedString => write!(f, "Unterminated string literal"),
LexerError::UnknownCharacterInString(c) => write!(f, "Unknown character \"{}\" in string literal", c), LexerError::UnknownCharacterInString(c) => {
LexerError::UnknownEscapeSequence(ref s) => write!(f, "Unknown escape sequence \"{}\" in string", s), write!(f, "Unknown character \"{}\" in string literal", c)
}
LexerError::UnknownEscapeSequence(ref s) => {
write!(f, "Unknown escape sequence \"{}\" in string", s)
}
LexerError::UnexpectedCharacter(c) => write!(f, "Unexpected character \"{}\"", c), LexerError::UnexpectedCharacter(c) => write!(f, "Unexpected character \"{}\"", c),
LexerError::UnexpectedEndOfFile => write!(f, "Unexpected end of input"), LexerError::UnexpectedEndOfFile => write!(f, "Unexpected end of input"),
LexerError::InvalidNumber => write!(f, "Invalid number literal"), LexerError::InvalidNumber => write!(f, "Invalid number literal"),

View file

@ -43,9 +43,7 @@ impl<'a> Parser<'a> {
} }
} }
Ok(Parser { Ok(Parser { tokens: tokens })
tokens: tokens,
})
} }
#[doc(hidden)] #[doc(hidden)]
@ -56,8 +54,7 @@ impl<'a> Parser<'a> {
#[doc(hidden)] #[doc(hidden)]
pub fn next(&mut self) -> ParseResult<'a, Token<'a>> { pub fn next(&mut self) -> ParseResult<'a, Token<'a>> {
if self.tokens.len() == 1 { if self.tokens.len() == 1 {
Err(Spanning::start_end( Err(Spanning::start_end(&self.peek().start.clone(),
&self.peek().start.clone(),
&self.peek().end.clone(), &self.peek().end.clone(),
ParseError::UnexpectedEndOfFile)) ParseError::UnexpectedEndOfFile))
} else { } else {
@ -69,41 +66,39 @@ impl<'a> Parser<'a> {
pub fn expect(&mut self, expected: &Token) -> ParseResult<'a, Token<'a>> { pub fn expect(&mut self, expected: &Token) -> ParseResult<'a, Token<'a>> {
if &self.peek().item != expected { if &self.peek().item != expected {
Err(self.next()?.map(ParseError::UnexpectedToken)) Err(self.next()?.map(ParseError::UnexpectedToken))
} } else {
else {
self.next() self.next()
} }
} }
#[doc(hidden)] #[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 { if &self.peek().item == expected {
Ok(Some(self.next()?)) Ok(Some(self.next()?))
} } else if self.peek().item == Token::EndOfFile {
else if self.peek().item == Token::EndOfFile { Err(Spanning::zero_width(&self.peek().start, ParseError::UnexpectedEndOfFile))
Err(Spanning::zero_width( } else {
&self.peek().start,
ParseError::UnexpectedEndOfFile))
}
else {
Ok(None) Ok(None)
} }
} }
#[doc(hidden)] #[doc(hidden)]
pub fn delimited_list<T, F>(&mut self, opening: &Token, parser: F, closing: &Token) pub fn delimited_list<T, F>(&mut self,
opening: &Token,
parser: F,
closing: &Token)
-> ParseResult<'a, Vec<Spanning<T>>> -> ParseResult<'a, Vec<Spanning<T>>>
where T: fmt::Debug, F: Fn(&mut Parser<'a>) -> ParseResult<'a, 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(); let mut items = Vec::new();
loop { loop {
if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) { if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) {
return Ok(Spanning::start_end( return Ok(Spanning::start_end(&start_pos, &end_pos, items));
&start_pos,
&end_pos,
items));
} }
items.push(try!(parser(self))); items.push(try!(parser(self)));
@ -111,9 +106,13 @@ impl<'a> Parser<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn delimited_nonempty_list<T, F>(&mut self, opening: &Token, parser: F, closing: &Token) pub fn delimited_nonempty_list<T, F>(&mut self,
opening: &Token,
parser: F,
closing: &Token)
-> ParseResult<'a, Vec<Spanning<T>>> -> ParseResult<'a, Vec<Spanning<T>>>
where T: fmt::Debug, F: Fn(&mut Parser<'a>) -> ParseResult<'a, 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(); let mut items = Vec::new();
@ -122,18 +121,19 @@ impl<'a> Parser<'a> {
items.push(try!(parser(self))); items.push(try!(parser(self)));
if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) { if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) {
return Ok(Spanning::start_end( return Ok(Spanning::start_end(&start_pos, &end_pos, items));
&start_pos,
&end_pos,
items));
} }
} }
} }
#[doc(hidden)] #[doc(hidden)]
pub fn unlocated_delimited_nonempty_list<T, F>(&mut self, opening: &Token, parser: F, closing: &Token) pub fn unlocated_delimited_nonempty_list<T, F>(&mut self,
opening: &Token,
parser: F,
closing: &Token)
-> ParseResult<'a, Vec<T>> -> ParseResult<'a, Vec<T>>
where T: fmt::Debug, F: Fn(&mut Parser<'a>) -> UnlocatedParseResult<'a, 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(); let mut items = Vec::new();
@ -142,10 +142,7 @@ impl<'a> Parser<'a> {
items.push(try!(parser(self))); items.push(try!(parser(self)));
if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) { if let Some(Spanning { end: end_pos, .. }) = try!(self.skip(closing)) {
return Ok(Spanning::start_end( return Ok(Spanning::start_end(&start_pos, &end_pos, items));
&start_pos,
&end_pos,
items));
} }
} }
} }
@ -153,19 +150,19 @@ impl<'a> Parser<'a> {
#[doc(hidden)] #[doc(hidden)]
pub fn expect_name(&mut self) -> ParseResult<'a, &'a str> { pub fn expect_name(&mut self) -> ParseResult<'a, &'a str> {
match *self.peek() { match *self.peek() {
Spanning { item: Token::Name(_), .. } => Spanning { item: Token::Name(_), .. } => {
Ok(self.next()?.map(|token| Ok(self.next()?
if let Token::Name(name) = token { .map(|token| if let Token::Name(name) = token {
name name
} } else {
else {
panic!("Internal parse error in `expect_name`"); panic!("Internal parse error in `expect_name`");
})), }))
Spanning { item: Token::EndOfFile, .. } => }
Err(Spanning::start_end( Spanning { item: Token::EndOfFile, .. } => {
&self.peek().start.clone(), Err(Spanning::start_end(&self.peek().start.clone(),
&self.peek().end.clone(), &self.peek().end.clone(),
ParseError::UnexpectedEndOfFile)), ParseError::UnexpectedEndOfFile))
}
_ => Err(self.next()?.map(ParseError::UnexpectedToken)), _ => Err(self.next()?.map(ParseError::UnexpectedToken)),
} }
} }

View file

@ -3,8 +3,7 @@ use parser::{Spanning, SourcePosition, ParseError, Token};
use parser::document::parse_document_source; use parser::document::parse_document_source;
fn parse_document(s: &str) -> Document { fn parse_document(s: &str) -> Document {
parse_document_source(s) parse_document_source(s).expect(&format!("Parse error on input {:#?}", s))
.expect(&format!("Parse error on input {:#?}", s))
} }
fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> { fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> {

View file

@ -12,7 +12,7 @@ fn tokenize_to_vec<'a>(s: &'a str) -> Vec<Spanning<Token<'a>>> {
if at_eof { if at_eof {
break; break;
} }
}, }
Some(Err(e)) => panic!("Error in input stream: {:#?} for {:#?}", e, s), Some(Err(e)) => panic!("Error in input stream: {:#?} for {:#?}", e, s),
None => panic!("EOF before EndOfFile token in {:#?}", s), None => panic!("EOF before EndOfFile token in {:#?}", s),
} }
@ -39,10 +39,10 @@ fn tokenize_error(s: &str) -> Spanning<LexerError> {
if t.item == Token::EndOfFile { if t.item == Token::EndOfFile {
panic!("Tokenizer did not return error for {:#?}", s); panic!("Tokenizer did not return error for {:#?}", s);
} }
}, }
Some(Err(e)) => { Some(Err(e)) => {
return e; return e;
}, }
None => panic!("Tokenizer did not return error for {:#?}", s), None => panic!("Tokenizer did not return error for {:#?}", s),
} }
} }
@ -274,7 +274,10 @@ fn string_errors() {
#[test] #[test]
fn numbers() { 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); let parsed = tokenize_single(source);
assert_eq!(parsed.start, start); assert_eq!(parsed.start, start);
assert_eq!(parsed.end, end); assert_eq!(parsed.end, end);
@ -297,14 +300,12 @@ fn numbers() {
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
Token::Int(4))); Token::Int(4)));
assert_float_token_eq( assert_float_token_eq("4.123",
"4.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
4.123); 4.123);
assert_float_token_eq( assert_float_token_eq("4.0",
"4.0",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(3, 0, 3), SourcePosition::new(3, 0, 3),
4.0); 4.0);
@ -330,68 +331,57 @@ fn numbers() {
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
Token::Int(0))); Token::Int(0)));
assert_float_token_eq( assert_float_token_eq("-4.123",
"-4.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
-4.123); -4.123);
assert_float_token_eq( assert_float_token_eq("0.123",
"0.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
0.123); 0.123);
assert_float_token_eq( assert_float_token_eq("123e4",
"123e4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
123e4); 123e4);
assert_float_token_eq( assert_float_token_eq("123E4",
"123E4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
123e4); 123e4);
assert_float_token_eq( assert_float_token_eq("123e-4",
"123e-4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
123e-4); 123e-4);
assert_float_token_eq( assert_float_token_eq("123e+4",
"123e+4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
123e4); 123e4);
assert_float_token_eq( assert_float_token_eq("-1.123e4",
"-1.123e4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8), SourcePosition::new(8, 0, 8),
-1.123e4); -1.123e4);
assert_float_token_eq( assert_float_token_eq("-1.123E4",
"-1.123E4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8), SourcePosition::new(8, 0, 8),
-1.123e4); -1.123e4);
assert_float_token_eq( assert_float_token_eq("-1.123e-4",
"-1.123e-4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e-4); -1.123e-4);
assert_float_token_eq( assert_float_token_eq("-1.123e+4",
"-1.123e+4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e4); -1.123e4);
assert_float_token_eq( assert_float_token_eq("-1.123e45",
"-1.123e45",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e45); -1.123e45);

View file

@ -6,11 +6,9 @@ use parser::value::parse_value_literal;
fn parse_value(s: &str) -> Spanning<InputValue> { fn parse_value(s: &str) -> Spanning<InputValue> {
let mut lexer = Lexer::new(s); let mut lexer = Lexer::new(s);
let mut parser = Parser::new(&mut lexer) let mut parser = Parser::new(&mut lexer).expect(&format!("Lexer error on input {:#?}", s));
.expect(&format!("Lexer error on input {:#?}", s));
parse_value_literal(&mut parser, false) parse_value_literal(&mut parser, false).expect(&format!("Parse error on input {:#?}", s))
.expect(&format!("Parse error on input {:#?}", s))
} }
#[test] #[test]

View file

@ -61,14 +61,14 @@ impl<T: fmt::Debug> Spanning<T> {
#[doc(hidden)] #[doc(hidden)]
pub fn spanning(v: Vec<Spanning<T>>) -> Option<Spanning<Vec<Spanning<T>>>> { 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 { Some(Spanning {
item: v, item: v,
start: start, start: start,
end: end, end: end,
}) })
} } else {
else {
None None
} }
} }
@ -92,7 +92,9 @@ impl<T: fmt::Debug> Spanning<T> {
} }
} }
impl<T> Clone for Spanning<T> where T: Clone + fmt::Debug { impl<T> Clone for Spanning<T>
where T: Clone + fmt::Debug
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
Spanning { Spanning {
start: self.start.clone(), start: self.start.clone(),
@ -102,7 +104,9 @@ impl<T> Clone for Spanning<T> where T: Clone + fmt::Debug {
} }
} }
impl<T> PartialEq for Spanning<T> where T: PartialEq + fmt::Debug { impl<T> PartialEq for Spanning<T>
where T: PartialEq + fmt::Debug
{
fn eq(&self, other: &Self) -> bool { fn eq(&self, other: &Self) -> bool {
self.start == other.start && self.end == other.end && self.item == other.item self.start == other.start && self.end == other.end && self.item == other.item
} }
@ -110,7 +114,9 @@ impl<T> PartialEq for Spanning<T> where T: PartialEq + fmt::Debug {
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 { impl<T> Hash for Spanning<T>
where T: Hash + fmt::Debug
{
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.start.hash(state); self.start.hash(state);
self.end.hash(state); self.end.hash(state);

View file

@ -2,31 +2,38 @@ use ast::InputValue;
use parser::{Parser, ParseResult, ParseError, Token, Spanning}; use parser::{Parser, ParseResult, ParseError, Token, Spanning};
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() { match *parser.peek() {
Spanning { item: Token::BracketOpen, .. } => parse_list_literal(parser, is_const), Spanning { item: Token::BracketOpen, .. } => parse_list_literal(parser, is_const),
Spanning { item: Token::CurlyOpen, .. } => parse_object_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::Dollar, .. } if !is_const => parse_variable_literal(parser),
Spanning { item: Token::Int(i), .. } => Spanning { item: Token::Int(i), .. } => Ok(parser.next()?.map(|_| InputValue::int(i))),
Ok(parser.next()?.map(|_| InputValue::int(i))), Spanning { item: Token::Float(f), .. } => Ok(parser.next()?.map(|_| InputValue::float(f))),
Spanning { item: Token::Float(f), .. } => Spanning { item: Token::String(_), .. } => {
Ok(parser.next()?.map(|_| InputValue::float(f))), Ok(parser
Spanning { item: Token::String(_), .. } => .next()?
Ok(parser.next()?.map(|t| .map(|t| if let Token::String(s) = t {
if let Token::String(s) = t {
InputValue::string(s) InputValue::string(s)
} } else {
else {
panic!("Internal parser error"); panic!("Internal parser error");
})), }))
Spanning { item: Token::Name("true"), .. } => }
Ok(parser.next()?.map(|_| InputValue::boolean(true))), Spanning { item: Token::Name("true"), .. } => {
Spanning { item: Token::Name("false"), .. } => Ok(parser.next()?.map(|_| InputValue::boolean(true)))
Ok(parser.next()?.map(|_| InputValue::boolean(false))), }
Spanning { item: Token::Name("null"), .. } => Spanning { item: Token::Name("false"), .. } => {
Ok(parser.next()?.map(|_| InputValue::null())), Ok(parser.next()?.map(|_| InputValue::boolean(false)))
Spanning { item: Token::Name(name), .. } => }
Ok(parser.next()?.map(|_| InputValue::enum_value(name.to_owned()))), 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)), _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
@ -36,36 +43,44 @@ fn parse_list_literal<'a>(parser: &mut Parser<'a>, is_const: bool) -> ParseResul
&Token::BracketOpen, &Token::BracketOpen,
|p| parse_value_literal(p, is_const), |p| parse_value_literal(p, is_const),
&Token::BracketClose &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> { fn parse_object_literal<'a>(parser: &mut Parser<'a>,
is_const: bool)
-> ParseResult<'a, InputValue> {
Ok(try!(parser.delimited_list( Ok(try!(parser.delimited_list(
&Token::CurlyOpen, &Token::CurlyOpen,
|p| parse_object_field(p, is_const), |p| parse_object_field(p, is_const),
&Token::CurlyClose &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()); let key = try!(parser.expect_name());
try!(parser.expect(&Token::Colon)); try!(parser.expect(&Token::Colon));
let value = try!(parse_value_literal(parser, is_const)); let value = try!(parse_value_literal(parser, is_const));
Ok(Spanning::start_end( Ok(Spanning::start_end(&key.start.clone(),
&key.start.clone(),
&value.end.clone(), &value.end.clone(),
(key.map(|s| s.to_owned()), value))) (key.map(|s| s.to_owned()), value)))
} }
fn parse_variable_literal<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, InputValue> { 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()); let Spanning {
item: name,
end: end_pos,
..
} = try!(parser.expect_name());
Ok(Spanning::start_end( Ok(Spanning::start_end(&start_pos, &end_pos, InputValue::variable(name)))
&start_pos,
&end_pos,
InputValue::variable(name)))
} }

View file

@ -179,8 +179,7 @@ impl<'a> MetaType<'a> {
MetaType::Enum(EnumMeta { name, .. }) | MetaType::Enum(EnumMeta { name, .. }) |
MetaType::Interface(InterfaceMeta { name, .. }) | MetaType::Interface(InterfaceMeta { name, .. }) |
MetaType::Union(UnionMeta { name, .. }) | MetaType::Union(UnionMeta { name, .. }) |
MetaType::InputObject(InputObjectMeta { name, .. }) => MetaType::InputObject(InputObjectMeta { name, .. }) => Some(name),
Some(name),
_ => None, _ => None,
} }
} }
@ -195,8 +194,7 @@ impl<'a> MetaType<'a> {
MetaType::Enum(EnumMeta { ref description, .. }) | MetaType::Enum(EnumMeta { ref description, .. }) |
MetaType::Interface(InterfaceMeta { ref description, .. }) | MetaType::Interface(InterfaceMeta { ref description, .. }) |
MetaType::Union(UnionMeta { ref description, .. }) | MetaType::Union(UnionMeta { ref description, .. }) |
MetaType::InputObject(InputObjectMeta { ref description, .. }) => MetaType::InputObject(InputObjectMeta { ref description, .. }) => description.as_ref(),
description.as_ref(),
_ => None, _ => None,
} }
} }
@ -225,8 +223,9 @@ impl<'a> MetaType<'a> {
pub fn field_by_name(&self, name: &str) -> Option<&Field> { pub fn field_by_name(&self, name: &str) -> Option<&Field> {
match *self { match *self {
MetaType::Object(ObjectMeta { ref fields, .. }) | MetaType::Object(ObjectMeta { ref fields, .. }) |
MetaType::Interface(InterfaceMeta { ref fields, .. }) => MetaType::Interface(InterfaceMeta { ref fields, .. }) => {
fields.iter().find(|f| f.name == name), fields.iter().find(|f| f.name == name)
}
_ => None, _ => None,
} }
} }
@ -236,8 +235,9 @@ impl<'a> MetaType<'a> {
/// Only input objects have input fields. This method always returns `None` for other types. /// 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> { pub fn input_field_by_name(&self, name: &str) -> Option<&Argument> {
match *self { match *self {
MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) => MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) => {
input_fields.iter().find(|f| f.name == name), input_fields.iter().find(|f| f.name == name)
}
_ => None, _ => None,
} }
} }
@ -250,16 +250,17 @@ impl<'a> MetaType<'a> {
MetaType::Enum(EnumMeta { name, .. }) | MetaType::Enum(EnumMeta { name, .. }) |
MetaType::Interface(InterfaceMeta { name, .. }) | MetaType::Interface(InterfaceMeta { name, .. }) |
MetaType::Union(UnionMeta { name, .. }) | MetaType::Union(UnionMeta { name, .. }) |
MetaType::InputObject(InputObjectMeta { name, .. }) => MetaType::InputObject(InputObjectMeta { name, .. }) => Type::NonNullNamed(name),
Type::NonNullNamed(name), MetaType::List(ListMeta { ref of_type }) => {
MetaType::List(ListMeta { ref of_type }) => Type::NonNullList(Box::new(of_type.clone()))
Type::NonNullList(Box::new(of_type.clone())), }
MetaType::Nullable(NullableMeta { ref of_type }) => MetaType::Nullable(NullableMeta { ref of_type }) => {
match *of_type { match *of_type {
Type::NonNullNamed(inner) => Type::Named(inner), Type::NonNullNamed(inner) => Type::Named(inner),
Type::NonNullList(ref inner) => Type::List(inner.clone()), Type::NonNullList(ref inner) => Type::List(inner.clone()),
ref t => t.clone(), ref t => t.clone(),
}, }
}
MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(), MetaType::Placeholder(PlaceholderMeta { ref of_type }) => of_type.clone(),
} }
} }
@ -274,8 +275,7 @@ impl<'a> MetaType<'a> {
match *self { match *self {
MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. }) | MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. }) |
MetaType::Enum(EnumMeta { ref try_parse_fn, .. }) | MetaType::Enum(EnumMeta { ref try_parse_fn, .. }) |
MetaType::InputObject(InputObjectMeta { ref try_parse_fn, .. }) => MetaType::InputObject(InputObjectMeta { ref try_parse_fn, .. }) => Some(try_parse_fn),
Some(try_parse_fn),
_ => None, _ => None,
} }
} }
@ -333,8 +333,7 @@ impl<'a> ScalarMeta<'a> {
ScalarMeta { ScalarMeta {
name: name, name: name,
description: None, description: None,
try_parse_fn: Box::new( try_parse_fn: Box::new(|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
} }
} }
@ -355,9 +354,7 @@ impl<'a> ScalarMeta<'a> {
impl<'a> ListMeta<'a> { impl<'a> ListMeta<'a> {
/// Build a new list type by wrapping the specified type /// Build a new list type by wrapping the specified type
pub fn new(of_type: Type<'a>) -> ListMeta<'a> { pub fn new(of_type: Type<'a>) -> ListMeta<'a> {
ListMeta { ListMeta { of_type: of_type }
of_type: of_type,
}
} }
/// Wrap the list in a generic meta type /// Wrap the list in a generic meta type
@ -369,9 +366,7 @@ impl<'a> ListMeta<'a> {
impl<'a> NullableMeta<'a> { impl<'a> NullableMeta<'a> {
/// Build a new nullable type by wrapping the specified type /// Build a new nullable type by wrapping the specified type
pub fn new(of_type: Type<'a>) -> NullableMeta<'a> { pub fn new(of_type: Type<'a>) -> NullableMeta<'a> {
NullableMeta { NullableMeta { of_type: of_type }
of_type: of_type,
}
} }
/// Wrap the nullable type in a generic meta type /// Wrap the nullable type in a generic meta type
@ -404,8 +399,10 @@ impl<'a> ObjectMeta<'a> {
/// If a list of interfaces already was provided prior to calling this method, they will be /// If a list of interfaces already was provided prior to calling this method, they will be
/// overwritten. /// overwritten.
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a> { pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a> {
self.interface_names = interfaces.iter() self.interface_names = interfaces
.map(|t| t.innermost_name().to_owned()).collect(); .iter()
.map(|t| t.innermost_name().to_owned())
.collect();
self self
} }
@ -422,8 +419,7 @@ impl<'a> EnumMeta<'a> {
name: name, name: name,
description: None, description: None,
values: values.to_vec(), values: values.to_vec(),
try_parse_fn: Box::new( try_parse_fn: Box::new(|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
} }
} }
@ -471,8 +467,10 @@ impl<'a> UnionMeta<'a> {
UnionMeta { UnionMeta {
name: name, name: name,
description: None, description: None,
of_type_names: of_types.iter() of_type_names: of_types
.map(|t| t.innermost_name().to_owned()).collect(), .iter()
.map(|t| t.innermost_name().to_owned())
.collect(),
} }
} }
@ -492,13 +490,14 @@ impl<'a> UnionMeta<'a> {
impl<'a> InputObjectMeta<'a> { impl<'a> InputObjectMeta<'a> {
/// Build a new input type with the specified name and input fields /// 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 { InputObjectMeta {
name: name, name: name,
description: None, description: None,
input_fields: input_fields.to_vec(), input_fields: input_fields.to_vec(),
try_parse_fn: Box::new( try_parse_fn: Box::new(|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
|v: &InputValue| <T as FromInputValue>::from(v).is_some()),
} }
} }
@ -530,8 +529,12 @@ impl<'a> Field<'a> {
/// Arguments are unordered and can't contain duplicates by name. /// Arguments are unordered and can't contain duplicates by name.
pub fn argument(mut self, argument: Argument<'a>) -> Field<'a> { pub fn argument(mut self, argument: Argument<'a>) -> Field<'a> {
match self.arguments { match self.arguments {
None => { self.arguments = Some(vec![argument]); } None => {
Some(ref mut args) => { args.push(argument); } self.arguments = Some(vec![argument]);
}
Some(ref mut args) => {
args.push(argument);
}
}; };
self self
@ -553,7 +556,7 @@ impl<'a> Argument<'a> {
name: name.to_owned(), name: name.to_owned(),
description: None, description: None,
arg_type: arg_type, arg_type: arg_type,
default_value: None default_value: None,
} }
} }

View file

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::fmt; use std::fmt;
use types::base::{GraphQLType}; use types::base::GraphQLType;
use executor::{Registry, Context}; use executor::{Registry, Context};
use ast::Type; use ast::Type;
use schema::meta::{MetaType, ObjectMeta, PlaceholderMeta, UnionMeta, InterfaceMeta, Argument}; use schema::meta::{MetaType, ObjectMeta, PlaceholderMeta, UnionMeta, InterfaceMeta, Argument};
@ -54,7 +54,7 @@ pub enum DirectiveLocation {
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT> impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
where QueryT: GraphQLType, where QueryT: GraphQLType,
MutationT: GraphQLType, MutationT: GraphQLType
{ {
/// Construct a new root node from query and mutation nodes /// Construct a new root node from query and mutation nodes
/// ///
@ -72,7 +72,7 @@ impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
impl<'a> SchemaType<'a> { impl<'a> SchemaType<'a> {
pub fn new<QueryT, MutationT>() -> SchemaType<'a> pub fn new<QueryT, MutationT>() -> SchemaType<'a>
where QueryT: GraphQLType, where QueryT: GraphQLType,
MutationT: GraphQLType, MutationT: GraphQLType
{ {
let mut directives = HashMap::new(); let mut directives = HashMap::new();
let query_type_name: String; let query_type_name: String;
@ -83,11 +83,8 @@ impl<'a> SchemaType<'a> {
mutation_type_name = registry.get_type::<MutationT>().innermost_name().to_owned(); mutation_type_name = registry.get_type::<MutationT>().innermost_name().to_owned();
registry.get_type::<SchemaType>(); registry.get_type::<SchemaType>();
directives.insert( directives.insert("skip".to_owned(), DirectiveType::new_skip(&mut registry));
"skip".to_owned(), directives.insert("include".to_owned(),
DirectiveType::new_skip(&mut registry));
directives.insert(
"include".to_owned(),
DirectiveType::new_include(&mut registry)); DirectiveType::new_include(&mut registry));
let mut meta_fields = vec![ let mut meta_fields = vec![
@ -99,12 +96,10 @@ impl<'a> SchemaType<'a> {
if let Some(root_type) = registry.types.get_mut(&query_type_name) { if let Some(root_type) = registry.types.get_mut(&query_type_name) {
if let MetaType::Object(ObjectMeta { ref mut fields, .. }) = *root_type { if let MetaType::Object(ObjectMeta { ref mut fields, .. }) = *root_type {
fields.append(&mut meta_fields); fields.append(&mut meta_fields);
} } else {
else {
panic!("Root type is not an object"); panic!("Root type is not an object");
} }
} } else {
else {
panic!("Root type not found"); panic!("Root type not found");
} }
@ -117,7 +112,11 @@ impl<'a> SchemaType<'a> {
SchemaType { SchemaType {
types: registry.types, types: registry.types,
query_type_name: query_type_name, query_type_name: query_type_name,
mutation_type_name: if &mutation_type_name != "_EmptyMutation" { Some(mutation_type_name) } else { None }, mutation_type_name: if &mutation_type_name != "_EmptyMutation" {
Some(mutation_type_name)
} else {
None
},
directives: directives, directives: directives,
} }
} }
@ -135,13 +134,14 @@ impl<'a> SchemaType<'a> {
} }
pub fn query_type(&self) -> TypeType { pub fn query_type(&self) -> TypeType {
TypeType::Concrete( TypeType::Concrete(self.types
self.types.get(&self.query_type_name) .get(&self.query_type_name)
.expect("Query type does not exist in schema")) .expect("Query type does not exist in schema"))
} }
pub fn concrete_query_type(&self) -> &MetaType { pub fn concrete_query_type(&self) -> &MetaType {
self.types.get(&self.query_type_name) self.types
.get(&self.query_type_name)
.expect("Query type does not exist in schema") .expect("Query type does not exist in schema")
} }
@ -149,16 +149,18 @@ impl<'a> SchemaType<'a> {
if let Some(ref mutation_type_name) = self.mutation_type_name { if let Some(ref mutation_type_name) = self.mutation_type_name {
Some(self.type_by_name(mutation_type_name) Some(self.type_by_name(mutation_type_name)
.expect("Mutation type does not exist in schema")) .expect("Mutation type does not exist in schema"))
} } else {
else {
None None
} }
} }
pub fn concrete_mutation_type(&self) -> Option<&MetaType> { pub fn concrete_mutation_type(&self) -> Option<&MetaType> {
self.mutation_type_name.as_ref().map(|name| self.mutation_type_name
.as_ref()
.map(|name| {
self.concrete_type_by_name(name) self.concrete_type_by_name(name)
.expect("Mutation type does not exist in schema")) .expect("Mutation type does not exist in schema")
})
} }
pub fn type_list(&self) -> Vec<TypeType> { pub fn type_list(&self) -> Vec<TypeType> {
@ -171,15 +173,14 @@ impl<'a> SchemaType<'a> {
pub fn make_type(&self, t: &Type) -> TypeType { pub fn make_type(&self, t: &Type) -> TypeType {
match *t { match *t {
Type::NonNullNamed(n) => Type::NonNullNamed(n) => {
TypeType::NonNull(Box::new( TypeType::NonNull(Box::new(self.type_by_name(n).expect("Type not found in schema")))
self.type_by_name(n).expect("Type not found in schema"))), }
Type::NonNullList(ref inner) => Type::NonNullList(ref inner) => {
TypeType::NonNull(Box::new( TypeType::NonNull(Box::new(TypeType::List(Box::new(self.make_type(inner)))))
TypeType::List(Box::new(self.make_type(inner))))), }
Type::Named(n) => self.type_by_name(n).expect("Type not found in schema"), Type::Named(n) => self.type_by_name(n).expect("Type not found in schema"),
Type::List(ref inner) => Type::List(ref inner) => TypeType::List(Box::new(self.make_type(inner))),
TypeType::List(Box::new(self.make_type(inner))),
} }
} }
@ -197,7 +198,11 @@ impl<'a> SchemaType<'a> {
} }
match (t1.is_abstract(), t2.is_abstract()) { 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), (true, false) => self.is_possible_type(t1, t2),
(false, true) => self.is_possible_type(t2, t1), (false, true) => self.is_possible_type(t2, t1),
(false, false) => false, (false, false) => false,
@ -206,21 +211,24 @@ impl<'a> SchemaType<'a> {
pub fn possible_types(&self, t: &MetaType) -> Vec<&MetaType> { pub fn possible_types(&self, t: &MetaType) -> Vec<&MetaType> {
match *t { match *t {
MetaType::Union(UnionMeta { ref of_type_names, .. }) => MetaType::Union(UnionMeta { ref of_type_names, .. }) => {
of_type_names of_type_names
.iter() .iter()
.flat_map(|t| self.concrete_type_by_name(t)) .flat_map(|t| self.concrete_type_by_name(t))
.collect(), .collect()
MetaType::Interface(InterfaceMeta { name, .. }) => }
MetaType::Interface(InterfaceMeta { name, .. }) => {
self.concrete_type_list() self.concrete_type_list()
.into_iter() .into_iter()
.filter(|t| match **t { .filter(|t| match **t {
MetaType::Object(ObjectMeta { ref interface_names, .. }) => MetaType::Object(ObjectMeta { ref interface_names, .. }) => {
interface_names.iter().any(|iname| iname == name), interface_names.iter().any(|iname| iname == name)
_ => false }
_ => false,
}) })
.collect(), .collect()
_ => panic!("Can't retrieve possible types from non-abstract meta type") }
_ => panic!("Can't retrieve possible types from non-abstract meta type"),
} }
} }
@ -240,26 +248,25 @@ impl<'a> SchemaType<'a> {
match (super_type, sub_type) { match (super_type, sub_type) {
(&NonNullNamed(super_name), &NonNullNamed(sub_name)) | (&NonNullNamed(super_name), &NonNullNamed(sub_name)) |
(&Named(super_name), &Named(sub_name)) | (&Named(super_name), &Named(sub_name)) |
(&Named(super_name), &NonNullNamed(sub_name)) => (&Named(super_name), &NonNullNamed(sub_name)) => {
self.is_named_subtype(sub_name, super_name), self.is_named_subtype(sub_name, super_name)
}
(&NonNullList(ref super_inner), &NonNullList(ref sub_inner)) | (&NonNullList(ref super_inner), &NonNullList(ref sub_inner)) |
(&List(ref super_inner), &List(ref sub_inner)) | (&List(ref super_inner), &List(ref sub_inner)) |
(&List(ref super_inner), &NonNullList(ref sub_inner)) => (&List(ref super_inner), &NonNullList(ref sub_inner)) => {
self.is_subtype(sub_inner, super_inner), self.is_subtype(sub_inner, super_inner)
_ => false }
_ => false,
} }
} }
pub fn is_named_subtype(&self, sub_type_name: &str, super_type_name: &str) -> bool { pub fn is_named_subtype(&self, sub_type_name: &str, super_type_name: &str) -> bool {
if sub_type_name == super_type_name { if sub_type_name == super_type_name {
true true
} } else if let (Some(sub_type), Some(super_type)) =
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)) {
= (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) super_type.is_abstract() && self.is_possible_type(super_type, sub_type)
} } else {
else {
false false
} }
} }
@ -269,13 +276,16 @@ impl<'a> TypeType<'a> {
pub fn to_concrete(&self) -> Option<&'a MetaType> { pub fn to_concrete(&self) -> Option<&'a MetaType> {
match *self { match *self {
TypeType::Concrete(t) => Some(t), TypeType::Concrete(t) => Some(t),
_ => None _ => None,
} }
} }
} }
impl<'a> DirectiveType<'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 { DirectiveType {
name: name.to_owned(), name: name.to_owned(),
description: None, description: None,
@ -285,29 +295,19 @@ impl<'a> DirectiveType<'a> {
} }
fn new_skip(registry: &mut Registry<'a>) -> DirectiveType<'a> { fn new_skip(registry: &mut Registry<'a>) -> DirectiveType<'a> {
Self::new( Self::new("skip",
"skip", &[DirectiveLocation::Field,
&[
DirectiveLocation::Field,
DirectiveLocation::FragmentSpread, DirectiveLocation::FragmentSpread,
DirectiveLocation::InlineFragment, DirectiveLocation::InlineFragment],
], &[registry.arg::<bool>("if")])
&[
registry.arg::<bool>("if"),
])
} }
fn new_include(registry: &mut Registry<'a>) -> DirectiveType<'a> { fn new_include(registry: &mut Registry<'a>) -> DirectiveType<'a> {
Self::new( Self::new("include",
"include", &[DirectiveLocation::Field,
&[
DirectiveLocation::Field,
DirectiveLocation::FragmentSpread, DirectiveLocation::FragmentSpread,
DirectiveLocation::InlineFragment, DirectiveLocation::InlineFragment],
], &[registry.arg::<bool>("if")])
&[
registry.arg::<bool>("if"),
])
} }
pub fn description(mut self, description: &str) -> DirectiveType<'a> { pub fn description(mut self, description: &str) -> DirectiveType<'a> {

View file

@ -19,13 +19,23 @@ impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT
QueryT::meta(registry) 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 { match field {
"__schema" => executor.replaced_context(&self.schema).resolve(&self.schema), "__schema" => {
executor
.replaced_context(&self.schema)
.resolve(&self.schema)
}
"__type" => { "__type" => {
let type_name: String = args.get("name").unwrap(); let type_name: String = args.get("name").unwrap();
executor.replaced_context(&self.schema).resolve(&self.schema.type_by_name(&type_name)) executor
}, .replaced_context(&self.schema)
.resolve(&self.schema.type_by_name(&type_name))
}
_ => self.query_type.resolve_field(field, args, executor), _ => self.query_type.resolve_field(field, args, executor),
} }
} }

View file

@ -148,15 +148,25 @@ fn test_possible_types() {
assert_eq!(errors, vec![]); assert_eq!(errors, vec![]);
let possible_types = result let possible_types = result
.as_object_value().expect("execution result not an object") .as_object_value()
.get("__type").expect("'__type' not present in result") .expect("execution result not an object")
.as_object_value().expect("'__type' not an object") .get("__type")
.get("possibleTypes").expect("'possibleTypes' not present in '__type'") .expect("'__type' not present in result")
.as_list_value().expect("'possibleTypes' not a list") .as_object_value()
.iter().map(|t| t .expect("'__type' not an object")
.as_object_value().expect("possible type not an object") .get("possibleTypes")
.get("name").expect("'name' not present in type") .expect("'possibleTypes' not present in '__type'")
.as_string_value().expect("'name' not a string")) .as_list_value()
.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")
})
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
assert_eq!( assert_eq!(

View file

@ -2,5 +2,7 @@
pub mod model; pub mod model;
mod schema; mod schema;
#[cfg(test)] mod query_tests; #[cfg(test)]
#[cfg(test)] mod introspection_tests; mod query_tests;
#[cfg(test)]
mod introspection_tests;

View file

@ -45,29 +45,57 @@ struct DroidData {
} }
impl Character for HumanData { impl Character for HumanData {
fn id(&self) -> &str { &self.id } fn id(&self) -> &str {
fn name(&self) -> &str { &self.name } &self.id
fn friend_ids(&self) -> &[String] { &self.friend_ids } }
fn appears_in(&self) -> &[Episode] { &self.appears_in } fn name(&self) -> &str {
fn secret_backstory(&self) -> &Option<String> { &self.secret_backstory } &self.name
fn as_character(&self) -> &Character { self } }
fn friend_ids(&self) -> &[String] {
&self.friend_ids
}
fn appears_in(&self) -> &[Episode] {
&self.appears_in
}
fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory
}
fn as_character(&self) -> &Character {
self
}
} }
impl Human for HumanData { impl Human for HumanData {
fn home_planet(&self) -> &Option<String> { &self.home_planet } fn home_planet(&self) -> &Option<String> {
&self.home_planet
}
} }
impl Character for DroidData { impl Character for DroidData {
fn id(&self) -> &str { &self.id } fn id(&self) -> &str {
fn name(&self) -> &str { &self.name } &self.id
fn friend_ids(&self) -> &[String] { &self.friend_ids } }
fn appears_in(&self) -> &[Episode] { &self.appears_in } fn name(&self) -> &str {
fn secret_backstory(&self) -> &Option<String> { &self.secret_backstory } &self.name
fn as_character(&self) -> &Character { self } }
fn friend_ids(&self) -> &[String] {
&self.friend_ids
}
fn appears_in(&self) -> &[Episode] {
&self.appears_in
}
fn secret_backstory(&self) -> &Option<String> {
&self.secret_backstory
}
fn as_character(&self) -> &Character {
self
}
} }
impl Droid for DroidData { impl Droid for DroidData {
fn primary_function(&self) -> &Option<String> { &self.primary_function } fn primary_function(&self) -> &Option<String> {
&self.primary_function
}
} }
pub struct Database { pub struct Database {
@ -76,18 +104,21 @@ pub struct Database {
} }
impl HumanData { impl HumanData {
pub fn new( pub fn new(id: &str,
id: &str,
name: &str, name: &str,
friend_ids: &[&str], friend_ids: &[&str],
appears_in: &[Episode], appears_in: &[Episode],
secret_backstory: Option<&str>, secret_backstory: Option<&str>,
home_planet: Option<&str>) -> HumanData home_planet: Option<&str>)
{ -> HumanData {
HumanData { HumanData {
id: id.to_owned(), id: id.to_owned(),
name: name.to_owned(), name: name.to_owned(),
friend_ids: friend_ids.to_owned().into_iter().map(|f| f.to_owned()).collect(), friend_ids: friend_ids
.to_owned()
.into_iter()
.map(|f| f.to_owned())
.collect(),
appears_in: appears_in.iter().cloned().collect(), appears_in: appears_in.iter().cloned().collect(),
secret_backstory: secret_backstory.map(|b| b.to_owned()), secret_backstory: secret_backstory.map(|b| b.to_owned()),
home_planet: home_planet.map(|p| p.to_owned()), home_planet: home_planet.map(|p| p.to_owned()),
@ -96,18 +127,21 @@ impl HumanData {
} }
impl DroidData { impl DroidData {
pub fn new( pub fn new(id: &str,
id: &str,
name: &str, name: &str,
friend_ids: &[&str], friend_ids: &[&str],
appears_in: &[Episode], appears_in: &[Episode],
secret_backstory: Option<&str>, secret_backstory: Option<&str>,
primary_function: Option<&str>) -> DroidData primary_function: Option<&str>)
{ -> DroidData {
DroidData { DroidData {
id: id.to_owned(), id: id.to_owned(),
name: name.to_owned(), name: name.to_owned(),
friend_ids: friend_ids.to_owned().into_iter().map(|f| f.to_owned()).collect(), friend_ids: friend_ids
.to_owned()
.into_iter()
.map(|f| f.to_owned())
.collect(),
appears_in: appears_in.iter().cloned().collect(), appears_in: appears_in.iter().cloned().collect(),
secret_backstory: secret_backstory.map(|b| b.to_owned()), secret_backstory: secret_backstory.map(|b| b.to_owned()),
primary_function: primary_function.map(|p| p.to_owned()), primary_function: primary_function.map(|p| p.to_owned()),
@ -120,68 +154,61 @@ impl Database {
let mut humans = HashMap::new(); let mut humans = HashMap::new();
let mut droids = HashMap::new(); let mut droids = HashMap::new();
humans.insert("1000".to_owned(), HumanData::new( humans.insert("1000".to_owned(),
"1000", HumanData::new("1000",
"Luke Skywalker", "Luke Skywalker",
&["1002", "1003", "2000", "2001"], &["1002", "1003", "2000", "2001"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
Some("Tatooine"), Some("Tatooine")));
));
humans.insert("1001".to_owned(), HumanData::new( humans.insert("1001".to_owned(),
"1001", HumanData::new("1001",
"Darth Vader", "Darth Vader",
&["1004"], &["1004"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
Some("Tatooine"), Some("Tatooine")));
));
humans.insert("1002".to_owned(), HumanData::new( humans.insert("1002".to_owned(),
"1002", HumanData::new("1002",
"Han Solo", "Han Solo",
&["1000", "1003", "2001"], &["1000", "1003", "2001"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
None, None));
));
humans.insert("1003".to_owned(), HumanData::new( humans.insert("1003".to_owned(),
"1003", HumanData::new("1003",
"Leia Organa", "Leia Organa",
&["1000", "1002", "2000", "2001"], &["1000", "1002", "2000", "2001"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
Some("Alderaan"), Some("Alderaan")));
));
humans.insert("1004".to_owned(), HumanData::new( humans.insert("1004".to_owned(),
"1004", HumanData::new("1004",
"Wilhuff Tarkin", "Wilhuff Tarkin",
&["1001"], &["1001"],
&[Episode::NewHope], &[Episode::NewHope],
None, None,
None, None));
));
droids.insert("2000".to_owned(), DroidData::new( droids.insert("2000".to_owned(),
"2000", DroidData::new("2000",
"C-3PO", "C-3PO",
&["1000", "1002", "1003", "2001"], &["1000", "1002", "1003", "2001"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
Some("Protocol"), Some("Protocol")));
));
droids.insert("2001".to_owned(), DroidData::new( droids.insert("2001".to_owned(),
"2001", DroidData::new("2001",
"R2-D2", "R2-D2",
&["1000", "1002", "1003"], &["1000", "1002", "1003"],
&[Episode::NewHope, Episode::Empire, Episode::Jedi], &[Episode::NewHope, Episode::Empire, Episode::Jedi],
None, None,
Some("Astromech"), Some("Astromech")));
));
Database { Database {
humans: humans, humans: humans,
@ -208,17 +235,16 @@ impl Database {
pub fn get_character(&self, id: &str) -> Option<&Character> { pub fn get_character(&self, id: &str) -> Option<&Character> {
if let Some(h) = self.humans.get(id) { if let Some(h) = self.humans.get(id) {
Some(h) Some(h)
} } else if let Some(d) = self.droids.get(id) {
else if let Some(d) = self.droids.get(id) {
Some(d) Some(d)
} } else {
else {
None None
} }
} }
pub fn get_friends(&self, c: &Character) -> Vec<&Character> { pub fn get_friends(&self, c: &Character) -> Vec<&Character> {
c.friend_ids().iter() c.friend_ids()
.iter()
.flat_map(|id| self.get_character(id)) .flat_map(|id| self.get_character(id))
.collect() .collect()
} }

View file

@ -251,7 +251,9 @@ fn test_query_name_variable() {
let vars = vec![ let vars = vec![
("someId".to_owned(), InputValue::string("1000")), ("someId".to_owned(), InputValue::string("1000")),
].into_iter().collect(); ]
.into_iter()
.collect();
assert_eq!( assert_eq!(
::execute(doc, None, &schema, &vars, &database), ::execute(doc, None, &schema, &vars, &database),
@ -271,7 +273,9 @@ fn test_query_name_invalid_variable() {
let vars = vec![ let vars = vec![
("someId".to_owned(), InputValue::string("some invalid id")), ("someId".to_owned(), InputValue::string("some invalid id")),
].into_iter().collect(); ]
.into_iter()
.collect();
assert_eq!( assert_eq!(
::execute(doc, None, &schema, &vars, &database), ::execute(doc, None, &schema, &vars, &database),

View file

@ -71,7 +71,9 @@ pub struct Arguments<'a> {
impl<'a> Arguments<'a> { impl<'a> Arguments<'a> {
#[doc(hidden)] #[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() { if meta_args.is_some() && args.is_none() {
args = Some(HashMap::new()); args = Some(HashMap::new());
} }
@ -88,9 +90,7 @@ impl<'a> Arguments<'a> {
} }
} }
Arguments { Arguments { args: args }
args: args
}
} }
/// Get and convert an argument into the desired type. /// Get and convert an argument into the desired type.
@ -100,12 +100,16 @@ impl<'a> Arguments<'a> {
/// ///
/// Returns `Some` if the argument is present _and_ type conversion /// Returns `Some` if the argument is present _and_ type conversion
/// succeeeds. /// succeeeds.
pub fn get<T>(&self, key: &str) -> Option<T> where T: FromInputValue { pub fn get<T>(&self, key: &str) -> Option<T>
where T: FromInputValue
{
match self.args { match self.args {
Some(ref args) => match args.get(key) { Some(ref args) => {
match args.get(key) {
Some(v) => Some(v.convert().unwrap()), Some(v) => Some(v.convert().unwrap()),
None => None, None => None,
}, }
}
None => None, None => None,
} }
} }
@ -237,9 +241,11 @@ pub trait GraphQLType: Sized {
/// ///
/// The default implementation panics. /// The default implementation panics.
#[allow(unused_variables)] #[allow(unused_variables)]
fn resolve_field(&self, field_name: &str, arguments: &Arguments, executor: &Executor<Self::Context>) fn resolve_field(&self,
-> ExecutionResult field_name: &str,
{ arguments: &Arguments,
executor: &Executor<Self::Context>)
-> ExecutionResult {
panic!("resolve_field must be implemented by object types"); panic!("resolve_field must be implemented by object types");
} }
@ -250,7 +256,11 @@ pub trait GraphQLType: Sized {
/// ///
/// The default implementation panics. /// The default implementation panics.
#[allow(unused_variables)] #[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 { if Self::name().unwrap() == type_name {
Ok(self.resolve(selection_set, executor)) Ok(self.resolve(selection_set, executor))
} else { } else {
@ -276,32 +286,38 @@ pub trait GraphQLType: Sized {
/// The default implementation uses `resolve_field` to resolve all fields, /// The default implementation uses `resolve_field` to resolve all fields,
/// including those through fragment expansion, for object types. For /// including those through fragment expansion, for object types. For
/// non-object types, this method panics. /// 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 { if let Some(selection_set) = selection_set {
let mut result = HashMap::new(); let mut result = HashMap::new();
resolve_selection_set_into(self, selection_set, executor, &mut result); resolve_selection_set_into(self, selection_set, executor, &mut result);
Value::object(result) Value::object(result)
} } else {
else {
panic!("resolve() must be implemented by non-object output types"); panic!("resolve() must be implemented by non-object output types");
} }
} }
} }
fn resolve_selection_set_into<T, CtxT>( fn resolve_selection_set_into<T, CtxT>(instance: &T,
instance: &T,
selection_set: &[Selection], selection_set: &[Selection],
executor: &Executor<CtxT>, executor: &Executor<CtxT>,
result: &mut HashMap<String, Value>) result: &mut HashMap<String, Value>)
where T: GraphQLType<Context = CtxT> where T: GraphQLType<Context = CtxT>
{ {
let meta_type = executor.schema() let meta_type = executor
.schema()
.concrete_type_by_name(T::name().expect("Resolving named type's selection set")) .concrete_type_by_name(T::name().expect("Resolving named type's selection set"))
.expect("Type not found in schema"); .expect("Type not found in schema");
for selection in selection_set { for selection in selection_set {
match *selection { match *selection {
Selection::Field(Spanning { item: ref f, start: ref start_pos, .. }) => { Selection::Field(Spanning {
item: ref f,
start: ref start_pos,
..
}) => {
if is_excluded(&f.directives, executor.variables()) { if is_excluded(&f.directives, executor.variables()) {
continue; continue;
} }
@ -309,10 +325,8 @@ fn resolve_selection_set_into<T, CtxT>(
let response_name = &f.alias.as_ref().unwrap_or(&f.name).item; let response_name = &f.alias.as_ref().unwrap_or(&f.name).item;
if f.name.item == "__typename" { if f.name.item == "__typename" {
result.insert( result.insert((*response_name).to_owned(),
(*response_name).to_owned(), Value::string(instance.concrete_type_name(executor.context())));
Value::string(
instance.concrete_type_name(executor.context())));
continue; continue;
} }
@ -321,17 +335,20 @@ fn resolve_selection_set_into<T, CtxT>(
let exec_vars = executor.variables(); let exec_vars = executor.variables();
let sub_exec = executor.sub_executor( let sub_exec =
Some(response_name), executor.sub_executor(Some(response_name),
start_pos.clone(), start_pos.clone(),
f.selection_set.as_ref().map(|v| &v[..])); f.selection_set.as_ref().map(|v| &v[..]));
let field_result = instance.resolve_field( let field_result = instance.resolve_field(f.name.item,
f.name.item, &Arguments::new(f.arguments
&Arguments::new( .as_ref()
f.arguments.as_ref().map(|m| .map(|m| {
m.item.iter().map(|&(ref k, ref v)| m.item
(k.item, v.item.clone().into_const(exec_vars))).collect()), .iter()
.map(|&(ref k, ref v)| (k.item, v.item.clone().into_const(exec_vars)))
.collect()
}),
&meta_field.arguments), &meta_field.arguments),
&sub_exec); &sub_exec);
@ -342,31 +359,33 @@ fn resolve_selection_set_into<T, CtxT>(
result.insert((*response_name).to_owned(), Value::null()); result.insert((*response_name).to_owned(), Value::null());
} }
} }
}, }
Selection::FragmentSpread(Spanning { item: ref spread, .. }) => { Selection::FragmentSpread(Spanning { item: ref spread, .. }) => {
if is_excluded(&spread.directives, executor.variables()) { if is_excluded(&spread.directives, executor.variables()) {
continue; continue;
} }
let fragment = &executor.fragment_by_name(spread.name.item) let fragment = &executor
.fragment_by_name(spread.name.item)
.expect("Fragment could not be found"); .expect("Fragment could not be found");
resolve_selection_set_into( resolve_selection_set_into(instance, &fragment.selection_set[..], executor, result);
instance, &fragment.selection_set[..], executor, result); }
}, Selection::InlineFragment(Spanning {
Selection::InlineFragment(Spanning { item: ref fragment, start: ref start_pos, .. }) => { item: ref fragment,
start: ref start_pos,
..
}) => {
if is_excluded(&fragment.directives, executor.variables()) { if is_excluded(&fragment.directives, executor.variables()) {
continue; continue;
} }
let sub_exec = executor.sub_executor( let sub_exec =
None, executor
start_pos.clone(), .sub_executor(None, start_pos.clone(), Some(&fragment.selection_set[..]));
Some(&fragment.selection_set[..]));
if let Some(ref type_condition) = fragment.type_condition { if let Some(ref type_condition) = fragment.type_condition {
let sub_result = instance.resolve_into_type( let sub_result = instance.resolve_into_type(type_condition.item,
type_condition.item,
Some(&fragment.selection_set[..]), Some(&fragment.selection_set[..]),
&sub_exec); &sub_exec);
@ -374,19 +393,16 @@ fn resolve_selection_set_into<T, CtxT>(
for (k, v) in hash_map.drain() { for (k, v) in hash_map.drain() {
result.insert(k, v); result.insert(k, v);
} }
} } else if let Err(e) = sub_result {
else if let Err(e) = sub_result {
sub_exec.push_error(e, start_pos.clone()); sub_exec.push_error(e, start_pos.clone());
} }
} } else {
else { resolve_selection_set_into(instance,
resolve_selection_set_into(
instance,
&fragment.selection_set[..], &fragment.selection_set[..],
&sub_exec, &sub_exec,
result); result);
} }
}, }
} }
} }
} }
@ -394,49 +410,44 @@ fn resolve_selection_set_into<T, CtxT>(
fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables) -> bool { fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables) -> bool {
if let Some(ref directives) = *directives { 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() let condition: bool = directive
.arguments
.iter()
.flat_map(|m| m.item.get("if")) .flat_map(|m| m.item.get("if"))
.flat_map(|v| v.item.clone().into_const(vars).convert()) .flat_map(|v| v.item.clone().into_const(vars).convert())
.next().unwrap(); .next()
.unwrap();
if (directive.name.item == "skip" && condition) || if (directive.name.item == "skip" && condition) ||
(directive.name.item == "include" && !condition) { (directive.name.item == "include" && !condition) {
return true return true;
} }
} }
} }
false false
} }
fn merge_key_into( fn merge_key_into(result: &mut HashMap<String, Value>, response_name: &str, value: Value) {
result: &mut HashMap<String, Value>,
response_name: &str,
value: Value,
) {
match result.entry(response_name.to_owned()) { match result.entry(response_name.to_owned()) {
Entry::Occupied(mut e) => { Entry::Occupied(mut e) => {
match (e.get_mut().as_mut_object_value(), value) { match (e.get_mut().as_mut_object_value(), value) {
(Some(dest_obj), Value::Object(src_obj)) => { (Some(dest_obj), Value::Object(src_obj)) => {
merge_maps(dest_obj, src_obj); merge_maps(dest_obj, src_obj);
}, }
_ => {} _ => {}
} }
}, }
Entry::Vacant(e) => { Entry::Vacant(e) => {
e.insert(value); e.insert(value);
}, }
} }
} }
fn merge_maps( fn merge_maps(dest: &mut HashMap<String, Value>, src: HashMap<String, Value>) {
dest: &mut HashMap<String, Value>,
src: HashMap<String, Value>,
) {
for (key, value) in src { for (key, value) in src {
if dest.contains_key(&key) { if dest.contains_key(&key) {
merge_key_into(dest, &key, value); merge_key_into(dest, &key, value);
} } else {
else {
dest.insert(key, value); dest.insert(key, value);
} }
} }

View file

@ -3,9 +3,11 @@ use value::Value;
use schema::meta::MetaType; use schema::meta::MetaType;
use executor::{Executor, Registry}; use executor::{Executor, Registry};
use types::base::{GraphQLType}; use types::base::GraphQLType;
impl<T, CtxT> GraphQLType for Option<T> where T: GraphQLType<Context=CtxT> { impl<T, CtxT> GraphQLType for Option<T>
where T: GraphQLType<Context = CtxT>
{
type Context = CtxT; type Context = CtxT;
fn name() -> Option<&'static str> { fn name() -> Option<&'static str> {
@ -24,19 +26,25 @@ impl<T, CtxT> GraphQLType for Option<T> where T: GraphQLType<Context=CtxT> {
} }
} }
impl<T> FromInputValue for Option<T> where T: FromInputValue { impl<T> FromInputValue for Option<T>
where T: FromInputValue
{
fn from(v: &InputValue) -> Option<Option<T>> { fn from(v: &InputValue) -> Option<Option<T>> {
match v { match v {
&InputValue::Null => Some(None), &InputValue::Null => Some(None),
v => match v.convert() { v => {
match v.convert() {
Some(x) => Some(Some(x)), Some(x) => Some(Some(x)),
None => None, None => None,
} }
} }
} }
} }
}
impl<T> ToInputValue for Option<T> where T: ToInputValue { impl<T> ToInputValue for Option<T>
where T: ToInputValue
{
fn to(&self) -> InputValue { fn to(&self) -> InputValue {
match *self { match *self {
Some(ref v) => v.to(), Some(ref v) => v.to(),
@ -45,7 +53,9 @@ impl<T> ToInputValue for Option<T> where T: ToInputValue {
} }
} }
impl<T, CtxT> GraphQLType for Vec<T> where T: GraphQLType<Context=CtxT> { impl<T, CtxT> GraphQLType for Vec<T>
where T: GraphQLType<Context = CtxT>
{
type Context = CtxT; type Context = CtxT;
fn name() -> Option<&'static str> { fn name() -> Option<&'static str> {
@ -57,26 +67,23 @@ impl<T, CtxT> GraphQLType for Vec<T> where T: GraphQLType<Context=CtxT> {
} }
fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value { fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value {
Value::list( Value::list(self.iter()
self.iter().map(|e| executor.resolve_into_value(e)).collect() .map(|e| executor.resolve_into_value(e))
) .collect())
} }
} }
impl<T> FromInputValue for Vec<T> where T: FromInputValue { impl<T> FromInputValue for Vec<T>
where T: FromInputValue
{
fn from(v: &InputValue) -> Option<Vec<T>> { fn from(v: &InputValue) -> Option<Vec<T>> {
match *v { match *v {
InputValue::List(ref ls) => { InputValue::List(ref ls) => {
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect(); let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
if v.len() == ls.len() { if v.len() == ls.len() { Some(v) } else { None }
Some(v)
} }
else { ref other => {
None
}
},
ref other =>
if let Some(e) = other.convert() { if let Some(e) = other.convert() {
Some(vec![ e ]) Some(vec![ e ])
} else { } else {
@ -85,14 +92,19 @@ impl<T> FromInputValue for Vec<T> where T: FromInputValue {
} }
} }
} }
}
impl<T> ToInputValue for Vec<T> where T: ToInputValue { impl<T> ToInputValue for Vec<T>
where T: ToInputValue
{
fn to(&self) -> InputValue { fn to(&self) -> InputValue {
InputValue::list(self.iter().map(|v| v.to()).collect()) InputValue::list(self.iter().map(|v| v.to()).collect())
} }
} }
impl<'a, T, CtxT> GraphQLType for &'a [T] where T: GraphQLType<Context=CtxT> { impl<'a, T, CtxT> GraphQLType for &'a [T]
where T: GraphQLType<Context = CtxT>
{
type Context = CtxT; type Context = CtxT;
fn name() -> Option<&'static str> { fn name() -> Option<&'static str> {
@ -104,13 +116,15 @@ impl<'a, T, CtxT> GraphQLType for &'a [T] where T: GraphQLType<Context=CtxT> {
} }
fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value { fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value {
Value::list( Value::list(self.iter()
self.iter().map(|e| executor.resolve_into_value(e)).collect() .map(|e| executor.resolve_into_value(e))
) .collect())
} }
} }
impl<'a, T> ToInputValue for &'a [T] where T: ToInputValue { impl<'a, T> ToInputValue for &'a [T]
where T: ToInputValue
{
fn to(&self) -> InputValue { fn to(&self) -> InputValue {
InputValue::list(self.iter().map(|v| v.to()).collect()) InputValue::list(self.iter().map(|v| v.to()).collect())
} }

View file

@ -5,7 +5,9 @@ use schema::meta::MetaType;
use executor::{Executor, Registry, ExecutionResult}; use executor::{Executor, Registry, ExecutionResult};
use types::base::{Arguments, GraphQLType}; use types::base::{Arguments, GraphQLType};
impl<T, CtxT> GraphQLType for Box<T> where T: GraphQLType<Context=CtxT> { impl<T, CtxT> GraphQLType for Box<T>
where T: GraphQLType<Context = CtxT>
{
type Context = CtxT; type Context = CtxT;
fn name() -> Option<&'static str> { fn name() -> Option<&'static str> {
@ -16,12 +18,19 @@ impl<T, CtxT> GraphQLType for Box<T> where T: GraphQLType<Context=CtxT> {
T::meta(registry) 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) (**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) (**self).resolve_field(field, args, executor)
} }
@ -30,7 +39,9 @@ impl<T, CtxT> GraphQLType for Box<T> where T: GraphQLType<Context=CtxT> {
} }
} }
impl<T> FromInputValue for Box<T> where T: FromInputValue { impl<T> FromInputValue for Box<T>
where T: FromInputValue
{
fn from(v: &InputValue) -> Option<Box<T>> { fn from(v: &InputValue) -> Option<Box<T>> {
match <T as FromInputValue>::from(v) { match <T as FromInputValue>::from(v) {
Some(v) => Some(Box::new(v)), Some(v) => Some(Box::new(v)),
@ -39,13 +50,17 @@ impl<T> FromInputValue for Box<T> where T: FromInputValue {
} }
} }
impl<T> ToInputValue for Box<T> where T: ToInputValue { impl<T> ToInputValue for Box<T>
where T: ToInputValue
{
fn to(&self) -> InputValue { fn to(&self) -> InputValue {
(**self).to() (**self).to()
} }
} }
impl<'a, T, CtxT> GraphQLType for &'a T where T: GraphQLType<Context=CtxT> { impl<'a, T, CtxT> GraphQLType for &'a T
where T: GraphQLType<Context = CtxT>
{
type Context = CtxT; type Context = CtxT;
fn name() -> Option<&'static str> { fn name() -> Option<&'static str> {
@ -56,12 +71,19 @@ impl<'a, T, CtxT> GraphQLType for &'a T where T: GraphQLType<Context=CtxT> {
T::meta(registry) 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) (**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) (**self).resolve_field(field, args, executor)
} }
@ -70,7 +92,9 @@ impl<'a, T, CtxT> GraphQLType for &'a T where T: GraphQLType<Context=CtxT> {
} }
} }
impl<'a, T> ToInputValue for &'a T where T: ToInputValue { impl<'a, T> ToInputValue for &'a T
where T: ToInputValue
{
fn to(&self) -> InputValue { fn to(&self) -> InputValue {
(**self).to() (**self).to()
} }

View file

@ -156,9 +156,7 @@ pub struct EmptyMutation<T> {
impl<T> EmptyMutation<T> { impl<T> EmptyMutation<T> {
/// Construct a new empty mutation /// Construct a new empty mutation
pub fn new() -> EmptyMutation<T> { pub fn new() -> EmptyMutation<T> {
EmptyMutation { EmptyMutation { phantom: PhantomData }
phantom: PhantomData,
}
} }
} }

View file

@ -3,27 +3,33 @@ use ast::InputValue;
use schema::model::{SchemaType, TypeType}; use schema::model::{SchemaType, TypeType};
use schema::meta::{MetaType, InputObjectMeta, EnumMeta}; use schema::meta::{MetaType, InputObjectMeta, EnumMeta};
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 { match *arg_type {
TypeType::NonNull(ref inner) => { TypeType::NonNull(ref inner) => {
if arg_value.is_null() { if arg_value.is_null() {
false false
} } else {
else {
is_valid_literal_value(schema, inner, arg_value) is_valid_literal_value(schema, inner, arg_value)
} }
} }
TypeType::List(ref inner) => { TypeType::List(ref inner) => {
match *arg_value { match *arg_value {
InputValue::List(ref items) => items.iter().all(|i| is_valid_literal_value(schema, inner, &i.item)), 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), ref v => is_valid_literal_value(schema, inner, v),
} }
} }
TypeType::Concrete(t) => { TypeType::Concrete(t) => {
// Even though InputValue::String can be parsed into an enum, they // Even though InputValue::String can be parsed into an enum, they
// are not valid as enum *literals* in a GraphQL query. // are not valid as enum *literals* in a GraphQL query.
if let (&InputValue::String(_), Some(&MetaType::Enum(EnumMeta { .. }))) if let (&InputValue::String(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
= (arg_value, arg_type.to_concrete()) { (arg_value, arg_type.to_concrete()) {
return false; return false;
} }
@ -40,31 +46,36 @@ pub fn is_valid_literal_value(schema: &SchemaType, arg_type: &TypeType, arg_valu
} else { } else {
false false
} }
}, }
InputValue::List(_) => false, InputValue::List(_) => false,
InputValue::Object(ref obj) => { InputValue::Object(ref obj) => {
if let MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) = *t { if let MetaType::InputObject(InputObjectMeta { ref input_fields, .. }) = *t {
let mut remaining_required_fields = input_fields.iter() let mut remaining_required_fields = input_fields
.filter_map(|f| if f.arg_type.is_non_null() { Some(&f.name) } else { None }) .iter()
.filter_map(|f| if f.arg_type.is_non_null() {
Some(&f.name)
} else {
None
})
.collect::<HashSet<_>>(); .collect::<HashSet<_>>();
let all_types_ok = obj.iter().all(|&(ref key, ref value)| { let all_types_ok = obj.iter()
.all(|&(ref key, ref value)| {
remaining_required_fields.remove(&key.item); remaining_required_fields.remove(&key.item);
if let Some(ref arg_type) = input_fields.iter() if let Some(ref arg_type) =
input_fields
.iter()
.filter(|f| f.name == key.item) .filter(|f| f.name == key.item)
.map(|f| schema.make_type(&f.arg_type)) .map(|f| schema.make_type(&f.arg_type))
.next() .next() {
{
is_valid_literal_value(schema, arg_type, &value.item) is_valid_literal_value(schema, arg_type, &value.item)
} } else {
else {
false false
} }
}); });
all_types_ok && remaining_required_fields.is_empty() all_types_ok && remaining_required_fields.is_empty()
} } else {
else {
false false
} }
} }

View file

@ -60,12 +60,13 @@ impl<'a> ValidatorContext<'a> {
parent_type_stack: Vec::new(), parent_type_stack: Vec::new(),
input_type_stack: Vec::new(), input_type_stack: Vec::new(),
input_type_literal_stack: Vec::new(), input_type_literal_stack: Vec::new(),
fragment_names: document.iter() fragment_names: document
.iter()
.filter_map(|def| match *def { .filter_map(|def| match *def {
Definition::Fragment(ref frag) => Some(frag.item.name.item), Definition::Fragment(ref frag) => Some(frag.item.name.item),
_ => None, _ => None,
}) })
.collect() .collect(),
} }
} }
@ -86,14 +87,13 @@ impl<'a> ValidatorContext<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn with_pushed_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) pub fn with_pushed_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) -> R
-> R
where F: FnOnce(&mut ValidatorContext<'a>) -> R where F: FnOnce(&mut ValidatorContext<'a>) -> R
{ {
if let Some(t) = t { if let Some(t) = t {
self.type_stack.push(self.schema.concrete_type_by_name(t.innermost_name())); self.type_stack
} .push(self.schema.concrete_type_by_name(t.innermost_name()));
else { } else {
self.type_stack.push(None); self.type_stack.push(None);
} }
@ -108,11 +108,11 @@ impl<'a> ValidatorContext<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn with_pushed_parent_type<F, R>(&mut self, f: F) pub fn with_pushed_parent_type<F, R>(&mut self, f: F) -> R
-> 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)); self.parent_type_stack
.push(*self.type_stack.last().unwrap_or(&None));
let res = f(self); let res = f(self);
self.parent_type_stack.pop(); self.parent_type_stack.pop();
@ -120,14 +120,13 @@ impl<'a> ValidatorContext<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn with_pushed_input_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) pub fn with_pushed_input_type<F, R>(&mut self, t: Option<&Type<'a>>, f: F) -> R
-> R
where F: FnOnce(&mut ValidatorContext<'a>) -> R where F: FnOnce(&mut ValidatorContext<'a>) -> R
{ {
if let Some(t) = t { if let Some(t) = t {
self.input_type_stack.push(self.schema.concrete_type_by_name(t.innermost_name())); self.input_type_stack
} .push(self.schema.concrete_type_by_name(t.innermost_name()));
else { } else {
self.input_type_stack.push(None); self.input_type_stack.push(None);
} }
@ -150,7 +149,7 @@ impl<'a> ValidatorContext<'a> {
pub fn current_type_literal(&self) -> Option<&Type<'a>> { pub fn current_type_literal(&self) -> Option<&Type<'a>> {
match self.type_literal_stack.last() { match self.type_literal_stack.last() {
Some(&Some(ref t)) => Some(t), Some(&Some(ref t)) => Some(t),
_ => None _ => None,
} }
} }

View file

@ -15,13 +15,10 @@ enum Path<'a> {
ObjectField(&'a str, &'a Path<'a>), ObjectField(&'a str, &'a Path<'a>),
} }
pub fn validate_input_values( pub fn validate_input_values(values: &Variables,
values: &Variables,
document: &Document, document: &Document,
schema: &SchemaType, schema: &SchemaType)
) -> Vec<RuleError> {
-> Vec<RuleError>
{
let mut errs = vec![]; let mut errs = vec![];
for def in document { for def in document {
@ -36,12 +33,10 @@ pub fn validate_input_values(
errs errs
} }
fn validate_var_defs( fn validate_var_defs(values: &Variables,
values: &Variables,
var_defs: &VariableDefinitions, var_defs: &VariableDefinitions,
schema: &SchemaType, schema: &SchemaType,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>) {
) {
for &(ref name, ref def) in var_defs.iter() { for &(ref name, ref def) in var_defs.iter() {
let raw_type_name = def.var_type.item.innermost_name(); let raw_type_name = def.var_type.item.innermost_name();
match schema.concrete_type_by_name(raw_type_name) { match schema.concrete_type_by_name(raw_type_name) {
@ -49,46 +44,42 @@ fn validate_var_defs(
let ct = schema.make_type(&def.var_type.item); 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)) { if def.var_type.item.is_non_null() && is_absent_or_null(values.get(name.item)) {
errors.push(RuleError::new( errors.push(RuleError::new(&format!(
&format!(
r#"Variable "${}" of required type "{}" was not provided."#, 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) { } else if let Some(v) = values.get(name.item) {
unify_value(name.item, &name.start, v, &ct, schema, errors, Path::Root); unify_value(name.item, &name.start, v, &ct, schema, errors, Path::Root);
} }
}, }
_ => errors.push(RuleError::new( _ => errors.push(RuleError::new(
&format!( &format!(
r#"Variable "${}" expected value of type "{}" which cannot be used as an input type."#, r#"Variable "${}" expected value of type "{}" which cannot be used as an input type."#,
name.item, def.var_type.item, name.item, def.var_type.item,
), ),
&[ name.start.clone() ], &[ name.start.clone() ],
)) )),
} }
} }
} }
fn unify_value<'a>( fn unify_value<'a>(var_name: &str,
var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue,
meta_type: &TypeType<'a>, meta_type: &TypeType<'a>,
schema: &SchemaType, schema: &SchemaType,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>,
path: Path<'a>, path: Path<'a>) {
) {
match *meta_type { match *meta_type {
TypeType::NonNull(ref inner) => { TypeType::NonNull(ref inner) => {
if value.is_null() { if value.is_null() {
push_unification_error( push_unification_error(errors,
errors, var_name, var_pos, &path, var_name,
&format!(r#"Expected "{}", found null"#, meta_type) var_pos,
); &path,
} &format!(r#"Expected "{}", found null"#, meta_type));
else { } else {
unify_value(var_name, var_pos, value, inner, schema, errors, path); unify_value(var_name, var_pos, value, inner, schema, errors, path);
} }
} }
@ -99,11 +90,18 @@ fn unify_value<'a>(
} }
match value.to_list_value() { match value.to_list_value() {
Some(l) => Some(l) => {
for (i, v) in l.iter().enumerate() { 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,
_ => unify_value(var_name, var_pos, value, inner, schema, errors, path) v,
inner,
schema,
errors,
Path::ArrayElement(i, &path));
}
}
_ => unify_value(var_name, var_pos, value, inner, schema, errors, path),
} }
} }
@ -113,76 +111,68 @@ fn unify_value<'a>(
} }
match *mt { match *mt {
MetaType::Scalar(ref sm) => MetaType::Scalar(ref sm) => {
unify_scalar(var_name, var_pos, value, sm, errors, &path), unify_scalar(var_name, var_pos, value, sm, errors, &path)
MetaType::Enum(ref em) => }
unify_enum(var_name, var_pos, value, em, errors, &path), MetaType::Enum(ref em) => unify_enum(var_name, var_pos, value, em, errors, &path),
MetaType::InputObject(ref iom) => MetaType::InputObject(ref iom) => {
unify_input_object(var_name, var_pos, value, iom, schema, errors, &path), unify_input_object(var_name, var_pos, value, iom, schema, errors, &path)
}
_ => panic!("Can't unify non-input concrete type"), _ => panic!("Can't unify non-input concrete type"),
} }
} }
} }
} }
fn unify_scalar<'a>( fn unify_scalar<'a>(var_name: &str,
var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue,
meta: &ScalarMeta, meta: &ScalarMeta,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>,
path: &Path<'a>, path: &Path<'a>) {
) {
if !(meta.try_parse_fn)(value) { if !(meta.try_parse_fn)(value) {
push_unification_error( push_unification_error(errors,
errors,
var_name, var_name,
var_pos, var_pos,
path, path,
&format!(r#"Expected "{}""#, meta.name), &format!(r#"Expected "{}""#, meta.name));
);
return; return;
} }
match *value { match *value {
InputValue::List(_) => InputValue::List(_) => {
push_unification_error( push_unification_error(errors,
errors,
var_name, var_name,
var_pos, var_pos,
path, path,
&format!(r#"Expected "{}", found list"#, meta.name), &format!(r#"Expected "{}", found list"#, meta.name))
), }
InputValue::Object(_) => InputValue::Object(_) => {
push_unification_error( push_unification_error(errors,
errors,
var_name, var_name,
var_pos, var_pos,
path, path,
&format!(r#"Expected "{}", found object"#, meta.name), &format!(r#"Expected "{}", found object"#, meta.name))
), }
_ => (), _ => (),
} }
} }
fn unify_enum<'a>( fn unify_enum<'a>(var_name: &str,
var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue,
meta: &EnumMeta, meta: &EnumMeta,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>,
path: &Path<'a>, path: &Path<'a>) {
) {
match *value { 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) { if !meta.values.iter().any(|ev| &ev.name == name) {
push_unification_error( push_unification_error(errors,
errors,
var_name, var_name,
var_pos, var_pos,
path, path,
&format!(r#"Invalid value for enum "{}""#, meta.name), &format!(r#"Invalid value for enum "{}""#, meta.name))
)
} }
} }
_ => push_unification_error( _ => push_unification_error(
@ -191,19 +181,17 @@ fn unify_enum<'a>(
var_pos, var_pos,
path, path,
&format!(r#"Expected "{}", found not a string or enum"#, meta.name), &format!(r#"Expected "{}", found not a string or enum"#, meta.name),
) ),
} }
} }
fn unify_input_object<'a>( fn unify_input_object<'a>(var_name: &str,
var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue,
meta: &InputObjectMeta, meta: &InputObjectMeta,
schema: &SchemaType, schema: &SchemaType,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>,
path: &Path<'a>, path: &Path<'a>) {
) {
if let Some(ref obj) = value.to_object_value() { if let Some(ref obj) = value.to_object_value() {
let mut keys = obj.keys().collect::<HashSet<&&str>>(); let mut keys = obj.keys().collect::<HashSet<&&str>>();
@ -215,15 +203,13 @@ fn unify_input_object<'a>(
if !value.is_null() { if !value.is_null() {
has_value = true; has_value = true;
unify_value( unify_value(var_name,
var_name,
var_pos, var_pos,
value, value,
&schema.make_type(&input_field.arg_type), &schema.make_type(&input_field.arg_type),
schema, schema,
errors, errors,
Path::ObjectField(&input_field.name, path), Path::ObjectField(&input_field.name, path));
);
} }
} }
@ -239,23 +225,18 @@ fn unify_input_object<'a>(
} }
for key in keys { for key in keys {
push_unification_error( push_unification_error(errors,
errors,
var_name, var_name,
var_pos, var_pos,
&Path::ObjectField(key, path), &Path::ObjectField(key, path),
"Unknown field", "Unknown field");
);
} }
} } else {
else { push_unification_error(errors,
push_unification_error(
errors,
var_name, var_name,
var_pos, var_pos,
path, path,
&format!(r#"Expected "{}", found not an object"#, meta.name), &format!(r#"Expected "{}", found not an object"#, meta.name));
);
} }
} }
@ -263,20 +244,16 @@ fn is_absent_or_null(v: Option<&InputValue>) -> bool {
v.map_or(true, InputValue::is_null) v.map_or(true, InputValue::is_null)
} }
fn push_unification_error<'a>( fn push_unification_error<'a>(errors: &mut Vec<RuleError>,
errors: &mut Vec<RuleError>,
var_name: &str, var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
path: &Path<'a>, path: &Path<'a>,
message: &str, message: &str) {
) { errors.push(RuleError::new(&format!(
errors.push(RuleError::new(
&format!(
r#"Variable "${}" got invalid value. {}{}."#, 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> { impl<'a> fmt::Display for Path<'a> {

View file

@ -18,6 +18,5 @@ pub use self::multi_visitor::{MultiVisitor, MultiVisitorNil};
pub use self::input_value::validate_input_values; pub use self::input_value::validate_input_values;
#[cfg(test)] #[cfg(test)]
pub use self::test_harness::{ pub use self::test_harness::{expect_passes_rule, expect_fails_rule,
expect_passes_rule, expect_fails_rule,
expect_passes_rule_with_schema, expect_fails_rule_with_schema}; expect_passes_rule_with_schema, expect_fails_rule_with_schema};

View file

@ -1,5 +1,5 @@
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
Directive, InputValue, Field, FragmentSpread, InlineFragment}; Field, FragmentSpread, InlineFragment};
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
@ -7,7 +7,9 @@ use validation::{ValidatorContext, Visitor};
pub trait MultiVisitor<'a> { pub trait MultiVisitor<'a> {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, f: F); 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 { fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self>
where Self: Sized
{
MultiVisitorCons(visitor, self) MultiVisitorCons(visitor, self)
} }
} }
@ -29,7 +31,9 @@ impl<'a, A: Visitor<'a>, B: MultiVisitor<'a>> MultiVisitor<'a> for MultiVisitorC
} }
} }
impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> { impl<'a, M> Visitor<'a> for M
where M: MultiVisitor<'a>
{
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
self.visit_all(|v| v.enter_document(ctx, doc)); self.visit_all(|v| v.enter_document(ctx, doc));
} }
@ -38,24 +42,36 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_document(ctx, doc)); 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)); 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)); 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)); 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)); 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)); 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)); self.visit_all(|v| v.exit_variable_definition(ctx, def));
} }
@ -66,10 +82,14 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_directive(ctx, d)); 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)); 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)); self.visit_all(|v| v.exit_argument(ctx, arg));
} }
@ -87,17 +107,25 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_field(ctx, f)); 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)); 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)); 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)); 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)); self.visit_all(|v| v.exit_inline_fragment(ctx, f));
} }
@ -150,24 +178,36 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_variable_value(ctx, s.clone())); 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())); 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())); 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())); 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())); 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)); 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)); self.visit_all(|v| v.exit_object_field(ctx, f));
} }
} }

View file

@ -9,13 +9,13 @@ pub struct ArgumentsOfCorrectType<'a> {
} }
pub fn factory<'a>() -> ArgumentsOfCorrectType<'a> { pub fn factory<'a>() -> ArgumentsOfCorrectType<'a> {
ArgumentsOfCorrectType { ArgumentsOfCorrectType { current_args: None }
current_args: None,
}
} }
impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> { impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
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>) {
self.current_args = ctx.schema self.current_args = ctx.schema
.directive_by_name(directive.item.name.item) .directive_by_name(directive.item.name.item)
.map(|d| &d.arguments); .map(|d| &d.arguments);
@ -35,15 +35,18 @@ impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
self.current_args = None; self.current_args = None;
} }
fn enter_argument(&mut self, ctx: &mut ValidatorContext<'a>, &(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning<InputValue>)) { fn enter_argument(&mut self,
if let Some(argument_meta) = self.current_args ctx: &mut ValidatorContext<'a>,
.and_then(|args| args.iter().find(|a| a.name == arg_name.item)) &(ref arg_name, ref arg_value): &'a (Spanning<&'a str>,
{ Spanning<InputValue>)) {
if let Some(argument_meta) =
self.current_args
.and_then(|args| args.iter().find(|a| a.name == arg_name.item)) {
let meta_type = ctx.schema.make_type(&argument_meta.arg_type); let meta_type = ctx.schema.make_type(&argument_meta.arg_type);
if !is_valid_literal_value(ctx.schema, &meta_type, &arg_value.item) { if !is_valid_literal_value(ctx.schema, &meta_type, &arg_value.item) {
ctx.report_error( ctx.report_error(&error_message(arg_name.item,
&error_message(arg_name.item, &format!("{}", argument_meta.arg_type)), &format!("{}", argument_meta.arg_type)),
&[arg_value.start.clone()]); &[arg_value.start.clone()]);
} }
} }
@ -66,7 +69,8 @@ mod tests {
#[test] #[test]
fn good_null_value() { fn good_null_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: null) intArgField(intArg: null)
@ -77,23 +81,22 @@ mod tests {
#[test] #[test]
fn null_into_int() { fn null_into_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
nonNullIntArgField(nonNullIntArg: null) nonNullIntArgField(nonNullIntArg: null)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("nonNullIntArg", "Int!"),
RuleError::new(&error_message("nonNullIntArg", "Int!"), &[ &[SourcePosition::new(97, 3, 50)])]);
SourcePosition::new(97, 3, 50),
])
]);
} }
#[test] #[test]
fn good_int_value() { fn good_int_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: 2) intArgField(intArg: 2)
@ -104,7 +107,8 @@ mod tests {
#[test] #[test]
fn good_boolean_value() { fn good_boolean_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: true) booleanArgField(booleanArg: true)
@ -115,7 +119,8 @@ mod tests {
#[test] #[test]
fn good_string_value() { fn good_string_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringArgField(stringArg: "foo") stringArgField(stringArg: "foo")
@ -126,7 +131,8 @@ mod tests {
#[test] #[test]
fn good_float_value() { fn good_float_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
floatArgField(floatArg: 1.1) floatArgField(floatArg: 1.1)
@ -137,7 +143,8 @@ mod tests {
#[test] #[test]
fn int_into_float() { fn int_into_float() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
floatArgField(floatArg: 1) floatArgField(floatArg: 1)
@ -148,7 +155,8 @@ mod tests {
#[test] #[test]
fn int_into_id() { fn int_into_id() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
idArgField(idArg: 1) idArgField(idArg: 1)
@ -159,7 +167,8 @@ mod tests {
#[test] #[test]
fn string_into_id() { fn string_into_id() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
idArgField(idArg: "someIdString") idArgField(idArg: "someIdString")
@ -170,7 +179,8 @@ mod tests {
#[test] #[test]
fn good_enum_value() { fn good_enum_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT)
@ -181,391 +191,344 @@ mod tests {
#[test] #[test]
fn int_into_string() { fn int_into_string() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringArgField(stringArg: 1) stringArgField(stringArg: 1)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringArg", "String"),
RuleError::new(&error_message("stringArg", "String"), &[ &[SourcePosition::new(89, 3, 42)])]);
SourcePosition::new(89, 3, 42),
])
]);
} }
#[test] #[test]
fn float_into_string() { fn float_into_string() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringArgField(stringArg: 1.0) stringArgField(stringArg: 1.0)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringArg", "String"),
RuleError::new(&error_message("stringArg", "String"), &[ &[SourcePosition::new(89, 3, 42)])]);
SourcePosition::new(89, 3, 42),
])
]);
} }
#[test] #[test]
fn boolean_into_string() { fn boolean_into_string() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringArgField(stringArg: true) stringArgField(stringArg: true)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringArg", "String"),
RuleError::new(&error_message("stringArg", "String"), &[ &[SourcePosition::new(89, 3, 42)])]);
SourcePosition::new(89, 3, 42),
])
]);
} }
#[test] #[test]
fn unquoted_string_into_string() { fn unquoted_string_into_string() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringArgField(stringArg: BAR) stringArgField(stringArg: BAR)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringArg", "String"),
RuleError::new(&error_message("stringArg", "String"), &[ &[SourcePosition::new(89, 3, 42)])]);
SourcePosition::new(89, 3, 42),
])
]);
} }
#[test] #[test]
fn string_into_int() { fn string_into_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: "3") intArgField(intArg: "3")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int"),
RuleError::new(&error_message("intArg", "Int"), &[ &[SourcePosition::new(83, 3, 36)])]);
SourcePosition::new(83, 3, 36),
])
]);
} }
#[test] #[test]
fn unquoted_string_into_int() { fn unquoted_string_into_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: FOO) intArgField(intArg: FOO)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int"),
RuleError::new(&error_message("intArg", "Int"), &[ &[SourcePosition::new(83, 3, 36)])]);
SourcePosition::new(83, 3, 36),
])
]);
} }
#[test] #[test]
fn simple_float_into_int() { fn simple_float_into_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: 3.0) intArgField(intArg: 3.0)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int"),
RuleError::new(&error_message("intArg", "Int"), &[ &[SourcePosition::new(83, 3, 36)])]);
SourcePosition::new(83, 3, 36),
])
]);
} }
#[test] #[test]
fn float_into_int() { fn float_into_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
intArgField(intArg: 3.333) intArgField(intArg: 3.333)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int"),
RuleError::new(&error_message("intArg", "Int"), &[ &[SourcePosition::new(83, 3, 36)])]);
SourcePosition::new(83, 3, 36),
])
]);
} }
#[test] #[test]
fn string_into_float() { fn string_into_float() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
floatArgField(floatArg: "3.333") floatArgField(floatArg: "3.333")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("floatArg", "Float"),
RuleError::new(&error_message("floatArg", "Float"), &[ &[SourcePosition::new(87, 3, 40)])]);
SourcePosition::new(87, 3, 40),
])
]);
} }
#[test] #[test]
fn boolean_into_float() { fn boolean_into_float() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
floatArgField(floatArg: true) floatArgField(floatArg: true)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("floatArg", "Float"),
RuleError::new(&error_message("floatArg", "Float"), &[ &[SourcePosition::new(87, 3, 40)])]);
SourcePosition::new(87, 3, 40),
])
]);
} }
#[test] #[test]
fn unquoted_into_float() { fn unquoted_into_float() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
floatArgField(floatArg: FOO) floatArgField(floatArg: FOO)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("floatArg", "Float"),
RuleError::new(&error_message("floatArg", "Float"), &[ &[SourcePosition::new(87, 3, 40)])]);
SourcePosition::new(87, 3, 40),
])
]);
} }
#[test] #[test]
fn int_into_boolean() { fn int_into_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: 2) booleanArgField(booleanArg: 2)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("booleanArg", "Boolean"),
RuleError::new(&error_message("booleanArg", "Boolean"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
])
]);
} }
#[test] #[test]
fn float_into_boolean() { fn float_into_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: 1.0) booleanArgField(booleanArg: 1.0)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("booleanArg", "Boolean"),
RuleError::new(&error_message("booleanArg", "Boolean"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
])
]);
} }
#[test] #[test]
fn string_into_boolean() { fn string_into_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: "true") booleanArgField(booleanArg: "true")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("booleanArg", "Boolean"),
RuleError::new(&error_message("booleanArg", "Boolean"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
])
]);
} }
#[test] #[test]
fn unquoted_into_boolean() { fn unquoted_into_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: TRUE) booleanArgField(booleanArg: TRUE)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("booleanArg", "Boolean"),
RuleError::new(&error_message("booleanArg", "Boolean"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
])
]);
} }
#[test] #[test]
fn float_into_id() { fn float_into_id() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
idArgField(idArg: 1.0) idArgField(idArg: 1.0)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("idArg", "ID"),
RuleError::new(&error_message("idArg", "ID"), &[ &[SourcePosition::new(81, 3, 34)])]);
SourcePosition::new(81, 3, 34),
])
]);
} }
#[test] #[test]
fn boolean_into_id() { fn boolean_into_id() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
idArgField(idArg: true) idArgField(idArg: true)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("idArg", "ID"),
RuleError::new(&error_message("idArg", "ID"), &[ &[SourcePosition::new(81, 3, 34)])]);
SourcePosition::new(81, 3, 34),
])
]);
} }
#[test] #[test]
fn unquoted_into_id() { fn unquoted_into_id() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
idArgField(idArg: SOMETHING) idArgField(idArg: SOMETHING)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("idArg", "ID"),
RuleError::new(&error_message("idArg", "ID"), &[ &[SourcePosition::new(81, 3, 34)])]);
SourcePosition::new(81, 3, 34),
])
]);
} }
#[test] #[test]
fn int_into_enum() { fn int_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: 2) doesKnowCommand(dogCommand: 2)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn float_into_enum() { fn float_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: 1.0) doesKnowCommand(dogCommand: 1.0)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn string_into_enum() { fn string_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: "SIT") doesKnowCommand(dogCommand: "SIT")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn boolean_into_enum() { fn boolean_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: true) doesKnowCommand(dogCommand: true)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn unknown_enum_value_into_enum() { fn unknown_enum_value_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: JUGGLE) doesKnowCommand(dogCommand: JUGGLE)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn different_case_enum_value_into_enum() { fn different_case_enum_value_into_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: sit) doesKnowCommand(dogCommand: sit)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("dogCommand", "DogCommand"),
RuleError::new(&error_message("dogCommand", "DogCommand"), &[ &[SourcePosition::new(79, 3, 44)])]);
SourcePosition::new(79, 3, 44),
])
]);
} }
#[test] #[test]
fn good_list_value() { fn good_list_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: ["one", "two"]) stringListArgField(stringListArg: ["one", "two"])
@ -576,7 +539,8 @@ mod tests {
#[test] #[test]
fn empty_list_value() { fn empty_list_value() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: []) stringListArgField(stringListArg: [])
@ -587,7 +551,8 @@ mod tests {
#[test] #[test]
fn single_value_into_list() { fn single_value_into_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: "one") stringListArgField(stringListArg: "one")
@ -598,39 +563,36 @@ mod tests {
#[test] #[test]
fn incorrect_item_type() { fn incorrect_item_type() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: ["one", 2]) stringListArgField(stringListArg: ["one", 2])
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringListArg", "[String]"),
RuleError::new(&error_message("stringListArg", "[String]"), &[ &[SourcePosition::new(97, 3, 50)])]);
SourcePosition::new(97, 3, 50),
])
]);
} }
#[test] #[test]
fn single_value_of_incorrect_type() { fn single_value_of_incorrect_type() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: 1) stringListArgField(stringListArg: 1)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringListArg", "[String]"),
RuleError::new(&error_message("stringListArg", "[String]"), &[ &[SourcePosition::new(97, 3, 50)])]);
SourcePosition::new(97, 3, 50),
])
]);
} }
#[test] #[test]
fn arg_on_optional_arg() { fn arg_on_optional_arg() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
isHousetrained(atOtherHomes: true) isHousetrained(atOtherHomes: true)
@ -641,7 +603,8 @@ mod tests {
#[test] #[test]
fn no_arg_on_optional_arg() { fn no_arg_on_optional_arg() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
isHousetrained isHousetrained
@ -652,7 +615,8 @@ mod tests {
#[test] #[test]
fn multiple_args() { fn multiple_args() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req1: 1, req2: 2) multipleReqs(req1: 1, req2: 2)
@ -663,7 +627,8 @@ mod tests {
#[test] #[test]
fn multiple_args_reverse_order() { fn multiple_args_reverse_order() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req2: 2, req1: 1) multipleReqs(req2: 2, req1: 1)
@ -674,7 +639,8 @@ mod tests {
#[test] #[test]
fn no_args_on_multiple_optional() { fn no_args_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts multipleOpts
@ -685,7 +651,8 @@ mod tests {
#[test] #[test]
fn one_arg_on_multiple_optional() { fn one_arg_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts(opt1: 1) multipleOpts(opt1: 1)
@ -696,7 +663,8 @@ mod tests {
#[test] #[test]
fn second_arg_on_multiple_optional() { fn second_arg_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts(opt2: 1) multipleOpts(opt2: 1)
@ -707,7 +675,8 @@ mod tests {
#[test] #[test]
fn multiple_reqs_on_mixed_list() { fn multiple_reqs_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4) multipleOptAndReq(req1: 3, req2: 4)
@ -718,7 +687,8 @@ mod tests {
#[test] #[test]
fn multiple_reqs_and_one_opt_on_mixed_list() { fn multiple_reqs_and_one_opt_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5) multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
@ -729,7 +699,8 @@ mod tests {
#[test] #[test]
fn all_reqs_and_opts_on_mixed_list() { fn all_reqs_and_opts_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
@ -740,42 +711,38 @@ mod tests {
#[test] #[test]
fn incorrect_value_type() { fn incorrect_value_type() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req2: "two", req1: "one") multipleReqs(req2: "two", req1: "one")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("req2", "Int!"),
RuleError::new(&error_message("req2", "Int!"), &[ &[SourcePosition::new(82, 3, 35)]),
SourcePosition::new(82, 3, 35), RuleError::new(&error_message("req1", "Int!"),
]), &[SourcePosition::new(95, 3, 48)])]);
RuleError::new(&error_message("req1", "Int!"), &[
SourcePosition::new(95, 3, 48),
]),
]);
} }
#[test] #[test]
fn incorrect_value_and_missing_argument() { fn incorrect_value_and_missing_argument() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req1: "one") multipleReqs(req1: "one")
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("req1", "Int!"),
RuleError::new(&error_message("req1", "Int!"), &[ &[SourcePosition::new(82, 3, 35)])]);
SourcePosition::new(82, 3, 35),
]),
]);
} }
#[test] #[test]
fn optional_arg_despite_required_field_in_type() { fn optional_arg_despite_required_field_in_type() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField complexArgField
@ -786,7 +753,8 @@ mod tests {
#[test] #[test]
fn partial_object_only_required() { fn partial_object_only_required() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { requiredField: true }) complexArgField(complexArg: { requiredField: true })
@ -797,7 +765,8 @@ mod tests {
#[test] #[test]
fn partial_object_required_field_can_be_falsy() { fn partial_object_required_field_can_be_falsy() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { requiredField: false }) complexArgField(complexArg: { requiredField: false })
@ -808,7 +777,8 @@ mod tests {
#[test] #[test]
fn partial_object_including_required() { fn partial_object_including_required() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { requiredField: true, intField: 4 }) complexArgField(complexArg: { requiredField: true, intField: 4 })
@ -819,7 +789,8 @@ mod tests {
#[test] #[test]
fn full_object() { fn full_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { complexArgField(complexArg: {
@ -836,7 +807,8 @@ mod tests {
#[test] #[test]
fn full_object_with_fields_in_different_order() { fn full_object_with_fields_in_different_order() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { complexArgField(complexArg: {
@ -853,23 +825,22 @@ mod tests {
#[test] #[test]
fn partial_object_missing_required() { fn partial_object_missing_required() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { intField: 4 }) complexArgField(complexArg: { intField: 4 })
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("complexArg", "ComplexInput"),
RuleError::new(&error_message("complexArg", "ComplexInput"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
]),
]);
} }
#[test] #[test]
fn partial_object_invalid_field_type() { fn partial_object_invalid_field_type() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { complexArgField(complexArg: {
@ -879,16 +850,14 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("complexArg", "ComplexInput"),
RuleError::new(&error_message("complexArg", "ComplexInput"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
]),
]);
} }
#[test] #[test]
fn partial_object_unknown_field_arg() { fn partial_object_unknown_field_arg() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
complexArgField(complexArg: { complexArgField(complexArg: {
@ -898,16 +867,14 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("complexArg", "ComplexInput"),
RuleError::new(&error_message("complexArg", "ComplexInput"), &[ &[SourcePosition::new(91, 3, 44)])]);
SourcePosition::new(91, 3, 44),
]),
]);
} }
#[test] #[test]
fn directive_with_valid_types() { fn directive_with_valid_types() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog @include(if: true) { dog @include(if: true) {
name name
@ -921,20 +888,17 @@ mod tests {
#[test] #[test]
fn directive_with_incorrect_types() { fn directive_with_incorrect_types() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog @include(if: "yes") { dog @include(if: "yes") {
name @skip(if: ENUM) name @skip(if: ENUM)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("if", "Boolean!"),
RuleError::new(&error_message("if", "Boolean!"), &[ &[SourcePosition::new(38, 2, 27)]),
SourcePosition::new(38, 2, 27), RuleError::new(&error_message("if", "Boolean!"),
]), &[SourcePosition::new(74, 3, 27)])]);
RuleError::new(&error_message("if", "Boolean!"), &[
SourcePosition::new(74, 3, 27),
]),
]);
} }
} }

View file

@ -3,28 +3,32 @@ use types::utilities::is_valid_literal_value;
use parser::Spanning; use parser::Spanning;
use validation::{Visitor, ValidatorContext}; use validation::{Visitor, ValidatorContext};
pub struct DefaultValuesOfCorrectType { pub struct DefaultValuesOfCorrectType {}
}
pub fn factory() -> DefaultValuesOfCorrectType { pub fn factory() -> DefaultValuesOfCorrectType {
DefaultValuesOfCorrectType { DefaultValuesOfCorrectType {}
}
} }
impl<'a> Visitor<'a> for 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,
if let Some(Spanning { item: ref var_value, ref start, .. }) = var_def.default_value { 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 {
if var_def.var_type.item.is_non_null() { if var_def.var_type.item.is_non_null() {
ctx.report_error( ctx.report_error(&non_null_error_message(var_name.item,
&non_null_error_message(var_name.item, &format!("{}", var_def.var_type.item)), &format!("{}", var_def.var_type.item)),
&[start.clone()]) &[start.clone()])
} } else {
else {
let meta_type = ctx.schema.make_type(&var_def.var_type.item); let meta_type = ctx.schema.make_type(&var_def.var_type.item);
if !is_valid_literal_value(ctx.schema, &meta_type, var_value) { if !is_valid_literal_value(ctx.schema, &meta_type, var_value) {
ctx.report_error( ctx.report_error(&type_error_message(var_name.item,
&type_error_message(var_name.item, &format!("{}", var_def.var_type.item)), &format!("{}", var_def.var_type.item)),
&[start.clone()]); &[start.clone()]);
} }
} }
@ -53,7 +57,8 @@ mod tests {
#[test] #[test]
fn variables_with_no_default_values() { fn variables_with_no_default_values() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query NullableValues($a: Int, $b: String, $c: ComplexInput) { query NullableValues($a: Int, $b: String, $c: ComplexInput) {
dog { name } dog { name }
} }
@ -62,7 +67,8 @@ mod tests {
#[test] #[test]
fn required_variables_without_default_values() { fn required_variables_without_default_values() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query RequiredValues($a: Int!, $b: String!) { query RequiredValues($a: Int!, $b: String!) {
dog { name } dog { name }
} }
@ -71,7 +77,8 @@ mod tests {
#[test] #[test]
fn variables_with_valid_default_values() { fn variables_with_valid_default_values() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query WithDefaultValues( query WithDefaultValues(
$a: Int = 1, $a: Int = 1,
$b: String = "ok", $b: String = "ok",
@ -84,24 +91,22 @@ mod tests {
#[test] #[test]
fn no_required_variables_with_default_values() { 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") { query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
dog { name } dog { name }
} }
"#, "#,
&[ &[RuleError::new(&non_null_error_message("a", "Int!"),
RuleError::new(&non_null_error_message("a", "Int!"), &[ &[SourcePosition::new(53, 1, 52)]),
SourcePosition::new(53, 1, 52), RuleError::new(&non_null_error_message("b", "String!"),
]), &[SourcePosition::new(70, 1, 69)])]);
RuleError::new(&non_null_error_message("b", "String!"), &[
SourcePosition::new(70, 1, 69),
]),
]);
} }
#[test] #[test]
fn variables_with_invalid_default_values() { fn variables_with_invalid_default_values() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query InvalidDefaultValues( query InvalidDefaultValues(
$a: Int = "one", $a: Int = "one",
$b: String = 4, $b: String = 4,
@ -110,45 +115,36 @@ mod tests {
dog { name } dog { name }
} }
"#, "#,
&[ &[RuleError::new(&type_error_message("a", "Int"),
RuleError::new(&type_error_message("a", "Int"), &[ &[SourcePosition::new(61, 2, 22)]),
SourcePosition::new(61, 2, 22), RuleError::new(&type_error_message("b", "String"),
]), &[SourcePosition::new(93, 3, 25)]),
RuleError::new(&type_error_message("b", "String"), &[ RuleError::new(&type_error_message("c", "ComplexInput"),
SourcePosition::new(93, 3, 25), &[SourcePosition::new(127, 4, 31)])]);
]),
RuleError::new(&type_error_message("c", "ComplexInput"), &[
SourcePosition::new(127, 4, 31),
]),
]);
} }
#[test] #[test]
fn complex_variables_missing_required_field() { fn complex_variables_missing_required_field() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query MissingRequiredField($a: ComplexInput = {intField: 3}) { query MissingRequiredField($a: ComplexInput = {intField: 3}) {
dog { name } dog { name }
} }
"#, "#,
&[ &[RuleError::new(&type_error_message("a", "ComplexInput"),
RuleError::new(&type_error_message("a", "ComplexInput"), &[ &[SourcePosition::new(57, 1, 56)])]);
SourcePosition::new(57, 1, 56),
]),
]);
} }
#[test] #[test]
fn list_variables_with_invalid_item() { fn list_variables_with_invalid_item() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query InvalidItem($a: [String] = ["one", 2]) { query InvalidItem($a: [String] = ["one", 2]) {
dog { name } dog { name }
} }
"#, "#,
&[ &[RuleError::new(&type_error_message("a", "[String]"),
RuleError::new(&type_error_message("a", "[String]"), &[ &[SourcePosition::new(44, 1, 43)])]);
SourcePosition::new(44, 1, 43),
]),
]);
} }
} }

View file

@ -16,8 +16,7 @@ impl<'a> Visitor<'a> for FieldsOnCorrectType {
let type_name = parent_type.name().unwrap_or("<unknown>"); let type_name = parent_type.name().unwrap_or("<unknown>");
if parent_type.field_by_name(field_name.item).is_none() { if parent_type.field_by_name(field_name.item).is_none() {
context.report_error( context.report_error(&error_message(field_name.item, type_name),
&error_message(field_name.item, type_name),
&[field_name.start.clone()]); &[field_name.start.clone()]);
} }
} }
@ -38,7 +37,8 @@ mod tests {
#[test] #[test]
fn selection_on_object() { fn selection_on_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment objectFieldSelection on Dog { fragment objectFieldSelection on Dog {
__typename __typename
name name
@ -48,7 +48,8 @@ mod tests {
#[test] #[test]
fn aliased_selection_on_object() { fn aliased_selection_on_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment aliasedObjectFieldSelection on Dog { fragment aliasedObjectFieldSelection on Dog {
tn : __typename tn : __typename
otherName : name otherName : name
@ -58,7 +59,8 @@ mod tests {
#[test] #[test]
fn selection_on_interface() { fn selection_on_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment interfaceFieldSelection on Pet { fragment interfaceFieldSelection on Pet {
__typename __typename
name name
@ -68,7 +70,8 @@ mod tests {
#[test] #[test]
fn aliased_selection_on_interface() { fn aliased_selection_on_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment interfaceFieldSelection on Pet { fragment interfaceFieldSelection on Pet {
otherName : name otherName : name
} }
@ -77,7 +80,8 @@ mod tests {
#[test] #[test]
fn lying_alias_selection() { fn lying_alias_selection() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment lyingAliasSelection on Dog { fragment lyingAliasSelection on Dog {
name : nickname name : nickname
} }
@ -86,7 +90,8 @@ mod tests {
#[test] #[test]
fn ignores_unknown_type() { fn ignores_unknown_type() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment unknownSelection on UnknownType { fragment unknownSelection on UnknownType {
unknownField unknownField
} }
@ -95,7 +100,8 @@ mod tests {
#[test] #[test]
fn nested_unknown_fields() { fn nested_unknown_fields() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment typeKnownAgain on Pet { fragment typeKnownAgain on Pet {
unknown_pet_field { unknown_pet_field {
... on Cat { ... on Cat {
@ -104,137 +110,118 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("unknown_pet_field", "Pet"),
RuleError::new(&error_message("unknown_pet_field", "Pet"), &[ &[SourcePosition::new(56, 2, 12)]),
SourcePosition::new(56, 2, 12) RuleError::new(&error_message("unknown_cat_field", "Cat"),
]), &[SourcePosition::new(119, 4, 16)])]);
RuleError::new(&error_message("unknown_cat_field", "Cat"), &[
SourcePosition::new(119, 4, 16)
]),
]);
} }
#[test] #[test]
fn unknown_field_on_fragment() { fn unknown_field_on_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fieldNotDefined on Dog { fragment fieldNotDefined on Dog {
meowVolume meowVolume
} }
"#, "#,
&[ &[RuleError::new(&error_message("meowVolume", "Dog"),
RuleError::new(&error_message("meowVolume", "Dog"), &[ &[SourcePosition::new(57, 2, 12)])]);
SourcePosition::new(57, 2, 12)
]),
]);
} }
#[test] #[test]
fn ignores_deeply_unknown_field() { fn ignores_deeply_unknown_field() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment deepFieldNotDefined on Dog { fragment deepFieldNotDefined on Dog {
unknown_field { unknown_field {
deeper_unknown_field deeper_unknown_field
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("unknown_field", "Dog"),
RuleError::new(&error_message("unknown_field", "Dog"), &[ &[SourcePosition::new(61, 2, 12)])]);
SourcePosition::new(61, 2, 12)
]),
]);
} }
#[test] #[test]
fn unknown_subfield() { fn unknown_subfield() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment subFieldNotDefined on Human { fragment subFieldNotDefined on Human {
pets { pets {
unknown_field unknown_field
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("unknown_field", "Pet"),
RuleError::new(&error_message("unknown_field", "Pet"), &[ &[SourcePosition::new(83, 3, 14)])]);
SourcePosition::new(83, 3, 14)
]),
]);
} }
#[test] #[test]
fn unknown_field_on_inline_fragment() { fn unknown_field_on_inline_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fieldNotDefined on Pet { fragment fieldNotDefined on Pet {
... on Dog { ... on Dog {
meowVolume meowVolume
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("meowVolume", "Dog"),
RuleError::new(&error_message("meowVolume", "Dog"), &[ &[SourcePosition::new(84, 3, 14)])]);
SourcePosition::new(84, 3, 14)
]),
]);
} }
#[test] #[test]
fn unknown_aliased_target() { fn unknown_aliased_target() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment aliasedFieldTargetNotDefined on Dog { fragment aliasedFieldTargetNotDefined on Dog {
volume : mooVolume volume : mooVolume
} }
"#, "#,
&[ &[RuleError::new(&error_message("mooVolume", "Dog"),
RuleError::new(&error_message("mooVolume", "Dog"), &[ &[SourcePosition::new(79, 2, 21)])]);
SourcePosition::new(79, 2, 21)
]),
]);
} }
#[test] #[test]
fn unknown_aliased_lying_field_target() { fn unknown_aliased_lying_field_target() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment aliasedLyingFieldTargetNotDefined on Dog { fragment aliasedLyingFieldTargetNotDefined on Dog {
barkVolume : kawVolume barkVolume : kawVolume
} }
"#, "#,
&[ &[RuleError::new(&error_message("kawVolume", "Dog"),
RuleError::new(&error_message("kawVolume", "Dog"), &[ &[SourcePosition::new(88, 2, 25)])]);
SourcePosition::new(88, 2, 25)
]),
]);
} }
#[test] #[test]
fn not_defined_on_interface() { fn not_defined_on_interface() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment notDefinedOnInterface on Pet { fragment notDefinedOnInterface on Pet {
tailLength tailLength
} }
"#, "#,
&[ &[RuleError::new(&error_message("tailLength", "Pet"),
RuleError::new(&error_message("tailLength", "Pet"), &[ &[SourcePosition::new(63, 2, 12)])]);
SourcePosition::new(63, 2, 12)
]),
]);
} }
#[test] #[test]
fn defined_in_concrete_types_but_not_interface() { fn defined_in_concrete_types_but_not_interface() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment definedOnImplementorsButNotInterface on Pet { fragment definedOnImplementorsButNotInterface on Pet {
nickname nickname
} }
"#, "#,
&[ &[RuleError::new(&error_message("nickname", "Pet"),
RuleError::new(&error_message("nickname", "Pet"), &[ &[SourcePosition::new(78, 2, 12)])]);
SourcePosition::new(78, 2, 12)
]),
]);
} }
#[test] #[test]
fn meta_field_on_union() { fn meta_field_on_union() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment definedOnImplementorsButNotInterface on Pet { fragment definedOnImplementorsButNotInterface on Pet {
__typename __typename
} }
@ -243,21 +230,20 @@ mod tests {
#[test] #[test]
fn fields_on_union() { fn fields_on_union() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment definedOnImplementorsQueriedOnUnion on CatOrDog { fragment definedOnImplementorsQueriedOnUnion on CatOrDog {
name name
} }
"#, "#,
&[ &[RuleError::new(&error_message("name", "CatOrDog"),
RuleError::new(&error_message("name", "CatOrDog"), &[ &[SourcePosition::new(82, 2, 12)])]);
SourcePosition::new(82, 2, 12)
]),
]);
} }
#[test] #[test]
fn valid_field_in_inline_fragment() { fn valid_field_in_inline_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment objectFieldSelection on Pet { fragment objectFieldSelection on Pet {
... on Dog { ... on Dog {
name name

View file

@ -9,35 +9,36 @@ pub fn factory() -> FragmentsOnCompositeTypes {
} }
impl<'a> Visitor<'a> for 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 let Some(current_type) = context.current_type() {
if !current_type.is_composite() { if !current_type.is_composite() {
let type_name = current_type.name().unwrap_or("<unknown>"); let type_name = current_type.name().unwrap_or("<unknown>");
let type_cond = &f.item.type_condition; let type_cond = &f.item.type_condition;
context.report_error( context.report_error(&error_message(Some(f.item.name.item), type_name),
&error_message(
Some(f.item.name.item),
type_name),
&[type_cond.start.clone()]); &[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 { if let Some(ref type_cond) = f.item.type_condition {
let invalid_type_name = context.current_type().iter() let invalid_type_name = context
.current_type()
.iter()
.filter(|&t| !t.is_composite()) .filter(|&t| !t.is_composite())
.map(|t| t.name().unwrap_or("<unknown>")) .map(|t| t.name().unwrap_or("<unknown>"))
.next(); .next();
if let Some(name) = invalid_type_name { if let Some(name) = invalid_type_name {
context.report_error( context.report_error(&error_message(None, name), &[type_cond.start.clone()]);
&error_message(None, name),
&[type_cond.start.clone()]);
} }
} }
} }
@ -49,8 +50,7 @@ fn error_message(fragment_name: Option<&str>, on_type: &str) -> String {
format!( format!(
r#"Fragment "{}" cannot condition non composite type "{}"#, r#"Fragment "{}" cannot condition non composite type "{}"#,
name, on_type) name, on_type)
} } else {
else {
format!( format!(
r#"Fragment cannot condition on non composite type "{}""#, r#"Fragment cannot condition on non composite type "{}""#,
on_type) on_type)
@ -66,7 +66,8 @@ mod tests {
#[test] #[test]
fn on_object() { fn on_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment validFragment on Dog { fragment validFragment on Dog {
barks barks
} }
@ -75,7 +76,8 @@ mod tests {
#[test] #[test]
fn on_interface() { fn on_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment validFragment on Pet { fragment validFragment on Pet {
name name
} }
@ -84,7 +86,8 @@ mod tests {
#[test] #[test]
fn on_object_inline() { fn on_object_inline() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment validFragment on Pet { fragment validFragment on Pet {
... on Dog { ... on Dog {
barks barks
@ -95,7 +98,8 @@ mod tests {
#[test] #[test]
fn on_inline_without_type_cond() { fn on_inline_without_type_cond() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment validFragment on Pet { fragment validFragment on Pet {
... { ... {
name name
@ -106,7 +110,8 @@ mod tests {
#[test] #[test]
fn on_union() { fn on_union() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment validFragment on CatOrDog { fragment validFragment on CatOrDog {
__typename __typename
} }
@ -115,59 +120,51 @@ mod tests {
#[test] #[test]
fn not_on_scalar() { fn not_on_scalar() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarFragment on Boolean { fragment scalarFragment on Boolean {
bad bad
} }
"#, "#,
&[ &[RuleError::new(&error_message(Some("scalarFragment"), "Boolean"),
RuleError::new(&error_message(Some("scalarFragment"), "Boolean"), &[ &[SourcePosition::new(38, 1, 37)])]);
SourcePosition::new(38, 1, 37),
]),
]);
} }
#[test] #[test]
fn not_on_enum() { fn not_on_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarFragment on FurColor { fragment scalarFragment on FurColor {
bad bad
} }
"#, "#,
&[ &[RuleError::new(&error_message(Some("scalarFragment"), "FurColor"),
RuleError::new(&error_message(Some("scalarFragment"), "FurColor"), &[ &[SourcePosition::new(38, 1, 37)])]);
SourcePosition::new(38, 1, 37),
]),
]);
} }
#[test] #[test]
fn not_on_input_object() { fn not_on_input_object() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment inputFragment on ComplexInput { fragment inputFragment on ComplexInput {
stringField stringField
} }
"#, "#,
&[ &[RuleError::new(&error_message(Some("inputFragment"), "ComplexInput"),
RuleError::new(&error_message(Some("inputFragment"), "ComplexInput"), &[ &[SourcePosition::new(37, 1, 36)])]);
SourcePosition::new(37, 1, 36),
]),
]);
} }
#[test] #[test]
fn not_on_scalar_inline() { fn not_on_scalar_inline() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidFragment on Pet { fragment invalidFragment on Pet {
... on String { ... on String {
barks barks
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message(None, "String"),
RuleError::new(&error_message(None, "String"), &[ &[SourcePosition::new(64, 2, 19)])]);
SourcePosition::new(64, 2, 19),
]),
]);
} }
} }

View file

@ -14,14 +14,15 @@ pub struct KnownArgumentNames<'a> {
} }
pub fn factory<'a>() -> KnownArgumentNames<'a> { pub fn factory<'a>() -> KnownArgumentNames<'a> {
KnownArgumentNames { KnownArgumentNames { current_args: None }
current_args: None,
}
} }
impl<'a> Visitor<'a> for KnownArgumentNames<'a> { impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, directive: &'a Spanning<Directive>) { fn enter_directive(&mut self,
self.current_args = ctx.schema ctx: &mut ValidatorContext<'a>,
directive: &'a Spanning<Directive>) {
self.current_args =
ctx.schema
.directive_by_name(directive.item.name.item) .directive_by_name(directive.item.name.item)
.map(|d| (ArgumentPosition::Directive(directive.item.name.item), &d.arguments)); .map(|d| (ArgumentPosition::Directive(directive.item.name.item), &d.arguments));
} }
@ -34,31 +35,35 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
self.current_args = ctx.parent_type() self.current_args = ctx.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item)) .and_then(|t| t.field_by_name(field.item.name.item))
.and_then(|f| f.arguments.as_ref()) .and_then(|f| f.arguments.as_ref())
.map(|args| ( .map(|args| {
ArgumentPosition::Field( (ArgumentPosition::Field(field.item.name.item,
field.item.name.item, ctx.parent_type()
ctx.parent_type().expect("Parent type should exist") .expect("Parent type should exist")
.name().expect("Parent type should be named")), .name()
args)); .expect("Parent type should be named")),
args)
});
} }
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {
self.current_args = None; 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 let Some((ref pos, args)) = self.current_args {
if args.iter().find(|a| a.name == arg_name.item).is_none() { if args.iter().find(|a| a.name == arg_name.item).is_none() {
let message = match *pos { let message = match *pos {
ArgumentPosition::Field(field_name, type_name) => ArgumentPosition::Field(field_name, type_name) => {
field_error_message(arg_name.item, field_name, type_name), field_error_message(arg_name.item, field_name, type_name)
ArgumentPosition::Directive(directive_name) => }
directive_error_message(arg_name.item, directive_name), ArgumentPosition::Directive(directive_name) => {
directive_error_message(arg_name.item, directive_name)
}
}; };
ctx.report_error( ctx.report_error(&message, &[arg_name.start.clone()]);
&message,
&[arg_name.start.clone()]);
} }
} }
} }
@ -85,7 +90,8 @@ mod tests {
#[test] #[test]
fn single_arg_is_known() { fn single_arg_is_known() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment argOnRequiredArg on Dog { fragment argOnRequiredArg on Dog {
doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT)
} }
@ -94,7 +100,8 @@ mod tests {
#[test] #[test]
fn multiple_args_are_known() { fn multiple_args_are_known() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment multipleArgs on ComplicatedArgs { fragment multipleArgs on ComplicatedArgs {
multipleReqs(req1: 1, req2: 2) multipleReqs(req1: 1, req2: 2)
} }
@ -103,7 +110,8 @@ mod tests {
#[test] #[test]
fn ignores_args_of_unknown_fields() { fn ignores_args_of_unknown_fields() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment argOnUnknownField on Dog { fragment argOnUnknownField on Dog {
unknownField(unknownArg: SIT) unknownField(unknownArg: SIT)
} }
@ -112,7 +120,8 @@ mod tests {
#[test] #[test]
fn multiple_args_in_reverse_order_are_known() { fn multiple_args_in_reverse_order_are_known() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment multipleArgsReverseOrder on ComplicatedArgs { fragment multipleArgsReverseOrder on ComplicatedArgs {
multipleReqs(req2: 2, req1: 1) multipleReqs(req2: 2, req1: 1)
} }
@ -121,7 +130,8 @@ mod tests {
#[test] #[test]
fn no_args_on_optional_arg() { fn no_args_on_optional_arg() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment noArgOnOptionalArg on Dog { fragment noArgOnOptionalArg on Dog {
isHousetrained isHousetrained
} }
@ -130,7 +140,8 @@ mod tests {
#[test] #[test]
fn args_are_known_deeply() { fn args_are_known_deeply() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT)
@ -148,7 +159,8 @@ mod tests {
#[test] #[test]
fn directive_args_are_known() { fn directive_args_are_known() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog @skip(if: true) dog @skip(if: true)
} }
@ -157,52 +169,52 @@ mod tests {
#[test] #[test]
fn undirective_args_are_invalid() { fn undirective_args_are_invalid() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog @skip(unless: true) dog @skip(unless: true)
} }
"#, "#,
&[ &[RuleError::new(&directive_error_message("unless", "skip"),
RuleError::new(&directive_error_message("unless", "skip"), &[ &[SourcePosition::new(35, 2, 22)])]);
SourcePosition::new(35, 2, 22),
]),
]);
} }
#[test] #[test]
fn invalid_arg_name() { fn invalid_arg_name() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidArgName on Dog { fragment invalidArgName on Dog {
doesKnowCommand(unknown: true) doesKnowCommand(unknown: true)
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("unknown",
RuleError::new(&field_error_message("unknown", "doesKnowCommand", "Dog"), &[ "doesKnowCommand",
SourcePosition::new(72, 2, 28), "Dog"),
]), &[SourcePosition::new(72, 2, 28)])]);
]);
} }
#[test] #[test]
fn unknown_args_amongst_known_args() { fn unknown_args_amongst_known_args() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment oneGoodArgOneInvalidArg on Dog { fragment oneGoodArgOneInvalidArg on Dog {
doesKnowCommand(whoknows: 1, dogCommand: SIT, unknown: true) doesKnowCommand(whoknows: 1, dogCommand: SIT, unknown: true)
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("whoknows",
RuleError::new(&field_error_message("whoknows", "doesKnowCommand", "Dog"), &[ "doesKnowCommand",
SourcePosition::new(81, 2, 28), "Dog"),
]), &[SourcePosition::new(81, 2, 28)]),
RuleError::new(&field_error_message("unknown", "doesKnowCommand", "Dog"), &[ RuleError::new(&field_error_message("unknown",
SourcePosition::new(111, 2, 58), "doesKnowCommand",
]), "Dog"),
]); &[SourcePosition::new(111, 2, 58)])]);
} }
#[test] #[test]
fn unknown_args_deeply() { fn unknown_args_deeply() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog { dog {
doesKnowCommand(unknown: true) doesKnowCommand(unknown: true)
@ -216,13 +228,13 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("unknown",
RuleError::new(&field_error_message("unknown", "doesKnowCommand", "Dog"), &[ "doesKnowCommand",
SourcePosition::new(61, 3, 30), "Dog"),
]), &[SourcePosition::new(61, 3, 30)]),
RuleError::new(&field_error_message("unknown", "doesKnowCommand", "Dog"), &[ RuleError::new(&field_error_message("unknown",
SourcePosition::new(193, 8, 34), "doesKnowCommand",
]), "Dog"),
]); &[SourcePosition::new(193, 8, 34)])]);
} }
} }

View file

@ -8,20 +8,23 @@ pub struct KnownDirectives {
} }
pub fn factory() -> KnownDirectives { pub fn factory() -> KnownDirectives {
KnownDirectives { KnownDirectives { location_stack: Vec::new() }
location_stack: Vec::new(),
}
} }
impl<'a> Visitor<'a> for KnownDirectives { impl<'a> Visitor<'a> for KnownDirectives {
fn enter_operation_definition(&mut self, _: &mut ValidatorContext<'a>, op: &'a Spanning<Operation>) { fn enter_operation_definition(&mut self,
self.location_stack.push(match op.item.operation_type { _: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>) {
self.location_stack
.push(match op.item.operation_type {
OperationType::Query => DirectiveLocation::Query, OperationType::Query => DirectiveLocation::Query,
OperationType::Mutation => DirectiveLocation::Mutation, 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(); let top = self.location_stack.pop();
assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation)); assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation));
} }
@ -35,48 +38,64 @@ impl<'a> Visitor<'a> for KnownDirectives {
assert_eq!(top, Some(DirectiveLocation::Field)); assert_eq!(top, Some(DirectiveLocation::Field));
} }
fn enter_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Fragment>) { fn enter_fragment_definition(&mut self,
self.location_stack.push(DirectiveLocation::FragmentDefinition); _: &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(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::FragmentDefinition)); 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); 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(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::FragmentSpread)); 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); 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(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::InlineFragment)); 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; let directive_name = &directive.item.name.item;
if let Some(directive_type) = ctx.schema.directive_by_name(directive_name) { if let Some(directive_type) = ctx.schema.directive_by_name(directive_name) {
if let Some(current_location) = self.location_stack.last() { if let Some(current_location) = self.location_stack.last() {
if directive_type.locations.iter().find(|l| l == &current_location).is_none() { if directive_type
ctx.report_error( .locations
&misplaced_error_message(directive_name, current_location), .iter()
.find(|l| l == &current_location)
.is_none() {
ctx.report_error(&misplaced_error_message(directive_name, current_location),
&[directive.start.clone()]); &[directive.start.clone()]);
} }
} }
} } else {
else { ctx.report_error(&unknown_error_message(directive_name),
ctx.report_error(
&unknown_error_message(directive_name),
&[directive.start.clone()]); &[directive.start.clone()]);
} }
} }
@ -100,7 +119,8 @@ mod tests {
#[test] #[test]
fn with_no_directives() { fn with_no_directives() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
name name
...Frag ...Frag
@ -114,7 +134,8 @@ mod tests {
#[test] #[test]
fn with_known_directives() { fn with_known_directives() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog @include(if: true) { dog @include(if: true) {
name name
@ -128,23 +149,22 @@ mod tests {
#[test] #[test]
fn with_unknown_directive() { fn with_unknown_directive() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog @unknown(directive: "value") { dog @unknown(directive: "value") {
name name
} }
} }
"#, "#,
&[ &[RuleError::new(&unknown_error_message("unknown"),
RuleError::new(&unknown_error_message("unknown"), &[ &[SourcePosition::new(29, 2, 16)])]);
SourcePosition::new(29, 2, 16),
]),
]);
} }
#[test] #[test]
fn with_many_unknown_directives() { fn with_many_unknown_directives() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog @unknown(directive: "value") { dog @unknown(directive: "value") {
name name
@ -157,22 +177,18 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&unknown_error_message("unknown"),
RuleError::new(&unknown_error_message("unknown"), &[ &[SourcePosition::new(29, 2, 16)]),
SourcePosition::new(29, 2, 16), RuleError::new(&unknown_error_message("unknown"),
]), &[SourcePosition::new(111, 5, 18)]),
RuleError::new(&unknown_error_message("unknown"), &[ RuleError::new(&unknown_error_message("unknown"),
SourcePosition::new(111, 5, 18), &[SourcePosition::new(180, 7, 19)])]);
]),
RuleError::new(&unknown_error_message("unknown"), &[
SourcePosition::new(180, 7, 19),
]),
]);
} }
#[test] #[test]
fn with_well_placed_directives() { fn with_well_placed_directives() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo @onQuery { query Foo @onQuery {
name @include(if: true) name @include(if: true)
...Frag @include(if: true) ...Frag @include(if: true)

View file

@ -9,11 +9,12 @@ pub fn factory() -> KnownFragmentNames {
} }
impl<'a> Visitor<'a> for 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; let spread_name = &spread.item.name;
if !context.is_known_fragment(spread_name.item) { if !context.is_known_fragment(spread_name.item) {
context.report_error( context.report_error(&error_message(spread_name.item),
&error_message(spread_name.item),
&[spread_name.start.clone()]); &[spread_name.start.clone()]);
} }
} }
@ -32,7 +33,8 @@ mod tests {
#[test] #[test]
fn known() { fn known() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
human(id: 4) { human(id: 4) {
...HumanFields1 ...HumanFields1
@ -59,7 +61,8 @@ mod tests {
#[test] #[test]
fn unknown() { fn unknown() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
human(id: 4) { human(id: 4) {
...UnknownFragment1 ...UnknownFragment1
@ -73,16 +76,11 @@ mod tests {
...UnknownFragment3 ...UnknownFragment3
} }
"#, "#,
&[ &[RuleError::new(&error_message("UnknownFragment1"),
RuleError::new(&error_message("UnknownFragment1"), &[ &[SourcePosition::new(57, 3, 17)]),
SourcePosition::new(57, 3, 17), RuleError::new(&error_message("UnknownFragment2"),
]), &[SourcePosition::new(122, 5, 19)]),
RuleError::new(&error_message("UnknownFragment2"), &[ RuleError::new(&error_message("UnknownFragment3"),
SourcePosition::new(122, 5, 19), &[SourcePosition::new(255, 11, 15)])]);
]),
RuleError::new(&error_message("UnknownFragment3"), &[
SourcePosition::new(255, 11, 15),
]),
]);
} }
} }

View file

@ -9,18 +9,24 @@ pub fn factory() -> KnownTypeNames {
} }
impl<'a> Visitor<'a> for 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 { if let Some(ref type_cond) = fragment.item.type_condition {
validate_type(ctx, type_cond.item, &type_cond.start); 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; let type_cond = &fragment.item.type_condition;
validate_type(ctx, type_cond.item, &type_cond.start); 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(); let type_name = var_def.var_type.item.innermost_name();
validate_type(ctx, type_name, &var_def.var_type.start); validate_type(ctx, type_name, &var_def.var_type.start);
} }
@ -28,9 +34,7 @@ impl<'a> Visitor<'a> for KnownTypeNames {
fn validate_type<'a>(ctx: &mut ValidatorContext<'a>, type_name: &str, location: &SourcePosition) { fn validate_type<'a>(ctx: &mut ValidatorContext<'a>, type_name: &str, location: &SourcePosition) {
if ctx.schema.type_by_name(type_name).is_none() { if ctx.schema.type_by_name(type_name).is_none() {
ctx.report_error( ctx.report_error(&error_message(type_name), &[location.clone()]);
&error_message(type_name),
&[location.clone()]);
} }
} }
@ -47,7 +51,8 @@ mod tests {
#[test] #[test]
fn known_type_names_are_valid() { fn known_type_names_are_valid() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($var: String, $required: [String!]!) { query Foo($var: String, $required: [String!]!) {
user(id: 4) { user(id: 4) {
pets { ... on Pet { name }, ...PetFields, ... { name } } pets { ... on Pet { name }, ...PetFields, ... { name } }
@ -61,7 +66,8 @@ mod tests {
#[test] #[test]
fn unknown_type_names_are_invalid() { fn unknown_type_names_are_invalid() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($var: JumbledUpLetters) { query Foo($var: JumbledUpLetters) {
user(id: 4) { user(id: 4) {
name name
@ -72,16 +78,11 @@ mod tests {
name name
} }
"#, "#,
&[ &[RuleError::new(&error_message("JumbledUpLetters"),
RuleError::new(&error_message("JumbledUpLetters"), &[ &[SourcePosition::new(27, 1, 26)]),
SourcePosition::new(27, 1, 26), RuleError::new(&error_message("Badger"),
]), &[SourcePosition::new(120, 4, 28)]),
RuleError::new(&error_message("Badger"), &[ RuleError::new(&error_message("Peettt"),
SourcePosition::new(120, 4, 28), &[SourcePosition::new(210, 7, 32)])]);
]),
RuleError::new(&error_message("Peettt"), &[
SourcePosition::new(210, 7, 32),
]),
]);
} }
} }

View file

@ -7,15 +7,12 @@ pub struct LoneAnonymousOperation {
} }
pub fn factory() -> LoneAnonymousOperation { pub fn factory() -> LoneAnonymousOperation {
LoneAnonymousOperation { LoneAnonymousOperation { operation_count: None }
operation_count: None,
}
} }
impl<'a> Visitor<'a> for LoneAnonymousOperation { impl<'a> Visitor<'a> for LoneAnonymousOperation {
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, doc: &'a Document) { fn enter_document(&mut self, _: &mut ValidatorContext<'a>, doc: &'a Document) {
self.operation_count = Some(doc self.operation_count = Some(doc.iter()
.iter()
.filter(|d| match **d { .filter(|d| match **d {
Definition::Operation(_) => true, Definition::Operation(_) => true,
Definition::Fragment(_) => false, Definition::Fragment(_) => false,
@ -23,7 +20,9 @@ impl<'a> Visitor<'a> for LoneAnonymousOperation {
.count()); .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 let Some(operation_count) = self.operation_count {
if operation_count > 1 && op.item.name.is_none() { if operation_count > 1 && op.item.name.is_none() {
ctx.report_error(error_message(), &[op.start.clone()]); ctx.report_error(error_message(), &[op.start.clone()]);
@ -45,7 +44,8 @@ mod tests {
#[test] #[test]
fn no_operations() { fn no_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment fragA on Type { fragment fragA on Type {
field field
} }
@ -54,7 +54,8 @@ mod tests {
#[test] #[test]
fn one_anon_operation() { fn one_anon_operation() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field field
} }
@ -63,7 +64,8 @@ mod tests {
#[test] #[test]
fn multiple_named_operations() { fn multiple_named_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
field field
} }
@ -76,7 +78,8 @@ mod tests {
#[test] #[test]
fn anon_operation_with_fragment() { fn anon_operation_with_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
...Foo ...Foo
} }
@ -88,7 +91,8 @@ mod tests {
#[test] #[test]
fn multiple_anon_operations() { fn multiple_anon_operations() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
fieldA fieldA
} }
@ -96,19 +100,14 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)]),
RuleError::new(error_message(), &[ RuleError::new(error_message(), &[SourcePosition::new(54, 4, 10)])]);
SourcePosition::new(11, 1, 10),
]),
RuleError::new(error_message(), &[
SourcePosition::new(54, 4, 10),
]),
]);
} }
#[test] #[test]
fn anon_operation_with_a_mutation() { fn anon_operation_with_a_mutation() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
fieldA fieldA
} }
@ -116,10 +115,6 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)])]);
RuleError::new(error_message(), &[
SourcePosition::new(11, 1, 10),
]),
]);
} }
} }

View file

@ -46,7 +46,9 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
ctx.append_errors(detector.errors); 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()); assert!(self.current_fragment.is_none());
let fragment_name = &fragment.item.name.item; let fragment_name = &fragment.item.name.item;
@ -54,18 +56,21 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
self.fragment_order.push(fragment_name); 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); assert_eq!(Some(fragment.item.name.item), self.current_fragment);
self.current_fragment = None; 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 { if let Some(current_fragment) = self.current_fragment {
self.spreads self.spreads
.entry(current_fragment) .entry(current_fragment)
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(Spanning::start_end( .push(Spanning::start_end(&spread.start.clone(),
&spread.start.clone(),
&spread.end.clone(), &spread.end.clone(),
spread.item.name.item)); spread.item.name.item));
} }
@ -93,11 +98,9 @@ impl<'a> CycleDetector<'a> {
node node
}; };
self.errors.push(RuleError::new( self.errors
&error_message(name), .push(RuleError::new(&error_message(name), &[err_pos.start.clone()]));
&[err_pos.start.clone()])); } else if !self.visited.contains(name) {
}
else if !self.visited.contains(name) {
path.push(node); path.push(node);
self.detect_from(name, path); self.detect_from(name, path);
path.pop(); path.pop();
@ -121,7 +124,8 @@ mod tests {
#[test] #[test]
fn single_reference_is_valid() { fn single_reference_is_valid() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
fragment fragB on Dog { name } fragment fragB on Dog { name }
"#); "#);
@ -129,7 +133,8 @@ mod tests {
#[test] #[test]
fn spreading_twice_is_not_circular() { fn spreading_twice_is_not_circular() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragB } fragment fragA on Dog { ...fragB, ...fragB }
fragment fragB on Dog { name } fragment fragB on Dog { name }
"#); "#);
@ -137,7 +142,8 @@ mod tests {
#[test] #[test]
fn spreading_twice_indirectly_is_not_circular() { fn spreading_twice_indirectly_is_not_circular() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragC } fragment fragA on Dog { ...fragB, ...fragC }
fragment fragB on Dog { ...fragC } fragment fragB on Dog { ...fragC }
fragment fragC on Dog { name } fragment fragC on Dog { name }
@ -146,7 +152,8 @@ mod tests {
#[test] #[test]
fn double_spread_within_abstract_types() { fn double_spread_within_abstract_types() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment nameFragment on Pet { fragment nameFragment on Pet {
... on Dog { name } ... on Dog { name }
... on Cat { name } ... on Cat { name }
@ -161,7 +168,8 @@ mod tests {
#[test] #[test]
fn does_not_false_positive_on_unknown_fragment() { fn does_not_false_positive_on_unknown_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment nameFragment on Pet { fragment nameFragment on Pet {
...UnknownFragment ...UnknownFragment
} }
@ -170,73 +178,64 @@ mod tests {
#[test] #[test]
fn spreading_recursively_within_field_fails() { fn spreading_recursively_within_field_fails() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Human { relatives { ...fragA } }, fragment fragA on Human { relatives { ...fragA } },
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(49, 1, 48)])]);
SourcePosition::new(49, 1, 48)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_directly() { fn no_spreading_itself_directly() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragA } fragment fragA on Dog { ...fragA }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)])]);
SourcePosition::new(35, 1, 34)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_directly_within_inline_fragment() { fn no_spreading_itself_directly_within_inline_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Pet { fragment fragA on Pet {
... on Dog { ... on Dog {
...fragA ...fragA
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(74, 3, 14)])]);
SourcePosition::new(74, 3, 14)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_indirectly() { fn no_spreading_itself_indirectly() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragA } fragment fragB on Dog { ...fragA }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)])]);
SourcePosition::new(35, 1, 34)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_indirectly_reports_opposite_order() { fn no_spreading_itself_indirectly_reports_opposite_order() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragB on Dog { ...fragA } fragment fragB on Dog { ...fragA }
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
"#, "#,
&[ &[RuleError::new(&error_message("fragB"),
RuleError::new(&error_message("fragB"), &[ &[SourcePosition::new(35, 1, 34)])]);
SourcePosition::new(35, 1, 34)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_indirectly_within_inline_fragment() { fn no_spreading_itself_indirectly_within_inline_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Pet { fragment fragA on Pet {
... on Dog { ... on Dog {
...fragB ...fragB
@ -248,16 +247,14 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(74, 3, 14)])]);
SourcePosition::new(74, 3, 14)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_deeply() { fn no_spreading_itself_deeply() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragC } fragment fragB on Dog { ...fragC }
fragment fragC on Dog { ...fragO } fragment fragC on Dog { ...fragO }
@ -267,67 +264,53 @@ mod tests {
fragment fragO on Dog { ...fragP } fragment fragO on Dog { ...fragP }
fragment fragP on Dog { ...fragA, ...fragX } fragment fragP on Dog { ...fragA, ...fragX }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)]),
SourcePosition::new(35, 1, 34) RuleError::new(&error_message("fragO"),
]), &[SourcePosition::new(305, 7, 34)])]);
RuleError::new(&error_message("fragO"), &[
SourcePosition::new(305, 7, 34)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_deeply_two_paths() { fn no_spreading_itself_deeply_two_paths() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragC } fragment fragA on Dog { ...fragB, ...fragC }
fragment fragB on Dog { ...fragA } fragment fragB on Dog { ...fragA }
fragment fragC on Dog { ...fragA } fragment fragC on Dog { ...fragA }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)]),
SourcePosition::new(35, 1, 34) RuleError::new(&error_message("fragA"),
]), &[SourcePosition::new(45, 1, 44)])]);
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(45, 1, 44)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_deeply_two_paths_alt_traversal_order() { 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 fragA on Dog { ...fragC }
fragment fragB on Dog { ...fragC } fragment fragB on Dog { ...fragC }
fragment fragC on Dog { ...fragA, ...fragB } fragment fragC on Dog { ...fragA, ...fragB }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)]),
SourcePosition::new(35, 1, 34) RuleError::new(&error_message("fragC"),
]), &[SourcePosition::new(135, 3, 44)])]);
RuleError::new(&error_message("fragC"), &[
SourcePosition::new(135, 3, 44)
]),
]);
} }
#[test] #[test]
fn no_spreading_itself_deeply_and_immediately() { fn no_spreading_itself_deeply_and_immediately() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragB, ...fragC } fragment fragB on Dog { ...fragB, ...fragC }
fragment fragC on Dog { ...fragA, ...fragB } fragment fragC on Dog { ...fragA, ...fragB }
"#, "#,
&[ &[RuleError::new(&error_message("fragA"),
RuleError::new(&error_message("fragA"), &[ &[SourcePosition::new(35, 1, 34)]),
SourcePosition::new(35, 1, 34) RuleError::new(&error_message("fragB"),
]), &[SourcePosition::new(80, 2, 34)]),
RuleError::new(&error_message("fragB"), &[ RuleError::new(&error_message("fragB"),
SourcePosition::new(80, 2, 34) &[SourcePosition::new(90, 2, 44)])]);
]),
RuleError::new(&error_message("fragB"), &[
SourcePosition::new(90, 2, 44)
]),
]);
} }
} }

View file

@ -26,7 +26,11 @@ pub fn factory<'a>() -> NoUndefinedVariables<'a> {
} }
impl<'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) { if visited.contains(scope) {
return; return;
} }
@ -54,39 +58,50 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
for (op_name, &(ref pos, ref def_vars)) in &self.defined_variables { for (op_name, &(ref pos, ref def_vars)) in &self.defined_variables {
let mut unused = Vec::new(); let mut unused = Vec::new();
let mut visited = HashSet::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 ctx.append_errors(unused
.into_iter() .into_iter()
.map(|var| RuleError::new( .map(|var| {
&error_message(var.item, *op_name), RuleError::new(&error_message(var.item, *op_name),
&[ &[var.start.clone(), pos.clone()])
var.start.clone(), })
pos.clone()
]))
.collect()); .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); let op_name = op.item.name.as_ref().map(|s| s.item);
self.current_scope = Some(Scope::Operation(op_name)); self.current_scope = Some(Scope::Operation(op_name));
self.defined_variables.insert(op_name, (op.start.clone(), HashSet::new())); 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)); 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 { if let Some(ref scope) = self.current_scope {
self.spreads.entry(scope.clone()) self.spreads
.entry(scope.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(spread.item.name.item); .push(spread.item.name.item);
} }
} }
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(Scope::Operation(ref name)) = self.current_scope {
if let Some(&mut (_, ref mut vars)) = self.defined_variables.get_mut(name) { if let Some(&mut (_, ref mut vars)) = self.defined_variables.get_mut(name) {
vars.insert(var_name.item); vars.insert(var_name.item);
@ -94,18 +109,22 @@ 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 { if let Some(ref scope) = self.current_scope {
self.used_variables self.used_variables
.entry(scope.clone()) .entry(scope.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.append(&mut value.item .append(&mut value
.item
.referenced_variables() .referenced_variables()
.iter() .iter()
.map(|&var_name| Spanning::start_end( .map(|&var_name| {
&value.start.clone(), Spanning::start_end(&value.start.clone(),
&value.end.clone(), &value.end.clone(),
var_name)) var_name)
})
.collect()); .collect());
} }
} }
@ -114,8 +133,7 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn error_message(var_name: &str, op_name: Option<&str>) -> String { fn error_message(var_name: &str, op_name: Option<&str>) -> String {
if let Some(op_name) = op_name { 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 {
else {
format!(r#"Variable "${}" is not defined"#, var_name) format!(r#"Variable "${}" is not defined"#, var_name)
} }
} }
@ -129,7 +147,8 @@ mod tests {
#[test] #[test]
fn all_variables_defined() { fn all_variables_defined() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
field(a: $a, b: $b, c: $c) field(a: $a, b: $b, c: $c)
} }
@ -138,7 +157,8 @@ mod tests {
#[test] #[test]
fn all_variables_deeply_defined() { fn all_variables_deeply_defined() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
field(a: $a) { field(a: $a) {
field(b: $b) { field(b: $b) {
@ -151,7 +171,8 @@ mod tests {
#[test] #[test]
fn all_variables_deeply_defined_in_inline_fragments_defined() { 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) { query Foo($a: String, $b: String, $c: String) {
... on Type { ... on Type {
field(a: $a) { field(a: $a) {
@ -168,7 +189,8 @@ mod tests {
#[test] #[test]
fn all_variables_in_fragments_deeply_defined() { 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) { query Foo($a: String, $b: String, $c: String) {
...FragA ...FragA
} }
@ -190,7 +212,8 @@ mod tests {
#[test] #[test]
fn variable_within_single_fragment_defined_in_multiple_operations() { fn variable_within_single_fragment_defined_in_multiple_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragA ...FragA
} }
@ -205,7 +228,8 @@ mod tests {
#[test] #[test]
fn variable_within_fragments_defined_in_operations() { fn variable_within_fragments_defined_in_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragA ...FragA
} }
@ -223,7 +247,8 @@ mod tests {
#[test] #[test]
fn variable_within_recursive_fragment_defined() { fn variable_within_recursive_fragment_defined() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragA ...FragA
} }
@ -237,56 +262,50 @@ mod tests {
#[test] #[test]
fn variable_not_defined() { fn variable_not_defined() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
field(a: $a, b: $b, c: $c, d: $d) field(a: $a, b: $b, c: $c, d: $d)
} }
"#, "#,
&[ &[RuleError::new(&error_message("d", Some("Foo")),
RuleError::new(&error_message("d", Some("Foo")), &[ &[SourcePosition::new(101, 2, 42),
SourcePosition::new(101, 2, 42), SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn variable_not_defined_by_unnamed_query() { fn variable_not_defined_by_unnamed_query() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field(a: $a) field(a: $a)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", None),
RuleError::new(&error_message("a", None), &[ &[SourcePosition::new(34, 2, 21),
SourcePosition::new(34, 2, 21), SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn multiple_variables_not_defined() { fn multiple_variables_not_defined() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
field(a: $a, b: $b, c: $c) field(a: $a, b: $b, c: $c)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(56, 2, 21),
SourcePosition::new(56, 2, 21), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("c", Some("Foo")),
]), &[SourcePosition::new(70, 2, 35),
RuleError::new(&error_message("c", Some("Foo")), &[ SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(70, 2, 35),
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn variable_in_fragment_not_defined_by_unnamed_query() { fn variable_in_fragment_not_defined_by_unnamed_query() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
...FragA ...FragA
} }
@ -294,17 +313,15 @@ mod tests {
field(a: $a) field(a: $a)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", None),
RuleError::new(&error_message("a", None), &[ &[SourcePosition::new(102, 5, 21),
SourcePosition::new(102, 5, 21), SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn variable_in_fragment_not_defined_by_operation() { fn variable_in_fragment_not_defined_by_operation() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String, $b: String) { query Foo($a: String, $b: String) {
...FragA ...FragA
} }
@ -322,17 +339,15 @@ mod tests {
field(c: $c) field(c: $c)
} }
"#, "#,
&[ &[RuleError::new(&error_message("c", Some("Foo")),
RuleError::new(&error_message("c", Some("Foo")), &[ &[SourcePosition::new(358, 15, 21),
SourcePosition::new(358, 15, 21), SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn multiple_variables_in_fragments_not_defined() { fn multiple_variables_in_fragments_not_defined() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragA ...FragA
} }
@ -350,21 +365,18 @@ mod tests {
field(c: $c) field(c: $c)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(124, 5, 21),
SourcePosition::new(124, 5, 21), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("c", Some("Foo")),
]), &[SourcePosition::new(346, 15, 21),
RuleError::new(&error_message("c", Some("Foo")), &[ SourcePosition::new(11, 1, 10)])]);
SourcePosition::new(346, 15, 21),
SourcePosition::new(11, 1, 10),
]),
]);
} }
#[test] #[test]
fn single_variable_in_fragment_not_defined_by_multiple_operations() { fn single_variable_in_fragment_not_defined_by_multiple_operations() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragAB ...FragAB
} }
@ -375,21 +387,18 @@ mod tests {
field(a: $a, b: $b) field(a: $a, b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("b", Some("Foo")),
RuleError::new(&error_message("b", Some("Foo")), &[ &[SourcePosition::new(201, 8, 28),
SourcePosition::new(201, 8, 28), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("b", Some("Bar")),
]), &[SourcePosition::new(201, 8, 28),
RuleError::new(&error_message("b", Some("Bar")), &[ SourcePosition::new(79, 4, 10)])]);
SourcePosition::new(201, 8, 28),
SourcePosition::new(79, 4, 10),
]),
]);
} }
#[test] #[test]
fn variables_in_fragment_not_defined_by_multiple_operations() { fn variables_in_fragment_not_defined_by_multiple_operations() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragAB ...FragAB
} }
@ -400,21 +409,18 @@ mod tests {
field(a: $a, b: $b) field(a: $a, b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(194, 8, 21),
SourcePosition::new(194, 8, 21), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("b", Some("Bar")),
]), &[SourcePosition::new(201, 8, 28),
RuleError::new(&error_message("b", Some("Bar")), &[ SourcePosition::new(79, 4, 10)])]);
SourcePosition::new(201, 8, 28),
SourcePosition::new(79, 4, 10),
]),
]);
} }
#[test] #[test]
fn variable_in_fragment_used_by_other_operation() { fn variable_in_fragment_used_by_other_operation() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragA ...FragA
} }
@ -428,21 +434,18 @@ mod tests {
field(b: $b) field(b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(191, 8, 21),
SourcePosition::new(191, 8, 21), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("b", Some("Bar")),
]), &[SourcePosition::new(263, 11, 21),
RuleError::new(&error_message("b", Some("Bar")), &[ SourcePosition::new(78, 4, 10)])]);
SourcePosition::new(263, 11, 21),
SourcePosition::new(78, 4, 10),
]),
]);
} }
#[test] #[test]
fn multiple_undefined_variables_produce_multiple_errors() { fn multiple_undefined_variables_produce_multiple_errors() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragAB ...FragAB
} }
@ -458,31 +461,23 @@ mod tests {
field2(c: $c) field2(c: $c)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(195, 8, 22),
SourcePosition::new(195, 8, 22), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(11, 1, 10), RuleError::new(&error_message("b", Some("Bar")),
]), &[SourcePosition::new(202, 8, 29),
RuleError::new(&error_message("b", Some("Bar")), &[ SourcePosition::new(79, 4, 10)]),
SourcePosition::new(202, 8, 29), RuleError::new(&error_message("a", Some("Foo")),
SourcePosition::new(79, 4, 10), &[SourcePosition::new(249, 10, 22),
]), SourcePosition::new(11, 1, 10)]),
RuleError::new(&error_message("a", Some("Foo")), &[ RuleError::new(&error_message("b", Some("Bar")),
SourcePosition::new(249, 10, 22), &[SourcePosition::new(256, 10, 29),
SourcePosition::new(11, 1, 10), SourcePosition::new(79, 4, 10)]),
]), RuleError::new(&error_message("c", Some("Foo")),
RuleError::new(&error_message("b", Some("Bar")), &[ &[SourcePosition::new(329, 13, 22),
SourcePosition::new(256, 10, 29), SourcePosition::new(11, 1, 10)]),
SourcePosition::new(79, 4, 10), RuleError::new(&error_message("c", Some("Bar")),
]), &[SourcePosition::new(329, 13, 22),
RuleError::new(&error_message("c", Some("Foo")), &[ SourcePosition::new(79, 4, 10)])]);
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

@ -29,8 +29,7 @@ impl<'a> NoUnusedFragments<'a> {
if let Scope::Fragment(name) = *from { if let Scope::Fragment(name) = *from {
if result.contains(name) { if result.contains(name) {
return; return;
} } else {
else {
result.insert(name); result.insert(name);
} }
} }
@ -56,29 +55,32 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
for fragment in &self.defined_fragments { for fragment in &self.defined_fragments {
if !reachable.contains(&fragment.item) { if !reachable.contains(&fragment.item) {
ctx.report_error( ctx.report_error(&error_message(fragment.item), &[fragment.start.clone()]);
&error_message(fragment.item),
&[fragment.start.clone()]);
} }
} }
} }
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()); let op_name = op.item.name.as_ref().map(|s| s.item.as_ref());
self.current_scope = Some(Scope::Operation(op_name)); 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.current_scope = Some(Scope::Fragment(f.item.name.item));
self.defined_fragments.insert(Spanning::start_end( self.defined_fragments
&f.start, .insert(Spanning::start_end(&f.start, &f.end, f.item.name.item));
&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 { if let Some(ref scope) = self.current_scope {
self.spreads.entry(scope.clone()) self.spreads
.entry(scope.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(spread.item.name.item); .push(spread.item.name.item);
} }
@ -98,7 +100,8 @@ mod tests {
#[test] #[test]
fn all_fragment_names_are_used() { fn all_fragment_names_are_used() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
human(id: 4) { human(id: 4) {
...HumanFields1 ...HumanFields1
@ -122,7 +125,8 @@ mod tests {
#[test] #[test]
fn all_fragment_names_are_used_by_multiple_operations() { fn all_fragment_names_are_used_by_multiple_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
human(id: 4) { human(id: 4) {
...HumanFields1 ...HumanFields1
@ -148,7 +152,8 @@ mod tests {
#[test] #[test]
fn contains_unknown_fragments() { fn contains_unknown_fragments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo { query Foo {
human(id: 4) { human(id: 4) {
...HumanFields1 ...HumanFields1
@ -176,19 +181,16 @@ mod tests {
name name
} }
"#, "#,
&[ &[RuleError::new(&error_message("Unused1"),
RuleError::new(&error_message("Unused1"), &[ &[SourcePosition::new(465, 21, 10)]),
SourcePosition::new(465, 21, 10), RuleError::new(&error_message("Unused2"),
]), &[SourcePosition::new(532, 24, 10)])]);
RuleError::new(&error_message("Unused2"), &[
SourcePosition::new(532, 24, 10),
]),
]);
} }
#[test] #[test]
fn contains_unknown_fragments_with_ref_cycle() { fn contains_unknown_fragments_with_ref_cycle() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo { query Foo {
human(id: 4) { human(id: 4) {
...HumanFields1 ...HumanFields1
@ -218,19 +220,16 @@ mod tests {
...Unused1 ...Unused1
} }
"#, "#,
&[ &[RuleError::new(&error_message("Unused1"),
RuleError::new(&error_message("Unused1"), &[ &[SourcePosition::new(465, 21, 10)]),
SourcePosition::new(465, 21, 10), RuleError::new(&error_message("Unused2"),
]), &[SourcePosition::new(555, 25, 10)])]);
RuleError::new(&error_message("Unused2"), &[
SourcePosition::new(555, 25, 10),
]),
]);
} }
#[test] #[test]
fn contains_unknown_and_undef_fragments() { fn contains_unknown_and_undef_fragments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo { query Foo {
human(id: 4) { human(id: 4) {
...bar ...bar
@ -240,10 +239,7 @@ mod tests {
name name
} }
"#, "#,
&[ &[RuleError::new(&error_message("foo"),
RuleError::new(&error_message("foo"), &[ &[SourcePosition::new(107, 6, 10)])]);
SourcePosition::new(107, 6, 10),
]),
]);
} }
} }

View file

@ -26,7 +26,11 @@ pub fn factory<'a>() -> NoUnusedVariables<'a> {
} }
impl<'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) { if visited.contains(from) {
return; return;
} }
@ -54,8 +58,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
for (op_name, def_vars) in &self.defined_variables { for (op_name, def_vars) in &self.defined_variables {
let mut used = HashSet::new(); let mut used = HashSet::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
self.find_used_vars( self.find_used_vars(&Scope::Operation(*op_name),
&Scope::Operation(*op_name),
&def_vars.iter().map(|def| def.item).collect(), &def_vars.iter().map(|def| def.item).collect(),
&mut used, &mut used,
&mut visited); &mut visited);
@ -63,32 +66,42 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
ctx.append_errors(def_vars ctx.append_errors(def_vars
.iter() .iter()
.filter(|var| !used.contains(var.item)) .filter(|var| !used.contains(var.item))
.map(|var| RuleError::new( .map(|var| {
&error_message(var.item, *op_name), RuleError::new(&error_message(var.item, *op_name),
&[var.start.clone()])) &[var.start.clone()])
})
.collect()); .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); let op_name = op.item.name.as_ref().map(|s| s.item);
self.current_scope = Some(Scope::Operation(op_name)); self.current_scope = Some(Scope::Operation(op_name));
self.defined_variables.insert(op_name, HashSet::new()); 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)); 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 { if let Some(ref scope) = self.current_scope {
self.spreads.entry(scope.clone()) self.spreads
.entry(scope.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.push(spread.item.name.item); .push(spread.item.name.item);
} }
} }
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(Scope::Operation(ref name)) = self.current_scope {
if let Some(vars) = self.defined_variables.get_mut(name) { if let Some(vars) = self.defined_variables.get_mut(name) {
vars.insert(var_name); vars.insert(var_name);
@ -96,7 +109,9 @@ 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 { if let Some(ref scope) = self.current_scope {
self.used_variables self.used_variables
.entry(scope.clone()) .entry(scope.clone())
@ -109,8 +124,7 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn error_message(var_name: &str, op_name: Option<&str>) -> String { fn error_message(var_name: &str, op_name: Option<&str>) -> String {
if let Some(op_name) = op_name { 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 {
else {
format!(r#"Variable "${}" is not defined"#, var_name) format!(r#"Variable "${}" is not defined"#, var_name)
} }
} }
@ -124,7 +138,8 @@ mod tests {
#[test] #[test]
fn uses_all_variables() { fn uses_all_variables() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query ($a: String, $b: String, $c: String) { query ($a: String, $b: String, $c: String) {
field(a: $a, b: $b, c: $c) field(a: $a, b: $b, c: $c)
} }
@ -133,7 +148,8 @@ mod tests {
#[test] #[test]
fn uses_all_variables_deeply() { fn uses_all_variables_deeply() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
field(a: $a) { field(a: $a) {
field(b: $b) { field(b: $b) {
@ -146,7 +162,8 @@ mod tests {
#[test] #[test]
fn uses_all_variables_deeply_in_inline_fragments() { 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) { query Foo($a: String, $b: String, $c: String) {
... on Type { ... on Type {
field(a: $a) { field(a: $a) {
@ -163,7 +180,8 @@ mod tests {
#[test] #[test]
fn uses_all_variables_in_fragments() { fn uses_all_variables_in_fragments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
...FragA ...FragA
} }
@ -185,7 +203,8 @@ mod tests {
#[test] #[test]
fn variable_used_by_fragment_in_multiple_operations() { fn variable_used_by_fragment_in_multiple_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragA ...FragA
} }
@ -203,7 +222,8 @@ mod tests {
#[test] #[test]
fn variable_used_by_recursive_fragment() { fn variable_used_by_recursive_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String) { query Foo($a: String) {
...FragA ...FragA
} }
@ -217,38 +237,34 @@ mod tests {
#[test] #[test]
fn variable_not_used() { fn variable_not_used() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query ($a: String, $b: String, $c: String) { query ($a: String, $b: String, $c: String) {
field(a: $a, b: $b) field(a: $a, b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("c", None),
RuleError::new(&error_message("c", None), &[ &[SourcePosition::new(42, 1, 41)])]);
SourcePosition::new(42, 1, 41),
]),
]);
} }
#[test] #[test]
fn multiple_variables_not_used_1() { fn multiple_variables_not_used_1() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
field(b: $b) field(b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(21, 1, 20)]),
SourcePosition::new(21, 1, 20), 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] #[test]
fn variable_not_used_in_fragment() { fn variable_not_used_in_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
...FragA ...FragA
} }
@ -266,16 +282,14 @@ mod tests {
field field
} }
"#, "#,
&[ &[RuleError::new(&error_message("c", Some("Foo")),
RuleError::new(&error_message("c", Some("Foo")), &[ &[SourcePosition::new(45, 1, 44)])]);
SourcePosition::new(45, 1, 44),
]),
]);
} }
#[test] #[test]
fn multiple_variables_not_used_2() { fn multiple_variables_not_used_2() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
...FragA ...FragA
} }
@ -293,19 +307,16 @@ mod tests {
field field
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", Some("Foo")),
RuleError::new(&error_message("a", Some("Foo")), &[ &[SourcePosition::new(21, 1, 20)]),
SourcePosition::new(21, 1, 20), 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] #[test]
fn variable_not_used_by_unreferenced_fragment() { fn variable_not_used_by_unreferenced_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragA ...FragA
} }
@ -316,16 +327,14 @@ mod tests {
field(b: $b) field(b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("b", Some("Foo")),
RuleError::new(&error_message("b", Some("Foo")), &[ &[SourcePosition::new(21, 1, 20)])]);
SourcePosition::new(21, 1, 20),
]),
]);
} }
#[test] #[test]
fn variable_not_used_by_fragment_used_by_other_operation() { fn variable_not_used_by_fragment_used_by_other_operation() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($b: String) { query Foo($b: String) {
...FragA ...FragA
} }
@ -339,13 +348,9 @@ mod tests {
field(b: $b) field(b: $b)
} }
"#, "#,
&[ &[RuleError::new(&error_message("b", Some("Foo")),
RuleError::new(&error_message("b", Some("Foo")), &[ &[SourcePosition::new(21, 1, 20)]),
SourcePosition::new(21, 1, 20), RuleError::new(&error_message("a", Some("Bar")),
]), &[SourcePosition::new(88, 4, 20)])]);
RuleError::new(&error_message("a", Some("Bar")), &[
SourcePosition::new(88, 4, 20),
]),
]);
} }
} }

View file

@ -9,9 +9,7 @@ pub struct PossibleFragmentSpreads<'a> {
} }
pub fn factory<'a>() -> PossibleFragmentSpreads<'a> { pub fn factory<'a>() -> PossibleFragmentSpreads<'a> {
PossibleFragmentSpreads { PossibleFragmentSpreads { fragment_types: HashMap::new() }
fragment_types: HashMap::new(),
}
} }
impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> { impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
@ -25,14 +23,17 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
} }
} }
fn enter_inline_fragment(&mut self, ctx: &mut ValidatorContext<'a>, frag: &'a Spanning<InlineFragment>) { fn enter_inline_fragment(&mut self,
if let (Some(parent_type), Some(frag_type)) ctx: &mut ValidatorContext<'a>,
= (ctx.parent_type(), frag.item.type_condition.as_ref().and_then(|s| ctx.schema.concrete_type_by_name(s.item))) 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) { if !ctx.schema.type_overlap(parent_type, frag_type) {
ctx.report_error( ctx.report_error(&error_message(None,
&error_message(
None,
parent_type.name().unwrap_or("<unknown>"), parent_type.name().unwrap_or("<unknown>"),
frag_type.name().unwrap_or("<unknown>")), frag_type.name().unwrap_or("<unknown>")),
&[frag.start.clone()]); &[frag.start.clone()]);
@ -40,14 +41,13 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
} }
} }
fn enter_fragment_spread(&mut self, ctx: &mut ValidatorContext<'a>, spread: &'a Spanning<FragmentSpread>) { fn enter_fragment_spread(&mut self,
if let (Some(parent_type), Some(frag_type)) ctx: &mut ValidatorContext<'a>,
= (ctx.parent_type(), self.fragment_types.get(spread.item.name.item)) 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) { if !ctx.schema.type_overlap(parent_type, frag_type) {
ctx.report_error( ctx.report_error(&error_message(Some(spread.item.name.item),
&error_message(
Some(spread.item.name.item),
parent_type.name().unwrap_or("<unknown>"), parent_type.name().unwrap_or("<unknown>"),
frag_type.name().unwrap_or("<unknown>")), frag_type.name().unwrap_or("<unknown>")),
&[spread.start.clone()]); &[spread.start.clone()]);
@ -62,8 +62,7 @@ fn error_message(frag_name: Option<&str>, parent_type_name: &str, frag_type: &st
"Fragment \"{}\" cannot be spread here as objects of type \ "Fragment \"{}\" cannot be spread here as objects of type \
\"{}\" can never be of type \"{}\"", \"{}\" can never be of type \"{}\"",
frag_name, parent_type_name, frag_type) frag_name, parent_type_name, frag_type)
} } else {
else {
format!( format!(
"Fragment cannot be spread here as objects of type \"{}\" \ "Fragment cannot be spread here as objects of type \"{}\" \
can never be of type \"{}\"", can never be of type \"{}\"",
@ -80,7 +79,8 @@ mod tests {
#[test] #[test]
fn of_the_same_object() { fn of_the_same_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment objectWithinObject on Dog { ...dogFragment } fragment objectWithinObject on Dog { ...dogFragment }
fragment dogFragment on Dog { barkVolume } fragment dogFragment on Dog { barkVolume }
"#); "#);
@ -88,14 +88,16 @@ mod tests {
#[test] #[test]
fn of_the_same_object_with_inline_fragment() { 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 } } fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
"#); "#);
} }
#[test] #[test]
fn object_into_an_implemented_interface() { fn object_into_an_implemented_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment objectWithinInterface on Pet { ...dogFragment } fragment objectWithinInterface on Pet { ...dogFragment }
fragment dogFragment on Dog { barkVolume } fragment dogFragment on Dog { barkVolume }
"#); "#);
@ -103,7 +105,8 @@ mod tests {
#[test] #[test]
fn object_into_containing_union() { fn object_into_containing_union() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment objectWithinUnion on CatOrDog { ...dogFragment } fragment objectWithinUnion on CatOrDog { ...dogFragment }
fragment dogFragment on Dog { barkVolume } fragment dogFragment on Dog { barkVolume }
"#); "#);
@ -111,7 +114,8 @@ mod tests {
#[test] #[test]
fn union_into_contained_object() { fn union_into_contained_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment unionWithinObject on Dog { ...catOrDogFragment } fragment unionWithinObject on Dog { ...catOrDogFragment }
fragment catOrDogFragment on CatOrDog { __typename } fragment catOrDogFragment on CatOrDog { __typename }
"#); "#);
@ -119,7 +123,8 @@ mod tests {
#[test] #[test]
fn union_into_overlapping_interface() { fn union_into_overlapping_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment unionWithinInterface on Pet { ...catOrDogFragment } fragment unionWithinInterface on Pet { ...catOrDogFragment }
fragment catOrDogFragment on CatOrDog { __typename } fragment catOrDogFragment on CatOrDog { __typename }
"#); "#);
@ -127,7 +132,8 @@ mod tests {
#[test] #[test]
fn union_into_overlapping_union() { fn union_into_overlapping_union() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment } fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
fragment catOrDogFragment on CatOrDog { __typename } fragment catOrDogFragment on CatOrDog { __typename }
"#); "#);
@ -135,7 +141,8 @@ mod tests {
#[test] #[test]
fn interface_into_implemented_object() { fn interface_into_implemented_object() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment interfaceWithinObject on Dog { ...petFragment } fragment interfaceWithinObject on Dog { ...petFragment }
fragment petFragment on Pet { name } fragment petFragment on Pet { name }
"#); "#);
@ -143,7 +150,8 @@ mod tests {
#[test] #[test]
fn interface_into_overlapping_interface() { fn interface_into_overlapping_interface() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment interfaceWithinInterface on Pet { ...beingFragment } fragment interfaceWithinInterface on Pet { ...beingFragment }
fragment beingFragment on Being { name } fragment beingFragment on Being { name }
"#); "#);
@ -151,14 +159,16 @@ mod tests {
#[test] #[test]
fn interface_into_overlapping_interface_in_inline_fragment() { 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 } } fragment interfaceWithinInterface on Pet { ... on Being { name } }
"#); "#);
} }
#[test] #[test]
fn interface_into_overlapping_union() { fn interface_into_overlapping_union() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment interfaceWithinUnion on CatOrDog { ...petFragment } fragment interfaceWithinUnion on CatOrDog { ...petFragment }
fragment petFragment on Pet { name } fragment petFragment on Pet { name }
"#); "#);
@ -166,149 +176,141 @@ mod tests {
#[test] #[test]
fn different_object_into_object() { fn different_object_into_object() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidObjectWithinObject on Cat { ...dogFragment } fragment invalidObjectWithinObject on Cat { ...dogFragment }
fragment dogFragment on Dog { barkVolume } fragment dogFragment on Dog { barkVolume }
"#, "#,
&[ &[RuleError::new(&error_message(Some("dogFragment"), "Cat", "Dog"),
RuleError::new(&error_message(Some("dogFragment"), "Cat", "Dog"), &[ &[SourcePosition::new(55, 1, 54)])]);
SourcePosition::new(55, 1, 54),
]),
]);
} }
#[test] #[test]
fn different_object_into_object_in_inline_fragment() { fn different_object_into_object_in_inline_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidObjectWithinObjectAnon on Cat { fragment invalidObjectWithinObjectAnon on Cat {
... on Dog { barkVolume } ... on Dog { barkVolume }
} }
"#, "#,
&[ &[RuleError::new(&error_message(None, "Cat", "Dog"),
RuleError::new(&error_message(None, "Cat", "Dog"), &[ &[SourcePosition::new(71, 2, 12)])]);
SourcePosition::new(71, 2, 12),
]),
]);
} }
#[test] #[test]
fn object_into_not_implementing_interface() { fn object_into_not_implementing_interface() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidObjectWithinInterface on Pet { ...humanFragment } fragment invalidObjectWithinInterface on Pet { ...humanFragment }
fragment humanFragment on Human { pets { name } } fragment humanFragment on Human { pets { name } }
"#, "#,
&[ &[RuleError::new(&error_message(Some("humanFragment"), "Pet", "Human"),
RuleError::new(&error_message(Some("humanFragment"), "Pet", "Human"), &[ &[SourcePosition::new(58, 1, 57)])]);
SourcePosition::new(58, 1, 57),
]),
]);
} }
#[test] #[test]
fn object_into_not_containing_union() { fn object_into_not_containing_union() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
fragment humanFragment on Human { pets { name } } fragment humanFragment on Human { pets { name } }
"#, "#,
&[ &[RuleError::new(&error_message(Some("humanFragment"),
RuleError::new(&error_message(Some("humanFragment"), "CatOrDog", "Human"), &[ "CatOrDog",
SourcePosition::new(59, 1, 58), "Human"),
]), &[SourcePosition::new(59, 1, 58)])]);
]);
} }
#[test] #[test]
fn union_into_not_contained_object() { fn union_into_not_contained_object() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidUnionWithinObject on Human { ...catOrDogFragment } fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
fragment catOrDogFragment on CatOrDog { __typename } fragment catOrDogFragment on CatOrDog { __typename }
"#, "#,
&[ &[RuleError::new(&error_message(Some("catOrDogFragment"),
RuleError::new(&error_message(Some("catOrDogFragment"), "Human", "CatOrDog"), &[ "Human",
SourcePosition::new(56, 1, 55), "CatOrDog"),
]), &[SourcePosition::new(56, 1, 55)])]);
]);
} }
#[test] #[test]
fn union_into_non_overlapping_interface() { fn union_into_non_overlapping_interface() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
fragment humanOrAlienFragment on HumanOrAlien { __typename } fragment humanOrAlienFragment on HumanOrAlien { __typename }
"#, "#,
&[ &[RuleError::new(&error_message(Some("humanOrAlienFragment"),
RuleError::new(&error_message(Some("humanOrAlienFragment"), "Pet", "HumanOrAlien"), &[ "Pet",
SourcePosition::new(57, 1, 56), "HumanOrAlien"),
]), &[SourcePosition::new(57, 1, 56)])]);
]);
} }
#[test] #[test]
fn union_into_non_overlapping_union() { fn union_into_non_overlapping_union() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
fragment humanOrAlienFragment on HumanOrAlien { __typename } fragment humanOrAlienFragment on HumanOrAlien { __typename }
"#, "#,
&[ &[RuleError::new(&error_message(Some("humanOrAlienFragment"),
RuleError::new(&error_message(Some("humanOrAlienFragment"), "CatOrDog", "HumanOrAlien"), &[ "CatOrDog",
SourcePosition::new(58, 1, 57), "HumanOrAlien"),
]), &[SourcePosition::new(58, 1, 57)])]);
]);
} }
#[test] #[test]
fn interface_into_non_implementing_object() { fn interface_into_non_implementing_object() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
fragment intelligentFragment on Intelligent { iq } fragment intelligentFragment on Intelligent { iq }
"#, "#,
&[ &[RuleError::new(&error_message(Some("intelligentFragment"),
RuleError::new(&error_message(Some("intelligentFragment"), "Cat", "Intelligent"), &[ "Cat",
SourcePosition::new(58, 1, 57), "Intelligent"),
]), &[SourcePosition::new(58, 1, 57)])]);
]);
} }
#[test] #[test]
fn interface_into_non_overlapping_interface() { fn interface_into_non_overlapping_interface() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidInterfaceWithinInterface on Pet { fragment invalidInterfaceWithinInterface on Pet {
...intelligentFragment ...intelligentFragment
} }
fragment intelligentFragment on Intelligent { iq } fragment intelligentFragment on Intelligent { iq }
"#, "#,
&[ &[RuleError::new(&error_message(Some("intelligentFragment"),
RuleError::new(&error_message(Some("intelligentFragment"), "Pet", "Intelligent"), &[ "Pet",
SourcePosition::new(73, 2, 12), "Intelligent"),
]), &[SourcePosition::new(73, 2, 12)])]);
]);
} }
#[test] #[test]
fn interface_into_non_overlapping_interface_in_inline_fragment() { fn interface_into_non_overlapping_interface_in_inline_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidInterfaceWithinInterfaceAnon on Pet { fragment invalidInterfaceWithinInterfaceAnon on Pet {
...on Intelligent { iq } ...on Intelligent { iq }
} }
"#, "#,
&[ &[RuleError::new(&error_message(None, "Pet", "Intelligent"),
RuleError::new(&error_message(None, "Pet", "Intelligent"), &[ &[SourcePosition::new(77, 2, 12)])]);
SourcePosition::new(77, 2, 12),
]),
]);
} }
#[test] #[test]
fn interface_into_non_overlapping_union() { fn interface_into_non_overlapping_union() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
fragment petFragment on Pet { name } fragment petFragment on Pet { name }
"#, "#,
&[ &[RuleError::new(&error_message(Some("petFragment"),
RuleError::new(&error_message(Some("petFragment"), "HumanOrAlien", "Pet"), &[ "HumanOrAlien",
SourcePosition::new(66, 1, 65), "Pet"),
]), &[SourcePosition::new(66, 1, 65)])]);
]);
} }
} }

View file

@ -1,11 +1,10 @@
use ast::{Field, Directive}; use ast::{Field, Directive};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use parser::Spanning; use parser::Spanning;
use schema::meta::{Field as FieldType}; use schema::meta::Field as FieldType;
use schema::model::DirectiveType; use schema::model::DirectiveType;
pub struct ProvidedNonNullArguments { pub struct ProvidedNonNullArguments {}
}
pub fn factory() -> ProvidedNonNullArguments { pub fn factory() -> ProvidedNonNullArguments {
ProvidedNonNullArguments {} ProvidedNonNullArguments {}
@ -15,29 +14,43 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) {
let field_name = &field.item.name.item; 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 { for meta_arg in meta_args {
if meta_arg.arg_type.is_non_null() if meta_arg.arg_type.is_non_null() &&
&& field.item.arguments.as_ref().and_then(|args| args.item.get(&meta_arg.name)).is_none() field
{ .item
ctx.report_error( .arguments
&field_error_message(field_name, &meta_arg.name, &format!("{}", meta_arg.arg_type)), .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.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; 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 { for meta_arg in meta_args {
if meta_arg.arg_type.is_non_null() if meta_arg.arg_type.is_non_null() &&
&& directive.item.arguments.as_ref().and_then(|args| args.item.get(&meta_arg.name)).is_none() directive
{ .item
ctx.report_error( .arguments
&directive_error_message(directive_name, &meta_arg.name, &format!("{}", meta_arg.arg_type)), .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.start.clone()]);
} }
} }
@ -66,7 +79,8 @@ mod tests {
#[test] #[test]
fn ignores_unknown_arguments() { fn ignores_unknown_arguments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
isHousetrained(unknownArgument: true) isHousetrained(unknownArgument: true)
@ -77,7 +91,8 @@ mod tests {
#[test] #[test]
fn arg_on_optional_arg() { fn arg_on_optional_arg() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
isHousetrained(atOtherHomes: true) isHousetrained(atOtherHomes: true)
@ -88,7 +103,8 @@ mod tests {
#[test] #[test]
fn no_arg_on_optional_arg() { fn no_arg_on_optional_arg() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog { dog {
isHousetrained isHousetrained
@ -99,7 +115,8 @@ mod tests {
#[test] #[test]
fn multiple_args() { fn multiple_args() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req1: 1, req2: 2) multipleReqs(req1: 1, req2: 2)
@ -110,7 +127,8 @@ mod tests {
#[test] #[test]
fn multiple_args_reverse_order() { fn multiple_args_reverse_order() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req2: 2, req1: 1) multipleReqs(req2: 2, req1: 1)
@ -121,7 +139,8 @@ mod tests {
#[test] #[test]
fn no_args_on_multiple_optional() { fn no_args_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts multipleOpts
@ -132,7 +151,8 @@ mod tests {
#[test] #[test]
fn one_arg_on_multiple_optional() { fn one_arg_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts(opt1: 1) multipleOpts(opt1: 1)
@ -143,7 +163,8 @@ mod tests {
#[test] #[test]
fn second_arg_on_multiple_optional() { fn second_arg_on_multiple_optional() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOpts(opt2: 1) multipleOpts(opt2: 1)
@ -154,7 +175,8 @@ mod tests {
#[test] #[test]
fn muliple_reqs_on_mixed_list() { fn muliple_reqs_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4) multipleOptAndReq(req1: 3, req2: 4)
@ -165,7 +187,8 @@ mod tests {
#[test] #[test]
fn multiple_reqs_and_one_opt_on_mixed_list() { fn multiple_reqs_and_one_opt_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5) multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
@ -176,7 +199,8 @@ mod tests {
#[test] #[test]
fn all_reqs_on_opts_on_mixed_list() { fn all_reqs_on_opts_on_mixed_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6) multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
@ -187,58 +211,52 @@ mod tests {
#[test] #[test]
fn missing_one_non_nullable_argument() { fn missing_one_non_nullable_argument() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req2: 2) multipleReqs(req2: 2)
} }
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"),
RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"), &[ &[SourcePosition::new(63, 3, 16)])]);
SourcePosition::new(63, 3, 16),
]),
]);
} }
#[test] #[test]
fn missing_multiple_non_nullable_arguments() { fn missing_multiple_non_nullable_arguments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs multipleReqs
} }
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"),
RuleError::new(&field_error_message("multipleReqs", "req1", "Int!"), &[ &[SourcePosition::new(63, 3, 16)]),
SourcePosition::new(63, 3, 16), 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] #[test]
fn incorrect_value_and_missing_argument() { fn incorrect_value_and_missing_argument() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
complicatedArgs { complicatedArgs {
multipleReqs(req1: "one") multipleReqs(req1: "one")
} }
} }
"#, "#,
&[ &[RuleError::new(&field_error_message("multipleReqs", "req2", "Int!"),
RuleError::new(&field_error_message("multipleReqs", "req2", "Int!"), &[ &[SourcePosition::new(63, 3, 16)])]);
SourcePosition::new(63, 3, 16),
]),
]);
} }
#[test] #[test]
fn ignores_unknown_directives() { fn ignores_unknown_directives() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog @unknown dog @unknown
} }
@ -247,7 +265,8 @@ mod tests {
#[test] #[test]
fn with_directives_of_valid_types() { fn with_directives_of_valid_types() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
dog @include(if: true) { dog @include(if: true) {
name name
@ -261,20 +280,17 @@ mod tests {
#[test] #[test]
fn with_directive_with_missing_types() { fn with_directive_with_missing_types() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
dog @include { dog @include {
name @skip name @skip
} }
} }
"#, "#,
&[ &[RuleError::new(&directive_error_message("include", "if", "Boolean!"),
RuleError::new(&directive_error_message("include", "if", "Boolean!"), &[ &[SourcePosition::new(33, 2, 18)]),
SourcePosition::new(33, 2, 18), RuleError::new(&directive_error_message("skip", "if", "Boolean!"),
]), &[SourcePosition::new(65, 3, 21)])]);
RuleError::new(&directive_error_message("skip", "if", "Boolean!"), &[
SourcePosition::new(65, 3, 21),
]),
]);
} }
} }

View file

@ -12,7 +12,8 @@ impl<'a> Visitor<'a> for ScalarLeafs {
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) {
let field_name = &field.item.name.item; 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()) { let error = if let (Some(field_type), Some(field_type_literal)) =
(ctx.current_type(), ctx.current_type_literal()) {
match (field_type.is_leaf(), &field.item.selection_set) { match (field_type.is_leaf(), &field.item.selection_set) {
(true, &Some(_)) => Some(RuleError::new( (true, &Some(_)) => Some(RuleError::new(
&no_allowed_error_message(field_name, &format!("{}", field_type_literal)), &no_allowed_error_message(field_name, &format!("{}", field_type_literal)),
@ -22,7 +23,9 @@ impl<'a> Visitor<'a> for ScalarLeafs {
&[field.start.clone()])), &[field.start.clone()])),
_ => None, _ => None,
} }
} else { None }; } else {
None
};
if let Some(error) = error { if let Some(error) = error {
ctx.append_errors(vec![error]); ctx.append_errors(vec![error]);
@ -51,7 +54,8 @@ mod tests {
#[test] #[test]
fn valid_scalar_selection() { fn valid_scalar_selection() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment scalarSelection on Dog { fragment scalarSelection on Dog {
barks barks
} }
@ -60,35 +64,32 @@ mod tests {
#[test] #[test]
fn object_type_missing_selection() { fn object_type_missing_selection() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query directQueryOnObjectWithoutSubFields { query directQueryOnObjectWithoutSubFields {
human human
} }
"#, "#,
&[ &[RuleError::new(&required_error_message("human", "Human"),
RuleError::new(&required_error_message("human", "Human"), &[ &[SourcePosition::new(67, 2, 12)])]);
SourcePosition::new(67, 2, 12),
]),
]);
} }
#[test] #[test]
fn interface_type_missing_selection() { fn interface_type_missing_selection() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
human { pets } human { pets }
} }
"#, "#,
&[ &[RuleError::new(&required_error_message("pets", "[Pet]"),
RuleError::new(&required_error_message("pets", "[Pet]"), &[ &[SourcePosition::new(33, 2, 20)])]);
SourcePosition::new(33, 2, 20),
]),
]);
} }
#[test] #[test]
fn valid_scalar_selection_with_args() { fn valid_scalar_selection_with_args() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment scalarSelectionWithArgs on Dog { fragment scalarSelectionWithArgs on Dog {
doesKnowCommand(dogCommand: SIT) doesKnowCommand(dogCommand: SIT)
} }
@ -97,72 +98,64 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_on_boolean() { fn scalar_selection_not_allowed_on_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarSelectionsNotAllowedOnBoolean on Dog { fragment scalarSelectionsNotAllowedOnBoolean on Dog {
barks { sinceWhen } barks { sinceWhen }
} }
"#, "#,
&[ &[RuleError::new(&no_allowed_error_message("barks", "Boolean"),
RuleError::new(&no_allowed_error_message("barks", "Boolean"), &[ &[SourcePosition::new(77, 2, 12)])]);
SourcePosition::new(77, 2, 12),
]),
]);
} }
#[test] #[test]
fn scalar_selection_not_allowed_on_enum() { fn scalar_selection_not_allowed_on_enum() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarSelectionsNotAllowedOnEnum on Cat { fragment scalarSelectionsNotAllowedOnEnum on Cat {
furColor { inHexdec } furColor { inHexdec }
} }
"#, "#,
&[ &[RuleError::new(&no_allowed_error_message("furColor", "FurColor"),
RuleError::new(&no_allowed_error_message("furColor", "FurColor"), &[ &[SourcePosition::new(74, 2, 12)])]);
SourcePosition::new(74, 2, 12),
]),
]);
} }
#[test] #[test]
fn scalar_selection_not_allowed_with_args() { fn scalar_selection_not_allowed_with_args() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarSelectionsNotAllowedWithArgs on Dog { fragment scalarSelectionsNotAllowedWithArgs on Dog {
doesKnowCommand(dogCommand: SIT) { sinceWhen } doesKnowCommand(dogCommand: SIT) { sinceWhen }
} }
"#, "#,
&[ &[RuleError::new(&no_allowed_error_message("doesKnowCommand",
RuleError::new(&no_allowed_error_message("doesKnowCommand", "Boolean"), &[ "Boolean"),
SourcePosition::new(76, 2, 12), &[SourcePosition::new(76, 2, 12)])]);
]),
]);
} }
#[test] #[test]
fn scalar_selection_not_allowed_with_directives() { fn scalar_selection_not_allowed_with_directives() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarSelectionsNotAllowedWithDirectives on Dog { fragment scalarSelectionsNotAllowedWithDirectives on Dog {
name @include(if: true) { isAlsoHumanName } name @include(if: true) { isAlsoHumanName }
} }
"#, "#,
&[ &[RuleError::new(&no_allowed_error_message("name", "String"),
RuleError::new(&no_allowed_error_message("name", "String"), &[ &[SourcePosition::new(82, 2, 12)])]);
SourcePosition::new(82, 2, 12),
]),
]);
} }
#[test] #[test]
fn scalar_selection_not_allowed_with_directives_and_args() { fn scalar_selection_not_allowed_with_directives_and_args() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen } doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
} }
"#, "#,
&[ &[RuleError::new(&no_allowed_error_message("doesKnowCommand",
RuleError::new(&no_allowed_error_message("doesKnowCommand", "Boolean"), &[ "Boolean"),
SourcePosition::new(89, 2, 12), &[SourcePosition::new(89, 2, 12)])]);
]),
]);
} }
} }

View file

@ -9,9 +9,7 @@ pub struct UniqueArgumentNames<'a> {
} }
pub fn factory<'a>() -> UniqueArgumentNames<'a> { pub fn factory<'a>() -> UniqueArgumentNames<'a> {
UniqueArgumentNames { UniqueArgumentNames { known_names: HashMap::new() }
known_names: HashMap::new(),
}
} }
impl<'a> Visitor<'a> for UniqueArgumentNames<'a> { impl<'a> Visitor<'a> for UniqueArgumentNames<'a> {
@ -23,11 +21,12 @@ impl<'a> Visitor<'a> for UniqueArgumentNames<'a> {
self.known_names = HashMap::new(); 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) { match self.known_names.entry(arg_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
ctx.report_error( ctx.report_error(&error_message(arg_name.item),
&error_message(arg_name.item),
&[e.get().clone(), arg_name.start.clone()]); &[e.get().clone(), arg_name.start.clone()]);
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -50,7 +49,8 @@ mod tests {
#[test] #[test]
fn no_arguments_on_field() { fn no_arguments_on_field() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field field
} }
@ -59,7 +59,8 @@ mod tests {
#[test] #[test]
fn no_arguments_on_directive() { fn no_arguments_on_directive() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field @directive field @directive
} }
@ -68,7 +69,8 @@ mod tests {
#[test] #[test]
fn argument_on_field() { fn argument_on_field() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg: "value") field(arg: "value")
} }
@ -77,7 +79,8 @@ mod tests {
#[test] #[test]
fn argument_on_directive() { fn argument_on_directive() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field @directive(arg: "value") field @directive(arg: "value")
} }
@ -86,7 +89,8 @@ mod tests {
#[test] #[test]
fn same_argument_on_two_fields() { fn same_argument_on_two_fields() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
one: field(arg: "value") one: field(arg: "value")
two: field(arg: "value") two: field(arg: "value")
@ -96,7 +100,8 @@ mod tests {
#[test] #[test]
fn same_argument_on_field_and_directive() { fn same_argument_on_field_and_directive() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg: "value") @directive(arg: "value") field(arg: "value") @directive(arg: "value")
} }
@ -105,7 +110,8 @@ mod tests {
#[test] #[test]
fn same_argument_on_two_directives() { fn same_argument_on_two_directives() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field @directive1(arg: "value") @directive2(arg: "value") field @directive1(arg: "value") @directive2(arg: "value")
} }
@ -114,7 +120,8 @@ mod tests {
#[test] #[test]
fn multiple_field_arguments() { fn multiple_field_arguments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg1: "value", arg2: "value", arg3: "value") field(arg1: "value", arg2: "value", arg3: "value")
} }
@ -123,7 +130,8 @@ mod tests {
#[test] #[test]
fn multiple_directive_arguments() { fn multiple_directive_arguments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field @directive(arg1: "value", arg2: "value", arg3: "value") field @directive(arg1: "value", arg2: "value", arg3: "value")
} }
@ -132,70 +140,60 @@ mod tests {
#[test] #[test]
fn duplicate_field_arguments() { fn duplicate_field_arguments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field(arg1: "value", arg1: "value") field(arg1: "value", arg1: "value")
} }
"#, "#,
&[ &[RuleError::new(&error_message("arg1"),
RuleError::new(&error_message("arg1"), &[ &[SourcePosition::new(31, 2, 18),
SourcePosition::new(31, 2, 18), SourcePosition::new(46, 2, 33)])]);
SourcePosition::new(46, 2, 33),
]),
]);
} }
#[test] #[test]
fn many_duplicate_field_arguments() { fn many_duplicate_field_arguments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field(arg1: "value", arg1: "value", arg1: "value") field(arg1: "value", arg1: "value", arg1: "value")
} }
"#, "#,
&[ &[RuleError::new(&error_message("arg1"),
RuleError::new(&error_message("arg1"), &[ &[SourcePosition::new(31, 2, 18),
SourcePosition::new(31, 2, 18), SourcePosition::new(46, 2, 33)]),
SourcePosition::new(46, 2, 33), RuleError::new(&error_message("arg1"),
]), &[SourcePosition::new(31, 2, 18),
RuleError::new(&error_message("arg1"), &[ SourcePosition::new(61, 2, 48)])]);
SourcePosition::new(31, 2, 18),
SourcePosition::new(61, 2, 48),
]),
]);
} }
#[test] #[test]
fn duplicate_directive_arguments() { fn duplicate_directive_arguments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field @directive(arg1: "value", arg1: "value") field @directive(arg1: "value", arg1: "value")
} }
"#, "#,
&[ &[RuleError::new(&error_message("arg1"),
RuleError::new(&error_message("arg1"), &[ &[SourcePosition::new(42, 2, 29),
SourcePosition::new(42, 2, 29), SourcePosition::new(57, 2, 44)])]);
SourcePosition::new(57, 2, 44),
]),
]);
} }
#[test] #[test]
fn many_duplicate_directive_arguments() { fn many_duplicate_directive_arguments() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field @directive(arg1: "value", arg1: "value", arg1: "value") field @directive(arg1: "value", arg1: "value", arg1: "value")
} }
"#, "#,
&[ &[RuleError::new(&error_message("arg1"),
RuleError::new(&error_message("arg1"), &[ &[SourcePosition::new(42, 2, 29),
SourcePosition::new(42, 2, 29), SourcePosition::new(57, 2, 44)]),
SourcePosition::new(57, 2, 44), RuleError::new(&error_message("arg1"),
]), &[SourcePosition::new(42, 2, 29),
RuleError::new(&error_message("arg1"), &[ SourcePosition::new(72, 2, 59)])]);
SourcePosition::new(42, 2, 29),
SourcePosition::new(72, 2, 59),
]),
]);
} }
} }

View file

@ -9,17 +9,16 @@ pub struct UniqueFragmentNames<'a> {
} }
pub fn factory<'a>() -> UniqueFragmentNames<'a> { pub fn factory<'a>() -> UniqueFragmentNames<'a> {
UniqueFragmentNames { UniqueFragmentNames { names: HashMap::new() }
names: HashMap::new(),
}
} }
impl<'a> Visitor<'a> for UniqueFragmentNames<'a> { 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) { match self.names.entry(f.item.name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
context.report_error( context.report_error(&duplicate_message(f.item.name.item),
&duplicate_message(f.item.name.item),
&[e.get().clone(), f.item.name.start.clone()]); &[e.get().clone(), f.item.name.start.clone()]);
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -42,7 +41,8 @@ mod tests {
#[test] #[test]
fn no_fragments() { fn no_fragments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field field
} }
@ -51,7 +51,8 @@ mod tests {
#[test] #[test]
fn one_fragment() { fn one_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
...fragA ...fragA
} }
@ -64,7 +65,8 @@ mod tests {
#[test] #[test]
fn many_fragments() { fn many_fragments() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
...fragA ...fragA
...fragB ...fragB
@ -84,7 +86,8 @@ mod tests {
#[test] #[test]
fn inline_fragments_always_unique() { fn inline_fragments_always_unique() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
...on Type { ...on Type {
fieldA fieldA
@ -98,7 +101,8 @@ mod tests {
#[test] #[test]
fn fragment_and_operation_named_the_same() { fn fragment_and_operation_named_the_same() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
...Foo ...Foo
} }
@ -110,7 +114,8 @@ mod tests {
#[test] #[test]
fn fragments_named_the_same() { fn fragments_named_the_same() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
...fragA ...fragA
} }
@ -121,17 +126,15 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(&duplicate_message("fragA"),
RuleError::new(&duplicate_message("fragA"), &[ &[SourcePosition::new(65, 4, 19),
SourcePosition::new(65, 4, 19), SourcePosition::new(131, 7, 19)])]);
SourcePosition::new(131, 7, 19)
]),
]);
} }
#[test] #[test]
fn fragments_named_the_same_no_reference() { fn fragments_named_the_same_no_reference() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment fragA on Type { fragment fragA on Type {
fieldA fieldA
} }
@ -139,11 +142,8 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(&duplicate_message("fragA"),
RuleError::new(&duplicate_message("fragA"), &[ &[SourcePosition::new(20, 1, 19),
SourcePosition::new(20, 1, 19), SourcePosition::new(86, 4, 19)])]);
SourcePosition::new(86, 4, 19)
]),
]);
} }
} }

View file

@ -9,26 +9,29 @@ pub struct UniqueInputFieldNames<'a> {
} }
pub fn factory<'a>() -> UniqueInputFieldNames<'a> { pub fn factory<'a>() -> UniqueInputFieldNames<'a> {
UniqueInputFieldNames { UniqueInputFieldNames { known_name_stack: Vec::new() }
known_name_stack: Vec::new(),
}
} }
impl<'a> Visitor<'a> for UniqueInputFieldNames<'a> { 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()); 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(); 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() { if let Some(ref mut known_names) = self.known_name_stack.last_mut() {
match known_names.entry(&field_name.item) { match known_names.entry(&field_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
ctx.report_error( ctx.report_error(&error_message(&field_name.item),
&error_message(&field_name.item),
&[e.get().clone(), field_name.start.clone()]); &[e.get().clone(), field_name.start.clone()]);
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -52,7 +55,8 @@ mod tests {
#[test] #[test]
fn input_object_with_fields() { fn input_object_with_fields() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg: { f: true }) field(arg: { f: true })
} }
@ -61,7 +65,8 @@ mod tests {
#[test] #[test]
fn same_input_object_within_two_args() { fn same_input_object_within_two_args() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg1: { f: true }, arg2: { f: true }) field(arg1: { f: true }, arg2: { f: true })
} }
@ -70,7 +75,8 @@ mod tests {
#[test] #[test]
fn multiple_input_object_fields() { fn multiple_input_object_fields() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg: { f1: "value", f2: "value", f3: "value" }) field(arg: { f1: "value", f2: "value", f3: "value" })
} }
@ -79,7 +85,8 @@ mod tests {
#[test] #[test]
fn allows_for_nested_input_objects_with_similar_fields() { fn allows_for_nested_input_objects_with_similar_fields() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field(arg: { field(arg: {
deep: { deep: {
@ -96,36 +103,31 @@ mod tests {
#[test] #[test]
fn duplicate_input_object_fields() { fn duplicate_input_object_fields() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field(arg: { f1: "value", f1: "value" }) field(arg: { f1: "value", f1: "value" })
} }
"#, "#,
&[ &[RuleError::new(&error_message("f1"),
RuleError::new(&error_message("f1"), &[ &[SourcePosition::new(38, 2, 25),
SourcePosition::new(38, 2, 25), SourcePosition::new(51, 2, 38)])]);
SourcePosition::new(51, 2, 38),
]),
]);
} }
#[test] #[test]
fn many_duplicate_input_object_fields() { fn many_duplicate_input_object_fields() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
{ {
field(arg: { f1: "value", f1: "value", f1: "value" }) field(arg: { f1: "value", f1: "value", f1: "value" })
} }
"#, "#,
&[ &[RuleError::new(&error_message("f1"),
RuleError::new(&error_message("f1"), &[ &[SourcePosition::new(38, 2, 25),
SourcePosition::new(38, 2, 25), SourcePosition::new(51, 2, 38)]),
SourcePosition::new(51, 2, 38), RuleError::new(&error_message("f1"),
]), &[SourcePosition::new(38, 2, 25),
RuleError::new(&error_message("f1"), &[ SourcePosition::new(64, 2, 51)])]);
SourcePosition::new(38, 2, 25),
SourcePosition::new(64, 2, 51),
]),
]);
} }
} }

View file

@ -9,18 +9,17 @@ pub struct UniqueOperationNames<'a> {
} }
pub fn factory<'a>() -> UniqueOperationNames<'a> { pub fn factory<'a>() -> UniqueOperationNames<'a> {
UniqueOperationNames { UniqueOperationNames { names: HashMap::new() }
names: HashMap::new(),
}
} }
impl<'a> Visitor<'a> for UniqueOperationNames<'a> { 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 { if let Some(ref op_name) = op.item.name {
match self.names.entry(op_name.item) { match self.names.entry(op_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
ctx.report_error( ctx.report_error(&error_message(op_name.item),
&error_message(op_name.item),
&[e.get().clone(), op.start.clone()]); &[e.get().clone(), op.start.clone()]);
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -44,7 +43,8 @@ mod tests {
#[test] #[test]
fn no_operations() { fn no_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment fragA on Type { fragment fragA on Type {
field field
} }
@ -53,7 +53,8 @@ mod tests {
#[test] #[test]
fn one_anon_operation() { fn one_anon_operation() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
{ {
field field
} }
@ -62,7 +63,8 @@ mod tests {
#[test] #[test]
fn one_named_operation() { fn one_named_operation() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
field field
} }
@ -71,7 +73,8 @@ mod tests {
#[test] #[test]
fn multiple_operations() { fn multiple_operations() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
field field
} }
@ -84,7 +87,8 @@ mod tests {
#[test] #[test]
fn multiple_operations_of_different_types() { fn multiple_operations_of_different_types() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
field field
} }
@ -97,7 +101,8 @@ mod tests {
#[test] #[test]
fn fragment_and_operation_named_the_same() { fn fragment_and_operation_named_the_same() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo { query Foo {
...Foo ...Foo
} }
@ -109,7 +114,8 @@ mod tests {
#[test] #[test]
fn multiple_operations_of_same_name() { fn multiple_operations_of_same_name() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo { query Foo {
fieldA fieldA
} }
@ -117,17 +123,15 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(&error_message("Foo"),
RuleError::new(&error_message("Foo"), &[ &[SourcePosition::new(11, 1, 10),
SourcePosition::new(11, 1, 10), SourcePosition::new(64, 4, 10)])]);
SourcePosition::new(64, 4, 10),
]),
]);
} }
#[test] #[test]
fn multiple_ops_of_same_name_of_different_types() { fn multiple_ops_of_same_name_of_different_types() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo { query Foo {
fieldA fieldA
} }
@ -135,11 +139,8 @@ mod tests {
fieldB fieldB
} }
"#, "#,
&[ &[RuleError::new(&error_message("Foo"),
RuleError::new(&error_message("Foo"), &[ &[SourcePosition::new(11, 1, 10),
SourcePosition::new(11, 1, 10), SourcePosition::new(64, 4, 10)])]);
SourcePosition::new(64, 4, 10),
]),
]);
} }
} }

View file

@ -9,21 +9,22 @@ pub struct UniqueVariableNames<'a> {
} }
pub fn factory<'a>() -> UniqueVariableNames<'a> { pub fn factory<'a>() -> UniqueVariableNames<'a> {
UniqueVariableNames { UniqueVariableNames { names: HashMap::new() }
names: HashMap::new(),
}
} }
impl<'a> Visitor<'a> for UniqueVariableNames<'a> { 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(); 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) { match self.names.entry(var_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
ctx.report_error( ctx.report_error(&error_message(var_name.item),
&error_message(var_name.item),
&[e.get().clone(), var_name.start.clone()]); &[e.get().clone(), var_name.start.clone()]);
} }
Entry::Vacant(e) => { Entry::Vacant(e) => {
@ -46,7 +47,8 @@ mod tests {
#[test] #[test]
fn unique_variable_names() { fn unique_variable_names() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query A($x: Int, $y: String) { __typename } query A($x: Int, $y: String) { __typename }
query B($x: String, $y: Int) { __typename } query B($x: String, $y: Int) { __typename }
"#); "#);
@ -54,28 +56,23 @@ mod tests {
#[test] #[test]
fn duplicate_variable_names() { fn duplicate_variable_names() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query A($x: Int, $x: Int, $x: String) { __typename } query A($x: Int, $x: Int, $x: String) { __typename }
query B($x: String, $x: Int) { __typename } query B($x: String, $x: Int) { __typename }
query C($x: Int, $x: Int) { __typename } query C($x: Int, $x: Int) { __typename }
"#, "#,
&[ &[RuleError::new(&error_message("x"),
RuleError::new(&error_message("x"), &[ &[SourcePosition::new(19, 1, 18),
SourcePosition::new(19, 1, 18), SourcePosition::new(28, 1, 27)]),
SourcePosition::new(28, 1, 27), RuleError::new(&error_message("x"),
]), &[SourcePosition::new(19, 1, 18),
RuleError::new(&error_message("x"), &[ SourcePosition::new(37, 1, 36)]),
SourcePosition::new(19, 1, 18), RuleError::new(&error_message("x"),
SourcePosition::new(37, 1, 36), &[SourcePosition::new(82, 2, 18),
]), SourcePosition::new(94, 2, 30)]),
RuleError::new(&error_message("x"), &[ RuleError::new(&error_message("x"),
SourcePosition::new(82, 2, 18), &[SourcePosition::new(136, 3, 18),
SourcePosition::new(94, 2, 30), SourcePosition::new(145, 3, 27)])]);
]),
RuleError::new(&error_message("x"), &[
SourcePosition::new(136, 3, 18),
SourcePosition::new(145, 3, 27),
]),
]);
} }
} }

View file

@ -9,11 +9,15 @@ pub fn factory() -> UniqueVariableNames {
} }
impl<'a> Visitor<'a> for 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,
if let Some(var_type) = ctx.schema.concrete_type_by_name(var_def.var_type.item.innermost_name()) { 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()) {
if !var_type.is_input() { if !var_type.is_input() {
ctx.report_error( ctx.report_error(&error_message(var_name.item,
&error_message(var_name.item, &format!("{}", var_def.var_type.item)), &format!("{}", var_def.var_type.item)),
&[var_def.var_type.start.clone()]); &[var_def.var_type.start.clone()]);
} }
} }
@ -33,7 +37,8 @@ mod tests {
#[test] #[test]
fn input_types_are_valid() { fn input_types_are_valid() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
field(a: $a, b: $b, c: $c) field(a: $a, b: $b, c: $c)
} }
@ -42,21 +47,17 @@ mod tests {
#[test] #[test]
fn output_types_are_invalid() { fn output_types_are_invalid() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {
field(a: $a, b: $b, c: $c) field(a: $a, b: $b, c: $c)
} }
"#, "#,
&[ &[RuleError::new(&error_message("a", "Dog"),
RuleError::new(&error_message("a", "Dog"), &[ &[SourcePosition::new(25, 1, 24)]),
SourcePosition::new(25, 1, 24), RuleError::new(&error_message("b", "[[CatOrDog!]]!"),
]), &[SourcePosition::new(34, 1, 33)]),
RuleError::new(&error_message("b", "[[CatOrDog!]]!"), &[ RuleError::new(&error_message("c", "Pet"),
SourcePosition::new(34, 1, 33), &[SourcePosition::new(54, 1, 53)])]);
]),
RuleError::new(&error_message("c", "Pet"), &[
SourcePosition::new(54, 1, 53),
]),
]);
} }
} }

View file

@ -27,14 +27,11 @@ pub fn factory<'a>() -> VariableInAllowedPosition<'a> {
} }
impl<'a> VariableInAllowedPosition<'a> { impl<'a> VariableInAllowedPosition<'a> {
fn collect_incorrect_usages( fn collect_incorrect_usages(&self,
&self,
from: &Scope<'a>, from: &Scope<'a>,
var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition)>, var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition)>,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a>,
visited: &mut HashSet<Scope<'a>>, visited: &mut HashSet<Scope<'a>>) {
)
{
if visited.contains(from) { if visited.contains(from) {
return; return;
} }
@ -43,10 +40,10 @@ impl<'a> VariableInAllowedPosition<'a> {
if let Some(usages) = self.variable_usages.get(from) { if let Some(usages) = self.variable_usages.get(from) {
for &(ref var_name, ref var_type) in usages { for &(ref var_name, ref var_type) in usages {
if let Some(&&(ref var_def_name, ref var_def)) = var_defs if let Some(&&(ref var_def_name, ref var_def)) =
var_defs
.iter() .iter()
.find(|&&&(ref n, _)| &n.item == var_name.item) .find(|&&&(ref n, _)| &n.item == var_name.item) {
{
let expected_type = match (&var_def.default_value, &var_def.var_type.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::List(ref inner)) => Type::NonNullList(inner.clone()),
(&Some(_), &Type::Named(inner)) => Type::NonNullNamed(inner), (&Some(_), &Type::Named(inner)) => Type::NonNullNamed(inner),
@ -54,8 +51,9 @@ impl<'a> VariableInAllowedPosition<'a> {
}; };
if !ctx.schema.is_subtype(&expected_type, var_type) { if !ctx.schema.is_subtype(&expected_type, var_type) {
ctx.report_error( ctx.report_error(&error_message(var_name.item,
&error_message(var_name.item, &format!("{}", expected_type), &format!("{}", var_type)), &format!("{}", expected_type),
&format!("{}", var_type)),
&[var_def_name.start.clone(), var_name.start.clone()]); &[var_def_name.start.clone(), var_name.start.clone()]);
} }
} }
@ -77,15 +75,21 @@ 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)); 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))); 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 { if let Some(ref scope) = self.current_scope {
self.spreads self.spreads
.entry(scope.clone()) .entry(scope.clone())
@ -94,7 +98,9 @@ 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 { if let Some(ref scope) = self.current_scope {
self.variable_defs self.variable_defs
.entry(scope.clone()) .entry(scope.clone())
@ -103,12 +109,16 @@ 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,
if let (&Some(ref scope), Some(input_type)) = (&self.current_scope, ctx.current_input_type_literal()) { 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.variable_usages self.variable_usages
.entry(scope.clone()) .entry(scope.clone())
.or_insert_with(Vec::new) .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()));
} }
} }
} }
@ -128,7 +138,8 @@ mod tests {
#[test] #[test]
fn boolean_into_boolean() { fn boolean_into_boolean() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($booleanArg: Boolean) query Query($booleanArg: Boolean)
{ {
complicatedArgs { complicatedArgs {
@ -140,7 +151,8 @@ mod tests {
#[test] #[test]
fn boolean_into_boolean_within_fragment() { fn boolean_into_boolean_within_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment booleanArgFrag on ComplicatedArgs { fragment booleanArgFrag on ComplicatedArgs {
booleanArgField(booleanArg: $booleanArg) booleanArgField(booleanArg: $booleanArg)
} }
@ -152,7 +164,8 @@ mod tests {
} }
"#); "#);
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($booleanArg: Boolean) query Query($booleanArg: Boolean)
{ {
complicatedArgs { complicatedArgs {
@ -167,7 +180,8 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_boolean() { fn non_null_boolean_into_boolean() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($nonNullBooleanArg: Boolean!) query Query($nonNullBooleanArg: Boolean!)
{ {
complicatedArgs { complicatedArgs {
@ -179,7 +193,8 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_boolean_within_fragment() { fn non_null_boolean_into_boolean_within_fragment() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
fragment booleanArgFrag on ComplicatedArgs { fragment booleanArgFrag on ComplicatedArgs {
booleanArgField(booleanArg: $nonNullBooleanArg) booleanArgField(booleanArg: $nonNullBooleanArg)
} }
@ -195,7 +210,8 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int_with_default() { fn int_into_non_null_int_with_default() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($intArg: Int = 1) query Query($intArg: Int = 1)
{ {
complicatedArgs { complicatedArgs {
@ -207,7 +223,8 @@ mod tests {
#[test] #[test]
fn string_list_into_string_list() { fn string_list_into_string_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($stringListVar: [String]) query Query($stringListVar: [String])
{ {
complicatedArgs { complicatedArgs {
@ -219,7 +236,8 @@ mod tests {
#[test] #[test]
fn non_null_string_list_into_string_list() { fn non_null_string_list_into_string_list() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($stringListVar: [String!]) query Query($stringListVar: [String!])
{ {
complicatedArgs { complicatedArgs {
@ -231,7 +249,8 @@ mod tests {
#[test] #[test]
fn string_into_string_list_in_item_position() { fn string_into_string_list_in_item_position() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($stringVar: String) query Query($stringVar: String)
{ {
complicatedArgs { complicatedArgs {
@ -243,7 +262,8 @@ mod tests {
#[test] #[test]
fn non_null_string_into_string_list_in_item_position() { fn non_null_string_into_string_list_in_item_position() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($stringVar: String!) query Query($stringVar: String!)
{ {
complicatedArgs { complicatedArgs {
@ -255,7 +275,8 @@ mod tests {
#[test] #[test]
fn complex_input_into_complex_input() { fn complex_input_into_complex_input() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($complexVar: ComplexInput) query Query($complexVar: ComplexInput)
{ {
complicatedArgs { complicatedArgs {
@ -267,7 +288,8 @@ mod tests {
#[test] #[test]
fn complex_input_into_complex_input_in_field_position() { fn complex_input_into_complex_input_in_field_position() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($boolVar: Boolean = false) query Query($boolVar: Boolean = false)
{ {
complicatedArgs { complicatedArgs {
@ -279,7 +301,8 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_non_null_boolean_in_directive() { fn non_null_boolean_into_non_null_boolean_in_directive() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($boolVar: Boolean!) query Query($boolVar: Boolean!)
{ {
dog @include(if: $boolVar) dog @include(if: $boolVar)
@ -289,7 +312,8 @@ mod tests {
#[test] #[test]
fn boolean_in_non_null_in_directive_with_default() { fn boolean_in_non_null_in_directive_with_default() {
expect_passes_rule(factory, r#" expect_passes_rule(factory,
r#"
query Query($boolVar: Boolean = false) query Query($boolVar: Boolean = false)
{ {
dog @include(if: $boolVar) dog @include(if: $boolVar)
@ -299,24 +323,23 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int() { fn int_into_non_null_int() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Query($intArg: Int) { query Query($intArg: Int) {
complicatedArgs { complicatedArgs {
nonNullIntArgField(nonNullIntArg: $intArg) nonNullIntArgField(nonNullIntArg: $intArg)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int", "Int!"),
RuleError::new(&error_message("intArg", "Int", "Int!"), &[ &[SourcePosition::new(23, 1, 22),
SourcePosition::new(23, 1, 22), SourcePosition::new(117, 3, 48)])]);
SourcePosition::new(117, 3, 48),
]),
]);
} }
#[test] #[test]
fn int_into_non_null_int_within_fragment() { fn int_into_non_null_int_within_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment nonNullIntArgFieldFrag on ComplicatedArgs { fragment nonNullIntArgFieldFrag on ComplicatedArgs {
nonNullIntArgField(nonNullIntArg: $intArg) nonNullIntArgField(nonNullIntArg: $intArg)
} }
@ -327,17 +350,15 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int", "Int!"),
RuleError::new(&error_message("intArg", "Int", "Int!"), &[ &[SourcePosition::new(154, 5, 22),
SourcePosition::new(154, 5, 22), SourcePosition::new(110, 2, 46)])]);
SourcePosition::new(110, 2, 46),
]),
]);
} }
#[test] #[test]
fn int_into_non_null_int_within_nested_fragment() { fn int_into_non_null_int_within_nested_fragment() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
fragment outerFrag on ComplicatedArgs { fragment outerFrag on ComplicatedArgs {
...nonNullIntArgFieldFrag ...nonNullIntArgFieldFrag
} }
@ -352,75 +373,64 @@ mod tests {
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("intArg", "Int", "Int!"),
RuleError::new(&error_message("intArg", "Int", "Int!"), &[ &[SourcePosition::new(255, 9, 22),
SourcePosition::new(255, 9, 22), SourcePosition::new(211, 6, 46)])]);
SourcePosition::new(211, 6, 46),
]),
]);
} }
#[test] #[test]
fn string_over_boolean() { fn string_over_boolean() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Query($stringVar: String) { query Query($stringVar: String) {
complicatedArgs { complicatedArgs {
booleanArgField(booleanArg: $stringVar) booleanArgField(booleanArg: $stringVar)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringVar", "String", "Boolean"),
RuleError::new(&error_message("stringVar", "String", "Boolean"), &[ &[SourcePosition::new(23, 1, 22),
SourcePosition::new(23, 1, 22), SourcePosition::new(117, 3, 42)])]);
SourcePosition::new(117, 3, 42),
]),
]);
} }
#[test] #[test]
fn string_into_string_list() { fn string_into_string_list() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Query($stringVar: String) { query Query($stringVar: String) {
complicatedArgs { complicatedArgs {
stringListArgField(stringListArg: $stringVar) stringListArgField(stringListArg: $stringVar)
} }
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringVar", "String", "[String]"),
RuleError::new(&error_message("stringVar", "String", "[String]"), &[ &[SourcePosition::new(23, 1, 22),
SourcePosition::new(23, 1, 22), SourcePosition::new(123, 3, 48)])]);
SourcePosition::new(123, 3, 48),
]),
]);
} }
#[test] #[test]
fn boolean_into_non_null_boolean_in_directive() { fn boolean_into_non_null_boolean_in_directive() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Query($boolVar: Boolean) { query Query($boolVar: Boolean) {
dog @include(if: $boolVar) dog @include(if: $boolVar)
} }
"#, "#,
&[ &[RuleError::new(&error_message("boolVar", "Boolean", "Boolean!"),
RuleError::new(&error_message("boolVar", "Boolean", "Boolean!"), &[ &[SourcePosition::new(23, 1, 22),
SourcePosition::new(23, 1, 22), SourcePosition::new(73, 2, 29)])]);
SourcePosition::new(73, 2, 29),
]),
]);
} }
#[test] #[test]
fn string_into_non_null_boolean_in_directive() { fn string_into_non_null_boolean_in_directive() {
expect_fails_rule(factory, r#" expect_fails_rule(factory,
r#"
query Query($stringVar: String) { query Query($stringVar: String) {
dog @include(if: $stringVar) dog @include(if: $stringVar)
} }
"#, "#,
&[ &[RuleError::new(&error_message("stringVar", "String", "Boolean!"),
RuleError::new(&error_message("stringVar", "String", "Boolean!"), &[ &[SourcePosition::new(23, 1, 22),
SourcePosition::new(23, 1, 22), SourcePosition::new(74, 2, 29)])]);
SourcePosition::new(74, 2, 29),
]),
]);
} }
} }

View file

@ -59,13 +59,11 @@ impl GraphQLType for Being {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields =&[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname"))];
];
registry.build_interface_type::<Self>(fields) registry.build_interface_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -77,13 +75,11 @@ impl GraphQLType for Pet {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname"))];
];
registry.build_interface_type::<Self>(fields) registry.build_interface_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -95,13 +91,11 @@ impl GraphQLType for Canine {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname"))];
];
registry.build_interface_type::<Self>(fields) registry.build_interface_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -113,11 +107,11 @@ impl GraphQLType for DogCommand {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
registry.build_enum_type::<Self>(&[ registry
EnumValue::new("SIT"), .build_enum_type::<Self>(&[EnumValue::new("SIT"),
EnumValue::new("HEEL"), EnumValue::new("HEEL"),
EnumValue::new("DOWN"), EnumValue::new("DOWN")])
]).into_meta() .into_meta()
} }
} }
@ -140,27 +134,28 @@ impl GraphQLType for Dog {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname")),
registry.field::<Option<String>>("nickname"), registry.field::<Option<String>>("nickname"),
registry.field::<Option<i32>>("barkVolume"), registry.field::<Option<i32>>("barkVolume"),
registry.field::<Option<bool>>("barks"), registry.field::<Option<bool>>("barks"),
registry.field::<Option<bool>>("doesKnowCommand") registry
.field::<Option<bool>>("doesKnowCommand")
.argument(registry.arg::<Option<DogCommand>>("dogCommand")), .argument(registry.arg::<Option<DogCommand>>("dogCommand")),
registry.field::<Option<bool>>("isHousetrained") registry
.field::<Option<bool>>("isHousetrained")
.argument(registry.arg_with_default("atOtherHomes", &true)), .argument(registry.arg_with_default("atOtherHomes", &true)),
registry.field::<Option<bool>>("isAtLocation") registry
.field::<Option<bool>>("isAtLocation")
.argument(registry.arg::<Option<i32>>("x")) .argument(registry.arg::<Option<i32>>("x"))
.argument(registry.arg::<Option<i32>>("y")), .argument(registry.arg::<Option<i32>>("y"))];
];
registry.build_object_type::<Self>(fields) registry
.interfaces(&[ .build_object_type::<Self>(fields)
registry.get_type::<Being>(), .interfaces(&[registry.get_type::<Being>(),
registry.get_type::<Pet>(), registry.get_type::<Pet>(),
registry.get_type::<Canine>(), registry.get_type::<Canine>()])
])
.into_meta() .into_meta()
} }
} }
@ -173,12 +168,11 @@ impl GraphQLType for FurColor {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
registry.build_enum_type::<Self>(&[ registry
EnumValue::new("BROWN"), .build_enum_type::<Self>(&[EnumValue::new("BROWN"),
EnumValue::new("BLACK"), EnumValue::new("BLACK"),
EnumValue::new("TAN"), EnumValue::new("TAN"),
EnumValue::new("SPOTTED"), EnumValue::new("SPOTTED")])
])
.into_meta() .into_meta()
} }
} }
@ -203,20 +197,17 @@ impl GraphQLType for Cat {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname")),
registry.field::<Option<String>>("nickname"), registry.field::<Option<String>>("nickname"),
registry.field::<Option<bool>>("meows"), registry.field::<Option<bool>>("meows"),
registry.field::<Option<i32>>("meowVolume"), registry.field::<Option<i32>>("meowVolume"),
registry.field::<Option<FurColor>>("furColor"), registry.field::<Option<FurColor>>("furColor")];
];
registry.build_object_type::<Self>(fields) registry
.interfaces(&[ .build_object_type::<Self>(fields)
registry.get_type::<Being>(), .interfaces(&[registry.get_type::<Being>(), registry.get_type::<Pet>()])
registry.get_type::<Pet>(),
])
.into_meta() .into_meta()
} }
} }
@ -229,13 +220,9 @@ impl GraphQLType for CatOrDog {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let types = &[ let types = &[registry.get_type::<Cat>(), registry.get_type::<Dog>()];
registry.get_type::<Cat>(),
registry.get_type::<Dog>(),
];
registry.build_union_type::<Self>(types) registry.build_union_type::<Self>(types).into_meta()
.into_meta()
} }
} }
@ -247,12 +234,9 @@ impl GraphQLType for Intelligent {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry.field::<Option<i32>>("iq")];
registry.field::<Option<i32>>("iq"),
];
registry.build_interface_type::<Self>(fields) registry.build_interface_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -264,18 +248,16 @@ impl GraphQLType for Human {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname")),
registry.field::<Option<Vec<Option<Pet>>>>("pets"), registry.field::<Option<Vec<Option<Pet>>>>("pets"),
registry.field::<Option<Vec<Human>>>("relatives"), registry.field::<Option<Vec<Human>>>("relatives"),
registry.field::<Option<i32>>("iq"), registry.field::<Option<i32>>("iq")];
]; registry
registry.build_object_type::<Self>(fields) .build_object_type::<Self>(fields)
.interfaces(&[ .interfaces(&[registry.get_type::<Being>(),
registry.get_type::<Being>(), registry.get_type::<Intelligent>()])
registry.get_type::<Intelligent>(),
])
.into_meta() .into_meta()
} }
} }
@ -288,18 +270,16 @@ impl GraphQLType for Alien {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<String>>("name") .field::<Option<String>>("name")
.argument(registry.arg::<Option<bool>>("surname")), .argument(registry.arg::<Option<bool>>("surname")),
registry.field::<Option<i32>>("iq"), registry.field::<Option<i32>>("iq"),
registry.field::<Option<i32>>("numEyes"), registry.field::<Option<i32>>("numEyes")];
];
registry.build_object_type::<Self>(fields) registry
.interfaces(&[ .build_object_type::<Self>(fields)
registry.get_type::<Being>(), .interfaces(&[registry.get_type::<Being>(),
registry.get_type::<Intelligent>(), registry.get_type::<Intelligent>()])
])
.into_meta() .into_meta()
} }
} }
@ -312,13 +292,9 @@ impl GraphQLType for DogOrHuman {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let types = &[ let types = &[registry.get_type::<Dog>(), registry.get_type::<Human>()];
registry.get_type::<Dog>(),
registry.get_type::<Human>(),
];
registry.build_union_type::<Self>(types) registry.build_union_type::<Self>(types).into_meta()
.into_meta()
} }
} }
@ -330,13 +306,9 @@ impl GraphQLType for HumanOrAlien {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let types = &[ let types = &[registry.get_type::<Human>(), registry.get_type::<Alien>()];
registry.get_type::<Human>(),
registry.get_type::<Alien>(),
];
registry.build_union_type::<Self>(types) registry.build_union_type::<Self>(types).into_meta()
.into_meta()
} }
} }
@ -348,16 +320,13 @@ impl GraphQLType for ComplexInput {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry.arg::<bool>("requiredField"),
registry.arg::<bool>("requiredField"),
registry.arg::<Option<i32>>("intField"), registry.arg::<Option<i32>>("intField"),
registry.arg::<Option<String>>("stringField"), registry.arg::<Option<String>>("stringField"),
registry.arg::<Option<bool>>("booleanField"), registry.arg::<Option<bool>>("booleanField"),
registry.arg::<Option<Vec<Option<String>>>>("stringListField"), registry.arg::<Option<Vec<Option<String>>>>("stringListField")];
];
registry.build_input_object_type::<Self>(fields) registry.build_input_object_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -389,40 +358,50 @@ impl GraphQLType for ComplicatedArgs {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields =
registry.field::<Option<String>>("intArgField") &[registry
.field::<Option<String>>("intArgField")
.argument(registry.arg::<Option<i32>>("intArg")), .argument(registry.arg::<Option<i32>>("intArg")),
registry.field::<Option<String>>("nonNullIntArgField") registry
.field::<Option<String>>("nonNullIntArgField")
.argument(registry.arg::<i32>("nonNullIntArg")), .argument(registry.arg::<i32>("nonNullIntArg")),
registry.field::<Option<String>>("stringArgField") registry
.field::<Option<String>>("stringArgField")
.argument(registry.arg::<Option<String>>("stringArg")), .argument(registry.arg::<Option<String>>("stringArg")),
registry.field::<Option<String>>("booleanArgField") registry
.field::<Option<String>>("booleanArgField")
.argument(registry.arg::<Option<bool>>("booleanArg")), .argument(registry.arg::<Option<bool>>("booleanArg")),
registry.field::<Option<String>>("enumArgField") registry
.field::<Option<String>>("enumArgField")
.argument(registry.arg::<Option<FurColor>>("enumArg")), .argument(registry.arg::<Option<FurColor>>("enumArg")),
registry.field::<Option<String>>("floatArgField") registry
.field::<Option<String>>("floatArgField")
.argument(registry.arg::<Option<f64>>("floatArg")), .argument(registry.arg::<Option<f64>>("floatArg")),
registry.field::<Option<String>>("idArgField") registry
.field::<Option<String>>("idArgField")
.argument(registry.arg::<Option<ID>>("idArg")), .argument(registry.arg::<Option<ID>>("idArg")),
registry.field::<Option<String>>("stringListArgField") registry
.field::<Option<String>>("stringListArgField")
.argument(registry.arg::<Option<Vec<Option<String>>>>("stringListArg")), .argument(registry.arg::<Option<Vec<Option<String>>>>("stringListArg")),
registry.field::<Option<String>>("complexArgField") registry
.field::<Option<String>>("complexArgField")
.argument(registry.arg::<Option<ComplexInput>>("complexArg")), .argument(registry.arg::<Option<ComplexInput>>("complexArg")),
registry.field::<Option<String>>("multipleReqs") registry
.field::<Option<String>>("multipleReqs")
.argument(registry.arg::<i32>("req1")) .argument(registry.arg::<i32>("req1"))
.argument(registry.arg::<i32>("req2")), .argument(registry.arg::<i32>("req2")),
registry.field::<Option<String>>("multipleOpts") registry
.field::<Option<String>>("multipleOpts")
.argument(registry.arg_with_default("opt1", &0i32)) .argument(registry.arg_with_default("opt1", &0i32))
.argument(registry.arg_with_default("opt2", &0i32)), .argument(registry.arg_with_default("opt2", &0i32)),
registry.field::<Option<String>>("multipleOptAndReq") registry
.field::<Option<String>>("multipleOptAndReq")
.argument(registry.arg::<i32>("req1")) .argument(registry.arg::<i32>("req1"))
.argument(registry.arg::<i32>("req2")) .argument(registry.arg::<i32>("req2"))
.argument(registry.arg_with_default("opt1", &0i32)) .argument(registry.arg_with_default("opt1", &0i32))
.argument(registry.arg_with_default("opt2", &0i32)), .argument(registry.arg_with_default("opt2", &0i32))];
];
registry.build_object_type::<Self>(fields) registry.build_object_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
@ -434,8 +413,8 @@ impl GraphQLType for QueryRoot {
} }
fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(registry: &mut Registry<'r>) -> MetaType<'r> {
let fields = &[ let fields = &[registry
registry.field::<Option<Human>>("human") .field::<Option<Human>>("human")
.argument(registry.arg::<Option<ID>>("id")), .argument(registry.arg::<Option<ID>>("id")),
registry.field::<Option<Alien>>("alien"), registry.field::<Option<Alien>>("alien"),
registry.field::<Option<Dog>>("dog"), registry.field::<Option<Dog>>("dog"),
@ -444,34 +423,40 @@ impl GraphQLType for QueryRoot {
registry.field::<Option<CatOrDog>>("catOrDog"), registry.field::<Option<CatOrDog>>("catOrDog"),
registry.field::<Option<DogOrHuman>>("dorOrHuman"), registry.field::<Option<DogOrHuman>>("dorOrHuman"),
registry.field::<Option<HumanOrAlien>>("humanOrAlien"), registry.field::<Option<HumanOrAlien>>("humanOrAlien"),
registry.field::<Option<ComplicatedArgs>>("complicatedArgs"), registry.field::<Option<ComplicatedArgs>>("complicatedArgs")];
];
registry.build_object_type::<Self>(fields) registry.build_object_type::<Self>(fields).into_meta()
.into_meta()
} }
} }
pub fn validate<'a, R, V, F>(r: R, q: &'a str, factory: F) pub fn validate<'a, R, V, F>(r: R, q: &'a str, factory: F) -> Vec<RuleError>
-> Vec<RuleError>
where R: GraphQLType, where R: GraphQLType,
V: Visitor<'a> + 'a, V: Visitor<'a> + 'a,
F: Fn() -> V F: Fn() -> V
{ {
let mut root = RootNode::new(r, EmptyMutation::<()>::new()); let mut root = RootNode::new(r, EmptyMutation::<()>::new());
root.schema.add_directive(DirectiveType::new("onQuery", &[DirectiveLocation::Query], &[])); root.schema
root.schema.add_directive(DirectiveType::new("onMutation", &[DirectiveLocation::Mutation], &[])); .add_directive(DirectiveType::new("onQuery", &[DirectiveLocation::Query], &[]));
root.schema.add_directive(DirectiveType::new("onField", &[DirectiveLocation::Field], &[])); root.schema
root.schema.add_directive(DirectiveType::new("onFragmentDefinition", &[DirectiveLocation::FragmentDefinition], &[])); .add_directive(DirectiveType::new("onMutation", &[DirectiveLocation::Mutation], &[]));
root.schema.add_directive(DirectiveType::new("onFragmentSpread", &[DirectiveLocation::FragmentSpread], &[])); root.schema
root.schema.add_directive(DirectiveType::new("onInlineFragment", &[DirectiveLocation::InlineFragment], &[])); .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) let doc = parse_document_source(q).expect(&format!("Parse error on input {:#?}", q));
.expect(&format!("Parse error on input {:#?}", q)); let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc);
let mut ctx = ValidatorContext::new(
unsafe { ::std::mem::transmute(&root.schema) },
&doc);
let mut mv = MultiVisitorNil.with(factory()); let mut mv = MultiVisitorNil.with(factory());
visit(&mut mv, &mut ctx, unsafe { ::std::mem::transmute(&doc) }); visit(&mut mv, &mut ctx, unsafe { ::std::mem::transmute(&doc) });
@ -506,7 +491,10 @@ pub fn expect_fails_rule<'a, V, F>(factory: F, q: &'a str, expected_errors: &[Ru
expect_fails_rule_with_schema(QueryRoot, factory, q, expected_errors); 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]) pub fn expect_fails_rule_with_schema<'a, R, V, F>(r: R,
factory: F,
q: &'a str,
expected_errors: &[RuleError])
where R: GraphQLType, where R: GraphQLType,
V: Visitor<'a> + 'a, V: Visitor<'a> + 'a,
F: Fn() -> V F: Fn() -> V
@ -515,8 +503,7 @@ pub fn expect_fails_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &'a str,
if errs.is_empty() { if errs.is_empty() {
panic!("Expected rule to fail, but no errors were found"); panic!("Expected rule to fail, but no errors were found");
} } else if errs != expected_errors {
else if errs != expected_errors {
println!("==> Expected errors:"); println!("==> Expected errors:");
print_errors(expected_errors); print_errors(expected_errors);

View file

@ -1,5 +1,5 @@
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
Directive, InputValue, Field, FragmentSpread, InlineFragment}; Field, FragmentSpread, InlineFragment};
use parser::Spanning; use parser::Spanning;
use validation::ValidatorContext; use validation::ValidatorContext;
@ -9,20 +9,44 @@ pub trait Visitor<'a> {
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {} fn enter_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {}
fn exit_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,
fn exit_operation_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Operation>) {} _: &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,
fn exit_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Fragment>) {} _: &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,
fn exit_variable_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, VariableDefinition)) {} _: &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 enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {}
fn exit_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,
fn exit_argument(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, Spanning<InputValue>)) {} _: &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>) {} fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {}
fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {} fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {}
@ -30,11 +54,23 @@ pub trait Visitor<'a> {
fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {} fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {}
fn exit_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,
fn exit_fragment_spread(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<FragmentSpread>) {} _: &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,
fn exit_inline_fragment(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<InlineFragment>) {} _: &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<()>) {} fn enter_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {}
fn exit_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {} fn exit_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {}
@ -57,12 +93,30 @@ pub trait Visitor<'a> {
fn enter_variable_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {} 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 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,
fn exit_list_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a Vec<Spanning<InputValue>>>) {} _: &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 exit_object_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {} fn exit_list_value(&mut self,
_: &mut ValidatorContext<'a>,
fn enter_object_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<String>, Spanning<InputValue>)) {} _: Spanning<&'a Vec<Spanning<InputValue>>>) {
fn exit_object_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a (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 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>)) {
}
} }

View file

@ -1,6 +1,5 @@
use ast::{Definition, Document, Fragment, VariableDefinitions, Type, InputValue, use ast::{Definition, Document, Fragment, VariableDefinitions, Type, InputValue, Directive,
Directive, Arguments, Selection, Field, FragmentSpread, InlineFragment, Arguments, Selection, Field, FragmentSpread, InlineFragment, Operation, OperationType};
Operation, OperationType};
use schema::meta::Argument; use schema::meta::Argument;
use parser::Spanning; use parser::Spanning;
use validation::{Visitor, ValidatorContext}; use validation::{Visitor, ValidatorContext};
@ -12,19 +11,31 @@ pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, d: &
v.exit_document(ctx, 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 { for def in d {
let def_type = match *def { let def_type =
match *def {
Definition::Fragment(Spanning { Definition::Fragment(Spanning {
item: Fragment { type_condition: Spanning { item: name, .. }, .. }, .. }) => item: Fragment {
Some(Type::NonNullNamed(name)), type_condition: Spanning { item: name, .. }, ..
},
..
}) => Some(Type::NonNullNamed(name)),
Definition::Operation(Spanning { Definition::Operation(Spanning {
item: Operation { operation_type: OperationType::Query, .. }, .. }) => item: Operation { operation_type: OperationType::Query, .. }, .. }) =>
Some(Type::NonNullNamed(ctx.schema.concrete_query_type().name().unwrap())), Some(Type::NonNullNamed(ctx.schema.concrete_query_type().name().unwrap())),
Definition::Operation(Spanning { Definition::Operation(Spanning {
item: Operation { operation_type: OperationType::Mutation, .. }, .. }) => item: Operation {
ctx.schema.concrete_mutation_type() operation_type: OperationType::Mutation, ..
.map(|t| Type::NonNullNamed(t.name().unwrap())), },
..
}) => {
ctx.schema
.concrete_mutation_type()
.map(|t| Type::NonNullNamed(t.name().unwrap()))
}
}; };
ctx.with_pushed_type(def_type.as_ref(), |ctx| { ctx.with_pushed_type(def_type.as_ref(), |ctx| {
@ -35,35 +46,43 @@ fn visit_definitions<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
} }
} }
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 { match *def {
Definition::Operation(ref op) => v.enter_operation_definition(ctx, op), Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f), 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 { match *def {
Definition::Operation(ref op) => v.exit_operation_definition(ctx, op), Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f), 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 { match *def {
Definition::Operation(ref op) => { Definition::Operation(ref op) => {
visit_variable_definitions(v, ctx, &op.item.variable_definitions); visit_variable_definitions(v, ctx, &op.item.variable_definitions);
visit_directives(v, ctx, &op.item.directives); visit_directives(v, ctx, &op.item.directives);
visit_selection_set(v, ctx, &op.item.selection_set); visit_selection_set(v, ctx, &op.item.selection_set);
}, }
Definition::Fragment(ref f) => { Definition::Fragment(ref f) => {
visit_directives(v, ctx, &f.item.directives); visit_directives(v, ctx, &f.item.directives);
visit_selection_set(v, ctx, &f.item.selection_set); visit_selection_set(v, ctx, &f.item.selection_set);
}, }
} }
} }
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 { if let Some(ref defs) = *defs {
for def in defs.item.iter() { for def in defs.item.iter() {
let var_type = def.1.var_type.item.clone(); let var_type = def.1.var_type.item.clone();
@ -81,10 +100,14 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut Validator
} }
} }
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 { if let Some(ref directives) = *directives {
for directive in directives { for directive in directives {
let directive_arguments = ctx.schema.directive_by_name(directive.item.name.item).map(|d| &d.arguments); let directive_arguments = ctx.schema
.directive_by_name(directive.item.name.item)
.map(|d| &d.arguments);
v.enter_directive(ctx, directive); v.enter_directive(ctx, directive);
visit_arguments(v, ctx, &directive_arguments, &directive.item.arguments); visit_arguments(v, ctx, &directive_arguments, &directive.item.arguments);
@ -93,7 +116,10 @@ fn visit_directives<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a
} }
} }
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 { if let Some(ref arguments) = *arguments {
for argument in arguments.item.iter() { for argument in arguments.item.iter() {
let arg_type = meta_args let arg_type = meta_args
@ -111,7 +137,9 @@ fn visit_arguments<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>
} }
} }
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| { ctx.with_pushed_parent_type(|ctx| {
v.enter_selection_set(ctx, selection_set); v.enter_selection_set(ctx, selection_set);
@ -123,7 +151,9 @@ fn visit_selection_set<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext
}); });
} }
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 { match *selection {
Selection::Field(ref field) => visit_field(v, ctx, field), Selection::Field(ref field) => visit_field(v, ctx, field),
Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread), Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread),
@ -131,7 +161,9 @@ fn visit_selection<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>
} }
} }
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() let meta_field = ctx.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item)); .and_then(|t| t.field_by_name(field.item.name.item));
@ -152,7 +184,9 @@ fn visit_field<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, fi
}); });
} }
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); v.enter_fragment_spread(ctx, spread);
visit_directives(v, ctx, &spread.item.directives); visit_directives(v, ctx, &spread.item.directives);
@ -160,7 +194,9 @@ fn visit_fragment_spread<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorConte
v.exit_fragment_spread(ctx, spread); 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>| { let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| {
v.enter_inline_fragment(ctx, fragment); v.enter_inline_fragment(ctx, fragment);
@ -172,13 +208,14 @@ fn visit_inline_fragment<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorConte
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); ctx.with_pushed_type(Some(&Type::NonNullNamed(type_name)), visit_fn);
} } else {
else {
visit_fn(ctx); 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); enter_input_value(v, ctx, input_value);
match input_value.item { match input_value.item {
@ -186,8 +223,8 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
for field in fields { for field in fields {
let inner_type = ctx.current_input_type_literal() let inner_type = ctx.current_input_type_literal()
.and_then(|t| match *t { .and_then(|t| match *t {
Type::NonNullNamed(name) | Type::Named(name) => Type::NonNullNamed(name) |
ctx.schema.concrete_type_by_name(name), Type::Named(name) => ctx.schema.concrete_type_by_name(name),
_ => None, _ => None,
}) })
.and_then(|ct| ct.input_field_by_name(&field.0.item)) .and_then(|ct| ct.input_field_by_name(&field.0.item))
@ -201,16 +238,15 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
} }
} }
InputValue::List(ref ls) => { InputValue::List(ref ls) => {
let inner_type = ctx.current_input_type_literal().and_then(|t| match *t { let inner_type = ctx.current_input_type_literal()
Type::List(ref inner) | Type::NonNullList(ref inner) => .and_then(|t| match *t {
Some(inner.as_ref().clone()), Type::List(ref inner) |
Type::NonNullList(ref inner) => Some(inner.as_ref().clone()),
_ => None, _ => None,
}); });
ctx.with_pushed_input_type(inner_type.as_ref(), |ctx| { ctx.with_pushed_input_type(inner_type.as_ref(), |ctx| for value in ls {
for value in ls {
visit_input_value(v, ctx, value); visit_input_value(v, ctx, value);
}
}) })
} }
_ => (), _ => (),
@ -219,7 +255,9 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
exit_input_value(v, ctx, input_value); 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::*; use InputValue::*;
let start = &input_value.start; let start = &input_value.start;
@ -238,7 +276,9 @@ fn enter_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
} }
} }
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::*; use InputValue::*;
let start = &input_value.start; let start = &input_value.start;

View file

@ -29,30 +29,40 @@ impl Value {
// CONSTRUCTORS // CONSTRUCTORS
/// Construct a null value. /// Construct a null value.
pub fn null() -> Value { Value::Null } pub fn null() -> Value {
Value::Null
}
/// Construct an integer value. /// Construct an integer value.
pub fn int(i: i32) -> Value { Value::Int(i) } pub fn int(i: i32) -> Value {
Value::Int(i)
}
/// Construct a floating point value. /// Construct a floating point value.
pub fn float(f: f64) -> Value { Value::Float(f) } pub fn float(f: f64) -> Value {
Value::Float(f)
}
/// Construct a string value. /// Construct a string value.
pub fn string<T: AsRef<str>>(s: T) -> Value { Value::String(s.as_ref().to_owned()) } pub fn string<T: AsRef<str>>(s: T) -> Value {
Value::String(s.as_ref().to_owned())
}
/// Construct a boolean value. /// Construct a boolean value.
pub fn boolean(b: bool) -> Value { Value::Boolean(b) } pub fn boolean(b: bool) -> Value {
Value::Boolean(b)
}
/// Construct a list value. /// Construct a list value.
pub fn list(l: Vec<Value>) -> Value { Value::List(l) } pub fn list(l: Vec<Value>) -> Value {
Value::List(l)
}
/// Construct an object value. /// Construct an object value.
pub fn object<K>(o: HashMap<K, Value>) -> Value pub fn object<K>(o: HashMap<K, Value>) -> Value
where K: Into<String> + Eq + Hash where K: Into<String> + Eq + Hash
{ {
Value::Object( Value::Object(o.into_iter().map(|(k, v)| (k.into(), v)).collect())
o.into_iter().map(|(k, v)| (k.into(), v)).collect()
)
} }
// DISCRIMINATORS // DISCRIMINATORS
@ -106,10 +116,17 @@ impl ToInputValue for Value {
Value::Float(f) => InputValue::Float(f), Value::Float(f) => InputValue::Float(f),
Value::String(ref s) => InputValue::String(s.clone()), Value::String(ref s) => InputValue::String(s.clone()),
Value::Boolean(b) => InputValue::Boolean(b), Value::Boolean(b) => InputValue::Boolean(b),
Value::List(ref l) => InputValue::List(l.iter().map(|x| Value::List(ref l) => {
Spanning::unlocated(x.to())).collect()), 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())
}
} }
} }
} }

26
juniper_iron/Cargo.toml Normal file
View file

@ -0,0 +1,26 @@
[package]
name = "juniper_iron"
version = "0.1.0"
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
description = "Iron integration for juniper"
license = "BSD-2-Clause"
documentation = "https://docs.rs/juniper_iron"
repository = "https://github.com/mhallin/juniper"
[dependencies]
serde = { version = "^1.0.2" }
serde_json = { version = "^1.0.2" }
urlencoded = { version = "^0.5.0" }
iron = "^0.5.1"
juniper = { version = "0.8.1", path = "../juniper" }
[dev-dependencies]
iron-test = "^0.5.0"
router = "^0.5.0"
mount = "^0.3.0"
logger = "^0.3.0"
juniper = { version = "0.8.1", path = "../juniper" , features = ["expose-test-schema", "serde_json"] }
[badges]
travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" }

View file

@ -3,6 +3,7 @@ extern crate mount;
extern crate logger; extern crate logger;
extern crate serde; extern crate serde;
extern crate juniper; extern crate juniper;
extern crate juniper_iron;
use std::env; use std::env;
@ -10,7 +11,7 @@ use mount::Mount;
use logger::Logger; use logger::Logger;
use iron::prelude::*; use iron::prelude::*;
use juniper::EmptyMutation; use juniper::EmptyMutation;
use juniper::iron_handlers::{GraphQLHandler, GraphiQLHandler}; use juniper_iron::{GraphQLHandler, GraphiQLHandler};
use juniper::tests::model::Database; use juniper::tests::model::Database;
fn context_factory(_: &mut Request) -> Database { fn context_factory(_: &mut Request) -> Database {

View file

@ -1,7 +1,103 @@
//! Optional handlers for the [Iron](http://ironframework.io) framework. Requires the `iron-handlers` feature enabled. /*!
//!
//! See the [server.rs](https://github.com/mhallin/juniper/blob/master/examples/server.rs) [Juniper][1] handlers for the [Iron][2] framework.
//! example for more information on how to use these handlers.
## Integrating with Iron
For example, continuing from the schema created above and using Iron to expose
the schema on an HTTP endpoint supporting both GET and POST requests:
```rust,no_run
extern crate iron;
# #[macro_use] extern crate juniper;
# extern crate juniper_iron;
# use std::collections::HashMap;
use iron::prelude::*;
use juniper_iron::GraphQLHandler;
use juniper::{Context, EmptyMutation};
# use juniper::FieldResult;
#
# struct User { id: String, name: String, friend_ids: Vec<String> }
# struct QueryRoot;
# struct Database { users: HashMap<String, User> }
#
# graphql_object!(User: Database |&self| {
# field id() -> FieldResult<&String> {
# Ok(&self.id)
# }
#
# field name() -> FieldResult<&String> {
# Ok(&self.name)
# }
#
# field friends(&executor) -> FieldResult<Vec<&User>> {
# Ok(self.friend_ids.iter()
# .filter_map(|id| executor.context().users.get(id))
# .collect())
# }
# });
#
# graphql_object!(QueryRoot: Database |&self| {
# field user(&executor, id: String) -> FieldResult<Option<&User>> {
# Ok(executor.context().users.get(&id))
# }
# });
// This function is executed for every request. Here, we would realistically
// provide a database connection or similar. For this example, we'll be
// creating the database from scratch.
fn context_factory(_: &mut Request) -> Database {
Database {
users: vec![
( "1000".to_owned(), User {
id: "1000".to_owned(), name: "Robin".to_owned(),
friend_ids: vec!["1001".to_owned()] } ),
( "1001".to_owned(), User {
id: "1001".to_owned(), name: "Max".to_owned(),
friend_ids: vec!["1000".to_owned()] } ),
].into_iter().collect()
}
}
impl Context for Database {}
fn main() {
// GraphQLHandler takes a context factory function, the root object,
// and the mutation object. If we don't have any mutations to expose, we
// can use the empty tuple () to indicate absence.
let graphql_endpoint = GraphQLHandler::new(
context_factory, QueryRoot, EmptyMutation::<Database>::new());
// Start serving the schema at the root on port 8080.
Iron::new(graphql_endpoint).http("localhost:8080").unwrap();
}
```
See the [iron_server.rs][5]
example for more information on how to use these handlers.
See the the [`GraphQLHandler`][3] documentation for more information on what request methods are
supported.
There's also a built-in [GraphiQL][4] handler included.
[1]: https://github.com/mhallin/Juniper
[2]: http://ironframework.io
[3]: ./struct.GraphQLHandler.html
[4]: https://github.com/graphql/graphiql
[5]: https://github.com/mhallin/juniper/blob/master/juniper_iron/examples/iron_server.rs
*/
extern crate serde_json;
extern crate juniper;
extern crate urlencoded;
#[macro_use] extern crate iron;
#[cfg(test)] extern crate iron_test;
use iron::prelude::*; use iron::prelude::*;
use iron::middleware::Handler; use iron::middleware::Handler;
@ -14,11 +110,10 @@ use std::io::Read;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use serde_json;
use serde_json::error::Error as SerdeError; use serde_json::error::Error as SerdeError;
use ::{InputValue, GraphQLType, RootNode}; use juniper::{InputValue, GraphQLType, RootNode};
use ::http; use juniper::http;
/// Handler that executes GraphQL queries in the given schema /// Handler that executes GraphQL queries in the given schema
/// ///
@ -169,7 +264,7 @@ impl Handler for GraphiQLHandler {
Ok(Response::with(( Ok(Response::with((
content_type, content_type,
status::Ok, status::Ok,
::graphiql::graphiql_source(&self.graphql_url), juniper::graphiql::graphiql_source(&self.graphql_url),
))) )))
} }
} }
@ -216,16 +311,15 @@ impl From<GraphQLIronError> for IronError {
} }
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use iron::prelude::*; use iron::prelude::*;
use iron_test::{request, response}; use iron_test::{request, response};
use iron::{Handler, Headers}; use iron::{Handler, Headers};
use ::tests::model::Database; use juniper::tests::model::Database;
use ::http::tests as http_tests; use ::http::tests as http_tests;
use types::scalars::EmptyMutation; use juniper::EmptyMutation;
use super::GraphQLHandler; use super::GraphQLHandler;

24
juniper_rocket/Cargo.toml Normal file
View file

@ -0,0 +1,24 @@
[package]
name = "juniper_rocket"
version = "0.0.1"
authors = ["Magnus Hallin <mhallin@fastmail.com>"]
description = "Juniper GraphQL integration with Rocket"
license = "BSD-2-Clause"
documentation = "https://docs.rs/juniper_rocket"
repository = "https://github.com/mhallin/juniper"
[dependencies]
serde = { version = "^1.0.8" }
serde_derive = {version="^1.0.8" }
serde_json = { version = "^1.0.2" }
rocket = { version = "^0.3.0" }
rocket_codegen = { version = "^0.3.0" }
juniper = { version = "0.8.1", path = "../juniper" }
juniper_codegen = { version = "0.8.1", path = "../juniper_codegen" }
[badges]
travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" }
[dev-dependencies]
juniper = { version = "0.8.1", path = "../juniper", features=["expose-test-schema", "serde_json"] }

View file

@ -0,0 +1,41 @@
[tasks.build-verbose]
command = "cargo"
args = ["build", "--verbose"]
condition_script = [
'''
if [ "$CARGO_MAKE_RUST_CHANNEL" = "nightly" ]; then
exit 0
else
exit 1
fi
'''
]
[tasks.build-verbose.windows]
command = "cargo"
args = ["build", "--verbose"]
condition_script = [
'''IF "%CARGO_MAKE_RUST_CHANNEL%"=="nightly" (exit 0) ELSE (exit 1)'''
]
[tasks.test-verbose]
command = "cargo"
args = ["test", "--verbose"]
dependencies = ["build-verbose"]
condition_script = [
'''
if [ "$CARGO_MAKE_RUST_CHANNEL" = "nightly" ]; then
exit 0
else
exit 1
fi
'''
]
[tasks.test-verbose.windows]
command = "cargo"
args = ["test", "--verbose"]
dependencies = ["build-verbose"]
condition_script = [
'''IF "%CARGO_MAKE_RUST_CHANNEL%"=="nightly" (exit 0) ELSE (exit 1)'''
]

View file

@ -3,6 +3,7 @@
extern crate rocket; extern crate rocket;
extern crate juniper; extern crate juniper;
extern crate juniper_rocket;
use rocket::response::content; use rocket::response::content;
use rocket::State; use rocket::State;
@ -10,30 +11,28 @@ use rocket::State;
use juniper::tests::model::Database; use juniper::tests::model::Database;
use juniper::{EmptyMutation, RootNode}; use juniper::{EmptyMutation, RootNode};
use juniper::rocket_handlers;
type Schema = RootNode<'static, Database, EmptyMutation<Database>>; type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
#[get("/")] #[get("/")]
fn graphiql() -> content::HTML<String> { fn graphiql() -> content::Html<String> {
rocket_handlers::graphiql_source("/graphql") juniper_rocket::graphiql_source("/graphql")
} }
#[get("/graphql?<request>")] #[get("/graphql?<request>")]
fn get_graphql_handler( fn get_graphql_handler(
context: State<Database>, context: State<Database>,
request: rocket_handlers::GraphQLRequest, request: juniper_rocket::GraphQLRequest,
schema: State<Schema>, schema: State<Schema>,
) -> rocket_handlers::GraphQLResponse { ) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context) request.execute(&schema, &context)
} }
#[post("/graphql", data="<request>")] #[post("/graphql", data="<request>")]
fn post_graphql_handler( fn post_graphql_handler(
context: State<Database>, context: State<Database>,
request: rocket_handlers::GraphQLRequest, request: juniper_rocket::GraphQLRequest,
schema: State<Schema>, schema: State<Schema>,
) -> rocket_handlers::GraphQLResponse { ) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context) request.execute(&schema, &context)
} }

View file

@ -1,30 +1,26 @@
//! Optional helper functions for the [Rocket](https://rocket.rs) framework. Requires the "rocket-handlers" feature enabled. #![feature(plugin)]
//! #![plugin(rocket_codegen)]
//! The two exposed types in this module are simple wrapper around the
//! types exposed by the `http` module, but they are better suited for use extern crate juniper;
//! in handler functions in the Rocket framework. extern crate serde_json;
//! extern crate rocket;
//! See the [rocket-server.rs](https://github.com/mhallin/juniper/blob/master/examples/rocket-server.rs)
//! example for how to use these tools.
use std::io::{Cursor, Read}; use std::io::{Cursor, Read};
use std::error::Error; use std::error::Error;
use serde_json;
use rocket::Request; use rocket::Request;
use rocket::request::{FromForm, FormItems, FromFormValue}; use rocket::request::{FromForm, FormItems};
use rocket::data::{FromData, Outcome as FromDataOutcome}; use rocket::data::{FromData, Outcome as FromDataOutcome};
use rocket::response::{Responder, Response, content}; use rocket::response::{Responder, Response, content};
use rocket::http::{ContentType, Status}; use rocket::http::{ContentType, Status};
use rocket::Data; use rocket::Data;
use rocket::Outcome::{Forward, Failure, Success}; use rocket::Outcome::{Forward, Failure, Success};
use ::InputValue; use juniper::InputValue;
use ::http; use juniper::http;
use types::base::GraphQLType; use juniper::GraphQLType;
use schema::model::RootNode; use juniper::RootNode;
/// Simple wrapper around an incoming GraphQL request /// Simple wrapper around an incoming GraphQL request
/// ///
@ -37,8 +33,8 @@ pub struct GraphQLRequest(http::GraphQLRequest);
pub struct GraphQLResponse(Status, String); pub struct GraphQLResponse(Status, String);
/// Generate an HTML page containing GraphiQL /// Generate an HTML page containing GraphiQL
pub fn graphiql_source(graphql_endpoint_url: &str) -> content::HTML<String> { pub fn graphiql_source(graphql_endpoint_url: &str) -> content::Html<String> {
content::HTML(::graphiql::graphiql_source(graphql_endpoint_url)) content::Html(juniper::graphiql::graphiql_source(graphql_endpoint_url))
} }
impl GraphQLRequest { impl GraphQLRequest {
@ -63,19 +59,22 @@ impl GraphQLRequest {
impl<'f> FromForm<'f> for GraphQLRequest { impl<'f> FromForm<'f> for GraphQLRequest {
type Error = String; type Error = String;
fn from_form_items(form_items: &mut FormItems<'f>) -> Result<Self, String> { fn from_form(
form_items: &mut FormItems<'f>,
strict: bool
) -> Result<Self, String> {
let mut query = None; let mut query = None;
let mut operation_name = None; let mut operation_name = None;
let mut variables = None; let mut variables = None;
for (key, value) in form_items { for (key, value) in form_items {
match key { match key.as_str() {
"query" => { "query" => {
if query.is_some() { if query.is_some() {
return Err("Query parameter must not occur more than once".to_owned()); return Err("Query parameter must not occur more than once".to_owned());
} }
else { else {
query = Some(String::from_form_value(value)?); query = Some(value.as_str().to_string());
} }
} }
"operation_name" => { "operation_name" => {
@ -83,7 +82,7 @@ impl<'f> FromForm<'f> for GraphQLRequest {
return Err("Operation name parameter must not occur more than once".to_owned()); return Err("Operation name parameter must not occur more than once".to_owned());
} }
else { else {
operation_name = Some(String::from_form_value(value)?); operation_name = Some(value.as_str().to_string());
} }
} }
"variables" => { "variables" => {
@ -91,11 +90,15 @@ impl<'f> FromForm<'f> for GraphQLRequest {
return Err("Variables parameter must not occur more than once".to_owned()); return Err("Variables parameter must not occur more than once".to_owned());
} }
else { else {
variables = Some(serde_json::from_str::<InputValue>(&String::from_form_value(value)?) variables = Some(serde_json::from_str::<InputValue>(value.as_str())
.map_err(|err| err.description().to_owned())?); .map_err(|err| err.description().to_owned())?);
} }
} }
_ => {} _ => {
if strict {
return Err(format!("Prohibited extra field '{}'", key).to_owned());
}
}
} }
} }
@ -115,7 +118,7 @@ impl<'f> FromForm<'f> for GraphQLRequest {
impl FromData for GraphQLRequest { impl FromData for GraphQLRequest {
type Error = String; type Error = String;
fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, String> { fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, Self::Error> {
if !request.content_type().map_or(false, |ct| ct.is_json()) { if !request.content_type().map_or(false, |ct| ct.is_json()) {
return Forward(data); return Forward(data);
} }
@ -135,7 +138,7 @@ impl FromData for GraphQLRequest {
} }
impl<'r> Responder<'r> for GraphQLResponse { impl<'r> Responder<'r> for GraphQLResponse {
fn respond(self) -> Result<Response<'r>, Status> { fn respond_to(self, _: &Request) -> Result<Response<'r>, Status> {
let GraphQLResponse(status, body) = self; let GraphQLResponse(status, body) = self;
Ok(Response::build() Ok(Response::build()
@ -148,19 +151,20 @@ impl<'r> Responder<'r> for GraphQLResponse {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use rocket; use rocket;
use rocket::Rocket; use rocket::Rocket;
use rocket::http::{ContentType, Method}; use rocket::http::{ContentType, Method};
use rocket::State; use rocket::State;
use rocket::testing::MockRequest;
use ::RootNode; use juniper::RootNode;
use ::tests::model::Database; use juniper::tests::model::Database;
use ::http::tests as http_tests; use juniper::http::tests as http_tests;
use types::scalars::EmptyMutation; use juniper::EmptyMutation;
type Schema = RootNode<'static, Database, EmptyMutation<Database>>; type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
#[get("/?<request>")] #[get("/?<request>")]
fn get_graphql_handler( fn get_graphql_handler(
context: State<Database>, context: State<Database>,
@ -183,6 +187,8 @@ mod tests {
rocket: Rocket, rocket: Rocket,
} }
/*
impl http_tests::HTTPIntegration for TestRocketIntegration impl http_tests::HTTPIntegration for TestRocketIntegration
{ {
fn get(&self, url: &str) -> http_tests::TestResponse { fn get(&self, url: &str) -> http_tests::TestResponse {
@ -230,4 +236,6 @@ mod tests {
content_type: content_type, content_type: content_type,
} }
} }
*/
} }