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.18.0
matrix:
allow_failures:
- rust: nightly
env:
global:
- secure: "SsepHEYRmW9ee3RhxPpqGuPigZINFfA/yOwUJFseQt4t+Zs90r1xdl3Q8eDfPlnvBsL7Rd0QQrFDO7JUaimVLlgQkUnrl62o0CYzkodp+qtocyAHS00W6WTqi8Y6E6KBxPshCl03dRLaySUfx5TqTLTIHkJ0G6vDW35k7hRrA3221lRphs5rrpvAZ21pqsDsNLH3HVo792L6A0kOtBa3ocw1pgHLxnBbArIViu2htUuFvY/TgsmVbAdlow0efw/xkcJ/p0/r5q7igLek6Iqk8udfRc7CktvoiFQ2vUnhtNtQu/zYll3Q7OUx5d+w5lhbzz2QINmsezBEisH9k1haL7dMviLPp0pn4WZed60KovO0Iqfgjy1utTaKvJVfNWYsgkfU8c9a/z2rcZOKwXNKQW2ptBrtVjaB9dk7eMoyuFCDZwNtKqvG+ZKmvMpun+R8mmx+buOmN8Vlf5ygIoGxz3nbEtlLYGVTXHfdXXqRkFIwtiYVJEO7SLRKT9pbx1E++ARsi2+y8bXJT4e4z0osYMq9EsiFUpw3J2gcshrgseqkB7UgCZ3SXuitJnJNfDAU3a3nwwS/JiAunZMNnC4rKUBbl7WbTB4Cpw7EgVOlCqcyyzlkNl3xabLzTFzLOfSHLTVX5FmGNsD21vBoS5/8ejftx9wuV3rGHxuO3i3+A3k="
script:
# Build and run tests for main juniper crate with certain features.
- cd juniper
- 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
- cargo install -f --debug cargo-make
- cargo make ci-flow
before_deploy:
- 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
[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

View file

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

View file

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

View file

@ -32,7 +32,7 @@ environment:
- TARGET: x86_64-pc-windows-msvc
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)
install:
- curl -sSf -o rustup-init.exe https://win.rustup.rs
@ -40,6 +40,7 @@ install:
- set PATH=%PATH%;C:\Users\appveyor\.cargo\bin
- rustc -Vv
- cargo -V
- cargo install --debug cargo-make
# 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents
@ -48,30 +49,9 @@ install:
build: false
test_script:
# Build and test main juniper crate with certain features.
- cd juniper
- cargo build --verbose --features iron-handlers
- IF "%CHANNEL%"=="nightly" (cargo build --verbose --features rocket-handlers)
# 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
- cd juniper && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper_codegen && cargo build --verbose && cargo test --verbose && cd ..
- cd juniper_iron && cargo build --verbose && cargo test --verbose && cd ..
- 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 .. ) )

View file

@ -7,12 +7,9 @@ license = "BSD-2-Clause"
documentation = "https://docs.rs/juniper/0.8.1/juniper/"
repository = "https://github.com/mhallin/juniper"
readme = "README.md"
keywords = ["graphql", "server", "iron", "web", "rocket"]
keywords = ["graphql", "server", "web", "rocket"]
categories = ["web-programming"]
[package.metadata.docs.rs]
features = [ "iron-handlers" ]
[badges]
travis-ci = { repository = "mhallin/juniper" }
appveyor = { repository = "mhallin/juniper" }
@ -22,36 +19,15 @@ name = "bench"
harness = false
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]
nightly = []
iron-handlers = ["iron", "serde_json", "urlencoded"]
rocket-handlers = ["rocket", "rocket_codegen", "serde_json"]
expose-test-schema = []
[dependencies]
serde = { version = "^1.0.8" }
serde_derive = {version="^1.0.8" }
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 }
serde_json = { version="^1.0.2", optional = true }
[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"
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;
use bencher::Bencher;

View file

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

View file

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

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 (result, errs) = ::execute(query, None, &schema, &vars, &())
.expect("Execution failed");
let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed");
assert_eq!(errs, []);
@ -42,42 +41,34 @@ fn run_query<F>(query: &str, f: F)
#[test]
fn scalar_include_true() {
run_query(
"{ a, b @include(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, b @include(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_include_false() {
run_query(
"{ a, b @include(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, b @include(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn scalar_skip_false() {
run_query(
"{ a, b @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, b @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_skip_true() {
run_query(
"{ a, b @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, b @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
@ -85,84 +76,74 @@ fn scalar_skip_true() {
#[test]
fn fragment_spread_include_true() {
run_query(
"{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn fragment_spread_include_false() {
run_query(
"{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn fragment_spread_skip_false() {
run_query(
"{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn fragment_spread_skip_true() {
run_query(
"{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn inline_fragment_include_true() {
run_query(
"{ a, ... on TestType @include(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ... on TestType @include(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn inline_fragment_include_false() {
run_query(
"{ a, ... on TestType @include(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ... on TestType @include(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn inline_fragment_skip_false() {
run_query(
"{ a, ... on TestType @skip(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ... on TestType @skip(if: false) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn inline_fragment_skip_true() {
run_query(
"{ a, ... on TestType @skip(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ... on TestType @skip(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
@ -170,42 +151,34 @@ fn inline_fragment_skip_true() {
#[test]
fn anonymous_inline_fragment_include_true() {
run_query(
"{ a, ... @include(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ... @include(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn anonymous_inline_fragment_include_false() {
run_query(
"{ a, ... @include(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ... @include(if: false) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn anonymous_inline_fragment_skip_false() {
run_query(
"{ a, ... @skip(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, ... @skip(if: false) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn anonymous_inline_fragment_skip_true() {
run_query(
"{ a, ... @skip(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, ... @skip(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
@ -213,40 +186,32 @@ fn anonymous_inline_fragment_skip_true() {
#[test]
fn scalar_include_true_skip_true() {
run_query(
"{ a, b @include(if: true) @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, b @include(if: true) @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn scalar_include_true_skip_false() {
run_query(
"{ a, b @include(if: true) @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
run_query("{ a, b @include(if: true) @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_include_false_skip_true() {
run_query(
"{ a, b @include(if: false) @skip(if: true) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, b @include(if: false) @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}
#[test]
fn scalar_include_false_skip_false() {
run_query(
"{ a, b @include(if: false) @skip(if: false) }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
run_query("{ a, b @include(if: false) @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
});
}

View file

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

View file

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

View file

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

View file

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

View file

@ -4,7 +4,7 @@ use value::Value;
use ast::InputValue;
use executor::Variables;
use schema::model::RootNode;
use ::GraphQLError::ValidationError;
use GraphQLError::ValidationError;
use validation::RuleError;
use parser::SourcePosition;
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 (result, errs) = ::execute(query, None, &schema, &vars, &())
.expect("Execution failed");
let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed");
assert_eq!(errs, []);
@ -238,10 +237,11 @@ fn variable_error_on_nested_non_null() {
("b", InputValue::string("bar")),
("c", InputValue::null()),
].into_iter().collect()))
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -258,10 +258,11 @@ fn variable_error_on_incorrect_type() {
let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#;
let vars = vec![
("input".to_owned(), InputValue::string("foo bar")),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -281,10 +282,11 @@ fn variable_error_on_omit_non_null() {
("a", InputValue::string("foo")),
("b", InputValue::string("bar")),
].into_iter().collect()))
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -298,17 +300,19 @@ fn variable_error_on_omit_non_null() {
fn variable_multiple_errors_with_nesting() {
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![
("input".to_owned(), InputValue::object(vec![
("na", InputValue::object(vec![
("a", InputValue::string("foo")),
].into_iter().collect())),
].into_iter().collect()))
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -334,10 +338,11 @@ fn variable_error_on_additional_field() {
("c", InputValue::string("baz")),
("extra", InputValue::string("dog")),
].into_iter().collect()))
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -349,35 +354,31 @@ fn variable_error_on_additional_field() {
#[test]
fn allow_nullable_inputs_to_be_omitted() {
run_query(
r#"{ fieldWithNullableStringInput }"#,
|result| {
assert_eq!(
run_query(r#"{ fieldWithNullableStringInput }"#, |result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)));
});
});
}
#[test]
fn allow_nullable_inputs_to_be_omitted_in_variable() {
run_query(
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| {
assert_eq!(
run_query(r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)));
});
});
}
#[test]
fn allow_nullable_inputs_to_be_explicitly_null() {
run_query(
r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| {
assert_eq!(
run_query(r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)));
});
});
}
#[test]
@ -410,13 +411,12 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
#[test]
fn allow_nullable_inputs_to_be_set_to_value_directly() {
run_query(
r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
run_query(r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
Some(&Value::string(r#"Some("a")"#)));
});
});
}
#[test]
@ -424,11 +424,9 @@ fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#;
let vars = vec![
].into_iter().collect();
let vars = vec![].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
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 vars = vec![
("value".to_owned(), InputValue::null()),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -474,61 +473,63 @@ fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
#[test]
fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
run_query(
r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
run_query(r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
result.get("fieldWithNonNullableStringInput"),
Some(&Value::string(r#""a""#)));
});
});
}
#[test]
fn allow_lists_to_be_null() {
run_variable_query(
r#"query q($input: [String]) { list(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
vec![
("input".to_owned(), InputValue::null()),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("list"),
Some(&Value::string(r#"None"#)));
});
});
}
#[test]
fn allow_lists_to_contain_values() {
run_variable_query(
r#"query q($input: [String]) { list(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("list"),
Some(&Value::string(r#"Some([Some("A")])"#)));
});
});
}
#[test]
fn allow_lists_to_contain_null() {
run_variable_query(
r#"query q($input: [String]) { list(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String]) { list(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
InputValue::null(),
InputValue::string("B"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("list"),
Some(&Value::string(r#"Some([Some("A"), None, Some("B")])"#)));
});
});
}
#[test]
@ -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 vars = vec![
("input".to_owned(), InputValue::null()),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -553,65 +555,69 @@ fn does_not_allow_non_null_lists_to_be_null() {
#[test]
fn allow_non_null_lists_to_contain_values() {
run_variable_query(
r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("nnList"),
Some(&Value::string(r#"[Some("A")]"#)));
});
});
}
#[test]
fn allow_non_null_lists_to_contain_null() {
run_variable_query(
r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
InputValue::null(),
InputValue::string("B"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("nnList"),
Some(&Value::string(r#"[Some("A"), None, Some("B")]"#)));
});
});
}
#[test]
fn allow_lists_of_non_null_to_be_null() {
run_variable_query(
r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![
("input".to_owned(), InputValue::null()),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("listNn"),
Some(&Value::string(r#"None"#)));
});
});
}
#[test]
fn allow_lists_of_non_null_to_contain_values() {
run_variable_query(
r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("listNn"),
Some(&Value::string(r#"Some(["A"])"#)));
});
});
}
#[test]
@ -625,10 +631,11 @@ fn does_not_allow_lists_of_non_null_to_contain_null() {
InputValue::null(),
InputValue::string("B"),
])),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -649,10 +656,11 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() {
InputValue::null(),
InputValue::string("B"),
])),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
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 vars = vec![
("value".to_owned(), InputValue::null()),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -684,18 +693,19 @@ fn does_not_allow_non_null_lists_of_non_null_to_be_null() {
#[test]
fn allow_non_null_lists_of_non_null_to_contain_values() {
run_variable_query(
r#"query q($input: [String!]!) { nnListNn(input: $input) }"#,
vec![
run_variable_query(r#"query q($input: [String!]!) { nnListNn(input: $input) }"#,
vec![
("input".to_owned(), InputValue::list(vec![
InputValue::string("A"),
])),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("nnListNn"),
Some(&Value::string(r#"["A"]"#)));
});
});
}
#[test]
@ -708,10 +718,11 @@ fn does_not_allow_invalid_types_to_be_used_as_values() {
InputValue::string("A"),
InputValue::string("B"),
])),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -731,10 +742,11 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
InputValue::string("A"),
InputValue::string("B"),
])),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -746,24 +758,21 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
#[test]
fn default_argument_when_not_provided() {
run_query(
r#"{ fieldWithDefaultArgumentValue }"#,
|result| {
assert_eq!(
run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| {
assert_eq!(
result.get("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#)));
});
});
}
#[test]
fn default_argument_when_nullable_variable_not_provided() {
run_query(
r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#,
|result| {
assert_eq!(
run_query(r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#,
|result| {
assert_eq!(
result.get("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#)));
});
});
}
#[test]
@ -782,56 +791,52 @@ fn default_argument_when_nullable_variable_set_to_null() {
#[test]
fn nullable_input_object_arguments_successful_without_variables() {
run_query(
r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#,
|result| {
assert_eq!(
run_query(r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#, |result| {
assert_eq!(
result.get("exampleInput"),
Some(&Value::string(r#"a: Some("abc"), b: 123"#)));
});
});
run_query(
r#"{ exampleInput(arg: {a: null, b: 1}) }"#,
|result| {
assert_eq!(
run_query(r#"{ exampleInput(arg: {a: null, b: 1}) }"#, |result| {
assert_eq!(
result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)));
});
});
}
#[test]
fn nullable_input_object_arguments_successful_with_variables() {
run_variable_query(
r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#,
vec![
run_variable_query(r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#,
vec![
("var".to_owned(), InputValue::int(123)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 123"#)));
});
});
run_variable_query(
r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
vec![
run_variable_query(r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
vec![
("var".to_owned(), InputValue::null()),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)));
});
});
run_variable_query(
r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
vec![
].into_iter().collect(),
|result| {
assert_eq!(
run_variable_query(r#"query q($var: String) { exampleInput(arg: {a: $var, b: 1}) }"#,
vec![].into_iter().collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)));
});
});
}
#[test]
@ -839,11 +844,9 @@ fn does_not_allow_missing_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"{ exampleInput(arg: {a: "abc"}) }"#;
let vars = vec![
].into_iter().collect();
let vars = vec![].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -858,11 +861,9 @@ fn does_not_allow_null_in_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"{ exampleInput(arg: {a: "abc", b: null}) }"#;
let vars = vec![
].into_iter().collect();
let vars = vec![].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -877,11 +878,9 @@ fn does_not_allow_missing_variable_for_required_field() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#;
let vars = vec![
].into_iter().collect();
let vars = vec![].into_iter().collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
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 vars = vec![
("var".to_owned(), InputValue::null()),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -913,45 +913,43 @@ fn does_not_allow_null_variable_for_required_field() {
#[test]
fn input_object_with_default_values() {
run_query(
r#"{ inputWithDefaults(arg: {a: 1}) }"#,
|result| {
assert_eq!(
run_query(r#"{ inputWithDefaults(arg: {a: 1}) }"#, |result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
});
run_variable_query(
r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
run_variable_query(r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
("var".to_owned(), InputValue::int(1)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
});
run_variable_query(
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
].into_iter().collect(),
|result| {
assert_eq!(
run_variable_query(r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![].into_iter().collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)));
});
});
run_variable_query(
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
run_variable_query(r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![
("var".to_owned(), InputValue::int(2)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
Some(&Value::string(r#"a: 2"#)));
});
});
}
@ -960,27 +958,29 @@ mod integers {
#[test]
fn positive_and_negative_should_work() {
run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![
run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![
("var".to_owned(), InputValue::int(1)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("integerInput"),
Some(&Value::string(r#"value: 1"#)));
});
});
run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![
run_variable_query(r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![
("var".to_owned(), InputValue::int(-1)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("integerInput"),
Some(&Value::string(r#"value: -1"#)));
});
});
}
#[test]
@ -990,10 +990,11 @@ mod integers {
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![
("var".to_owned(), InputValue::float(10.0)),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -1010,10 +1011,11 @@ mod integers {
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![
("var".to_owned(), InputValue::string("10")),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(
@ -1030,30 +1032,32 @@ mod floats {
#[test]
fn float_values_should_work() {
run_variable_query(
r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![
run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![
("var".to_owned(), InputValue::float(10.0)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("floatInput"),
Some(&Value::string(r#"value: 10"#)));
});
});
}
#[test]
fn coercion_from_integers_should_work() {
run_variable_query(
r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![
run_variable_query(r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![
("var".to_owned(), InputValue::int(-1)),
].into_iter().collect(),
|result| {
assert_eq!(
]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get("floatInput"),
Some(&Value::string(r#"value: -1"#)));
});
});
}
#[test]
@ -1063,10 +1067,11 @@ mod floats {
let query = r#"query q($var: Float!) { floatInput(value: $var) }"#;
let vars = vec![
("var".to_owned(), InputValue::string("10")),
].into_iter().collect();
]
.into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &())
.unwrap_err();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(error, ValidationError(vec![
RuleError::new(

View file

@ -64,4 +64,4 @@ pub fn graphiql_source(graphql_endpoint_url: &str) -> String {
stylesheet_source = stylesheet_source,
fetcher_source = fetcher_source)
}
}

View file

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

View file

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

View file

@ -10,14 +10,12 @@ statically verify their queries against a server without actually executing
them.
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
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
lightweight and easy to drop into any project. If you enable any of the
optional framework integrations, it will naturally depend on those frameworks
too.
lightweight and easy to drop into any project.
## 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
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.
To support this, the library provides optional and customizable handlers for
both Iron and Rocket.
To support this, Juniper offers additional crates that integrate with popular web frameworks.
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;
# 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.
* [juniper_iron][juniper_iron]: Handlers for [Iron][Iron]
* [juniper_rocket][juniper_rocket]: Handlers for [Rocket][Rocket]
[1]: http://graphql.org
[2]: http://ironframework.io
[3]: macro.graphql_object!.html
[4]: iron_handlers/index.html
[5]: iron_handlers/struct.GraphQLHandler.html
[6]: https://github.com/graphql/graphiql
[Iron]: http://ironframework.io
[Rocket]: https://rocket.rs
*/
@ -190,22 +113,19 @@ built-in [GraphiQL][6] handler included.
#![cfg_attr(feature="nightly", feature(test))]
#![warn(missing_docs)]
#![cfg_attr(feature="rocket-handlers", feature(plugin))]
#![cfg_attr(feature="rocket-handlers", plugin(rocket_codegen))]
#[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;
#[cfg(feature="nightly")]
extern crate test;
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;
#[macro_use] mod macros;
#[macro_use]
mod macros;
mod ast;
pub mod parser;
mod value;
@ -216,12 +136,16 @@ mod executor;
mod integrations;
pub mod graphiql;
pub mod http;
#[macro_use] mod result_ext;
#[macro_use]
mod result_ext;
#[cfg(all(test, not(feature="expose-test-schema")))] mod tests;
#[cfg(feature="expose-test-schema")] pub mod tests;
#[cfg(all(test, not(feature="expose-test-schema")))]
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 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 value::Value;
pub use types::base::{Arguments, GraphQLType, TypeKind};
pub use executor::{
Executor, ExecutionError, Registry,
Context, FromContext, IntoResolvable,
FieldResult, ExecutionResult, Variables,
};
pub use executor::{Executor, ExecutionError, Registry, Context, FromContext, IntoResolvable,
FieldResult, ExecutionResult, Variables};
pub use validation::RuleError;
pub use types::scalars::{EmptyMutation, ID};
pub use schema::model::RootNode;
@ -242,9 +163,6 @@ pub use result_ext::ResultExt;
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
#[derive(Debug, PartialEq)]
#[allow(missing_docs)]
@ -257,16 +175,15 @@ pub enum GraphQLError<'a> {
}
/// Execute a query in a provided schema
pub fn execute<'a, CtxT, QueryT, MutationT>(
document_source: &'a str,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>,
variables: &Variables,
context: &CtxT,
)
-> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
where QueryT: GraphQLType<Context=CtxT>,
MutationT: GraphQLType<Context=CtxT>,
pub fn execute<'a, CtxT, QueryT, MutationT>
(document_source: &'a str,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>,
variables: &Variables,
context: &CtxT)
-> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
where QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>
{
let document = try!(parse_document_source(document_source));
@ -304,15 +221,17 @@ pub fn to_camel_case<'a>(s: &'a str) -> Cow<'a, str> {
for (i, part) in s.split('_').enumerate() {
if i > 0 && part.len() == 1 {
dest += Cow::Owned(part.to_uppercase());
}
else if i > 0 && part.len() > 1 {
let first = part.chars().next().unwrap().to_uppercase().collect::<String>();
} else if i > 0 && part.len() > 1 {
let first = part.chars()
.next()
.unwrap()
.to_uppercase()
.collect::<String>();
let second = &part[1..];
dest += Cow::Owned(first);
dest += second;
}
else if i == 0 {
} else if i == 0 {
dest = Cow::Borrowed(part);
}
}

View file

@ -1,10 +1,19 @@
#[macro_use] mod enums;
#[macro_use] mod object;
#[macro_use] mod interface;
#[macro_use] mod scalar;
#[macro_use] mod args;
#[macro_use] mod field;
#[macro_use] mod input_object;
#[macro_use] mod union;
#[macro_use]
mod enums;
#[macro_use]
mod object;
#[macro_use]
mod interface;
#[macro_use]
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);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
.as_object_value()
.expect("Result is not an object")
.get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields").expect("fields field missing")
.as_list_value().expect("fields not a list");
.get("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields not a list");
let field = fields
.into_iter().filter(
|f| f.as_object_value().expect("Field not an object")
.get("name").expect("name field missing from field")
.as_string_value().expect("name is not a string")
== field_name)
.next().expect("Field not found")
.as_object_value().expect("Field is not an object");
.into_iter()
.filter(|f| {
f.as_object_value()
.expect("Field not an object")
.get("name")
.expect("name field missing from field")
.as_string_value()
.expect("name is not a string") == field_name
})
.next()
.expect("Field not found")
.as_object_value()
.expect("Field is not an object");
println!("Field: {:?}", field);
let args = field
.get("args").expect("args missing from field")
.as_list_value().expect("args is not a list");
.get("args")
.expect("args missing from field")
.as_list_value()
.expect("args is not a list");
println!("Args: {:?}", args);

View file

@ -6,12 +6,30 @@ use schema::model::RootNode;
use types::scalars::EmptyMutation;
enum DefaultName { Foo, Bar }
enum Named { Foo, Bar }
enum NoTrailingComma { Foo, Bar }
enum EnumDescription { Foo, Bar }
enum EnumValueDescription { Foo, Bar }
enum EnumDeprecation { Foo, Bar }
enum DefaultName {
Foo,
Bar,
}
enum Named {
Foo,
Bar,
}
enum NoTrailingComma {
Foo,
Bar,
}
enum EnumDescription {
Foo,
Bar,
}
enum EnumValueDescription {
Foo,
Bar,
}
enum EnumDeprecation {
Foo,
Bar,
}
struct Root;
@ -68,7 +86,9 @@ graphql_object!(Root: () |&self| {
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 (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);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
.as_object_value()
.expect("Result is not an object")
.get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let values = type_info
.get("enumValues").expect("enumValues field missing")
.as_list_value().expect("enumValues not a list");
.get("enumValues")
.expect("enumValues field missing")
.as_list_value()
.expect("enumValues not a list");
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 vars = vec![
("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect();
]
.into_iter()
.collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &())
.expect("Execution failed");
let (result, errs) = ::execute(doc, None, &schema, &vars, &()).expect("Execution failed");
assert_eq!(errs, []);
println!("Result: {:?}", result);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
.as_object_value()
.expect("Result is not an object")
.get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields").expect("fields field missing")
.as_list_value().expect("fields not a list");
.get("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields not a list");
let field = fields
.into_iter().filter(
|f| f.as_object_value().expect("Field not an object")
.get("name").expect("name field missing from field")
.as_string_value().expect("name is not a string")
== field_name)
.next().expect("Field not found")
.as_object_value().expect("Field is not an object");
.into_iter()
.filter(|f| {
f.as_object_value()
.expect("Field not an object")
.get("name")
.expect("name field missing from field")
.as_string_value()
.expect("name is not a string") == field_name
})
.next()
.expect("Field not found")
.as_object_value()
.expect("Field is not an object");
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 (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);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
.as_object_value()
.expect("Result is not an object")
.get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("inputFields").expect("inputFields field missing")
.as_list_value().expect("inputFields not a list");
.get("inputFields")
.expect("inputFields field missing")
.as_list_value()
.expect("inputFields not a list");
f(type_info, fields);
}
@ -181,7 +188,9 @@ fn default_name_input_value() {
let iv = InputValue::object(vec![
("fieldOne", InputValue::string("number one")),
("fieldTwo", InputValue::string("number two")),
].into_iter().collect());
]
.into_iter()
.collect());
let dv: Option<DefaultName> = FromInputValue::from(&iv);

View file

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

View file

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

View file

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

View file

@ -70,7 +70,9 @@ graphql_object!(Root: () |&self| {
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 (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);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
.as_object_value()
.expect("Result is not an object")
.get("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
f(type_info);
}

View file

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

View file

@ -1,9 +1,9 @@
use ast::{Definition, Document, OperationType,
VariableDefinitions, VariableDefinition, InputValue,
Operation, Fragment, Selection, Directive, Field, Arguments,
FragmentSpread, InlineFragment, Type};
use ast::{Definition, Document, OperationType, VariableDefinitions, VariableDefinition,
InputValue, Operation, Fragment, Selection, Directive, Field, Arguments, 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;
#[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>> {
match parser.peek().item {
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") =>
Ok(Definition::Operation(try!(parse_operation_definition(parser)))),
Token::Name("fragment") =>
Ok(Definition::Fragment(try!(parse_fragment_definition(parser)))),
Token::CurlyOpen |
Token::Name("query") |
Token::Name("mutation") => {
Ok(Definition::Operation(try!(parse_operation_definition(parser))))
}
Token::Name("fragment") => {
Ok(Definition::Fragment(try!(parse_fragment_definition(parser))))
}
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
}
}
@ -39,50 +43,48 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
if parser.peek().item == Token::CurlyOpen {
let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end(
&selection_set.start,
&selection_set.end,
Operation {
operation_type: OperationType::Query,
name: None,
variable_definitions: None,
directives: None,
selection_set: selection_set.item,
}))
}
else {
Ok(Spanning::start_end(&selection_set.start,
&selection_set.end,
Operation {
operation_type: OperationType::Query,
name: None,
variable_definitions: None,
directives: None,
selection_set: selection_set.item,
}))
} else {
let start_pos = parser.peek().start.clone();
let operation_type = try!(parse_operation_type(parser));
let name = match parser.peek().item {
Token::Name(_) => Some(try!(parser.expect_name())),
_ => None
_ => None,
};
let variable_definitions = try!(parse_variable_definitions(parser));
let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end(
&start_pos,
&selection_set.end,
Operation {
operation_type: operation_type.item,
name: name,
variable_definitions: variable_definitions,
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
}))
Ok(Spanning::start_end(&start_pos,
&selection_set.end,
Operation {
operation_type: operation_type.item,
name: name,
variable_definitions: variable_definitions,
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
}))
}
}
fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fragment<'a>> {
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Name("fragment")));
let 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"))));
}
else {
} else {
n
},
}
}
Err(e) => return Err(e),
};
@ -91,31 +93,27 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser));
Ok(Spanning::start_end(
&start_pos,
&selection_set.end,
Fragment {
name: name,
type_condition: type_cond,
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
}))
Ok(Spanning::start_end(&start_pos,
&selection_set.end,
Fragment {
name: name,
type_condition: type_cond,
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
}))
}
fn parse_optional_selection_set<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Vec<Selection<'a>>> {
fn parse_optional_selection_set<'a>(parser: &mut Parser<'a>)
-> OptionParseResult<'a, Vec<Selection<'a>>> {
if parser.peek().item == Token::CurlyOpen {
Ok(Some(try!(parse_selection_set(parser))))
}
else {
} else {
Ok(None)
}
}
fn parse_selection_set<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Vec<Selection<'a>>> {
parser.unlocated_delimited_nonempty_list(
&Token::CurlyOpen,
parse_selection,
&Token::CurlyClose)
parser.unlocated_delimited_nonempty_list(&Token::CurlyOpen, parse_selection, &Token::CurlyClose)
}
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 selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment(
Spanning::start_end(
&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: Some(name),
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
})))
},
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: Some(name),
directives: directives.map(|s| {
s.item
}),
selection_set: selection_set.item,
})))
}
Token::CurlyOpen => {
let selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment(
Spanning::start_end(
&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: None,
directives: None,
selection_set: selection_set.item,
})))
},
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: None,
directives: None,
selection_set: selection_set.item,
})))
}
Token::Name(_) => {
let frag_name = try!(parser.expect_name());
let directives = try!(parse_directives(parser));
Ok(Selection::FragmentSpread(
Spanning::start_end(
&start_pos.clone(),
&directives.as_ref().map_or(&frag_name.end, |s| &s.end).clone(),
FragmentSpread {
name: frag_name,
directives: directives.map(|s| s.item),
})))
},
Ok(Selection::FragmentSpread(Spanning::start_end(&start_pos.clone(),
&directives
.as_ref()
.map_or(&frag_name.end,
|s| &s.end)
.clone(),
FragmentSpread {
name: frag_name,
directives: directives.map(|s| {
s.item
}),
})))
}
Token::At => {
let directives = try!(parse_directives(parser));
let selection_set = try!(parse_selection_set(parser));
Ok(Selection::InlineFragment(
Spanning::start_end(
&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: None,
directives: directives.map(|s| s.item),
selection_set: selection_set.item,
})))
},
Ok(Selection::InlineFragment(Spanning::start_end(&start_pos.clone(),
&selection_set.end,
InlineFragment {
type_condition: None,
directives: directives.map(|s| {
s.item
}),
selection_set: selection_set.item,
})))
}
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
}
}
@ -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() {
try!(parser.expect_name())
}
else {
} else {
alias.take().unwrap()
};
@ -203,20 +202,21 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
let directives = try!(parse_directives(parser));
let selection_set = try!(parse_optional_selection_set(parser));
Ok(Spanning::start_end(
&alias.as_ref().unwrap_or(&name).start.clone(),
&selection_set.as_ref().map(|s| &s.end)
.or_else(|| directives.as_ref().map(|s| &s.end))
.or_else(|| arguments.as_ref().map(|s| &s.end))
.unwrap_or(&name.end)
.clone(),
Field {
alias: alias,
name: name,
arguments: arguments,
directives: directives.map(|s| s.item),
selection_set: selection_set.map(|s| s.item),
}))
Ok(Spanning::start_end(&alias.as_ref().unwrap_or(&name).start.clone(),
&selection_set
.as_ref()
.map(|s| &s.end)
.or_else(|| directives.as_ref().map(|s| &s.end))
.or_else(|| arguments.as_ref().map(|s| &s.end))
.unwrap_or(&name.end)
.clone(),
Field {
alias: alias,
name: name,
arguments: arguments,
directives: directives.map(|s| s.item),
selection_set: selection_set.map(|s| s.item),
}))
}
fn parse_arguments<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Arguments<'a>> {
@ -227,76 +227,79 @@ fn parse_arguments<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Argumen
&Token::ParenOpen,
parse_argument,
&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());
try!(parser.expect(&Token::Colon));
let value = try!(parse_value_literal(parser, false));
Ok(Spanning::start_end(
&name.start.clone(),
&value.end.clone(),
(name, value)))
Ok(Spanning::start_end(&name.start.clone(), &value.end.clone(), (name, value)))
}
fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, OperationType> {
match parser.peek().item {
Token::Name("query") => Ok(parser.next()?.map(|_| OperationType::Query)),
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 {
Ok(None)
}
else {
} else {
Ok(Some(try!(parser.delimited_nonempty_list(
&Token::ParenOpen,
parse_variable_definition,
&Token::ParenClose
)).map(|defs| VariableDefinitions { items: defs.into_iter().map(|s| s.item).collect() })))
))
.map(|defs| {
VariableDefinitions {
items: defs.into_iter().map(|s| s.item).collect(),
}
})))
}
}
fn parse_variable_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a>)> {
fn parse_variable_definition<'a>
(parser: &mut Parser<'a>)
-> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a>)> {
let Spanning { start: start_pos, .. } = try!(parser.expect(&Token::Dollar));
let var_name = try!(parser.expect_name());
try!(parser.expect(&Token::Colon));
let var_type = try!(parse_type(parser));
let default_value = if try!(parser.skip(&Token::Equals)).is_some() {
Some(try!(parse_value_literal(parser, true)))
}
else {
None
};
Some(try!(parse_value_literal(parser, true)))
} else {
None
};
Ok(Spanning::start_end(
&start_pos,
&default_value.as_ref().map_or(&var_type.end, |s| &s.end).clone(),
(
Spanning::start_end(
&start_pos,
&var_name.end,
var_name.item,
),
VariableDefinition {
var_type: var_type,
default_value: default_value,
}
)))
Ok(Spanning::start_end(&start_pos,
&default_value
.as_ref()
.map_or(&var_type.end, |s| &s.end)
.clone(),
(Spanning::start_end(&start_pos, &var_name.end, var_name.item),
VariableDefinition {
var_type: var_type,
default_value: default_value,
})))
}
fn parse_directives<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Vec<Spanning<Directive<'a>>>> {
fn parse_directives<'a>(parser: &mut Parser<'a>)
-> OptionParseResult<'a, Vec<Spanning<Directive<'a>>>> {
if parser.peek().item != Token::At {
Ok(None)
}
else {
} else {
let mut items = Vec::new();
while parser.peek().item == Token::At {
items.push(try!(parse_directive(parser)));
@ -311,36 +314,35 @@ fn parse_directive<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Directive<'a>
let name = try!(parser.expect_name());
let arguments = try!(parse_arguments(parser));
Ok(Spanning::start_end(
&start_pos,
&arguments.as_ref().map_or(&name.end, |s| &s.end).clone(),
Directive {
name: name,
arguments: arguments,
}))
Ok(Spanning::start_end(&start_pos,
&arguments.as_ref().map_or(&name.end, |s| &s.end).clone(),
Directive {
name: name,
arguments: arguments,
}))
}
pub fn parse_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Type<'a>> {
let parsed_type = if let Some(Spanning { start: start_pos, ..}) = try!(parser.skip(&Token::BracketOpen)) {
let parsed_type = if let Some(Spanning { start: start_pos, .. }) =
try!(parser.skip(&Token::BracketOpen)) {
let inner_type = try!(parse_type(parser));
let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::BracketClose));
Spanning::start_end(
&start_pos,
&end_pos,
Type::List(Box::new(inner_type.item)))
}
else {
Spanning::start_end(&start_pos, &end_pos, Type::List(Box::new(inner_type.item)))
} else {
try!(parser.expect_name()).map(Type::Named)
};
Ok(match *parser.peek() {
Spanning { item: Token::ExclamationMark, .. } =>
try!(wrap_non_null(parser, parsed_type)),
_ => parsed_type
})
Spanning { item: Token::ExclamationMark, .. } => {
try!(wrap_non_null(parser, parsed_type))
}
_ => parsed_type,
})
}
fn wrap_non_null<'a>(parser: &mut Parser<'a>, inner: Spanning<Type<'a>>) -> ParseResult<'a, Type<'a>> {
fn wrap_non_null<'a>(parser: &mut Parser<'a>,
inner: Spanning<Type<'a>>)
-> ParseResult<'a, Type<'a>> {
let Spanning { end: end_pos, .. } = try!(parser.expect(&Token::ExclamationMark));
let wrapped = match inner.item {

View file

@ -45,7 +45,7 @@ pub enum Token<'a> {
pub enum LexerError {
/// An unknown character was found
///
/// Unknown characters are characters that do not occur anywhere in the
/// Unknown characters are characters that do not occur anywhere in the
/// GraphQL language, such as `?` or `%`.
UnknownCharacter(char),
@ -114,8 +114,7 @@ impl<'a> Lexer<'a> {
if let Some((_, ch)) = next {
if ch == '\n' {
self.position.advance_line();
}
else {
} else {
self.position.advance_col();
}
}
@ -138,24 +137,20 @@ impl<'a> Lexer<'a> {
while let Some((_, ch)) = self.peek_char() {
if ch == '\t' || ch == ' ' || ch == '\n' || ch == '\r' || ch == ',' {
self.next_char();
}
else if ch == '#' {
} else if ch == '#' {
self.next_char();
while let Some((_, ch)) = self.peek_char() {
if is_source_char(ch) && (ch == '\n' || ch == '\r') {
self.next_char();
break;
}
else if is_source_char(ch) {
} else if is_source_char(ch) {
self.next_char();
}
else {
} else {
break;
}
}
}
else {
} else {
break;
}
}
@ -176,7 +171,8 @@ impl<'a> Lexer<'a> {
fn scan_name(&mut self) -> LexerResult<'a> {
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)));
assert!(is_name_start(start_ch));
@ -186,21 +182,20 @@ impl<'a> Lexer<'a> {
if is_name_cont(ch) {
self.next_char();
end_idx = idx;
}
else {
} else {
break;
}
}
Ok(Spanning::start_end(
&start_pos,
&self.position,
Token::Name(&self.source[start_idx..end_idx + 1])))
Ok(Spanning::start_end(&start_pos,
&self.position,
Token::Name(&self.source[start_idx..end_idx + 1])))
}
fn scan_string(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone();
let (_, start_ch) = try!(self.next_char().ok_or(
let (_, start_ch) =
try!(self.next_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
assert!(start_ch == '"');
@ -209,64 +204,72 @@ impl<'a> Lexer<'a> {
while let Some((_, ch)) = self.peek_char() {
if ch == '"' {
self.next_char();
return Ok(Spanning::start_end(
&start_pos,
&self.position,
Token::String(acc)));
}
else if ch == '\\' {
return Ok(Spanning::start_end(&start_pos, &self.position, Token::String(acc)));
} else if ch == '\\' {
self.next_char();
match self.peek_char() {
Some((_, '"')) => { self.next_char(); acc.push('"'); },
Some((_, '\\')) => { self.next_char(); acc.push('\\'); },
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((_, '"')) => {
self.next_char();
acc.push('"');
}
Some((_, '\\')) => {
self.next_char();
acc.push('\\');
}
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')) => {
let start_pos = self.position.clone();
self.next_char();
acc.push(try!(self.scan_escaped_unicode(&start_pos)));
},
}
Some((_, ch)) => {
let mut s = String::from("\\");
s.push(ch);
return Err(Spanning::zero_width(
&self.position,
LexerError::UnknownEscapeSequence(s)));
},
return Err(Spanning::zero_width(&self.position,
LexerError::UnknownEscapeSequence(s)));
}
None => {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnterminatedString));
},
}
if let Some((_, ch)) = self.peek_char() {
if ch == 'n' {
return Err(Spanning::zero_width(&self.position,
LexerError::UnterminatedString));
}
}
else {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnterminatedString));
if let Some((_, ch)) = self.peek_char() {
if ch == 'n' {}
} else {
return Err(Spanning::zero_width(&self.position,
LexerError::UnterminatedString));
}
}
else if ch == '\n' || ch == '\r' {
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)));
}
else {
} else if ch == '\n' || ch == '\r' {
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)));
} else {
self.next_char();
acc.push(ch);
}
@ -275,14 +278,18 @@ impl<'a> Lexer<'a> {
Err(Spanning::zero_width(&self.position, LexerError::UnterminatedString))
}
fn scan_escaped_unicode(&mut self, start_pos: &SourcePosition) -> Result<char, Spanning<LexerError>> {
let (start_idx, _) = try!(self.peek_char().ok_or(
fn scan_escaped_unicode(&mut self,
start_pos: &SourcePosition)
-> Result<char, Spanning<LexerError>> {
let (start_idx, _) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
let mut end_idx = start_idx;
let mut len = 0;
for _ in 0..4 {
let (idx, ch) = try!(self.next_char().ok_or(
let (idx, ch) =
try!(self.next_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnterminatedString)));
if !ch.is_alphanumeric() {
@ -293,12 +300,12 @@ impl<'a> Lexer<'a> {
len += 1;
}
let escape = &self.source[start_idx..end_idx+1];
let escape = &self.source[start_idx..end_idx + 1];
if len != 4 {
return Err(Spanning::zero_width(
start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape)));
return Err(Spanning::zero_width(start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() +
escape)));
}
let code_point = try!(u32::from_str_radix(escape, 16).map_err(|_|
@ -306,10 +313,10 @@ impl<'a> Lexer<'a> {
start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))));
char::from_u32(code_point).ok_or_else(||
Spanning::zero_width(
start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape)))
char::from_u32(code_point).ok_or_else(|| {
Spanning::zero_width(start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape))
})
}
fn scan_number(&mut self) -> LexerResult<'a> {
@ -334,8 +341,7 @@ impl<'a> Lexer<'a> {
if ch == '-' {
self.next_char();
is_negative = true;
}
else if ch == '+' {
} else if ch == '+' {
self.next_char();
}
}
@ -343,37 +349,35 @@ impl<'a> Lexer<'a> {
}
}
let mantissa = frac_part.map(|f| f as f64).map(|frac|
if frac > 0f64 {
frac / 10f64.powf(frac.log10().floor() + 1f64)
}
else {
0f64
}).map(|m| if int_part < 0 { -m } else { m });
let mantissa = frac_part
.map(|f| f as f64)
.map(|frac| if frac > 0f64 {
frac / 10f64.powf(frac.log10().floor() + 1f64)
} else {
0f64
})
.map(|m| if int_part < 0 { -m } else { m });
let exp = exp_part.map(|e| e as f64).map(|e| 10f64.powf(e));
Ok(Spanning::start_end(
&start_pos,
&self.position,
match (mantissa, exp) {
(None, None) => Token::Int(int_part),
(None, Some(exp)) => Token::Float((int_part as f64) * exp),
(Some(mantissa), None) => Token::Float((int_part as f64) + mantissa),
(Some(mantissa), Some(exp)) => Token::Float(((int_part as f64) + mantissa) * exp),
}))
Ok(Spanning::start_end(&start_pos, &self.position, match (mantissa, exp) {
(None, None) => Token::Int(int_part),
(None, Some(exp)) => Token::Float((int_part as f64) * exp),
(Some(mantissa), None) => Token::Float((int_part as f64) + mantissa),
(Some(mantissa), Some(exp)) => Token::Float(((int_part as f64) + mantissa) * exp),
}))
}
fn scan_integer_part(&mut self) -> Result<i32, Spanning<LexerError>> {
let is_negative = {
let (_, init_ch) = try!(self.peek_char().ok_or(
let (_, init_ch) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
if init_ch == '-' {
self.next_char();
true
}
else {
} else {
false
}
};
@ -385,18 +389,20 @@ impl<'a> Lexer<'a> {
self.next_char();
match self.peek_char() {
Some((_, '0')) => Err(Spanning::zero_width(&self.position, LexerError::UnexpectedCharacter(ch))),
Some((_, '0')) => {
Err(Spanning::zero_width(&self.position, LexerError::UnexpectedCharacter(ch)))
}
_ => Ok(0),
}
}
else {
} else {
Ok(try!(self.scan_digits()) * if is_negative { -1 } else { 1 })
}
}
fn scan_digits(&mut self) -> Result<i32, Spanning<LexerError>> {
let start_pos = self.position.clone();
let (start_idx, ch) = try!(self.peek_char().ok_or(
let (start_idx, ch) =
try!(self.peek_char().ok_or(
Spanning::zero_width(&self.position, LexerError::UnexpectedEndOfFile)));
let mut end_idx = start_idx;
@ -407,14 +413,13 @@ impl<'a> Lexer<'a> {
while let Some((idx, ch)) = self.peek_char() {
if !ch.is_digit(10) {
break;
}
else {
} else {
self.next_char();
end_idx = idx;
}
}
i32::from_str_radix(&self.source[start_idx..end_idx+1], 10)
i32::from_str_radix(&self.source[start_idx..end_idx + 1], 10)
.map_err(|_| Spanning::zero_width(&start_pos, LexerError::InvalidNumber))
}
}
@ -432,37 +437,34 @@ impl<'a> Iterator for Lexer<'a> {
let ch = self.iterator.peek().map(|&(_, ch)| ch);
Some(match ch {
Some('!') => Ok(self.emit_single_char(Token::ExclamationMark)),
Some('$') => Ok(self.emit_single_char(Token::Dollar)),
Some('(') => Ok(self.emit_single_char(Token::ParenOpen)),
Some(')') => Ok(self.emit_single_char(Token::ParenClose)),
Some('[') => Ok(self.emit_single_char(Token::BracketOpen)),
Some(']') => Ok(self.emit_single_char(Token::BracketClose)),
Some('{') => Ok(self.emit_single_char(Token::CurlyOpen)),
Some('}') => Ok(self.emit_single_char(Token::CurlyClose)),
Some(':') => Ok(self.emit_single_char(Token::Colon)),
Some('=') => Ok(self.emit_single_char(Token::Equals)),
Some('@') => Ok(self.emit_single_char(Token::At)),
Some('|') => Ok(self.emit_single_char(Token::Pipe)),
Some('.') => self.scan_ellipsis(),
Some('"') => self.scan_string(),
Some(ch) => {
if is_number_start(ch) {
self.scan_number()
}
else if is_name_start(ch) {
self.scan_name()
}
else {
Err(Spanning::zero_width(&self.position, LexerError::UnknownCharacter(ch)))
}
},
None => {
self.has_reached_eof = true;
Ok(Spanning::zero_width(
&self.position, Token::EndOfFile))
},
})
Some('!') => Ok(self.emit_single_char(Token::ExclamationMark)),
Some('$') => Ok(self.emit_single_char(Token::Dollar)),
Some('(') => Ok(self.emit_single_char(Token::ParenOpen)),
Some(')') => Ok(self.emit_single_char(Token::ParenClose)),
Some('[') => Ok(self.emit_single_char(Token::BracketOpen)),
Some(']') => Ok(self.emit_single_char(Token::BracketClose)),
Some('{') => Ok(self.emit_single_char(Token::CurlyOpen)),
Some('}') => Ok(self.emit_single_char(Token::CurlyClose)),
Some(':') => Ok(self.emit_single_char(Token::Colon)),
Some('=') => Ok(self.emit_single_char(Token::Equals)),
Some('@') => Ok(self.emit_single_char(Token::At)),
Some('|') => Ok(self.emit_single_char(Token::Pipe)),
Some('.') => self.scan_ellipsis(),
Some('"') => self.scan_string(),
Some(ch) => {
if is_number_start(ch) {
self.scan_number()
} else if is_name_start(ch) {
self.scan_name()
} else {
Err(Spanning::zero_width(&self.position, LexerError::UnknownCharacter(ch)))
}
}
None => {
self.has_reached_eof = true;
Ok(Spanning::zero_width(&self.position, Token::EndOfFile))
}
})
}
}
@ -472,7 +474,9 @@ impl<'a> fmt::Display for Token<'a> {
Token::Name(name) => write!(f, "{}", name),
Token::Int(i) => write!(f, "{}", i),
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::Dollar => write!(f, "$"),
Token::ParenOpen => write!(f, "("),
@ -512,8 +516,12 @@ impl fmt::Display for LexerError {
match *self {
LexerError::UnknownCharacter(c) => write!(f, "Unknown character \"{}\"", c),
LexerError::UnterminatedString => write!(f, "Unterminated string literal"),
LexerError::UnknownCharacterInString(c) => write!(f, "Unknown character \"{}\" in string literal", c),
LexerError::UnknownEscapeSequence(ref s) => write!(f, "Unknown escape sequence \"{}\" in string", s),
LexerError::UnknownCharacterInString(c) => {
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::UnexpectedEndOfFile => write!(f, "Unexpected end of input"),
LexerError::InvalidNumber => write!(f, "Invalid number literal"),

View file

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

View file

@ -3,8 +3,7 @@ use parser::{Spanning, SourcePosition, ParseError, Token};
use parser::document::parse_document_source;
fn parse_document(s: &str) -> Document {
parse_document_source(s)
.expect(&format!("Parse error on input {:#?}", s))
parse_document_source(s).expect(&format!("Parse error on input {:#?}", s))
}
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 {
break;
}
},
}
Some(Err(e)) => panic!("Error in input stream: {:#?} for {:#?}", e, 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 {
panic!("Tokenizer did not return error for {:#?}", s);
}
},
}
Some(Err(e)) => {
return e;
},
}
None => panic!("Tokenizer did not return error for {:#?}", s),
}
}
@ -274,7 +274,10 @@ fn string_errors() {
#[test]
fn numbers() {
fn assert_float_token_eq(source: &str, start: SourcePosition, end: SourcePosition, expected: f64) {
fn assert_float_token_eq(source: &str,
start: SourcePosition,
end: SourcePosition,
expected: f64) {
let parsed = tokenize_single(source);
assert_eq!(parsed.start, start);
assert_eq!(parsed.end, end);
@ -297,17 +300,15 @@ fn numbers() {
&SourcePosition::new(1, 0, 1),
Token::Int(4)));
assert_float_token_eq(
"4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
4.123);
assert_float_token_eq("4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
4.123);
assert_float_token_eq(
"4.0",
SourcePosition::new(0, 0, 0),
SourcePosition::new(3, 0, 3),
4.0);
assert_float_token_eq("4.0",
SourcePosition::new(0, 0, 0),
SourcePosition::new(3, 0, 3),
4.0);
assert_eq!(
tokenize_single("-4"),
@ -330,71 +331,60 @@ fn numbers() {
&SourcePosition::new(1, 0, 1),
Token::Int(0)));
assert_float_token_eq(
"-4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
-4.123);
assert_float_token_eq("-4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
-4.123);
assert_float_token_eq(
"0.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
0.123);
assert_float_token_eq("0.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
0.123);
assert_float_token_eq(
"123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4);
assert_float_token_eq("123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4);
assert_float_token_eq(
"123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4);
assert_float_token_eq("123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4);
assert_float_token_eq(
"123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e-4);
assert_float_token_eq("123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e-4);
assert_float_token_eq(
"123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e4);
assert_float_token_eq("123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e4);
assert_float_token_eq(
"-1.123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4);
assert_float_token_eq("-1.123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4);
assert_float_token_eq(
"-1.123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4);
assert_float_token_eq("-1.123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4);
assert_float_token_eq(
"-1.123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e-4);
assert_float_token_eq("-1.123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e-4);
assert_float_token_eq(
"-1.123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e4);
assert_float_token_eq("-1.123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e4);
assert_float_token_eq(
"-1.123e45",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e45);
assert_float_token_eq("-1.123e45",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e45);
}
#[test]

View file

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

View file

@ -61,14 +61,14 @@ impl<T: fmt::Debug> Spanning<T> {
#[doc(hidden)]
pub fn spanning(v: Vec<Spanning<T>>) -> Option<Spanning<Vec<Spanning<T>>>> {
if let (Some(start), Some(end)) = (v.first().map(|s| s.start.clone()), v.last().map(|s| s.end.clone())) {
if let (Some(start), Some(end)) =
(v.first().map(|s| s.start.clone()), v.last().map(|s| s.end.clone())) {
Some(Spanning {
item: v,
start: start,
end: end,
})
}
else {
item: v,
start: start,
end: end,
})
} else {
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 {
Spanning {
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 {
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> 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) {
self.start.hash(state);
self.end.hash(state);
@ -155,7 +161,7 @@ impl SourcePosition {
/// The index of the character in the input source
///
/// Zero-based index. Take a substring of the original source starting at
/// this index to access the item pointed to by this `SourcePosition`.
/// this index to access the item pointed to by this `SourcePosition`.
pub fn index(&self) -> usize {
self.index
}

View file

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

View file

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

View file

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

View file

@ -6,8 +6,8 @@ use schema::meta::{MetaType, ObjectMeta, EnumMeta, InputObjectMeta, UnionMeta, I
use schema::model::{RootNode, SchemaType, TypeType, DirectiveType, DirectiveLocation};
impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT>
where QueryT: GraphQLType<Context=CtxT>,
MutationT: GraphQLType<Context=CtxT>
where QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>
{
type Context = CtxT;
@ -19,14 +19,24 @@ impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT
QueryT::meta(registry)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult {
fn resolve_field(&self,
field: &str,
args: &Arguments,
executor: &Executor<CtxT>)
-> ExecutionResult {
match field {
"__schema" => executor.replaced_context(&self.schema).resolve(&self.schema),
"__schema" => {
executor
.replaced_context(&self.schema)
.resolve(&self.schema)
}
"__type" => {
let type_name: String = args.get("name").unwrap();
executor.replaced_context(&self.schema).resolve(&self.schema.type_by_name(&type_name))
},
_=> self.query_type.resolve_field(field, args, executor),
executor
.replaced_context(&self.schema)
.resolve(&self.schema.type_by_name(&type_name))
}
_ => self.query_type.resolve_field(field, args, executor),
}
}
}

View file

@ -148,15 +148,25 @@ fn test_possible_types() {
assert_eq!(errors, vec![]);
let possible_types = result
.as_object_value().expect("execution result not an object")
.get("__type").expect("'__type' not present in result")
.as_object_value().expect("'__type' not an object")
.get("possibleTypes").expect("'possibleTypes' not present in '__type'")
.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"))
.as_object_value()
.expect("execution result not an object")
.get("__type")
.expect("'__type' not present in result")
.as_object_value()
.expect("'__type' not an object")
.get("possibleTypes")
.expect("'possibleTypes' not present in '__type'")
.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<_>>();
assert_eq!(

View file

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

View file

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

View file

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

View file

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

View file

@ -3,9 +3,11 @@ use value::Value;
use schema::meta::MetaType;
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;
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>> {
match v {
&InputValue::Null => Some(None),
v => match v.convert() {
Some(x) => Some(Some(x)),
None => None,
v => {
match v.convert() {
Some(x) => Some(Some(x)),
None => None,
}
}
}
}
}
impl<T> ToInputValue for Option<T> where T: ToInputValue {
impl<T> ToInputValue for Option<T>
where T: ToInputValue
{
fn to(&self) -> InputValue {
match *self {
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;
fn name() -> Option<&'static str> {
@ -57,42 +67,44 @@ impl<T, CtxT> GraphQLType for Vec<T> where T: GraphQLType<Context=CtxT> {
}
fn resolve(&self, _: Option<&[Selection]>, executor: &Executor<CtxT>) -> Value {
Value::list(
self.iter().map(|e| executor.resolve_into_value(e)).collect()
)
Value::list(self.iter()
.map(|e| executor.resolve_into_value(e))
.collect())
}
}
impl<T> FromInputValue for Vec<T> where T: FromInputValue {
impl<T> FromInputValue for Vec<T>
where T: FromInputValue
{
fn from(v: &InputValue) -> Option<Vec<T>> {
match *v {
InputValue::List(ref ls) => {
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
if v.len() == ls.len() {
Some(v)
}
else {
None
}
},
ref other =>
if v.len() == ls.len() { Some(v) } else { None }
}
ref other => {
if let Some(e) = other.convert() {
Some(vec![ e ])
} else {
None
}
}
}
}
}
impl<T> ToInputValue for Vec<T> where T: ToInputValue {
impl<T> ToInputValue for Vec<T>
where T: ToInputValue
{
fn to(&self) -> InputValue {
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;
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 {
Value::list(
self.iter().map(|e| executor.resolve_into_value(e)).collect()
)
Value::list(self.iter()
.map(|e| executor.resolve_into_value(e))
.collect())
}
}
impl<'a, T> ToInputValue for &'a [T] where T: ToInputValue {
impl<'a, T> ToInputValue for &'a [T]
where T: ToInputValue
{
fn to(&self) -> InputValue {
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 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;
fn name() -> Option<&'static str> {
@ -16,12 +18,19 @@ impl<T, CtxT> GraphQLType for Box<T> where T: GraphQLType<Context=CtxT> {
T::meta(registry)
}
fn resolve_into_type(&self, name: &str, selection_set: Option<&[Selection]>, executor: &Executor<CtxT>) -> ExecutionResult {
fn resolve_into_type(&self,
name: &str,
selection_set: Option<&[Selection]>,
executor: &Executor<CtxT>)
-> ExecutionResult {
(**self).resolve_into_type(name, selection_set, executor)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
{
fn resolve_field(&self,
field: &str,
args: &Arguments,
executor: &Executor<CtxT>)
-> ExecutionResult {
(**self).resolve_field(field, args, executor)
}
@ -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>> {
match <T as FromInputValue>::from(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 {
(**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;
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)
}
fn resolve_into_type(&self, name: &str, selection_set: Option<&[Selection]>, executor: &Executor<CtxT>) -> ExecutionResult {
fn resolve_into_type(&self,
name: &str,
selection_set: Option<&[Selection]>,
executor: &Executor<CtxT>)
-> ExecutionResult {
(**self).resolve_into_type(name, selection_set, executor)
}
fn resolve_field(&self, field: &str, args: &Arguments, executor: &Executor<CtxT>) -> ExecutionResult
{
fn resolve_field(&self,
field: &str,
args: &Arguments,
executor: &Executor<CtxT>)
-> ExecutionResult {
(**self).resolve_field(field, args, executor)
}
@ -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 {
(**self).to()
}

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
use ast::{Document, Operation, Fragment, VariableDefinition, Selection,
Directive, InputValue, Field, FragmentSpread, InlineFragment};
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
Field, FragmentSpread, InlineFragment};
use parser::Spanning;
use validation::{ValidatorContext, Visitor};
@ -7,7 +7,9 @@ use validation::{ValidatorContext, Visitor};
pub trait MultiVisitor<'a> {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, f: F);
fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self> where Self: Sized {
fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self>
where Self: Sized
{
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) {
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));
}
fn enter_operation_definition(&mut self, ctx: &mut ValidatorContext<'a>, op: &'a Spanning<Operation>) {
fn enter_operation_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>) {
self.visit_all(|v| v.enter_operation_definition(ctx, op));
}
fn exit_operation_definition(&mut self, ctx: &mut ValidatorContext<'a>, op: &'a Spanning<Operation>) {
fn exit_operation_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>) {
self.visit_all(|v| v.exit_operation_definition(ctx, op));
}
fn enter_fragment_definition(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<Fragment>) {
fn enter_fragment_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<Fragment>) {
self.visit_all(|v| v.enter_fragment_definition(ctx, f));
}
fn exit_fragment_definition(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<Fragment>) {
fn exit_fragment_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<Fragment>) {
self.visit_all(|v| v.exit_fragment_definition(ctx, f));
}
fn enter_variable_definition(&mut self, ctx: &mut ValidatorContext<'a>, def: &'a (Spanning<&'a str>, VariableDefinition)) {
fn enter_variable_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
def: &'a (Spanning<&'a str>, VariableDefinition)) {
self.visit_all(|v| v.enter_variable_definition(ctx, def));
}
fn exit_variable_definition(&mut self, ctx: &mut ValidatorContext<'a>, def: &'a (Spanning<&'a str>, VariableDefinition)) {
fn exit_variable_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
def: &'a (Spanning<&'a str>, VariableDefinition)) {
self.visit_all(|v| v.exit_variable_definition(ctx, def));
}
@ -66,10 +82,14 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_directive(ctx, d));
}
fn enter_argument(&mut self, ctx: &mut ValidatorContext<'a>, arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
fn enter_argument(&mut self,
ctx: &mut ValidatorContext<'a>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
self.visit_all(|v| v.enter_argument(ctx, arg));
}
fn exit_argument(&mut self, ctx: &mut ValidatorContext<'a>, arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
fn exit_argument(&mut self,
ctx: &mut ValidatorContext<'a>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
self.visit_all(|v| v.exit_argument(ctx, arg));
}
@ -87,17 +107,25 @@ impl<'a, M> Visitor<'a> for M where M: MultiVisitor<'a> {
self.visit_all(|v| v.exit_field(ctx, f));
}
fn enter_fragment_spread(&mut self, ctx: &mut ValidatorContext<'a>, s: &'a Spanning<FragmentSpread>) {
fn enter_fragment_spread(&mut self,
ctx: &mut ValidatorContext<'a>,
s: &'a Spanning<FragmentSpread>) {
self.visit_all(|v| v.enter_fragment_spread(ctx, s));
}
fn exit_fragment_spread(&mut self, ctx: &mut ValidatorContext<'a>, s: &'a Spanning<FragmentSpread>) {
fn exit_fragment_spread(&mut self,
ctx: &mut ValidatorContext<'a>,
s: &'a Spanning<FragmentSpread>) {
self.visit_all(|v| v.exit_fragment_spread(ctx, s));
}
fn enter_inline_fragment(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<InlineFragment>) {
fn enter_inline_fragment(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<InlineFragment>) {
self.visit_all(|v| v.enter_inline_fragment(ctx, f));
}
fn exit_inline_fragment(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<InlineFragment>) {
fn exit_inline_fragment(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<InlineFragment>) {
self.visit_all(|v| v.exit_inline_fragment(ctx, f));
}
@ -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()));
}
fn enter_list_value(&mut self, ctx: &mut ValidatorContext<'a>, l: Spanning<&'a Vec<Spanning<InputValue>>>) {
fn enter_list_value(&mut self,
ctx: &mut ValidatorContext<'a>,
l: Spanning<&'a Vec<Spanning<InputValue>>>) {
self.visit_all(|v| v.enter_list_value(ctx, l.clone()));
}
fn exit_list_value(&mut self, ctx: &mut ValidatorContext<'a>, l: Spanning<&'a Vec<Spanning<InputValue>>>) {
fn exit_list_value(&mut self,
ctx: &mut ValidatorContext<'a>,
l: Spanning<&'a Vec<Spanning<InputValue>>>) {
self.visit_all(|v| v.exit_list_value(ctx, l.clone()));
}
fn enter_object_value(&mut self, ctx: &mut ValidatorContext<'a>, o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
fn enter_object_value(&mut self,
ctx: &mut ValidatorContext<'a>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
self.visit_all(|v| v.enter_object_value(ctx, o.clone()));
}
fn exit_object_value(&mut self, ctx: &mut ValidatorContext<'a>, o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
fn exit_object_value(&mut self,
ctx: &mut ValidatorContext<'a>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>) {
self.visit_all(|v| v.exit_object_value(ctx, o.clone()));
}
fn enter_object_field(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a (Spanning<String>, Spanning<InputValue>)) {
fn enter_object_field(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a (Spanning<String>, Spanning<InputValue>)) {
self.visit_all(|v| v.enter_object_field(ctx, f));
}
fn exit_object_field(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a (Spanning<String>, Spanning<InputValue>)) {
fn exit_object_field(&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a (Spanning<String>, Spanning<InputValue>)) {
self.visit_all(|v| v.exit_object_field(ctx, f));
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -7,23 +7,22 @@ pub struct LoneAnonymousOperation {
}
pub fn factory() -> LoneAnonymousOperation {
LoneAnonymousOperation {
operation_count: None,
}
LoneAnonymousOperation { operation_count: None }
}
impl<'a> Visitor<'a> for LoneAnonymousOperation {
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, doc: &'a Document) {
self.operation_count = Some(doc
.iter()
.filter(|d| match **d {
Definition::Operation(_) => true,
Definition::Fragment(_) => false,
})
.count());
self.operation_count = Some(doc.iter()
.filter(|d| match **d {
Definition::Operation(_) => true,
Definition::Fragment(_) => false,
})
.count());
}
fn enter_operation_definition(&mut self, ctx: &mut ValidatorContext<'a>, op: &'a Spanning<Operation>) {
fn enter_operation_definition(&mut self,
ctx: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>) {
if let Some(operation_count) = self.operation_count {
if operation_count > 1 && op.item.name.is_none() {
ctx.report_error(error_message(), &[op.start.clone()]);
@ -45,7 +44,8 @@ mod tests {
#[test]
fn no_operations() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment fragA on Type {
field
}
@ -54,7 +54,8 @@ mod tests {
#[test]
fn one_anon_operation() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
{
field
}
@ -63,7 +64,8 @@ mod tests {
#[test]
fn multiple_named_operations() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
query Foo {
field
}
@ -76,7 +78,8 @@ mod tests {
#[test]
fn anon_operation_with_fragment() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
{
...Foo
}
@ -88,7 +91,8 @@ mod tests {
#[test]
fn multiple_anon_operations() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
{
fieldA
}
@ -96,19 +100,14 @@ mod tests {
fieldB
}
"#,
&[
RuleError::new(error_message(), &[
SourcePosition::new(11, 1, 10),
]),
RuleError::new(error_message(), &[
SourcePosition::new(54, 4, 10),
]),
]);
&[RuleError::new(error_message(), &[SourcePosition::new(11, 1, 10)]),
RuleError::new(error_message(), &[SourcePosition::new(54, 4, 10)])]);
}
#[test]
fn anon_operation_with_a_mutation() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
{
fieldA
}
@ -116,10 +115,6 @@ mod tests {
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);
}
fn enter_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, fragment: &'a Spanning<Fragment>) {
fn enter_fragment_definition(&mut self,
_: &mut ValidatorContext<'a>,
fragment: &'a Spanning<Fragment>) {
assert!(self.current_fragment.is_none());
let fragment_name = &fragment.item.name.item;
@ -54,20 +56,23 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
self.fragment_order.push(fragment_name);
}
fn exit_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, fragment: &'a Spanning<Fragment>) {
fn exit_fragment_definition(&mut self,
_: &mut ValidatorContext<'a>,
fragment: &'a Spanning<Fragment>) {
assert_eq!(Some(fragment.item.name.item), self.current_fragment);
self.current_fragment = None;
}
fn enter_fragment_spread(&mut self, _: &mut ValidatorContext<'a>, spread: &'a Spanning<FragmentSpread>) {
fn enter_fragment_spread(&mut self,
_: &mut ValidatorContext<'a>,
spread: &'a Spanning<FragmentSpread>) {
if let Some(current_fragment) = self.current_fragment {
self.spreads
.entry(current_fragment)
.or_insert_with(Vec::new)
.push(Spanning::start_end(
&spread.start.clone(),
&spread.end.clone(),
spread.item.name.item));
.push(Spanning::start_end(&spread.start.clone(),
&spread.end.clone(),
spread.item.name.item));
}
}
}
@ -88,16 +93,14 @@ impl<'a> CycleDetector<'a> {
if let Some(index) = index {
let err_pos = if index < path.len() {
path[index]
} else {
node
};
path[index]
} else {
node
};
self.errors.push(RuleError::new(
&error_message(name),
&[err_pos.start.clone()]));
}
else if !self.visited.contains(name) {
self.errors
.push(RuleError::new(&error_message(name), &[err_pos.start.clone()]));
} else if !self.visited.contains(name) {
path.push(node);
self.detect_from(name, path);
path.pop();
@ -121,7 +124,8 @@ mod tests {
#[test]
fn single_reference_is_valid() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB }
fragment fragB on Dog { name }
"#);
@ -129,7 +133,8 @@ mod tests {
#[test]
fn spreading_twice_is_not_circular() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragB }
fragment fragB on Dog { name }
"#);
@ -137,7 +142,8 @@ mod tests {
#[test]
fn spreading_twice_indirectly_is_not_circular() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragC }
fragment fragB on Dog { ...fragC }
fragment fragC on Dog { name }
@ -146,7 +152,8 @@ mod tests {
#[test]
fn double_spread_within_abstract_types() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment nameFragment on Pet {
... on Dog { name }
... on Cat { name }
@ -161,7 +168,8 @@ mod tests {
#[test]
fn does_not_false_positive_on_unknown_fragment() {
expect_passes_rule(factory, r#"
expect_passes_rule(factory,
r#"
fragment nameFragment on Pet {
...UnknownFragment
}
@ -170,73 +178,64 @@ mod tests {
#[test]
fn spreading_recursively_within_field_fails() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Human { relatives { ...fragA } },
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(49, 1, 48)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(49, 1, 48)])]);
}
#[test]
fn no_spreading_itself_directly() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragA }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)])]);
}
#[test]
fn no_spreading_itself_directly_within_inline_fragment() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Pet {
... on Dog {
...fragA
}
}
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(74, 3, 14)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(74, 3, 14)])]);
}
#[test]
fn no_spreading_itself_indirectly() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragA }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)])]);
}
#[test]
fn no_spreading_itself_indirectly_reports_opposite_order() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragB on Dog { ...fragA }
fragment fragA on Dog { ...fragB }
"#,
&[
RuleError::new(&error_message("fragB"), &[
SourcePosition::new(35, 1, 34)
]),
]);
&[RuleError::new(&error_message("fragB"),
&[SourcePosition::new(35, 1, 34)])]);
}
#[test]
fn no_spreading_itself_indirectly_within_inline_fragment() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Pet {
... on Dog {
...fragB
@ -248,16 +247,14 @@ mod tests {
}
}
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(74, 3, 14)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(74, 3, 14)])]);
}
#[test]
fn no_spreading_itself_deeply() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragC }
fragment fragC on Dog { ...fragO }
@ -267,67 +264,53 @@ mod tests {
fragment fragO on Dog { ...fragP }
fragment fragP on Dog { ...fragA, ...fragX }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
RuleError::new(&error_message("fragO"), &[
SourcePosition::new(305, 7, 34)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)]),
RuleError::new(&error_message("fragO"),
&[SourcePosition::new(305, 7, 34)])]);
}
#[test]
fn no_spreading_itself_deeply_two_paths() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB, ...fragC }
fragment fragB on Dog { ...fragA }
fragment fragC on Dog { ...fragA }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(45, 1, 44)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)]),
RuleError::new(&error_message("fragA"),
&[SourcePosition::new(45, 1, 44)])]);
}
#[test]
fn no_spreading_itself_deeply_two_paths_alt_traversal_order() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragC }
fragment fragB on Dog { ...fragC }
fragment fragC on Dog { ...fragA, ...fragB }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
RuleError::new(&error_message("fragC"), &[
SourcePosition::new(135, 3, 44)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)]),
RuleError::new(&error_message("fragC"),
&[SourcePosition::new(135, 3, 44)])]);
}
#[test]
fn no_spreading_itself_deeply_and_immediately() {
expect_fails_rule(factory, r#"
expect_fails_rule(factory,
r#"
fragment fragA on Dog { ...fragB }
fragment fragB on Dog { ...fragB, ...fragC }
fragment fragC on Dog { ...fragA, ...fragB }
"#,
&[
RuleError::new(&error_message("fragA"), &[
SourcePosition::new(35, 1, 34)
]),
RuleError::new(&error_message("fragB"), &[
SourcePosition::new(80, 2, 34)
]),
RuleError::new(&error_message("fragB"), &[
SourcePosition::new(90, 2, 44)
]),
]);
&[RuleError::new(&error_message("fragA"),
&[SourcePosition::new(35, 1, 34)]),
RuleError::new(&error_message("fragB"),
&[SourcePosition::new(80, 2, 34)]),
RuleError::new(&error_message("fragB"),
&[SourcePosition::new(90, 2, 44)])]);
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,5 +1,5 @@
use ast::{Document, Operation, Fragment, VariableDefinition, Selection,
Directive, InputValue, Field, FragmentSpread, InlineFragment};
use ast::{Document, Operation, Fragment, VariableDefinition, Selection, Directive, InputValue,
Field, FragmentSpread, InlineFragment};
use parser::Spanning;
use validation::ValidatorContext;
@ -9,20 +9,44 @@ pub trait Visitor<'a> {
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {}
fn exit_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {}
fn enter_operation_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Operation>) {}
fn exit_operation_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Operation>) {}
fn enter_operation_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<Operation>) {
}
fn exit_operation_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<Operation>) {
}
fn enter_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Fragment>) {}
fn exit_fragment_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Fragment>) {}
fn enter_fragment_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<Fragment>) {
}
fn exit_fragment_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<Fragment>) {
}
fn enter_variable_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, VariableDefinition)) {}
fn exit_variable_definition(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, VariableDefinition)) {}
fn enter_variable_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a (Spanning<&'a str>, VariableDefinition)) {
}
fn exit_variable_definition(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a (Spanning<&'a str>, VariableDefinition)) {
}
fn enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {}
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {}
fn enter_argument(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, Spanning<InputValue>)) {}
fn exit_argument(&mut self, _: &mut ValidatorContext<'a>, _: &'a (Spanning<&'a str>, Spanning<InputValue>)) {}
fn enter_argument(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
}
fn exit_argument(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a (Spanning<&'a str>, Spanning<InputValue>)) {
}
fn enter_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 exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {}
fn enter_fragment_spread(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<FragmentSpread>) {}
fn exit_fragment_spread(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<FragmentSpread>) {}
fn enter_fragment_spread(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<FragmentSpread>) {
}
fn exit_fragment_spread(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<FragmentSpread>) {
}
fn enter_inline_fragment(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<InlineFragment>) {}
fn exit_inline_fragment(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<InlineFragment>) {}
fn enter_inline_fragment(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<InlineFragment>) {
}
fn exit_inline_fragment(&mut self,
_: &mut ValidatorContext<'a>,
_: &'a Spanning<InlineFragment>) {
}
fn enter_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 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 exit_list_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a Vec<Spanning<InputValue>>>) {}
fn enter_list_value(&mut self,
_: &mut ValidatorContext<'a>,
_: Spanning<&'a Vec<Spanning<InputValue>>>) {
}
fn exit_list_value(&mut self,
_: &mut ValidatorContext<'a>,
_: Spanning<&'a Vec<Spanning<InputValue>>>) {
}
fn 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_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>)) {}
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,
Directive, Arguments, Selection, Field, FragmentSpread, InlineFragment,
Operation, OperationType};
use ast::{Definition, Document, Fragment, VariableDefinitions, Type, InputValue, Directive,
Arguments, Selection, Field, FragmentSpread, InlineFragment, Operation, OperationType};
use schema::meta::Argument;
use parser::Spanning;
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);
}
fn visit_definitions<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, d: &'a Vec<Definition>) {
fn visit_definitions<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
d: &'a Vec<Definition>) {
for def in d {
let def_type = match *def {
let def_type =
match *def {
Definition::Fragment(Spanning {
item: Fragment { type_condition: Spanning { item: name, .. }, .. }, .. }) =>
Some(Type::NonNullNamed(name)),
item: Fragment {
type_condition: Spanning { item: name, .. }, ..
},
..
}) => Some(Type::NonNullNamed(name)),
Definition::Operation(Spanning {
item: Operation { operation_type: OperationType::Query, .. }, .. }) =>
Some(Type::NonNullNamed(ctx.schema.concrete_query_type().name().unwrap())),
Definition::Operation(Spanning {
item: Operation { operation_type: OperationType::Mutation, .. }, .. }) =>
ctx.schema.concrete_mutation_type()
.map(|t| Type::NonNullNamed(t.name().unwrap())),
item: Operation {
operation_type: OperationType::Mutation, ..
},
..
}) => {
ctx.schema
.concrete_mutation_type()
.map(|t| Type::NonNullNamed(t.name().unwrap()))
}
};
ctx.with_pushed_type(def_type.as_ref(), |ctx| {
@ -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 {
Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f),
}
}
fn exit_definition<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, def: &'a Definition) {
fn exit_definition<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
def: &'a Definition) {
match *def {
Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f),
}
}
fn visit_definition<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, def: &'a Definition) {
fn visit_definition<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
def: &'a Definition) {
match *def {
Definition::Operation(ref op) => {
visit_variable_definitions(v, ctx, &op.item.variable_definitions);
visit_directives(v, ctx, &op.item.directives);
visit_selection_set(v, ctx, &op.item.selection_set);
},
}
Definition::Fragment(ref f) => {
visit_directives(v, ctx, &f.item.directives);
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 {
for def in defs.item.iter() {
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 {
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);
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 {
for argument in arguments.item.iter() {
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| {
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 {
Selection::Field(ref field) => visit_field(v, ctx, field),
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()
.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);
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);
}
fn visit_inline_fragment<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, fragment: &'a Spanning<InlineFragment>) {
fn visit_inline_fragment<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
fragment: &'a Spanning<InlineFragment>) {
let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| {
v.enter_inline_fragment(ctx, fragment);
@ -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 {
ctx.with_pushed_type(Some(&Type::NonNullNamed(type_name)), visit_fn);
}
else {
} else {
visit_fn(ctx);
}
}
fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, input_value: &'a Spanning<InputValue>) {
fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
input_value: &'a Spanning<InputValue>) {
enter_input_value(v, ctx, input_value);
match input_value.item {
@ -186,10 +223,10 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
for field in fields {
let inner_type = ctx.current_input_type_literal()
.and_then(|t| match *t {
Type::NonNullNamed(name) | Type::Named(name) =>
ctx.schema.concrete_type_by_name(name),
_ => None,
})
Type::NonNullNamed(name) |
Type::Named(name) => ctx.schema.concrete_type_by_name(name),
_ => None,
})
.and_then(|ct| ct.input_field_by_name(&field.0.item))
.map(|f| &f.arg_type);
@ -201,16 +238,15 @@ fn visit_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'
}
}
InputValue::List(ref ls) => {
let inner_type = ctx.current_input_type_literal().and_then(|t| match *t {
Type::List(ref inner) | Type::NonNullList(ref inner) =>
Some(inner.as_ref().clone()),
_ => None,
});
let inner_type = ctx.current_input_type_literal()
.and_then(|t| match *t {
Type::List(ref inner) |
Type::NonNullList(ref inner) => Some(inner.as_ref().clone()),
_ => None,
});
ctx.with_pushed_input_type(inner_type.as_ref(), |ctx| {
for value in ls {
visit_input_value(v, ctx, value);
}
ctx.with_pushed_input_type(inner_type.as_ref(), |ctx| for value in ls {
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);
}
fn enter_input_value<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, input_value: &'a Spanning<InputValue>) {
fn enter_input_value<'a, V: Visitor<'a>>(v: &mut V,
ctx: &mut ValidatorContext<'a>,
input_value: &'a Spanning<InputValue>) {
use InputValue::*;
let start = &input_value.start;
@ -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::*;
let start = &input_value.start;

View file

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

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 serde;
extern crate juniper;
extern crate juniper_iron;
use std::env;
@ -10,7 +11,7 @@ use mount::Mount;
use logger::Logger;
use iron::prelude::*;
use juniper::EmptyMutation;
use juniper::iron_handlers::{GraphQLHandler, GraphiQLHandler};
use juniper_iron::{GraphQLHandler, GraphiQLHandler};
use juniper::tests::model::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)
//! example for more information on how to use these handlers.
/*!
[Juniper][1] handlers for the [Iron][2] framework.
## 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::middleware::Handler;
@ -14,11 +110,10 @@ use std::io::Read;
use std::error::Error;
use std::fmt;
use serde_json;
use serde_json::error::Error as SerdeError;
use ::{InputValue, GraphQLType, RootNode};
use ::http;
use juniper::{InputValue, GraphQLType, RootNode};
use juniper::http;
/// Handler that executes GraphQL queries in the given schema
///
@ -50,18 +145,18 @@ fn get_single_value<T>(mut values: Vec<T>) -> IronResult<T> {
if values.len() == 1 {
Ok(values.remove(0))
}
else {
Err(GraphQLIronError::InvalidData("Duplicate URL query parameter").into())
}
else {
Err(GraphQLIronError::InvalidData("Duplicate URL query parameter").into())
}
}
fn parse_url_param(params: Option<Vec<String>>) -> IronResult<Option<String>> {
if let Some(values) = params {
get_single_value(values).map(Some)
}
else {
Ok(None)
}
else {
Ok(None)
}
}
fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputValue>> {
@ -70,14 +165,14 @@ fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputV
.map(Some)
.map_err(GraphQLIronError::Serde)?)
}
else {
Ok(None)
}
else {
Ok(None)
}
}
impl<'a, CtxFactory, Query, Mutation, CtxT>
GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
CtxT: 'static,
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
@ -100,7 +195,7 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
fn handle_get(&self, req: &mut Request) -> IronResult<http::GraphQLRequest> {
let url_query_string = req.get_mut::<UrlEncodedQuery>()
.map_err(|e| GraphQLIronError::Url(e))?;
let input_query = parse_url_param(url_query_string.remove("query"))?
.ok_or_else(|| GraphQLIronError::InvalidData("No query provided"))?;
let operation_name = parse_url_param(url_query_string.remove("operationName"))?;
@ -112,7 +207,7 @@ impl<'a, CtxFactory, Query, Mutation, CtxT>
fn handle_post(&self, req: &mut Request) -> IronResult<http::GraphQLRequest> {
let mut request_payload = String::new();
itry!(req.body.read_to_string(&mut request_payload));
Ok(serde_json::from_str::<http::GraphQLRequest>(request_payload.as_str())
.map_err(|err| GraphQLIronError::Serde(err))?)
}
@ -142,8 +237,8 @@ impl GraphiQLHandler {
}
impl<'a, CtxFactory, Query, Mutation, CtxT>
Handler
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
Handler
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
where CtxFactory: Fn(&mut Request) -> CtxT + Send + Sync + 'static,
CtxT: 'static,
Query: GraphQLType<Context=CtxT> + Send + Sync + 'static,
@ -169,7 +264,7 @@ impl Handler for GraphiQLHandler {
Ok(Response::with((
content_type,
status::Ok,
::graphiql::graphiql_source(&self.graphql_url),
juniper::graphiql::graphiql_source(&self.graphql_url),
)))
}
}
@ -193,11 +288,11 @@ impl fmt::Display for GraphQLIronError {
impl Error for GraphQLIronError {
fn description(&self) -> &str {
match *self {
GraphQLIronError::Serde(ref err) => err.description(),
GraphQLIronError::Url(ref err) => err.description(),
GraphQLIronError::InvalidData(ref err) => err,
}
match *self {
GraphQLIronError::Serde(ref err) => err.description(),
GraphQLIronError::Url(ref err) => err.description(),
GraphQLIronError::InvalidData(ref err) => err,
}
}
fn cause(&self) -> Option<&Error> {
@ -216,16 +311,15 @@ impl From<GraphQLIronError> for IronError {
}
}
#[cfg(test)]
mod tests {
use iron::prelude::*;
use iron_test::{request, response};
use iron::{Handler, Headers};
use ::tests::model::Database;
use juniper::tests::model::Database;
use ::http::tests as http_tests;
use types::scalars::EmptyMutation;
use juniper::EmptyMutation;
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 juniper;
extern crate juniper_rocket;
use rocket::response::content;
use rocket::State;
@ -10,30 +11,28 @@ use rocket::State;
use juniper::tests::model::Database;
use juniper::{EmptyMutation, RootNode};
use juniper::rocket_handlers;
type Schema = RootNode<'static, Database, EmptyMutation<Database>>;
#[get("/")]
fn graphiql() -> content::HTML<String> {
rocket_handlers::graphiql_source("/graphql")
fn graphiql() -> content::Html<String> {
juniper_rocket::graphiql_source("/graphql")
}
#[get("/graphql?<request>")]
fn get_graphql_handler(
context: State<Database>,
request: rocket_handlers::GraphQLRequest,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> rocket_handlers::GraphQLResponse {
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
#[post("/graphql", data="<request>")]
fn post_graphql_handler(
context: State<Database>,
request: rocket_handlers::GraphQLRequest,
request: juniper_rocket::GraphQLRequest,
schema: State<Schema>,
) -> rocket_handlers::GraphQLResponse {
) -> juniper_rocket::GraphQLResponse {
request.execute(&schema, &context)
}
@ -43,4 +42,4 @@ fn main() {
.manage(Schema::new(Database::new(), EmptyMutation::<Database>::new()))
.mount("/", routes![graphiql, get_graphql_handler, post_graphql_handler])
.launch();
}
}

View file

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