Introduce an abstraction for scalar values (#251)

Introduce an abstraction for scalar values

Before this change,  possible scalar values were hard coded to be representable
by one of the following types: `i32`, `f64`, `String` or `bool`. This
restricts the types of custom scalar values that can be defined. For
example, it was not possible to define a scalar value that represents an
`i64` without mapping it to a string (which would be inefficient).

One solution to fix the example above would simply be to change the
internal representation to allow it to represent an `i64`, but this would
only fix the problem for one type (until someone wants to support
`i128` for example). Also this would make juniper not follow the
GraphQL standard closely.

This commit takes another approach, by making the exact "internal"
representation of scalar values swappable (in such a way that a downstream crate could provide its own representation tailored to their needs). This allows juniper to provide a default type that only
contains the types described in the standard whereas other crates could define custom scalars for their needs.

To accomplish this we need to change several things in the current implementation:

* Add some traits that abstract the behavior of such a scalar value representation
* Change `Value` and `InputValue` to have a scalar variant (with a
  generic type) instead of hard coded variants for the standard
  types. This implies adding a generic parameter to both enums that
  needs to be added in the whole crate.
* Change the parser to allow deciding between different types of
  scalar values. The problem is basically that the original parser
  implementation had no way to know whether a parsed integer number is
  a `i32` or a `i64` (for example). To fix this we added some knowledge
  of the existing schema to the parser.
* Fix some macros and derives to follow the new behavior.

This commit also contains an unrelated change about the way `juniper_codegen`
resolves items from `juniper`. The `_internal` flag is removed and
the resolution is replaced by a macro.

The scalar parsing strategy is as follows:

* Pass optional type information all the way down in the parser. If a
  field/type/… does note exist, just do not pass down the type
  information.
* The lexer now distinguishes between several fundamental scalar types (`String`, `Float`, `Int`). It does not try to actually parse those values, instead it just annotates them that this is a floating point number, an integer number, or a string value, etc.
* If type information exists while parsing a scalar value, try the following:
    1. Try parsing the value using that type information.
    2. If that fails try parsing the value using the inferred type information from the lexer.
* If no type information exists, try parsing the scalar value using the inferred type from the lexer,

All macros support the introduced scalar value abstraction. It is now possible to specify if a certain implementation should be based on a specific scalar value representation or be generic about the exact representation. All macros now default to the `DefaultScalarValue` type provided by
`juniper` if no scalar value representation is specified. This is done with usability and backwards compatibility in mind.

Finally, we allow specifying the scalar value representations via an attribute
(`#[graphql(scalar = "Type")]`). A default generic implementation
is provided.
This commit is contained in:
Georg Semmler 2018-10-23 03:40:14 +00:00 committed by Christian Legnitto
parent bd455c9130
commit 2e5df9f8a4
108 changed files with 8052 additions and 4928 deletions

View file

@ -6,4 +6,6 @@ members = [
"juniper_hyper", "juniper_hyper",
"juniper_iron", "juniper_iron",
"juniper_rocket", "juniper_rocket",
"juniper_warp",
"juniper_warp/examples/warp_server/",
] ]

View file

@ -1,3 +1,16 @@
# [master] yyyy-mm-dd # [master] yyyy-mm-dd
## Changes ## Changes
- Juniper is now generic about the exact representation of scalar values. This
allows downstream crates to add support for own scalar value representations.
There are two use cases for this feature:
* You want to support new scalar types not representable by the provided default
scalar value representation like for example `i64`
* You want to support a type from a third party crate that is not supported by juniper
**Note:** This may need some changes in down stream code, especially if working with
generic code. To retain the current behaviour use `DefaultScalarValue` as scalar value type
[#251](https://github.com/graphql-rust/juniper/pull/251)

View file

@ -27,7 +27,7 @@ expose-test-schema = []
default = [ default = [
"chrono", "chrono",
"url", "url",
"uuid" "uuid",
] ]
[dependencies] [dependencies]

View file

@ -8,6 +8,7 @@ use indexmap::IndexMap;
use executor::Variables; use executor::Variables;
use parser::Spanning; use parser::Spanning;
use value::{ScalarRefValue, ScalarValue, DefaultScalarValue};
/// A type literal in the syntax tree /// A type literal in the syntax tree
/// ///
@ -35,56 +36,53 @@ pub enum Type<'a> {
/// ///
/// Lists and objects variants are _spanned_, i.e. they contain a reference to /// Lists and objects variants are _spanned_, i.e. they contain a reference to
/// their position in the source file, if available. /// their position in the source file, if available.
#[derive(Clone, PartialEq, Debug)] #[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum InputValue { pub enum InputValue<S = DefaultScalarValue> {
Null, Null,
Int(i32), Scalar(S),
Float(f64),
String(String),
Boolean(bool),
Enum(String), Enum(String),
Variable(String), Variable(String),
List(Vec<Spanning<InputValue>>), List(Vec<Spanning<InputValue<S>>>),
Object(Vec<(Spanning<String>, Spanning<InputValue>)>), Object(Vec<(Spanning<String>, Spanning<InputValue<S>>)>),
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct VariableDefinition<'a> { pub struct VariableDefinition<'a, S> {
pub var_type: Spanning<Type<'a>>, pub var_type: Spanning<Type<'a>>,
pub default_value: Option<Spanning<InputValue>>, pub default_value: Option<Spanning<InputValue<S>>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Arguments<'a> { pub struct Arguments<'a, S> {
pub items: Vec<(Spanning<&'a str>, Spanning<InputValue>)>, pub items: Vec<(Spanning<&'a str>, Spanning<InputValue<S>>)>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct VariableDefinitions<'a> { pub struct VariableDefinitions<'a, S> {
pub items: Vec<(Spanning<&'a str>, VariableDefinition<'a>)>, pub items: Vec<(Spanning<&'a str>, VariableDefinition<'a, S>)>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Field<'a> { pub struct Field<'a, S> {
pub alias: Option<Spanning<&'a str>>, pub alias: Option<Spanning<&'a str>>,
pub name: Spanning<&'a str>, pub name: Spanning<&'a str>,
pub arguments: Option<Spanning<Arguments<'a>>>, pub arguments: Option<Spanning<Arguments<'a, S>>>,
pub directives: Option<Vec<Spanning<Directive<'a>>>>, pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
pub selection_set: Option<Vec<Selection<'a>>>, pub selection_set: Option<Vec<Selection<'a, S>>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct FragmentSpread<'a> { pub struct FragmentSpread<'a, S> {
pub name: Spanning<&'a str>, pub name: Spanning<&'a str>,
pub directives: Option<Vec<Spanning<Directive<'a>>>>, pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct InlineFragment<'a> { pub struct InlineFragment<'a, S> {
pub type_condition: Option<Spanning<&'a str>>, pub type_condition: Option<Spanning<&'a str>>,
pub directives: Option<Vec<Spanning<Directive<'a>>>>, pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
pub selection_set: Vec<Selection<'a>>, pub selection_set: Vec<Selection<'a, S>>,
} }
/// Entry in a GraphQL selection set /// Entry in a GraphQL selection set
@ -104,16 +102,16 @@ pub struct InlineFragment<'a> {
/// ``` /// ```
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum Selection<'a> { pub enum Selection<'a, S = DefaultScalarValue> {
Field(Spanning<Field<'a>>), Field(Spanning<Field<'a, S>>),
FragmentSpread(Spanning<FragmentSpread<'a>>), FragmentSpread(Spanning<FragmentSpread<'a, S>>),
InlineFragment(Spanning<InlineFragment<'a>>), InlineFragment(Spanning<InlineFragment<'a, S>>),
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Directive<'a> { pub struct Directive<'a, S> {
pub name: Spanning<&'a str>, pub name: Spanning<&'a str>,
pub arguments: Option<Spanning<Arguments<'a>>>, pub arguments: Option<Spanning<Arguments<'a, S>>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
@ -123,29 +121,29 @@ pub enum OperationType {
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Operation<'a> { pub struct Operation<'a, S> {
pub operation_type: OperationType, pub operation_type: OperationType,
pub name: Option<Spanning<&'a str>>, pub name: Option<Spanning<&'a str>>,
pub variable_definitions: Option<Spanning<VariableDefinitions<'a>>>, pub variable_definitions: Option<Spanning<VariableDefinitions<'a, S>>>,
pub directives: Option<Vec<Spanning<Directive<'a>>>>, pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
pub selection_set: Vec<Selection<'a>>, pub selection_set: Vec<Selection<'a, S>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub struct Fragment<'a> { pub struct Fragment<'a, S> {
pub name: Spanning<&'a str>, pub name: Spanning<&'a str>,
pub type_condition: Spanning<&'a str>, pub type_condition: Spanning<&'a str>,
pub directives: Option<Vec<Spanning<Directive<'a>>>>, pub directives: Option<Vec<Spanning<Directive<'a, S>>>>,
pub selection_set: Vec<Selection<'a>>, pub selection_set: Vec<Selection<'a, S>>,
} }
#[derive(Clone, PartialEq, Debug)] #[derive(Clone, PartialEq, Debug)]
pub enum Definition<'a> { pub enum Definition<'a, S> {
Operation(Spanning<Operation<'a>>), Operation(Spanning<Operation<'a, S>>),
Fragment(Spanning<Fragment<'a>>), Fragment(Spanning<Fragment<'a, S>>),
} }
pub type Document<'a> = Vec<Definition<'a>>; pub type Document<'a, S> = Vec<Definition<'a, S>>;
/// Parse an unstructured input value into a Rust data type. /// Parse an unstructured input value into a Rust data type.
/// ///
@ -153,15 +151,17 @@ pub type Document<'a> = Vec<Definition<'a>>;
/// automatically by the convenience macro `graphql_scalar!` or by deriving GraphQLEnum. /// automatically by the convenience macro `graphql_scalar!` or by deriving GraphQLEnum.
/// ///
/// Must be implemented manually when manually exposing new enums or scalars. /// Must be implemented manually when manually exposing new enums or scalars.
pub trait FromInputValue: Sized { pub trait FromInputValue<S = DefaultScalarValue>: Sized {
/// Performs the conversion. /// Performs the conversion.
fn from_input_value(v: &InputValue) -> Option<Self>; fn from_input_value(v: &InputValue<S>) -> Option<Self>
where
for<'b> &'b S: ScalarRefValue<'b>;
} }
/// Losslessly clones a Rust data type into an InputValue. /// Losslessly clones a Rust data type into an InputValue.
pub trait ToInputValue: Sized { pub trait ToInputValue<S = DefaultScalarValue>: Sized {
/// Performs the conversion. /// Performs the conversion.
fn to_input_value(&self) -> InputValue; fn to_input_value(&self) -> InputValue<S>;
} }
impl<'a> Type<'a> { impl<'a> Type<'a> {
@ -205,39 +205,54 @@ impl<'a> fmt::Display for Type<'a> {
} }
} }
impl InputValue { impl<S> InputValue<S>
where
S: ScalarValue,
{
/// Construct a null value. /// Construct a null value.
pub fn null() -> InputValue { pub fn null() -> Self {
InputValue::Null InputValue::Null
} }
/// Construct an integer value. /// Construct an integer value.
pub fn int(i: i32) -> InputValue { #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")]
InputValue::Int(i) pub fn int(i: i32) -> Self {
Self::scalar(i)
} }
/// Construct a floating point value. /// Construct a floating point value.
pub fn float(f: f64) -> InputValue { #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")]
InputValue::Float(f) pub fn float(f: f64) -> Self {
Self::scalar(f)
} }
/// Construct a boolean value. /// Construct a boolean value.
pub fn boolean(b: bool) -> InputValue { #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")]
InputValue::Boolean(b) pub fn boolean(b: bool) -> Self {
Self::scalar(b)
} }
/// Construct a string value. /// Construct a string value.
pub fn string<T: AsRef<str>>(s: T) -> InputValue { #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")]
InputValue::String(s.as_ref().to_owned()) pub fn string<T: AsRef<str>>(s: T) -> Self {
InputValue::scalar(s.as_ref().to_owned())
}
/// Construct a scalar value
pub fn scalar<T>(v: T) -> Self
where
T: Into<S>,
{
InputValue::Scalar(v.into())
} }
/// Construct an enum value. /// Construct an enum value.
pub fn enum_value<T: AsRef<str>>(s: T) -> InputValue { pub fn enum_value<T: AsRef<str>>(s: T) -> Self {
InputValue::Enum(s.as_ref().to_owned()) InputValue::Enum(s.as_ref().to_owned())
} }
/// Construct a variable value. /// Construct a variable value.
pub fn variable<T: AsRef<str>>(v: T) -> InputValue { pub fn variable<T: AsRef<str>>(v: T) -> Self {
InputValue::Variable(v.as_ref().to_owned()) InputValue::Variable(v.as_ref().to_owned())
} }
@ -246,12 +261,12 @@ impl InputValue {
/// Convenience function to make each `InputValue` in the input vector /// Convenience function to make each `InputValue` in the input vector
/// not contain any location information. Can be used from `ToInputValue` /// not contain any location information. Can be used from `ToInputValue`
/// implementations, where no source code position information is available. /// implementations, where no source code position information is available.
pub fn list(l: Vec<InputValue>) -> InputValue { pub fn list(l: Vec<Self>) -> Self {
InputValue::List(l.into_iter().map(Spanning::unlocated).collect()) InputValue::List(l.into_iter().map(Spanning::unlocated).collect())
} }
/// Construct a located list. /// Construct a located list.
pub fn parsed_list(l: Vec<Spanning<InputValue>>) -> InputValue { pub fn parsed_list(l: Vec<Spanning<Self>>) -> Self {
InputValue::List(l) InputValue::List(l)
} }
@ -259,7 +274,7 @@ impl InputValue {
/// ///
/// Similar to `InputValue::list`, it makes each key and value in the given /// Similar to `InputValue::list`, it makes each key and value in the given
/// hash map not contain any location information. /// hash map not contain any location information.
pub fn object<K>(o: IndexMap<K, InputValue>) -> InputValue pub fn object<K>(o: IndexMap<K, Self>) -> Self
where where
K: AsRef<str> + Eq + Hash, K: AsRef<str> + Eq + Hash,
{ {
@ -270,18 +285,17 @@ impl InputValue {
Spanning::unlocated(k.as_ref().to_owned()), Spanning::unlocated(k.as_ref().to_owned()),
Spanning::unlocated(v), Spanning::unlocated(v),
) )
}) }).collect(),
.collect(),
) )
} }
/// Construct a located object. /// Construct a located object.
pub fn parsed_object(o: Vec<(Spanning<String>, Spanning<InputValue>)>) -> InputValue { pub fn parsed_object(o: Vec<(Spanning<String>, Spanning<Self>)>) -> Self {
InputValue::Object(o) InputValue::Object(o)
} }
/// Resolve all variables to their values. /// Resolve all variables to their values.
pub fn into_const(self, vars: &Variables) -> InputValue { pub fn into_const(self, vars: &Variables<S>) -> Self {
match self { match self {
InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone), InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone),
InputValue::List(l) => InputValue::List( InputValue::List(l) => InputValue::List(
@ -301,9 +315,10 @@ impl InputValue {
/// Shorthand form of invoking `FromInputValue::from()`. /// Shorthand form of invoking `FromInputValue::from()`.
pub fn convert<T>(&self) -> Option<T> pub fn convert<T>(&self) -> Option<T>
where where
T: FromInputValue, T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
<T as FromInputValue>::from_input_value(self) <T as FromInputValue<S>>::from_input_value(self)
} }
/// Does the value represent null? /// Does the value represent null?
@ -331,34 +346,63 @@ impl InputValue {
} }
/// View the underlying int value, if present. /// View the underlying int value, if present.
pub fn as_int_value(&self) -> Option<i32> { #[deprecated(
match *self { since = "0.11.0",
InputValue::Int(i) => Some(i), note = "Use `InputValue::as_scalar_value` instead"
_ => None, )]
} pub fn as_int_value<'a>(&'a self) -> Option<i32>
where
&'a S: Into<Option<&'a i32>>,
{
self.as_scalar_value().map(|i| *i)
} }
/// View the underlying float value, if present. /// View the underlying float value, if present.
pub fn as_float_value(&self) -> Option<f64> { #[deprecated(
since = "0.11.0",
note = "Use `InputValue::as_scalar_value` instead"
)]
pub fn as_float_value<'a>(&'a self) -> Option<f64>
where
&'a S: Into<Option<&'a f64>>,
{
self.as_scalar_value().map(|f| *f)
}
/// View the underlying string value, if present.
#[deprecated(
since = "0.11.0",
note = "Use `InputValue::as_scalar_value` instead"
)]
pub fn as_string_value<'a>(&'a self) -> Option<&'a str>
where
&'a S: Into<Option<&'a String>>,
{
self.as_scalar_value().map(|s| s as &str)
}
/// View the underlying scalar value, if present.
pub fn as_scalar(&self) -> Option<&S> {
match *self { match *self {
InputValue::Float(f) => Some(f), InputValue::Scalar(ref s) => Some(s),
_ => None, _ => None,
} }
} }
/// View the underlying string value, if present. /// View the underlying scalar value, if present.
pub fn as_string_value(&self) -> Option<&str> { pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T>
match *self { where
InputValue::String(ref s) => Some(s), T: 'a,
_ => None, &'a S: Into<Option<&'a T>>,
} {
self.as_scalar().and_then(Into::into)
} }
/// Convert the input value to an unlocated object value. /// Convert the input value to an unlocated object value.
/// ///
/// This constructs a new IndexMap that contain references to the keys /// This constructs a new IndexMap that contain references to the keys
/// and values in `self`. /// and values in `self`.
pub fn to_object_value(&self) -> Option<IndexMap<&str, &InputValue>> { pub fn to_object_value<'a>(&'a self) -> Option<IndexMap<&'a str, &'a Self>> {
match *self { match *self {
InputValue::Object(ref o) => Some( InputValue::Object(ref o) => Some(
o.iter() o.iter()
@ -373,7 +417,7 @@ impl InputValue {
/// ///
/// This constructs a new vector that contain references to the values /// This constructs a new vector that contain references to the values
/// in `self`. /// in `self`.
pub fn to_list_value(&self) -> Option<Vec<&InputValue>> { pub fn to_list_value(&self) -> Option<Vec<&Self>> {
match *self { match *self {
InputValue::List(ref l) => Some(l.iter().map(|s| &s.item).collect()), InputValue::List(ref l) => Some(l.iter().map(|s| &s.item).collect()),
_ => None, _ => None,
@ -384,10 +428,12 @@ impl InputValue {
pub fn referenced_variables(&self) -> Vec<&str> { pub fn referenced_variables(&self) -> Vec<&str> {
match *self { match *self {
InputValue::Variable(ref name) => vec![name], InputValue::Variable(ref name) => vec![name],
InputValue::List(ref l) => l.iter() InputValue::List(ref l) => l
.iter()
.flat_map(|v| v.item.referenced_variables()) .flat_map(|v| v.item.referenced_variables())
.collect(), .collect(),
InputValue::Object(ref obj) => obj.iter() InputValue::Object(ref obj) => obj
.iter()
.flat_map(|&(_, ref v)| v.item.referenced_variables()) .flat_map(|&(_, ref v)| v.item.referenced_variables())
.collect(), .collect(),
_ => vec![], _ => vec![],
@ -395,18 +441,15 @@ impl InputValue {
} }
/// Compare equality with another `InputValue` ignoring any source position information. /// Compare equality with another `InputValue` ignoring any source position information.
pub fn unlocated_eq(&self, other: &InputValue) -> bool { pub fn unlocated_eq(&self, other: &Self) -> bool {
use InputValue::*; use InputValue::*;
match (self, other) { match (self, other) {
(&Null, &Null) => true, (&Null, &Null) => true,
(&Int(i1), &Int(i2)) => i1 == i2, (&Scalar(ref s1), &Scalar(ref s2)) => s1 == s2,
(&Float(f1), &Float(f2)) => f1 == f2, (&Enum(ref s1), &Enum(ref s2)) | (&Variable(ref s1), &Variable(ref s2)) => s1 == s2,
(&String(ref s1), &String(ref s2)) (&List(ref l1), &List(ref l2)) => l1
| (&Enum(ref s1), &Enum(ref s2)) .iter()
| (&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()) .zip(l2.iter())
.all(|(v1, v2)| v1.item.unlocated_eq(&v2.item)), .all(|(v1, v2)| v1.item.unlocated_eq(&v2.item)),
(&Object(ref o1), &Object(ref o2)) => { (&Object(ref o1), &Object(ref o2)) => {
@ -421,14 +464,16 @@ impl InputValue {
} }
} }
impl fmt::Display for InputValue { impl<S> fmt::Display for InputValue<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
InputValue::Null => write!(f, "null"), InputValue::Null => write!(f, "null"),
InputValue::Int(v) => write!(f, "{}", v), InputValue::Scalar(ref s) if s.is_type::<String>() => write!(f, "\"{}\"", s),
InputValue::Float(v) => write!(f, "{}", v), InputValue::Scalar(ref s) => write!(f, "{}", s),
InputValue::String(ref v) => write!(f, "\"{}\"", v),
InputValue::Boolean(v) => write!(f, "{}", v),
InputValue::Enum(ref v) => write!(f, "{}", v), InputValue::Enum(ref v) => write!(f, "{}", v),
InputValue::Variable(ref v) => write!(f, "${}", v), InputValue::Variable(ref v) => write!(f, "${}", v),
InputValue::List(ref v) => { InputValue::List(ref v) => {
@ -460,16 +505,16 @@ impl fmt::Display for InputValue {
} }
} }
impl<'a> Arguments<'a> { impl<'a, S> Arguments<'a, S> {
pub fn into_iter(self) -> vec::IntoIter<(Spanning<&'a str>, Spanning<InputValue>)> { pub fn into_iter(self) -> vec::IntoIter<(Spanning<&'a str>, Spanning<InputValue<S>>)> {
self.items.into_iter() self.items.into_iter()
} }
pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, Spanning<InputValue>)> { pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, Spanning<InputValue<S>>)> {
self.items.iter() self.items.iter()
} }
pub fn iter_mut(&mut self) -> slice::IterMut<(Spanning<&'a str>, Spanning<InputValue>)> { pub fn iter_mut(&mut self) -> slice::IterMut<(Spanning<&'a str>, Spanning<InputValue<S>>)> {
self.items.iter_mut() self.items.iter_mut()
} }
@ -477,7 +522,7 @@ impl<'a> Arguments<'a> {
self.items.len() self.items.len()
} }
pub fn get(&self, key: &str) -> Option<&Spanning<InputValue>> { pub fn get(&self, key: &str) -> Option<&Spanning<InputValue<S>>> {
self.items self.items
.iter() .iter()
.filter(|&&(ref k, _)| k.item == key) .filter(|&&(ref k, _)| k.item == key)
@ -486,8 +531,8 @@ impl<'a> Arguments<'a> {
} }
} }
impl<'a> VariableDefinitions<'a> { impl<'a, S> VariableDefinitions<'a, S> {
pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, VariableDefinition)> { pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, VariableDefinition<S>)> {
self.items.iter() self.items.iter()
} }
} }
@ -499,42 +544,42 @@ mod tests {
#[test] #[test]
fn test_input_value_fmt() { fn test_input_value_fmt() {
let value = InputValue::null(); let value: InputValue = InputValue::null();
assert_eq!(format!("{}", value), "null"); assert_eq!(format!("{}", value), "null");
let value = InputValue::int(123); let value: InputValue = InputValue::scalar(123);
assert_eq!(format!("{}", value), "123"); assert_eq!(format!("{}", value), "123");
let value = InputValue::float(12.3); let value: InputValue = InputValue::scalar(12.3);
assert_eq!(format!("{}", value), "12.3"); assert_eq!(format!("{}", value), "12.3");
let value = InputValue::string("FOO".to_owned()); let value: InputValue = InputValue::scalar("FOO".to_owned());
assert_eq!(format!("{}", value), "\"FOO\""); assert_eq!(format!("{}", value), "\"FOO\"");
let value = InputValue::boolean(true); let value: InputValue = InputValue::scalar(true);
assert_eq!(format!("{}", value), "true"); assert_eq!(format!("{}", value), "true");
let value = InputValue::enum_value("BAR".to_owned()); let value: InputValue = InputValue::enum_value("BAR".to_owned());
assert_eq!(format!("{}", value), "BAR"); assert_eq!(format!("{}", value), "BAR");
let value = InputValue::variable("baz".to_owned()); let value: InputValue = InputValue::variable("baz".to_owned());
assert_eq!(format!("{}", value), "$baz"); assert_eq!(format!("{}", value), "$baz");
let list = vec![InputValue::int(1), InputValue::int(2)]; let list = vec![InputValue::scalar(1), InputValue::scalar(2)];
let value = InputValue::list(list); let value: InputValue = InputValue::list(list);
assert_eq!(format!("{}", value), "[1, 2]"); assert_eq!(format!("{}", value), "[1, 2]");
let object = vec![ let object = vec![
( (
Spanning::unlocated("foo".to_owned()), Spanning::unlocated("foo".to_owned()),
Spanning::unlocated(InputValue::int(1)), Spanning::unlocated(InputValue::scalar(1)),
), ),
( (
Spanning::unlocated("bar".to_owned()), Spanning::unlocated("bar".to_owned()),
Spanning::unlocated(InputValue::int(2)), Spanning::unlocated(InputValue::scalar(2)),
), ),
]; ];
let value = InputValue::parsed_object(object); let value: InputValue = InputValue::parsed_object(object);
assert_eq!(format!("{}", value), "{foo: 1, bar: 2}"); assert_eq!(format!("{}", value), "{foo: 1, bar: 2}");
} }
} }

View file

@ -1,5 +1,6 @@
use ast::{Directive, Fragment, InputValue, Selection}; use ast::{Directive, Fragment, InputValue, Selection};
use parser::Spanning; use parser::Spanning;
use value::{ScalarRefValue, ScalarValue};
use std::collections::HashMap; use std::collections::HashMap;
@ -21,25 +22,22 @@ pub enum Applies<'a> {
/// meaning that variables are already resolved. /// meaning that variables are already resolved.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum LookAheadValue<'a> { pub enum LookAheadValue<'a, S: 'a> {
Null, Null,
Int(i32), Scalar(&'a S),
Float(f64),
String(&'a str),
Boolean(bool),
Enum(&'a str), Enum(&'a str),
List(Vec<LookAheadValue<'a>>), List(Vec<LookAheadValue<'a, S>>),
Object(Vec<(&'a str, LookAheadValue<'a>)>), Object(Vec<(&'a str, LookAheadValue<'a, S>)>),
} }
impl<'a> LookAheadValue<'a> { impl<'a, S> LookAheadValue<'a, S>
fn from_input_value(input_value: &'a InputValue, vars: &'a Variables) -> Self { where
S: ScalarValue,
{
fn from_input_value(input_value: &'a InputValue<S>, vars: &'a Variables<S>) -> Self {
match *input_value { match *input_value {
InputValue::Null => LookAheadValue::Null, InputValue::Null => LookAheadValue::Null,
InputValue::Int(i) => LookAheadValue::Int(i), InputValue::Scalar(ref s) => LookAheadValue::Scalar(s),
InputValue::Float(f) => LookAheadValue::Float(f),
InputValue::String(ref s) => LookAheadValue::String(s),
InputValue::Boolean(b) => LookAheadValue::Boolean(b),
InputValue::Enum(ref e) => LookAheadValue::Enum(e), InputValue::Enum(ref e) => LookAheadValue::Enum(e),
InputValue::Variable(ref v) => Self::from_input_value(vars.get(v).unwrap(), vars), InputValue::Variable(ref v) => Self::from_input_value(vars.get(v).unwrap(), vars),
InputValue::List(ref l) => LookAheadValue::List( InputValue::List(ref l) => LookAheadValue::List(
@ -54,8 +52,7 @@ impl<'a> LookAheadValue<'a> {
&n.item as &str, &n.item as &str,
LookAheadValue::from_input_value(&i.item, vars), LookAheadValue::from_input_value(&i.item, vars),
) )
}) }).collect(),
.collect(),
), ),
} }
} }
@ -63,15 +60,18 @@ impl<'a> LookAheadValue<'a> {
/// An argument passed into the query /// An argument passed into the query
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LookAheadArgument<'a> { pub struct LookAheadArgument<'a, S: 'a> {
name: &'a str, name: &'a str,
value: LookAheadValue<'a>, value: LookAheadValue<'a, S>,
} }
impl<'a> LookAheadArgument<'a> { impl<'a, S> LookAheadArgument<'a, S>
where
S: ScalarValue,
{
pub(super) fn new( pub(super) fn new(
&(ref name, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>), &(ref name, ref value): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
vars: &'a Variables, vars: &'a Variables<S>,
) -> Self { ) -> Self {
LookAheadArgument { LookAheadArgument {
name: name.item, name: name.item,
@ -80,86 +80,100 @@ impl<'a> LookAheadArgument<'a> {
} }
/// The value of the argument /// The value of the argument
pub fn value(&'a self) -> &LookAheadValue<'a> { pub fn value(&'a self) -> &LookAheadValue<'a, S> {
&self.value &self.value
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct ChildSelection<'a> { pub struct ChildSelection<'a, S: 'a> {
pub(super) inner: LookAheadSelection<'a>, pub(super) inner: LookAheadSelection<'a, S>,
pub(super) applies_for: Applies<'a>, pub(super) applies_for: Applies<'a>,
} }
/// A selection performed by a query /// A selection performed by a query
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct LookAheadSelection<'a> { pub struct LookAheadSelection<'a, S: 'a> {
pub(super) name: &'a str, pub(super) name: &'a str,
pub(super) alias: Option<&'a str>, pub(super) alias: Option<&'a str>,
pub(super) arguments: Vec<LookAheadArgument<'a>>, pub(super) arguments: Vec<LookAheadArgument<'a, S>>,
pub(super) children: Vec<ChildSelection<'a>>, pub(super) children: Vec<ChildSelection<'a, S>>,
} }
impl<'a> LookAheadSelection<'a> { impl<'a, S> LookAheadSelection<'a, S>
fn should_include(directives: Option<&Vec<Spanning<Directive>>>, vars: &Variables) -> bool { where
S: ScalarValue,
&'a S: ScalarRefValue<'a>,
{
fn should_include<'b, 'c>(
directives: Option<&'b Vec<Spanning<Directive<S>>>>,
vars: &'c Variables<S>,
) -> bool
where
'b: 'a,
'c: 'a,
{
directives directives
.map(|d| { .map(|d| {
d.iter().all(|d| { d.iter().all(|d| {
let d = &d.item; let d = &d.item;
let arguments = &d.arguments; let arguments = &d.arguments;
match (d.name.item, arguments) { match (d.name.item, arguments) {
("include", &Some(ref a)) => a.item ("include", &Some(ref a)) => a
.item
.items .items
.iter() .iter()
.find(|item| item.0.item == "if") .find(|item| item.0.item == "if")
.map(|&(_, ref v)| { .map(|&(_, ref v)| {
if let LookAheadValue::Boolean(b) = if let LookAheadValue::Scalar(s) =
LookAheadValue::from_input_value(&v.item, vars) LookAheadValue::from_input_value(&v.item, vars)
{ {
b <&S as Into<Option<&bool>>>::into(s)
.map(|b| *b)
.unwrap_or(false)
} else { } else {
false false
} }
}) }).unwrap_or(false),
.unwrap_or(false), ("skip", &Some(ref a)) => a
("skip", &Some(ref a)) => a.item .item
.items .items
.iter() .iter()
.find(|item| item.0.item == "if") .find(|item| item.0.item == "if")
.map(|&(_, ref v)| { .map(|&(_, ref v)| {
if let LookAheadValue::Boolean(b) = if let LookAheadValue::Scalar(b) =
LookAheadValue::from_input_value(&v.item, vars) LookAheadValue::from_input_value(&v.item, vars)
{ {
!b <&S as Into<Option<&bool>>>::into(b)
.map(::std::ops::Not::not)
.unwrap_or(false)
} else { } else {
false false
} }
}) }).unwrap_or(false),
.unwrap_or(false),
("skip", &None) => false, ("skip", &None) => false,
("include", &None) => true, ("include", &None) => true,
(_, _) => unreachable!(), (_, _) => unreachable!(),
} }
}) })
}) }).unwrap_or(true)
.unwrap_or(true)
} }
pub(super) fn build_from_selection( pub(super) fn build_from_selection(
s: &'a Selection<'a>, s: &'a Selection<'a, S>,
vars: &'a Variables, vars: &'a Variables<S>,
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>, fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
) -> LookAheadSelection<'a> { ) -> LookAheadSelection<'a, S> {
Self::build_from_selection_with_parent(s, None, vars, fragments).unwrap() Self::build_from_selection_with_parent(s, None, vars, fragments).unwrap()
} }
fn build_from_selection_with_parent( fn build_from_selection_with_parent(
s: &'a Selection<'a>, s: &'a Selection<'a, S>,
parent: Option<&mut Self>, parent: Option<&mut Self>,
vars: &'a Variables, vars: &'a Variables<S>,
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>, fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
) -> Option<LookAheadSelection<'a>> { ) -> Option<LookAheadSelection<'a, S>> {
let empty: &[Selection] = &[]; let empty: &[Selection<S>] = &[];
match *s { match *s {
Selection::Field(ref field) => { Selection::Field(ref field) => {
let field = &field.item; let field = &field.item;
@ -178,8 +192,7 @@ impl<'a> LookAheadSelection<'a> {
.iter() .iter()
.map(|p| LookAheadArgument::new(p, vars)) .map(|p| LookAheadArgument::new(p, vars))
.collect() .collect()
}) }).unwrap_or_else(Vec::new);
.unwrap_or_else(Vec::new);
let mut ret = LookAheadSelection { let mut ret = LookAheadSelection {
name, name,
alias, alias,
@ -256,9 +269,10 @@ impl<'a> LookAheadSelection<'a> {
} }
/// Convert a eventually type independent selection into one for a concrete type /// Convert a eventually type independent selection into one for a concrete type
pub fn for_explicit_type(&self, type_name: &str) -> ConcreteLookAheadSelection<'a> { pub fn for_explicit_type(&self, type_name: &str) -> ConcreteLookAheadSelection<'a, S> {
ConcreteLookAheadSelection { ConcreteLookAheadSelection {
children: self.children children: self
.children
.iter() .iter()
.filter_map(|c| match c.applies_for { .filter_map(|c| match c.applies_for {
Applies::OnlyType(ref t) if *t == type_name => { Applies::OnlyType(ref t) if *t == type_name => {
@ -266,8 +280,7 @@ impl<'a> LookAheadSelection<'a> {
} }
Applies::All => Some(c.inner.for_explicit_type(type_name)), Applies::All => Some(c.inner.for_explicit_type(type_name)),
Applies::OnlyType(_) => None, Applies::OnlyType(_) => None,
}) }).collect(),
.collect(),
name: self.name, name: self.name,
alias: self.alias, alias: self.alias,
arguments: self.arguments.clone(), arguments: self.arguments.clone(),
@ -277,15 +290,15 @@ impl<'a> LookAheadSelection<'a> {
/// A selection performed by a query on a concrete type /// A selection performed by a query on a concrete type
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ConcreteLookAheadSelection<'a> { pub struct ConcreteLookAheadSelection<'a, S: 'a> {
name: &'a str, name: &'a str,
alias: Option<&'a str>, alias: Option<&'a str>,
arguments: Vec<LookAheadArgument<'a>>, arguments: Vec<LookAheadArgument<'a, S>>,
children: Vec<ConcreteLookAheadSelection<'a>>, children: Vec<ConcreteLookAheadSelection<'a, S>>,
} }
/// A set of common methods for `ConcreteLookAheadSelection` and `LookAheadSelection` /// A set of common methods for `ConcreteLookAheadSelection` and `LookAheadSelection`
pub trait LookAheadMethods { pub trait LookAheadMethods<S> {
/// Get the name of the field represented by the current selection /// Get the name of the field represented by the current selection
fn field_name(&self) -> &str; fn field_name(&self) -> &str;
@ -298,15 +311,15 @@ pub trait LookAheadMethods {
} }
/// Get the top level arguments for the current selection /// Get the top level arguments for the current selection
fn arguments(&self) -> &[LookAheadArgument]; fn arguments(&self) -> &[LookAheadArgument<S>];
/// Get the top level argument with a given name from the current selection /// Get the top level argument with a given name from the current selection
fn argument(&self, name: &str) -> Option<&LookAheadArgument> { fn argument(&self, name: &str) -> Option<&LookAheadArgument<S>> {
self.arguments().iter().find(|a| a.name == name) self.arguments().iter().find(|a| a.name == name)
} }
} }
impl<'a> LookAheadMethods for ConcreteLookAheadSelection<'a> { impl<'a, S> LookAheadMethods<S> for ConcreteLookAheadSelection<'a, S> {
fn field_name(&self) -> &str { fn field_name(&self) -> &str {
self.alias.unwrap_or(self.name) self.alias.unwrap_or(self.name)
} }
@ -315,12 +328,12 @@ impl<'a> LookAheadMethods for ConcreteLookAheadSelection<'a> {
self.children.iter().find(|c| c.name == name) self.children.iter().find(|c| c.name == name)
} }
fn arguments(&self) -> &[LookAheadArgument] { fn arguments(&self) -> &[LookAheadArgument<S>] {
&self.arguments &self.arguments
} }
} }
impl<'a> LookAheadMethods for LookAheadSelection<'a> { impl<'a, S> LookAheadMethods<S> for LookAheadSelection<'a, S> {
fn field_name(&self) -> &str { fn field_name(&self) -> &str {
self.alias.unwrap_or(self.name) self.alias.unwrap_or(self.name)
} }
@ -332,7 +345,7 @@ impl<'a> LookAheadMethods for LookAheadSelection<'a> {
.map(|s| &s.inner) .map(|s| &s.inner)
} }
fn arguments(&self) -> &[LookAheadArgument] { fn arguments(&self) -> &[LookAheadArgument<S>] {
&self.arguments &self.arguments
} }
} }
@ -341,9 +354,21 @@ impl<'a> LookAheadMethods for LookAheadSelection<'a> {
mod tests { mod tests {
use super::*; use super::*;
use ast::Document; use ast::Document;
use parser::UnlocatedParseResult;
use schema::model::SchemaType;
use std::collections::HashMap; use std::collections::HashMap;
use validation::test_harness::{MutationRoot, QueryRoot};
use value::{DefaultScalarValue, ScalarRefValue, ScalarValue};
fn extract_fragments<'a>(doc: &'a Document) -> HashMap<&'a str, &'a Fragment<'a>> { fn parse_document_source<S>(q: &str) -> UnlocatedParseResult<Document<S>>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
::parse_document_source(q, &SchemaType::new::<QueryRoot, MutationRoot>(&(), &()))
}
fn extract_fragments<'a, S>(doc: &'a Document<S>) -> HashMap<&'a str, &'a Fragment<'a, S>> {
let mut fragments = HashMap::new(); let mut fragments = HashMap::new();
for d in doc { for d in doc {
if let ::ast::Definition::Fragment(ref f) = *d { if let ::ast::Definition::Fragment(ref f) = *d {
@ -356,7 +381,7 @@ mod tests {
#[test] #[test]
fn check_simple_query() { fn check_simple_query() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -408,7 +433,7 @@ query Hero {
#[test] #[test]
fn check_query_with_alias() { fn check_query_with_alias() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
custom_hero: hero { custom_hero: hero {
@ -460,7 +485,7 @@ query Hero {
#[test] #[test]
fn check_query_with_child() { fn check_query_with_child() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -544,7 +569,7 @@ query Hero {
#[test] #[test]
fn check_query_with_argument() { fn check_query_with_argument() {
let docs = ::parse_document_source( let docs = parse_document_source(
" "
query Hero { query Hero {
hero(episode: EMPIRE) { hero(episode: EMPIRE) {
@ -586,7 +611,7 @@ query Hero {
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "uppercase", name: "uppercase",
value: LookAheadValue::Boolean(true), value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)),
}], }],
children: Vec::new(), children: Vec::new(),
}, },
@ -602,7 +627,7 @@ query Hero {
#[test] #[test]
fn check_query_with_variable() { fn check_query_with_variable() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero($episode: Episode) { query Hero($episode: Episode) {
hero(episode: $episode) { hero(episode: $episode) {
@ -658,7 +683,7 @@ query Hero($episode: Episode) {
#[test] #[test]
fn check_query_with_fragment() { fn check_query_with_fragment() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -724,7 +749,7 @@ fragment commonFields on Character {
#[test] #[test]
fn check_query_with_directives() { fn check_query_with_directives() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -777,7 +802,7 @@ query Hero {
#[test] #[test]
fn check_query_with_inline_fragments() { fn check_query_with_inline_fragments() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -842,7 +867,7 @@ query Hero {
#[test] #[test]
fn check_complex_query() { fn check_complex_query() {
let docs = ::parse_document_source( let docs = parse_document_source(
" "
query HeroNameAndFriends($id: Integer!, $withFriends: Boolean! = true) { query HeroNameAndFriends($id: Integer!, $withFriends: Boolean! = true) {
hero(id: $id) { hero(id: $id) {
@ -867,9 +892,12 @@ fragment comparisonFields on Character {
if let ::ast::Definition::Operation(ref op) = docs[0] { if let ::ast::Definition::Operation(ref op) = docs[0] {
let mut vars = Variables::default(); let mut vars = Variables::default();
vars.insert("id".into(), InputValue::Int(42)); vars.insert("id".into(), InputValue::Scalar(DefaultScalarValue::Int(42)));
// This will normally be there // This will normally be there
vars.insert("withFriends".into(), InputValue::Boolean(true)); vars.insert(
"withFriends".into(),
InputValue::Scalar(DefaultScalarValue::Boolean(true)),
);
let look_ahead = LookAheadSelection::build_from_selection( let look_ahead = LookAheadSelection::build_from_selection(
&op.item.selection_set[0], &op.item.selection_set[0],
&vars, &vars,
@ -880,7 +908,7 @@ fragment comparisonFields on Character {
alias: None, alias: None,
arguments: vec![LookAheadArgument { arguments: vec![LookAheadArgument {
name: "id", name: "id",
value: LookAheadValue::Int(42), value: LookAheadValue::Scalar(&DefaultScalarValue::Int(42)),
}], }],
children: vec![ children: vec![
ChildSelection { ChildSelection {
@ -1002,7 +1030,7 @@ fragment comparisonFields on Character {
#[test] #[test]
fn check_resolve_concrete_type() { fn check_resolve_concrete_type() {
let docs = ::parse_document_source( let docs = parse_document_source::<DefaultScalarValue>(
" "
query Hero { query Hero {
hero { hero {
@ -1052,7 +1080,7 @@ query Hero {
#[test] #[test]
fn check_select_child() { fn check_select_child() {
let lookahead = LookAheadSelection { let lookahead: LookAheadSelection<DefaultScalarValue> = LookAheadSelection {
name: "hero", name: "hero",
alias: None, alias: None,
arguments: Vec::new(), arguments: Vec::new(),

View file

@ -22,6 +22,7 @@ use schema::model::{RootNode, SchemaType, TypeType};
use types::base::GraphQLType; use types::base::GraphQLType;
use types::name::Name; use types::name::Name;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
mod look_ahead; mod look_ahead;
@ -35,9 +36,9 @@ pub use self::look_ahead::{
/// The registry gathers metadata for all types in a schema. It provides /// The registry gathers metadata for all types in a schema. It provides
/// convenience methods to convert types implementing the `GraphQLType` trait /// convenience methods to convert types implementing the `GraphQLType` trait
/// into `Type` instances and automatically registers them. /// into `Type` instances and automatically registers them.
pub struct Registry<'r> { pub struct Registry<'r, S = DefaultScalarValue> {
/// Currently registered types /// Currently registered types
pub types: FnvHashMap<Name, MetaType<'r>>, pub types: FnvHashMap<Name, MetaType<'r, S>>,
} }
#[derive(Clone)] #[derive(Clone)]
@ -50,18 +51,19 @@ pub enum FieldPath<'a> {
/// ///
/// The executor helps drive the query execution in a schema. It keeps track /// The executor helps drive the query execution in a schema. It keeps track
/// of the current field stack, context, variables, and errors. /// of the current field stack, context, variables, and errors.
pub struct Executor<'a, CtxT> pub struct Executor<'a, CtxT, S = DefaultScalarValue>
where where
CtxT: 'a, CtxT: 'a,
S: 'a,
{ {
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>, fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
variables: &'a Variables, variables: &'a Variables<S>,
current_selection_set: Option<&'a [Selection<'a>]>, current_selection_set: Option<&'a [Selection<'a, S>]>,
parent_selection_set: Option<&'a [Selection<'a>]>, parent_selection_set: Option<&'a [Selection<'a, S>]>,
current_type: TypeType<'a>, current_type: TypeType<'a, S>,
schema: &'a SchemaType<'a>, schema: &'a SchemaType<'a, S>,
context: &'a CtxT, context: &'a CtxT,
errors: &'a RwLock<Vec<ExecutionError>>, errors: &'a RwLock<Vec<ExecutionError<S>>>,
field_path: FieldPath<'a>, field_path: FieldPath<'a>,
} }
@ -70,15 +72,17 @@ where
/// All execution errors contain the source position in the query of the field /// All execution errors contain the source position in the query of the field
/// that failed to resolve. It also contains the field stack. /// that failed to resolve. It also contains the field stack.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct ExecutionError { pub struct ExecutionError<S> {
location: SourcePosition, location: SourcePosition,
path: Vec<String>, path: Vec<String>,
error: FieldError, error: FieldError<S>,
} }
impl ExecutionError { impl<S> Eq for ExecutionError<S> where Self: PartialEq {}
impl<S> ExecutionError<S> {
/// Construct a new execution error occuring at the beginning of the query /// Construct a new execution error occuring at the beginning of the query
pub fn at_origin(error: FieldError) -> ExecutionError { pub fn at_origin(error: FieldError<S>) -> ExecutionError<S> {
ExecutionError { ExecutionError {
location: SourcePosition::new_origin(), location: SourcePosition::new_origin(),
path: Vec::new(), path: Vec::new(),
@ -87,10 +91,11 @@ impl ExecutionError {
} }
} }
impl Eq for ExecutionError {} impl<S> PartialOrd for ExecutionError<S>
where
impl PartialOrd for ExecutionError { Self: PartialEq,
fn partial_cmp(&self, other: &ExecutionError) -> Option<Ordering> { {
fn partial_cmp(&self, other: &ExecutionError<S>) -> Option<Ordering> {
(&self.location, &self.path, &self.error.message).partial_cmp(&( (&self.location, &self.path, &self.error.message).partial_cmp(&(
&other.location, &other.location,
&other.path, &other.path,
@ -99,8 +104,11 @@ impl PartialOrd for ExecutionError {
} }
} }
impl Ord for ExecutionError { impl<S> Ord for ExecutionError<S>
fn cmp(&self, other: &ExecutionError) -> Ordering { where
Self: Eq,
{
fn cmp(&self, other: &ExecutionError<S>) -> Ordering {
(&self.location, &self.path, &self.error.message).cmp(&( (&self.location, &self.path, &self.error.message).cmp(&(
&other.location, &other.location,
&other.path, &other.path,
@ -118,20 +126,24 @@ impl Ord for ExecutionError {
/// which makes error chaining with the `?` operator a breeze: /// which makes error chaining with the `?` operator a breeze:
/// ///
/// ```rust /// ```rust
/// # use juniper::FieldError; /// # use juniper::{FieldError, ScalarValue};
/// fn get_string(data: Vec<u8>) -> Result<String, FieldError> { /// fn get_string(data: Vec<u8>) -> Result<String, FieldError>
/// {
/// let s = String::from_utf8(data)?; /// let s = String::from_utf8(data)?;
/// Ok(s) /// Ok(s)
/// } /// }
/// ``` /// ```
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct FieldError { pub struct FieldError<S = DefaultScalarValue> {
message: String, message: String,
extensions: Value, extensions: Value<S>,
} }
impl<T: Display> From<T> for FieldError { impl<T: Display, S> From<T> for FieldError<S>
fn from(e: T) -> FieldError { where
S: ::value::ScalarValue,
{
fn from(e: T) -> FieldError<S> {
FieldError { FieldError {
message: format!("{}", e), message: format!("{}", e),
extensions: Value::null(), extensions: Value::null(),
@ -139,7 +151,7 @@ impl<T: Display> From<T> for FieldError {
} }
} }
impl FieldError { impl<S> FieldError<S> {
/// Construct a new error with additional data /// Construct a new error with additional data
/// ///
/// You can use the `graphql_value!` macro to construct an error: /// You can use the `graphql_value!` macro to construct an error:
@ -147,8 +159,10 @@ impl FieldError {
/// ```rust /// ```rust
/// # #[macro_use] extern crate juniper; /// # #[macro_use] extern crate juniper;
/// use juniper::FieldError; /// use juniper::FieldError;
/// # use juniper::DefaultScalarValue;
/// ///
/// # fn sample() { /// # fn sample() {
/// # let _: FieldError<DefaultScalarValue> =
/// FieldError::new( /// FieldError::new(
/// "Could not open connection to the database", /// "Could not open connection to the database",
/// graphql_value!({ "internal_error": "Connection refused" }) /// graphql_value!({ "internal_error": "Connection refused" })
@ -173,7 +187,7 @@ impl FieldError {
/// ``` /// ```
/// ///
/// If the argument is `Value::null()`, no extra data will be included. /// If the argument is `Value::null()`, no extra data will be included.
pub fn new<T: Display>(e: T, extensions: Value) -> FieldError { pub fn new<T: Display>(e: T, extensions: Value<S>) -> FieldError<S> {
FieldError { FieldError {
message: format!("{}", e), message: format!("{}", e),
extensions, extensions,
@ -186,82 +200,111 @@ impl FieldError {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn extensions(&self) -> &Value { pub fn extensions(&self) -> &Value<S> {
&self.extensions &self.extensions
} }
} }
/// The result of resolving the value of a field of type `T` /// The result of resolving the value of a field of type `T`
pub type FieldResult<T> = Result<T, FieldError>; pub type FieldResult<T, S = DefaultScalarValue> = Result<T, FieldError<S>>;
/// The result of resolving an unspecified field /// The result of resolving an unspecified field
pub type ExecutionResult = Result<Value, FieldError>; pub type ExecutionResult<S = DefaultScalarValue> = Result<Value<S>, FieldError<S>>;
/// The map of variables used for substitution during query execution /// The map of variables used for substitution during query execution
pub type Variables = HashMap<String, InputValue>; pub type Variables<S = DefaultScalarValue> = HashMap<String, InputValue<S>>;
/// Custom error handling trait to enable Error types other than `FieldError` to be specified /// Custom error handling trait to enable Error types other than `FieldError` to be specified
/// as return value. /// as return value.
/// ///
/// Any custom error type should implement this trait to convert it to `FieldError`. /// Any custom error type should implement this trait to convert it to `FieldError`.
pub trait IntoFieldError { pub trait IntoFieldError<S = DefaultScalarValue> {
#[doc(hidden)] #[doc(hidden)]
fn into_field_error(self) -> FieldError; fn into_field_error(self) -> FieldError<S>;
} }
impl IntoFieldError for FieldError { impl<S> IntoFieldError<S> for FieldError<S> {
fn into_field_error(self) -> FieldError { fn into_field_error(self) -> FieldError<S> {
self self
} }
} }
#[doc(hidden)] #[doc(hidden)]
pub trait IntoResolvable<'a, T: GraphQLType, C>: Sized { pub trait IntoResolvable<'a, S, T: GraphQLType<S>, C>: Sized
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
#[doc(hidden)] #[doc(hidden)]
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>>; fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S>;
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for T impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for T
where where
T: GraphQLType<S>,
S: ScalarValue,
T::Context: FromContext<C>, T::Context: FromContext<C>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
Ok(Some((FromContext::from(ctx), self))) Ok(Some((FromContext::from(ctx), self)))
} }
} }
impl<'a, T: GraphQLType, C, E: IntoFieldError> IntoResolvable<'a, T, C> for Result<T, E> impl<'a, S, T, C, E: IntoFieldError<S>> IntoResolvable<'a, S, T, C> for Result<T, E>
where where
S: ScalarValue,
T: GraphQLType<S>,
T::Context: FromContext<C>, T::Context: FromContext<C>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(|v| Some((FromContext::from(ctx), v))) self.map(|v: T| Some((<T::Context as FromContext<C>>::from(ctx), v)))
.map_err(|e| e.into_field_error()) .map_err(|e| e.into_field_error())
} }
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for (&'a T::Context, T) { impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for (&'a T::Context, T)
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
Ok(Some(self)) Ok(Some(self))
} }
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C> for Option<(&'a T::Context, T)> { impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C> for Option<(&'a T::Context, T)>
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>> { where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
Ok(self.map(|(ctx, v)| (ctx, Some(v)))) Ok(self.map(|(ctx, v)| (ctx, Some(v))))
} }
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<(&'a T::Context, T)> { impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'a T::Context, T), S>
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> { where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(Some) self.map(Some)
} }
} }
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C> impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>> for FieldResult<Option<(&'a T::Context, T)>, S>
where
S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>> { fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>, S> {
self.map(|o| o.map(|(ctx, v)| (ctx, Some(v)))) self.map(|o| o.map(|(ctx, v)| (ctx, Some(v))))
} }
} }
@ -304,37 +347,36 @@ where
} }
} }
impl<'a, CtxT> Executor<'a, CtxT> { impl<'a, CtxT, S> Executor<'a, CtxT, S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// Resolve a single arbitrary value, mapping the context to a new type /// Resolve a single arbitrary value, mapping the context to a new type
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>( pub fn resolve_with_ctx<NewCtxT, T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
&self,
info: &T::TypeInfo,
value: &T,
) -> ExecutionResult
where where
NewCtxT: FromContext<CtxT>, NewCtxT: FromContext<CtxT>,
T: GraphQLType<S, Context = NewCtxT>,
{ {
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context)) self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
.resolve(info, value) .resolve(info, value)
} }
/// Resolve a single arbitrary value into an `ExecutionResult` /// Resolve a single arbitrary value into an `ExecutionResult`
pub fn resolve<T: GraphQLType<Context = CtxT>>( pub fn resolve<T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
&self, where
info: &T::TypeInfo, T: GraphQLType<S, Context = CtxT>,
value: &T, {
) -> ExecutionResult {
Ok(value.resolve(info, self.current_selection_set, self)) Ok(value.resolve(info, self.current_selection_set, self))
} }
/// Resolve a single arbitrary value into a return value /// Resolve a single arbitrary value into a return value
/// ///
/// If the field fails to resolve, `null` will be returned. /// If the field fails to resolve, `null` will be returned.
pub fn resolve_into_value<T: GraphQLType<Context = CtxT>>( pub fn resolve_into_value<T>(&self, info: &T::TypeInfo, value: &T) -> Value<S>
&self, where
info: &T::TypeInfo, T: GraphQLType<S, Context = CtxT>,
value: &T, {
) -> Value {
match self.resolve(info, value) { match self.resolve(info, value) {
Ok(v) => v, Ok(v) => v,
Err(e) => { Err(e) => {
@ -348,7 +390,7 @@ impl<'a, CtxT> Executor<'a, CtxT> {
/// ///
/// This can be used to connect different types, e.g. from different Rust /// This can be used to connect different types, e.g. from different Rust
/// libraries, that require different context types. /// libraries, that require different context types.
pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT> { pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT, S> {
Executor { Executor {
fragments: self.fragments, fragments: self.fragments,
variables: self.variables, variables: self.variables,
@ -368,15 +410,16 @@ impl<'a, CtxT> Executor<'a, CtxT> {
field_alias: &'a str, field_alias: &'a str,
field_name: &'a str, field_name: &'a str,
location: SourcePosition, location: SourcePosition,
selection_set: Option<&'a [Selection]>, selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT> { ) -> Executor<CtxT, S> {
Executor { Executor {
fragments: self.fragments, fragments: self.fragments,
variables: self.variables, variables: self.variables,
current_selection_set: selection_set, current_selection_set: selection_set,
parent_selection_set: self.current_selection_set, parent_selection_set: self.current_selection_set,
current_type: self.schema.make_type( current_type: self.schema.make_type(
&self.current_type &self
.current_type
.innermost_concrete() .innermost_concrete()
.field_by_name(field_name) .field_by_name(field_name)
.expect("Field not found on inner type") .expect("Field not found on inner type")
@ -393,8 +436,8 @@ impl<'a, CtxT> Executor<'a, CtxT> {
pub fn type_sub_executor( pub fn type_sub_executor(
&self, &self,
type_name: Option<&'a str>, type_name: Option<&'a str>,
selection_set: Option<&'a [Selection]>, selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT> { ) -> Executor<CtxT, S> {
Executor { Executor {
fragments: self.fragments, fragments: self.fragments,
variables: self.variables, variables: self.variables,
@ -420,22 +463,22 @@ impl<'a, CtxT> Executor<'a, CtxT> {
} }
/// The currently executing schema /// The currently executing schema
pub fn schema(&self) -> &'a SchemaType { pub fn schema(&self) -> &'a SchemaType<S> {
self.schema self.schema
} }
#[doc(hidden)] #[doc(hidden)]
pub fn current_type(&self) -> &TypeType<'a> { pub fn current_type(&self) -> &TypeType<'a, S> {
&self.current_type &self.current_type
} }
#[doc(hidden)] #[doc(hidden)]
pub fn variables(&self) -> &'a Variables { pub fn variables(&self) -> &'a Variables<S> {
self.variables self.variables
} }
#[doc(hidden)] #[doc(hidden)]
pub fn fragment_by_name(&self, name: &str) -> Option<&'a Fragment> { pub fn fragment_by_name(&self, name: &str) -> Option<&'a Fragment<S>> {
self.fragments.get(name).map(|f| *f) self.fragments.get(name).map(|f| *f)
} }
@ -445,13 +488,13 @@ impl<'a, CtxT> Executor<'a, CtxT> {
} }
/// Add an error to the execution engine at the current executor location /// Add an error to the execution engine at the current executor location
pub fn push_error(&self, error: FieldError) { pub fn push_error(&self, error: FieldError<S>) {
let location = self.location().clone(); let location = self.location().clone();
self.push_error_at(error, location); self.push_error_at(error, location);
} }
/// Add an error to the execution engine at a specific location /// Add an error to the execution engine at a specific location
pub fn push_error_at(&self, error: FieldError, location: SourcePosition) { pub fn push_error_at(&self, error: FieldError<S>, location: SourcePosition) {
let mut path = Vec::new(); let mut path = Vec::new();
self.field_path.construct_path(&mut path); self.field_path.construct_path(&mut path);
@ -468,16 +511,16 @@ impl<'a, CtxT> Executor<'a, CtxT> {
/// ///
/// This allows to see the whole selection and preform operations /// This allows to see the whole selection and preform operations
/// affecting the childs /// affecting the childs
pub fn look_ahead(&'a self) -> LookAheadSelection<'a> { pub fn look_ahead(&'a self) -> LookAheadSelection<'a, S> {
self.parent_selection_set self.parent_selection_set
.map(|p| { .map(|p| {
LookAheadSelection::build_from_selection(&p[0], self.variables, self.fragments) LookAheadSelection::build_from_selection(&p[0], self.variables, self.fragments)
}) }).unwrap_or_else(|| LookAheadSelection {
.unwrap_or_else(|| LookAheadSelection {
name: self.current_type.innermost_concrete().name().unwrap_or(""), name: self.current_type.innermost_concrete().name().unwrap_or(""),
alias: None, alias: None,
arguments: Vec::new(), arguments: Vec::new(),
children: self.current_selection_set children: self
.current_selection_set
.map(|s| { .map(|s| {
s.iter() s.iter()
.map(|s| ChildSelection { .map(|s| ChildSelection {
@ -487,10 +530,8 @@ impl<'a, CtxT> Executor<'a, CtxT> {
self.fragments, self.fragments,
), ),
applies_for: Applies::All, applies_for: Applies::All,
}) }).collect()
.collect() }).unwrap_or_else(Vec::new),
})
.unwrap_or_else(Vec::new),
}) })
} }
} }
@ -513,9 +554,9 @@ impl<'a> FieldPath<'a> {
} }
} }
impl ExecutionError { impl<S> ExecutionError<S> {
#[doc(hidden)] #[doc(hidden)]
pub fn new(location: SourcePosition, path: &[&str], error: FieldError) -> ExecutionError { pub fn new(location: SourcePosition, path: &[&str], error: FieldError<S>) -> ExecutionError<S> {
ExecutionError { ExecutionError {
location: location, location: location,
path: path.iter().map(|s| (*s).to_owned()).collect(), path: path.iter().map(|s| (*s).to_owned()).collect(),
@ -524,7 +565,7 @@ impl ExecutionError {
} }
/// The error message /// The error message
pub fn error(&self) -> &FieldError { pub fn error(&self) -> &FieldError<S> {
&self.error &self.error
} }
@ -539,16 +580,18 @@ impl ExecutionError {
} }
} }
pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>( pub fn execute_validated_query<'a, QueryT, MutationT, CtxT, S>(
document: Document, document: Document<S>,
operation_name: Option<&str>, operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>, root_node: &RootNode<QueryT, MutationT, S>,
variables: &Variables, variables: &Variables<S>,
context: &CtxT, context: &CtxT,
) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> ) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
where where
QueryT: GraphQLType<Context = CtxT>, S: ScalarValue,
MutationT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let mut fragments = vec![]; let mut fragments = vec![];
let mut operation = None; let mut operation = None;
@ -561,7 +604,7 @@ where
} }
let move_op = operation_name.is_none() let move_op = operation_name.is_none()
|| op.item.name.as_ref().map(|s| s.item.as_ref()) == operation_name; || op.item.name.as_ref().map(|s| s.item) == operation_name;
if move_op { if move_op {
operation = Some(op); operation = Some(op);
@ -584,8 +627,7 @@ where
def.default_value def.default_value
.as_ref() .as_ref()
.map(|i| (name.item.to_owned(), i.item.clone())) .map(|i| (name.item.to_owned(), i.item.clone()))
}) }).collect::<HashMap<String, InputValue<S>>>()
.collect::<HashMap<String, InputValue>>()
}); });
let errors = RwLock::new(Vec::new()); let errors = RwLock::new(Vec::new());
@ -642,9 +684,12 @@ where
Ok((value, errors)) Ok((value, errors))
} }
impl<'r> Registry<'r> { impl<'r, S> Registry<'r, S>
where
S: ScalarValue + 'r,
{
/// Construct a new registry /// Construct a new registry
pub fn new(types: FnvHashMap<Name, MetaType<'r>>) -> Registry<'r> { pub fn new(types: FnvHashMap<Name, MetaType<'r, S>>) -> Registry<'r, S> {
Registry { types: types } Registry { types: types }
} }
@ -654,7 +699,8 @@ impl<'r> Registry<'r> {
/// construct its metadata and store it. /// construct its metadata and store it.
pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r> pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r>
where where
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
if let Some(name) = T::name(info) { if let Some(name) = T::name(info) {
let validated_name = name.parse::<Name>().unwrap(); let validated_name = name.parse::<Name>().unwrap();
@ -673,9 +719,10 @@ impl<'r> Registry<'r> {
} }
/// Create a field with the provided name /// Create a field with the provided name
pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r> pub fn field<T>(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S>
where where
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
Field { Field {
name: name.to_owned(), name: name.to_owned(),
@ -687,13 +734,14 @@ impl<'r> Registry<'r> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn field_convert<'a, T: IntoResolvable<'a, I, C>, I, C>( pub fn field_convert<'a, T: IntoResolvable<'a, S, I, C>, I, C>(
&mut self, &mut self,
name: &str, name: &str,
info: &I::TypeInfo, info: &I::TypeInfo,
) -> Field<'r> ) -> Field<'r, S>
where where
I: GraphQLType, I: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
Field { Field {
name: name.to_owned(), name: name.to_owned(),
@ -705,9 +753,10 @@ impl<'r> Registry<'r> {
} }
/// Create an argument with the provided name /// Create an argument with the provided name
pub fn arg<T>(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r> pub fn arg<T>(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r, S>
where where
T: GraphQLType + FromInputValue, T: GraphQLType<S> + FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
Argument::new(name, self.get_type::<T>(info)) Argument::new(name, self.get_type::<T>(info))
} }
@ -716,9 +765,15 @@ impl<'r> Registry<'r> {
/// ///
/// When called with type `T`, the actual argument will be given the type /// When called with type `T`, the actual argument will be given the type
/// `Option<T>`. /// `Option<T>`.
pub fn arg_with_default<T>(&mut self, name: &str, value: &T, info: &T::TypeInfo) -> Argument<'r> pub fn arg_with_default<T>(
&mut self,
name: &str,
value: &T,
info: &T::TypeInfo,
) -> Argument<'r, S>
where where
T: GraphQLType + ToInputValue + FromInputValue, T: GraphQLType<S> + ToInputValue<S> + FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
Argument::new(name, self.get_type::<Option<T>>(info)).default_value(value.to_input_value()) Argument::new(name, self.get_type::<Option<T>>(info)).default_value(value.to_input_value())
} }
@ -735,22 +790,29 @@ impl<'r> Registry<'r> {
/// Create a scalar meta type /// Create a scalar meta type
/// ///
/// This expects the type to implement `FromInputValue`. /// This expects the type to implement `FromInputValue`.
pub fn build_scalar_type<T>(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r> pub fn build_scalar_type<T>(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S>
where where
T: FromInputValue + GraphQLType, T: FromInputValue<S> + GraphQLType<S> + ParseScalarValue<S> + 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Scalar types must be named. Implement name()"); let name = T::name(info).expect("Scalar types must be named. Implement name()");
ScalarMeta::new::<T>(Cow::Owned(name.to_string())) ScalarMeta::new::<T>(Cow::Owned(name.to_string()))
} }
/// Create a list meta type /// Create a list meta type
pub fn build_list_type<T: GraphQLType>(&mut self, info: &T::TypeInfo) -> ListMeta<'r> { pub fn build_list_type<T: GraphQLType<S>>(&mut self, info: &T::TypeInfo) -> ListMeta<'r>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
let of_type = self.get_type::<T>(info); let of_type = self.get_type::<T>(info);
ListMeta::new(of_type) ListMeta::new(of_type)
} }
/// Create a nullable meta type /// Create a nullable meta type
pub fn build_nullable_type<T: GraphQLType>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> { pub fn build_nullable_type<T: GraphQLType<S>>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
let of_type = self.get_type::<T>(info); let of_type = self.get_type::<T>(info);
NullableMeta::new(of_type) NullableMeta::new(of_type)
} }
@ -762,10 +824,11 @@ impl<'r> Registry<'r> {
pub fn build_object_type<T>( pub fn build_object_type<T>(
&mut self, &mut self,
info: &T::TypeInfo, info: &T::TypeInfo,
fields: &[Field<'r>], fields: &[Field<'r, S>],
) -> ObjectMeta<'r> ) -> ObjectMeta<'r, S>
where where
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Object types must be named. Implement name()"); let name = T::name(info).expect("Object types must be named. Implement name()");
@ -775,9 +838,14 @@ impl<'r> Registry<'r> {
} }
/// Create an enum meta type /// Create an enum meta type
pub fn build_enum_type<T>(&mut self, info: &T::TypeInfo, values: &[EnumValue]) -> EnumMeta<'r> pub fn build_enum_type<T>(
&mut self,
info: &T::TypeInfo,
values: &[EnumValue],
) -> EnumMeta<'r, S>
where where
T: FromInputValue + GraphQLType, T: FromInputValue<S> + GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Enum types must be named. Implement name()"); let name = T::name(info).expect("Enum types must be named. Implement name()");
@ -789,10 +857,11 @@ impl<'r> Registry<'r> {
pub fn build_interface_type<T>( pub fn build_interface_type<T>(
&mut self, &mut self,
info: &T::TypeInfo, info: &T::TypeInfo,
fields: &[Field<'r>], fields: &[Field<'r, S>],
) -> InterfaceMeta<'r> ) -> InterfaceMeta<'r, S>
where where
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Interface types must be named. Implement name()"); let name = T::name(info).expect("Interface types must be named. Implement name()");
@ -804,7 +873,8 @@ impl<'r> Registry<'r> {
/// Create a union meta type builder /// Create a union meta type builder
pub fn build_union_type<T>(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r> pub fn build_union_type<T>(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r>
where where
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Union types must be named. Implement name()"); let name = T::name(info).expect("Union types must be named. Implement name()");
@ -815,10 +885,11 @@ impl<'r> Registry<'r> {
pub fn build_input_object_type<T>( pub fn build_input_object_type<T>(
&mut self, &mut self,
info: &T::TypeInfo, info: &T::TypeInfo,
args: &[Argument<'r>], args: &[Argument<'r, S>],
) -> InputObjectMeta<'r> ) -> InputObjectMeta<'r, S>
where where
T: FromInputValue + GraphQLType, T: FromInputValue<S> + GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let name = T::name(info).expect("Input object types must be named. Implement name()"); let name = T::name(info).expect("Input object types must be named. Implement name()");

View file

@ -1,7 +1,7 @@
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{Value, Object, DefaultScalarValue};
struct TestType; struct TestType;
@ -15,9 +15,9 @@ graphql_object!(TestType: () |&self| {
} }
}); });
fn run_variable_query<F>(query: &str, vars: Variables, f: F) fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -34,7 +34,7 @@ where
fn run_query<F>(query: &str, f: F) fn run_query<F>(query: &str, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
run_variable_query(query, Variables::new(), f); run_variable_query(query, Variables::new(), f);
} }
@ -42,15 +42,15 @@ where
#[test] #[test]
fn scalar_include_true() { fn scalar_include_true() {
run_query("{ a, b @include(if: true) }", |result| { run_query("{ a, b @include(if: true) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn scalar_include_false() { fn scalar_include_false() {
run_query("{ a, b @include(if: false) }", |result| { run_query("{ a, b @include(if: false) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -58,15 +58,15 @@ fn scalar_include_false() {
#[test] #[test]
fn scalar_skip_false() { fn scalar_skip_false() {
run_query("{ a, b @skip(if: false) }", |result| { run_query("{ a, b @skip(if: false) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn scalar_skip_true() { fn scalar_skip_true() {
run_query("{ a, b @skip(if: true) }", |result| { run_query("{ a, b @skip(if: true) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -76,8 +76,8 @@ fn fragment_spread_include_true() {
run_query( run_query(
"{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }", "{ a, ...Frag @include(if: true) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}, },
); );
} }
@ -87,7 +87,7 @@ fn fragment_spread_include_false() {
run_query( run_query(
"{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }", "{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}, },
); );
@ -98,8 +98,8 @@ fn fragment_spread_skip_false() {
run_query( run_query(
"{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }", "{ a, ...Frag @skip(if: false) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}, },
); );
} }
@ -109,7 +109,7 @@ fn fragment_spread_skip_true() {
run_query( run_query(
"{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }", "{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}, },
); );
@ -120,8 +120,8 @@ fn inline_fragment_include_true() {
run_query( run_query(
"{ a, ... on TestType @include(if: true) { b } }", "{ a, ... on TestType @include(if: true) { b } }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}, },
); );
} }
@ -131,7 +131,7 @@ fn inline_fragment_include_false() {
run_query( run_query(
"{ a, ... on TestType @include(if: false) { b } }", "{ a, ... on TestType @include(if: false) { b } }",
|result| { |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}, },
); );
@ -140,15 +140,15 @@ fn inline_fragment_include_false() {
#[test] #[test]
fn inline_fragment_skip_false() { fn inline_fragment_skip_false() {
run_query("{ a, ... on TestType @skip(if: false) { b } }", |result| { run_query("{ a, ... on TestType @skip(if: false) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn inline_fragment_skip_true() { fn inline_fragment_skip_true() {
run_query("{ a, ... on TestType @skip(if: true) { b } }", |result| { run_query("{ a, ... on TestType @skip(if: true) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -156,15 +156,15 @@ fn inline_fragment_skip_true() {
#[test] #[test]
fn anonymous_inline_fragment_include_true() { fn anonymous_inline_fragment_include_true() {
run_query("{ a, ... @include(if: true) { b } }", |result| { run_query("{ a, ... @include(if: true) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn anonymous_inline_fragment_include_false() { fn anonymous_inline_fragment_include_false() {
run_query("{ a, ... @include(if: false) { b } }", |result| { run_query("{ a, ... @include(if: false) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -172,15 +172,15 @@ fn anonymous_inline_fragment_include_false() {
#[test] #[test]
fn anonymous_inline_fragment_skip_false() { fn anonymous_inline_fragment_skip_false() {
run_query("{ a, ... @skip(if: false) { b } }", |result| { run_query("{ a, ... @skip(if: false) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn anonymous_inline_fragment_skip_true() { fn anonymous_inline_fragment_skip_true() {
run_query("{ a, ... @skip(if: true) { b } }", |result| { run_query("{ a, ... @skip(if: true) { b } }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -188,7 +188,7 @@ fn anonymous_inline_fragment_skip_true() {
#[test] #[test]
fn scalar_include_true_skip_true() { fn scalar_include_true_skip_true() {
run_query("{ a, b @include(if: true) @skip(if: true) }", |result| { run_query("{ a, b @include(if: true) @skip(if: true) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -196,15 +196,15 @@ fn scalar_include_true_skip_true() {
#[test] #[test]
fn scalar_include_true_skip_false() { fn scalar_include_true_skip_false() {
run_query("{ a, b @include(if: true) @skip(if: false) }", |result| { run_query("{ a, b @include(if: true) @skip(if: false) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b"))); assert_eq!(result.get_field_value("b"), Some(&Value::scalar("b")));
}); });
} }
#[test] #[test]
fn scalar_include_false_skip_true() { fn scalar_include_false_skip_true() {
run_query("{ a, b @include(if: false) @skip(if: true) }", |result| { run_query("{ a, b @include(if: false) @skip(if: true) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }
@ -212,7 +212,7 @@ fn scalar_include_false_skip_true() {
#[test] #[test]
fn scalar_include_false_skip_false() { fn scalar_include_false_skip_false() {
run_query("{ a, b @include(if: false) @skip(if: false) }", |result| { run_query("{ a, b @include(if: false) @skip(if: false) }", |result| {
assert_eq!(result.get_field_value("a"), Some(&Value::string("a"))); assert_eq!(result.get_field_value("a"), Some(&Value::scalar("a")));
assert_eq!(result.get_field_value("b"), None); assert_eq!(result.get_field_value("b"), None);
}); });
} }

View file

@ -4,11 +4,10 @@ use parser::SourcePosition;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use validation::RuleError; use validation::RuleError;
use value::{Value, Object}; use value::{DefaultScalarValue, Object, Value};
use GraphQLError::ValidationError; use GraphQLError::ValidationError;
#[derive(GraphQLEnum, Debug)] #[derive(GraphQLEnum, Debug)]
#[graphql(_internal)]
enum Color { enum Color {
Red, Red,
Green, Green,
@ -26,9 +25,9 @@ graphql_object!(TestType: () |&self| {
} }
}); });
fn run_variable_query<F>(query: &str, vars: Variables, f: F) fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -45,7 +44,7 @@ where
fn run_query<F>(query: &str, f: F) fn run_query<F>(query: &str, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
run_variable_query(query, Variables::new(), f); run_variable_query(query, Variables::new(), f);
} }
@ -53,14 +52,20 @@ where
#[test] #[test]
fn accepts_enum_literal() { fn accepts_enum_literal() {
run_query("{ toString(color: RED) }", |result| { run_query("{ toString(color: RED) }", |result| {
assert_eq!(result.get_field_value("toString"), Some(&Value::string("Color::Red"))); assert_eq!(
result.get_field_value("toString"),
Some(&Value::scalar("Color::Red"))
);
}); });
} }
#[test] #[test]
fn serializes_as_output() { fn serializes_as_output() {
run_query("{ aColor }", |result| { run_query("{ aColor }", |result| {
assert_eq!(result.get_field_value("aColor"), Some(&Value::string("RED"))); assert_eq!(
result.get_field_value("aColor"),
Some(&Value::scalar("RED"))
);
}); });
} }
@ -86,11 +91,14 @@ fn does_not_accept_string_literals() {
fn accepts_strings_in_variables() { fn accepts_strings_in_variables() {
run_variable_query( run_variable_query(
"query q($color: Color!) { toString(color: $color) }", "query q($color: Color!) { toString(color: $color) }",
vec![("color".to_owned(), InputValue::string("RED"))] vec![("color".to_owned(), InputValue::scalar("RED"))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!(result.get_field_value("toString"), Some(&Value::string("Color::Red"))); assert_eq!(
result.get_field_value("toString"),
Some(&Value::scalar("Color::Red"))
);
}, },
); );
} }
@ -100,7 +108,7 @@ fn does_not_accept_incorrect_enum_name_in_variables() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($color: Color!) { toString(color: $color) }"#; let query = r#"query q($color: Color!) { toString(color: $color) }"#;
let vars = vec![("color".to_owned(), InputValue::string("BLURPLE"))] let vars = vec![("color".to_owned(), InputValue::scalar("BLURPLE"))]
.into_iter() .into_iter()
.collect(); .collect();
@ -120,7 +128,7 @@ fn does_not_accept_incorrect_type_in_variables() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($color: Color!) { toString(color: $color) }"#; let query = r#"query q($color: Color!) { toString(color: $color) }"#;
let vars = vec![("color".to_owned(), InputValue::int(123))] let vars = vec![("color".to_owned(), InputValue::scalar(123))]
.into_iter() .into_iter()
.collect(); .collect();

View file

@ -61,7 +61,7 @@ mod field_execution {
e e
}"; }";
let vars = vec![("size".to_owned(), InputValue::int(100))] let vars = vec![("size".to_owned(), InputValue::scalar(100))]
.into_iter() .into_iter()
.collect(); .collect();
@ -75,25 +75,25 @@ mod field_execution {
result, result,
Value::object( Value::object(
vec![ vec![
("a", Value::string("Apple")), ("a", Value::scalar("Apple")),
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
("x", Value::string("Cookie")), ("x", Value::scalar("Cookie")),
("d", Value::string("Donut")), ("d", Value::scalar("Donut")),
("e", Value::string("Egg")), ("e", Value::scalar("Egg")),
("f", Value::string("Fish")), ("f", Value::scalar("Fish")),
("pic", Value::string("Pic of size: 100")), ("pic", Value::scalar("Pic of size: 100")),
( (
"deep", "deep",
Value::object( Value::object(
vec![ vec![
("a", Value::string("Already Been Done")), ("a", Value::scalar("Already Been Done")),
("b", Value::string("Boring")), ("b", Value::scalar("Boring")),
( (
"c", "c",
Value::list(vec![ Value::list(vec![
Value::string("Contrived"), Value::scalar("Contrived"),
Value::null(), Value::null(),
Value::string("Confusing"), Value::scalar("Confusing"),
]), ]),
), ),
( (
@ -101,27 +101,27 @@ mod field_execution {
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![ vec![
("a", Value::string("Apple")), ("a", Value::scalar("Apple")),
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::null(), Value::null(),
Value::object( Value::object(
vec![ vec![
("a", Value::string("Apple")), ("a", Value::scalar("Apple")),
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -167,31 +167,31 @@ mod merge_parallel_fragments {
result, result,
Value::object( Value::object(
vec![ vec![
("a", Value::string("Apple")), ("a", Value::scalar("Apple")),
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
( (
"deep", "deep",
Value::object( Value::object(
vec![ vec![
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
( (
"deeper", "deeper",
Value::object( Value::object(
vec![ vec![
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -261,13 +261,13 @@ mod merge_parallel_inline_fragments {
result, result,
Value::object( Value::object(
vec![ vec![
("a", Value::string("Apple")), ("a", Value::scalar("Apple")),
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
( (
"deep", "deep",
Value::object( Value::object(
vec![ vec![
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
( (
"deeper", "deeper",
Value::list(vec![ Value::list(vec![
@ -276,37 +276,37 @@ mod merge_parallel_inline_fragments {
"deepest", "deepest",
Value::object( Value::object(
vec![ vec![
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![( vec![(
"deepest", "deepest",
Value::object( Value::object(
vec![ vec![
("b", Value::string("Banana")), ("b", Value::scalar("Banana")),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("c", Value::string("Cherry")), ("c", Value::scalar("Cherry")),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -354,7 +354,7 @@ mod threads_context_correctly {
assert_eq!( assert_eq!(
result, result,
Value::object( Value::object(
vec![("a", Value::string("Context value"))] vec![("a", Value::scalar("Context value"))]
.into_iter() .into_iter()
.collect() .collect()
) )
@ -439,7 +439,7 @@ mod dynamic_context_switching {
}, },
), ),
].into_iter() ].into_iter()
.collect(), .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");
@ -455,14 +455,14 @@ mod dynamic_context_switching {
( (
"first", "first",
Value::object( Value::object(
vec![("value", Value::string("First value"))] vec![("value", Value::scalar("First value"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
("missing", Value::null()), ("missing", Value::null()),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -493,7 +493,7 @@ mod dynamic_context_switching {
}, },
), ),
].into_iter() ].into_iter()
.collect(), .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");
@ -508,12 +508,12 @@ mod dynamic_context_switching {
vec![( vec![(
"first", "first",
Value::object( Value::object(
vec![("value", Value::string("First value"))] vec![("value", Value::scalar("First value"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
) )
); );
} }
@ -544,7 +544,7 @@ mod dynamic_context_switching {
}, },
), ),
].into_iter() ].into_iter()
.collect(), .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");
@ -591,7 +591,7 @@ mod dynamic_context_switching {
}, },
), ),
].into_iter() ].into_iter()
.collect(), .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");
@ -614,7 +614,7 @@ mod dynamic_context_switching {
( (
"first", "first",
Value::object( Value::object(
vec![("value", Value::string("First value"))] vec![("value", Value::scalar("First value"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
@ -622,7 +622,7 @@ mod dynamic_context_switching {
("missing", Value::null()), ("missing", Value::null()),
("tooLarge", Value::null()), ("tooLarge", Value::null()),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -649,7 +649,7 @@ mod dynamic_context_switching {
}, },
), ),
].into_iter() ].into_iter()
.collect(), .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");
@ -664,12 +664,12 @@ mod dynamic_context_switching {
vec![( vec![(
"first", "first",
Value::object( Value::object(
vec![("value", Value::string("First value"))] vec![("value", Value::scalar("First value"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
) )
); );
} }
@ -680,7 +680,7 @@ mod propagates_errors_to_nullable_fields {
use parser::SourcePosition; use parser::SourcePosition;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::Value; use value::{ScalarValue, Value};
struct Schema; struct Schema;
struct Inner; struct Inner;
@ -689,15 +689,18 @@ mod propagates_errors_to_nullable_fields {
NotFound, NotFound,
} }
impl IntoFieldError for CustomError { impl<S> IntoFieldError<S> for CustomError
fn into_field_error(self) -> FieldError { where
S: ScalarValue,
{
fn into_field_error(self) -> FieldError<S> {
match self { match self {
CustomError::NotFound => FieldError::new( CustomError::NotFound => {
"Not Found", let v: Value<S> = graphql_value!({
graphql_value!({
"type": "NOT_FOUND" "type": "NOT_FOUND"
}), });
), FieldError::new("Not Found", v)
}
} }
} }
} }
@ -960,7 +963,7 @@ mod named_operations {
assert_eq!( assert_eq!(
result, result,
Value::object(vec![("a", Value::string("b"))].into_iter().collect()) Value::object(vec![("a", Value::scalar("b"))].into_iter().collect())
); );
} }
@ -977,7 +980,7 @@ mod named_operations {
assert_eq!( assert_eq!(
result, result,
Value::object(vec![("a", Value::string("b"))].into_iter().collect()) Value::object(vec![("a", Value::scalar("b"))].into_iter().collect())
); );
} }
@ -995,7 +998,7 @@ mod named_operations {
assert_eq!( assert_eq!(
result, result,
Value::object(vec![("second", Value::string("b"))].into_iter().collect()) Value::object(vec![("second", Value::scalar("b"))].into_iter().collect())
); );
} }

View file

@ -121,21 +121,21 @@ mod interface {
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![ vec![
("name", Value::string("Odie")), ("name", Value::scalar("Odie")),
("woofs", Value::boolean(true)), ("woofs", Value::scalar(true)),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![ vec![
("name", Value::string("Garfield")), ("name", Value::scalar("Garfield")),
("meows", Value::boolean(false)), ("meows", Value::scalar(false)),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
]), ]),
)].into_iter() )].into_iter()
.collect() .collect()
) )
); );
} }
@ -252,23 +252,23 @@ mod union {
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![ vec![
("__typename", Value::string("Dog")), ("__typename", Value::scalar("Dog")),
("name", Value::string("Odie")), ("name", Value::scalar("Odie")),
("woofs", Value::boolean(true)), ("woofs", Value::scalar(true)),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![ vec![
("__typename", Value::string("Cat")), ("__typename", Value::scalar("Cat")),
("name", Value::string("Garfield")), ("name", Value::scalar("Garfield")),
("meows", Value::boolean(false)), ("meows", Value::scalar(false)),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
]), ]),
)].into_iter() )].into_iter()
.collect() .collect()
) )
); );
} }

View file

@ -1,7 +1,7 @@
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{Value, Object, DefaultScalarValue};
/* /*
@ -16,35 +16,32 @@ Syntax to validate:
*/ */
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(_internal)]
enum DefaultName { enum DefaultName {
Foo, Foo,
Bar, Bar,
} }
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(name = "ANamedEnum", _internal)] #[graphql(name = "ANamedEnum")]
enum Named { enum Named {
Foo, Foo,
Bar, Bar,
} }
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(_internal)]
enum NoTrailingComma { enum NoTrailingComma {
Foo, Foo,
Bar, Bar,
} }
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(description = "A description of the enum itself", _internal)] #[graphql(description = "A description of the enum itself")]
enum EnumDescription { enum EnumDescription {
Foo, Foo,
Bar, Bar,
} }
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(_internal)]
enum EnumValueDescription { enum EnumValueDescription {
#[graphql(description = "The FOO value")] #[graphql(description = "The FOO value")]
Foo, Foo,
@ -53,7 +50,6 @@ enum EnumValueDescription {
} }
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(_internal)]
enum EnumDeprecation { enum EnumDeprecation {
#[graphql(deprecated = "Please don't use FOO any more")] #[graphql(deprecated = "Please don't use FOO any more")]
Foo, Foo,
@ -74,7 +70,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F) fn run_type_info_query<F>(doc: &str, f: F)
where where
F: Fn((&Object, &Vec<Value>)) -> (), F: Fn((&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>)) -> (),
{ {
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -120,7 +116,7 @@ fn default_name_introspection() {
"#; "#;
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("DefaultName"))); assert_eq!(type_info.get_field_value("name"), Some(&Value::scalar("DefaultName")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2); assert_eq!(values.len(), 2);
@ -128,9 +124,9 @@ fn default_name_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -140,9 +136,9 @@ fn default_name_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -169,7 +165,7 @@ fn named_introspection() {
"#; "#;
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("ANamedEnum"))); assert_eq!(type_info.get_field_value("name"), Some(&Value::scalar("ANamedEnum")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2); assert_eq!(values.len(), 2);
@ -177,9 +173,9 @@ fn named_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -189,9 +185,9 @@ fn named_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -220,7 +216,7 @@ fn no_trailing_comma_introspection() {
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("NoTrailingComma")) Some(&Value::scalar("NoTrailingComma"))
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
@ -229,9 +225,9 @@ fn no_trailing_comma_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -241,9 +237,9 @@ fn no_trailing_comma_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -272,11 +268,11 @@ fn enum_description_introspection() {
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("EnumDescription")) Some(&Value::scalar("EnumDescription"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
Some(&Value::string("A description of the enum itself")) Some(&Value::scalar("A description of the enum itself"))
); );
assert_eq!(values.len(), 2); assert_eq!(values.len(), 2);
@ -284,9 +280,9 @@ fn enum_description_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -296,9 +292,9 @@ fn enum_description_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -327,7 +323,7 @@ fn enum_value_description_introspection() {
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("EnumValueDescription")) Some(&Value::scalar("EnumValueDescription"))
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
@ -336,9 +332,9 @@ fn enum_value_description_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::string("The FOO value")), ("description", Value::scalar("The FOO value")),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -348,9 +344,9 @@ fn enum_value_description_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::string("The BAR value")), ("description", Value::scalar("The BAR value")),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -379,7 +375,7 @@ fn enum_deprecation_introspection() {
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("EnumDeprecation")) Some(&Value::scalar("EnumDeprecation"))
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
@ -388,12 +384,12 @@ fn enum_deprecation_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("FOO")), ("name", Value::scalar("FOO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(true)), ("isDeprecated", Value::scalar(true)),
( (
"deprecationReason", "deprecationReason",
Value::string("Please don't use FOO any more"), Value::scalar("Please don't use FOO any more"),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -403,12 +399,12 @@ fn enum_deprecation_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("BAR")), ("name", Value::scalar("BAR")),
("description", Value::string("The BAR value")), ("description", Value::scalar("The BAR value")),
("isDeprecated", Value::boolean(true)), ("isDeprecated", Value::scalar(true)),
( (
"deprecationReason", "deprecationReason",
Value::string("Please don't use BAR any more"), Value::scalar("Please don't use BAR any more"),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
@ -437,7 +433,7 @@ fn enum_deprecation_no_values_introspection() {
run_type_info_query(doc, |(type_info, values)| { run_type_info_query(doc, |(type_info, values)| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("EnumDeprecation")) Some(&Value::scalar("EnumDeprecation"))
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));

View file

@ -2,50 +2,46 @@ use ast::{FromInputValue, InputValue};
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{DefaultScalarValue, Object, Value};
struct Root; struct Root;
#[derive(GraphQLInputObject)] #[derive(GraphQLInputObject)]
#[graphql(_internal)]
struct DefaultName { struct DefaultName {
field_one: String, field_one: String,
field_two: String, field_two: String,
} }
#[derive(GraphQLInputObject)] #[derive(GraphQLInputObject)]
#[graphql(_internal)]
struct NoTrailingComma { struct NoTrailingComma {
field_one: String, field_one: String,
field_two: String, field_two: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct Derive { struct Derive {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(name = "ANamedInputObject", _internal)] #[graphql(name = "ANamedInputObject")]
struct Named { struct Named {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(description = "Description for the input object", _internal)] #[graphql(description = "Description for the input object")]
struct Description { struct Description {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
pub struct Public { pub struct Public {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(description = "Description for the input object", _internal)] #[graphql(description = "Description for the input object")]
pub struct PublicWithDescription { pub struct PublicWithDescription {
field_one: String, field_one: String,
} }
@ -54,20 +50,18 @@ pub struct PublicWithDescription {
#[graphql( #[graphql(
name = "APublicNamedInputObjectWithDescription", name = "APublicNamedInputObjectWithDescription",
description = "Description for the input object", description = "Description for the input object",
_internal
)] )]
pub struct NamedPublicWithDescription { pub struct NamedPublicWithDescription {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(name = "APublicNamedInputObject", _internal)] #[graphql(name = "APublicNamedInputObject")]
pub struct NamedPublic { pub struct NamedPublic {
field_one: String, field_one: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct FieldDescription { struct FieldDescription {
#[graphql(description = "The first field")] #[graphql(description = "The first field")]
field_one: String, field_one: String,
@ -76,7 +70,6 @@ struct FieldDescription {
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct FieldWithDefaults { struct FieldWithDefaults {
#[graphql(default = "123")] #[graphql(default = "123")]
field_one: i32, field_one: i32,
@ -104,7 +97,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F) fn run_type_info_query<F>(doc: &str, f: F)
where where
F: Fn(&Object, &Vec<Value>) -> (), F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{ {
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -154,15 +147,21 @@ fn default_name_introspection() {
"#; "#;
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("DefaultName"))); assert_eq!(
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); type_info.get_field_value("name"),
Some(&Value::scalar("DefaultName"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
);
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -170,24 +169,24 @@ fn default_name_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldTwo")), ("name", Value::scalar("fieldTwo")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -195,17 +194,17 @@ fn default_name_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -213,12 +212,12 @@ fn default_name_introspection() {
#[test] #[test]
fn default_name_input_value() { fn default_name_input_value() {
let iv = InputValue::object( let iv: InputValue<DefaultScalarValue> = InputValue::object(
vec![ vec![
("fieldOne", InputValue::string("number one")), ("fieldOne", InputValue::scalar("number one")),
("fieldTwo", InputValue::string("number two")), ("fieldTwo", InputValue::scalar("number two")),
].into_iter() ].into_iter()
.collect(), .collect(),
); );
let dv: Option<DefaultName> = FromInputValue::from_input_value(&iv); let dv: Option<DefaultName> = FromInputValue::from_input_value(&iv);
@ -255,16 +254,19 @@ fn no_trailing_comma_introspection() {
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("NoTrailingComma")) Some(&Value::scalar("NoTrailingComma"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -272,24 +274,24 @@ fn no_trailing_comma_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldTwo")), ("name", Value::scalar("fieldTwo")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -297,17 +299,17 @@ fn no_trailing_comma_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -335,15 +337,21 @@ fn derive_introspection() {
"#; "#;
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("Derive"))); assert_eq!(
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); type_info.get_field_value("name"),
Some(&Value::scalar("Derive"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
);
assert_eq!(fields.len(), 1); assert_eq!(fields.len(), 1);
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -351,17 +359,17 @@ fn derive_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -404,16 +412,19 @@ fn named_introspection() {
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("ANamedInputObject")) Some(&Value::scalar("ANamedInputObject"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 1); assert_eq!(fields.len(), 1);
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -421,17 +432,17 @@ fn named_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -459,10 +470,13 @@ fn description_introspection() {
"#; "#;
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("Description"))); assert_eq!(
type_info.get_field_value("name"),
Some(&Value::scalar("Description"))
);
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
Some(&Value::string("Description for the input object")) Some(&Value::scalar("Description for the input object"))
); );
assert_eq!(fields.len(), 1); assert_eq!(fields.len(), 1);
@ -470,7 +484,7 @@ fn description_introspection() {
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::null()), ("description", Value::null()),
( (
"type", "type",
@ -478,17 +492,17 @@ fn description_introspection() {
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -518,59 +532,62 @@ fn field_description_introspection() {
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("FieldDescription")) Some(&Value::scalar("FieldDescription"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
); );
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
("description", Value::string("The first field")), ("description", Value::scalar("The first field")),
( (
"type", "type",
Value::object( Value::object(
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldTwo")), ("name", Value::scalar("fieldTwo")),
("description", Value::string("The second field")), ("description", Value::scalar("The second field")),
( (
"type", "type",
Value::object( Value::object(
vec![( vec![(
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("String"))] vec![("name", Value::scalar("String"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -596,7 +613,7 @@ fn field_with_defaults_introspection() {
run_type_info_query(doc, |type_info, fields| { run_type_info_query(doc, |type_info, fields| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("FieldWithDefaults")) Some(&Value::scalar("FieldWithDefaults"))
); );
assert_eq!(fields.len(), 2); assert_eq!(fields.len(), 2);
@ -604,28 +621,28 @@ fn field_with_defaults_introspection() {
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldOne")), ("name", Value::scalar("fieldOne")),
( (
"type", "type",
Value::object(vec![("name", Value::string("Int"))].into_iter().collect()), Value::object(vec![("name", Value::scalar("Int"))].into_iter().collect()),
), ),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("fieldTwo")), ("name", Value::scalar("fieldTwo")),
( (
"type", "type",
Value::object(vec![("name", Value::string("Int"))].into_iter().collect()), Value::object(vec![("name", Value::scalar("Int"))].into_iter().collect()),
), ),
("defaultValue", Value::string("456")), ("defaultValue", Value::scalar("456")),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });

View file

@ -8,10 +8,10 @@ use self::input_object::{NamedPublic, NamedPublicWithDescription};
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::Value; use value::{ParseScalarResult, ParseScalarValue, Value};
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(name = "SampleEnum", _internal)] #[graphql(name = "SampleEnum")]
enum Sample { enum Sample {
One, One,
Two, Two,
@ -19,17 +19,21 @@ enum Sample {
struct Scalar(i32); struct Scalar(i32);
struct Interface {} struct Interface;
struct Root {} struct Root;
graphql_scalar!(Scalar as "SampleScalar" { graphql_scalar!(Scalar as "SampleScalar" {
resolve(&self) -> Value { resolve(&self) -> Value {
Value::int(self.0) Value::scalar(self.0)
} }
from_input_value(v: &InputValue) -> Option<Scalar> { from_input_value(v: &InputValue) -> Option<Scalar> {
v.as_int_value().map(|i| Scalar(i)) v.as_scalar_value().map(|i: &i32| Scalar(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<i32 as ParseScalarValue>::from_str(value)
} }
}); });
@ -41,7 +45,7 @@ graphql_interface!(Interface: () as "SampleInterface" |&self| {
} }
instance_resolvers: |&_| { instance_resolvers: |&_| {
Root => Some(Root {}), Root => Some(Root),
} }
}); });
@ -71,7 +75,7 @@ fn test_execution() {
second: sampleScalar(first: 10 second: 20) second: sampleScalar(first: 10 second: 20)
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -84,11 +88,11 @@ fn test_execution() {
result, result,
Value::object( Value::object(
vec![ vec![
("sampleEnum", Value::string("ONE")), ("sampleEnum", Value::scalar("ONE")),
("first", Value::int(123)), ("first", Value::scalar(123)),
("second", Value::int(30)), ("second", Value::scalar(30)),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }
@ -114,7 +118,7 @@ fn enum_introspection() {
} }
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -133,11 +137,11 @@ fn enum_introspection() {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("SampleEnum")) Some(&Value::scalar("SampleEnum"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("kind"), type_info.get_field_value("kind"),
Some(&Value::string("ENUM")) Some(&Value::scalar("ENUM"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
@ -168,24 +172,24 @@ fn enum_introspection() {
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("ONE")), ("name", Value::scalar("ONE")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
values.contains(&Value::object( values.contains(&Value::object(
vec![ vec![
("name", Value::string("TWO")), ("name", Value::scalar("TWO")),
("description", Value::null()), ("description", Value::null()),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
} }
@ -225,7 +229,7 @@ fn interface_introspection() {
} }
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -244,15 +248,15 @@ fn interface_introspection() {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("SampleInterface")) Some(&Value::scalar("SampleInterface"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("kind"), type_info.get_field_value("kind"),
Some(&Value::string("INTERFACE")) Some(&Value::scalar("INTERFACE"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
Some(&Value::string("A sample interface")) Some(&Value::scalar("A sample interface"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("interfaces"), type_info.get_field_value("interfaces"),
@ -277,7 +281,7 @@ fn interface_introspection() {
assert_eq!(possible_types.len(), 1); assert_eq!(possible_types.len(), 1);
assert!(possible_types.contains(&Value::object( assert!(possible_types.contains(&Value::object(
vec![("name", Value::string("Root"))].into_iter().collect() vec![("name", Value::scalar("Root"))].into_iter().collect()
))); )));
let fields = type_info let fields = type_info
@ -291,10 +295,10 @@ fn interface_introspection() {
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("sampleEnum")), ("name", Value::scalar("sampleEnum")),
( (
"description", "description",
Value::string("A sample field in the interface"), Value::scalar("A sample field in the interface"),
), ),
("args", Value::list(vec![])), ("args", Value::list(vec![])),
( (
@ -302,25 +306,25 @@ fn interface_introspection() {
Value::object( Value::object(
vec![ vec![
("name", Value::null()), ("name", Value::null()),
("kind", Value::string("NON_NULL")), ("kind", Value::scalar("NON_NULL")),
( (
"ofType", "ofType",
Value::object( Value::object(
vec![ vec![
("name", Value::string("SampleEnum")), ("name", Value::scalar("SampleEnum")),
("kind", Value::string("ENUM")), ("kind", Value::scalar("ENUM")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
} }
@ -371,7 +375,7 @@ fn object_introspection() {
} }
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -390,20 +394,20 @@ fn object_introspection() {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("Root")) Some(&Value::scalar("Root"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("kind"), type_info.get_field_value("kind"),
Some(&Value::string("OBJECT")) Some(&Value::scalar("OBJECT"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
Some(&Value::string("The root query object in the schema")) Some(&Value::scalar("The root query object in the schema"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("interfaces"), type_info.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![("name", Value::string("SampleInterface"))] vec![("name", Value::scalar("SampleInterface"))]
.into_iter() .into_iter()
.collect(), .collect(),
)])) )]))
@ -435,7 +439,7 @@ fn object_introspection() {
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("sampleEnum")), ("name", Value::scalar("sampleEnum")),
("description", Value::null()), ("description", Value::null()),
("args", Value::list(vec![])), ("args", Value::list(vec![])),
( (
@ -443,86 +447,86 @@ fn object_introspection() {
Value::object( Value::object(
vec![ vec![
("name", Value::null()), ("name", Value::null()),
("kind", Value::string("NON_NULL")), ("kind", Value::scalar("NON_NULL")),
( (
"ofType", "ofType",
Value::object( Value::object(
vec![ vec![
("name", Value::string("SampleEnum")), ("name", Value::scalar("SampleEnum")),
("kind", Value::string("ENUM")), ("kind", Value::scalar("ENUM")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![ vec![
("name", Value::string("sampleScalar")), ("name", Value::scalar("sampleScalar")),
( (
"description", "description",
Value::string("A sample scalar field on the object"), Value::scalar("A sample scalar field on the object"),
), ),
( (
"args", "args",
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![ vec![
("name", Value::string("first")), ("name", Value::scalar("first")),
("description", Value::string("The first number")), ("description", Value::scalar("The first number")),
( (
"type", "type",
Value::object( Value::object(
vec![ vec![
("name", Value::null()), ("name", Value::null()),
("kind", Value::string("NON_NULL")), ("kind", Value::scalar("NON_NULL")),
( (
"ofType", "ofType",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Int")), ("name", Value::scalar("Int")),
("kind", Value::string("SCALAR")), ("kind", Value::scalar("SCALAR")),
("ofType", Value::null()), ("ofType", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![ vec![
("name", Value::string("second")), ("name", Value::scalar("second")),
("description", Value::string("The second number")), ("description", Value::scalar("The second number")),
( (
"type", "type",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Int")), ("name", Value::scalar("Int")),
("kind", Value::string("SCALAR")), ("kind", Value::scalar("SCALAR")),
("ofType", Value::null()), ("ofType", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
@ -531,25 +535,25 @@ fn object_introspection() {
Value::object( Value::object(
vec![ vec![
("name", Value::null()), ("name", Value::null()),
("kind", Value::string("NON_NULL")), ("kind", Value::scalar("NON_NULL")),
( (
"ofType", "ofType",
Value::object( Value::object(
vec![ vec![
("name", Value::string("SampleScalar")), ("name", Value::scalar("SampleScalar")),
("kind", Value::string("SCALAR")), ("kind", Value::scalar("SCALAR")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
("isDeprecated", Value::boolean(false)), ("isDeprecated", Value::scalar(false)),
("deprecationReason", Value::null()), ("deprecationReason", Value::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
} }
@ -571,7 +575,7 @@ fn scalar_introspection() {
} }
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -590,8 +594,8 @@ fn scalar_introspection() {
type_info, type_info,
&Value::object( &Value::object(
vec![ vec![
("name", Value::string("SampleScalar")), ("name", Value::scalar("SampleScalar")),
("kind", Value::string("SCALAR")), ("kind", Value::scalar("SCALAR")),
("description", Value::null()), ("description", Value::null()),
("fields", Value::null()), ("fields", Value::null()),
("interfaces", Value::null()), ("interfaces", Value::null()),
@ -600,7 +604,7 @@ fn scalar_introspection() {
("inputFields", Value::null()), ("inputFields", Value::null()),
("ofType", Value::null()), ("ofType", Value::null()),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }

View file

@ -1,10 +1,10 @@
use ast::{FromInputValue, InputValue}; use ast::InputValue;
use executor::Variables; use executor::Variables;
use parser::SourcePosition; use parser::SourcePosition;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use validation::RuleError; use validation::RuleError;
use value::{Object, Value}; use value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, Value};
use GraphQLError::ValidationError; use GraphQLError::ValidationError;
#[derive(Debug)] #[derive(Debug)]
@ -14,22 +14,26 @@ struct TestType;
graphql_scalar!(TestComplexScalar { graphql_scalar!(TestComplexScalar {
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string("SerializedValue") Value::scalar(String::from("SerializedValue"))
} }
from_input_value(v: &InputValue) -> Option<TestComplexScalar> { from_input_value(v: &InputValue) -> Option<TestComplexScalar> {
if let Some(s) = v.as_string_value() { if let Some(s) = v.as_scalar_value::<String>() {
if s == "SerializedValue" { if *s == "SerializedValue" {
return Some(TestComplexScalar); return Some(TestComplexScalar);
} }
} }
None None
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<String as ParseScalarValue<_>>::from_str(value)
}
}); });
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)] #[graphql(scalar = "DefaultScalarValue")]
struct TestInputObject { struct TestInputObject {
a: Option<String>, a: Option<String>,
b: Option<Vec<Option<String>>>, b: Option<Vec<Option<String>>>,
@ -38,67 +42,24 @@ struct TestInputObject {
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)] #[graphql(scalar = "DefaultScalarValue")]
struct TestNestedInputObject { struct TestNestedInputObject {
na: TestInputObject, na: TestInputObject,
nb: String, nb: String,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct ExampleInputObject { struct ExampleInputObject {
a: Option<String>, a: Option<String>,
b: i32, b: i32,
} }
#[derive(GraphQLInputObject, Debug)] #[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct InputWithDefaults { struct InputWithDefaults {
#[graphql(default = "123")] #[graphql(default = "123")]
a: i32, a: i32,
} }
#[derive(Debug)]
enum CustomInput {
A(String),
B(String),
}
impl FromInputValue for CustomInput {
fn from_input_value(v: &InputValue) -> Option<Self> {
let obj = v.to_object_value()?;
match (obj.get("variant"), obj.get("value")) {
(Some(&&InputValue::String(ref variant)), Some(&&InputValue::String(ref value))) => {
match variant.as_str() {
"A" => Some(CustomInput::A(value.to_owned())),
"B" => Some(CustomInput::B(value.to_owned())),
_ => None,
}
}
_ => None,
}
}
}
impl ::GraphQLType for CustomInput {
type Context = ();
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
Some("CustomInput")
}
fn meta<'r>(_: &(), registry: &mut ::Registry<'r>) -> ::meta::MetaType<'r> {
let fields = &[
registry.arg::<String>("variant", &()),
registry.arg::<String>("value", &()),
];
registry
.build_input_object_type::<CustomInput>(&(), fields)
.into_meta()
}
}
graphql_object!(TestType: () |&self| { graphql_object!(TestType: () |&self| {
field field_with_object_input(input: Option<TestInputObject>) -> String { field field_with_object_input(input: Option<TestInputObject>) -> String {
format!("{:?}", input) format!("{:?}", input)
@ -144,10 +105,6 @@ graphql_object!(TestType: () |&self| {
format!("a: {:?}", arg.a) format!("a: {:?}", arg.a)
} }
field input_with_custom(input: CustomInput) -> String {
format!("{:?}", input)
}
field integer_input(value: i32) -> String { field integer_input(value: i32) -> String {
format!("value: {}", value) format!("value: {}", value)
} }
@ -157,9 +114,9 @@ graphql_object!(TestType: () |&self| {
} }
}); });
fn run_variable_query<F>(query: &str, vars: Variables, f: F) fn run_variable_query<F>(query: &str, vars: Variables<DefaultScalarValue>, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -167,7 +124,7 @@ where
assert_eq!(errs, []); assert_eq!(errs, []);
println!("Result: {:#?}", result); println!("Result: {:?}", result);
let obj = result.as_object_value().expect("Result is not an object"); let obj = result.as_object_value().expect("Result is not an object");
@ -176,7 +133,7 @@ where
fn run_query<F>(query: &str, f: F) fn run_query<F>(query: &str, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
run_variable_query(query, Variables::new(), f); run_variable_query(query, Variables::new(), f);
} }
@ -185,10 +142,10 @@ where
fn inline_complex_input() { fn inline_complex_input() {
run_query( run_query(
r#"{ fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"}) }"#, r#"{ fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"}) }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
}, },
); );
} }
@ -197,10 +154,10 @@ fn inline_complex_input() {
fn inline_parse_single_value_to_list() { fn inline_parse_single_value_to_list() {
run_query( run_query(
r#"{ fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"}) }"#, r#"{ fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"}) }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
}, },
); );
} }
@ -209,67 +166,14 @@ fn inline_parse_single_value_to_list() {
fn inline_runs_from_input_value_on_scalar() { fn inline_runs_from_input_value_on_scalar() {
run_query( run_query(
r#"{ fieldWithObjectInput(input: {c: "baz", d: "SerializedValue"}) }"#, r#"{ fieldWithObjectInput(input: {c: "baz", d: "SerializedValue"}) }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#)));
}, },
); );
} }
#[test]
fn variable_valid_custom_input() {
run_variable_query(
r#"query q($input: CustomInput!) { inputWithCustom(input: $input) }"#,
vec![(
"input".to_owned(),
InputValue::object(
vec![
("variant", InputValue::string("B")),
("value", InputValue::string("whatever")),
].into_iter()
.collect(),
),
)].into_iter()
.collect(),
|result| {
assert_eq!(
result.get_field_value("inputWithCustom"),
Some(&Value::string(r#"B("whatever")"#))
);
},
);
}
#[test]
fn variable_invalid_custom_input() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($input: CustomInput!) { inputWithCustom(input: $input) }"#;
let vars = vec![(
"input".to_owned(),
InputValue::object(
vec![
("variant", InputValue::string("C")),
("value", InputValue::string("whatever")),
].into_iter()
.collect(),
),
)].into_iter()
.collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
assert_eq!(
error,
ValidationError(vec![RuleError::new(
r#"Variable "$input" got invalid value. Expected input of type "CustomInput". Got: "{variant: "C", value: "whatever"}"."#,
&[SourcePosition::new(8, 0, 8)],
)])
);
}
#[test] #[test]
fn variable_complex_input() { fn variable_complex_input() {
run_variable_query( run_variable_query(
@ -278,18 +182,18 @@ fn variable_complex_input() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("a", InputValue::string("foo")), ("a", InputValue::scalar("foo")),
("b", InputValue::list(vec![InputValue::string("bar")])), ("b", InputValue::list(vec![InputValue::scalar("bar")])),
("c", InputValue::string("baz")), ("c", InputValue::scalar("baz")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
}, },
); );
} }
@ -302,18 +206,18 @@ fn variable_parse_single_value_to_list() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("a", InputValue::string("foo")), ("a", InputValue::scalar("foo")),
("b", InputValue::string("bar")), ("b", InputValue::scalar("bar")),
("c", InputValue::string("baz")), ("c", InputValue::scalar("baz")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
}, },
); );
} }
@ -326,17 +230,17 @@ fn variable_runs_from_input_value_on_scalar() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("c", InputValue::string("baz")), ("c", InputValue::scalar("baz")),
("d", InputValue::string("SerializedValue")), ("d", InputValue::scalar("SerializedValue")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithObjectInput"), result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#))); Some(&Value::scalar(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#)));
}, },
); );
} }
@ -350,14 +254,14 @@ fn variable_error_on_nested_non_null() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("a", InputValue::string("foo")), ("a", InputValue::scalar("foo")),
("b", InputValue::string("bar")), ("b", InputValue::scalar("bar")),
("c", InputValue::null()), ("c", InputValue::null()),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -375,7 +279,7 @@ fn variable_error_on_incorrect_type() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#;
let vars = vec![("input".to_owned(), InputValue::string("foo bar"))] let vars = vec![("input".to_owned(), InputValue::scalar("foo bar"))]
.into_iter() .into_iter()
.collect(); .collect();
@ -398,13 +302,13 @@ fn variable_error_on_omit_non_null() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("a", InputValue::string("foo")), ("a", InputValue::scalar("foo")),
("b", InputValue::string("bar")), ("b", InputValue::scalar("bar")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -428,12 +332,12 @@ fn variable_multiple_errors_with_nesting() {
InputValue::object( InputValue::object(
vec![( vec![(
"na", "na",
InputValue::object(vec![("a", InputValue::string("foo"))].into_iter().collect()), InputValue::object(vec![("a", InputValue::scalar("foo"))].into_iter().collect()),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -458,15 +362,15 @@ fn variable_error_on_additional_field() {
"input".to_owned(), "input".to_owned(),
InputValue::object( InputValue::object(
vec![ vec![
("a", InputValue::string("foo")), ("a", InputValue::scalar("foo")),
("b", InputValue::string("bar")), ("b", InputValue::scalar("bar")),
("c", InputValue::string("baz")), ("c", InputValue::scalar("baz")),
("extra", InputValue::string("dog")), ("extra", InputValue::scalar("dog")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -481,22 +385,25 @@ fn variable_error_on_additional_field() {
#[test] #[test]
fn allow_nullable_inputs_to_be_omitted() { fn allow_nullable_inputs_to_be_omitted() {
run_query(r#"{ fieldWithNullableStringInput }"#, |result| { run_query(
assert_eq!( r#"{ fieldWithNullableStringInput }"#,
result.get_field_value("fieldWithNullableStringInput"), |result: &Object<DefaultScalarValue>| {
Some(&Value::string(r#"None"#)) assert_eq!(
); result.get_field_value("fieldWithNullableStringInput"),
}); Some(&Value::scalar(r#"None"#))
);
},
);
} }
#[test] #[test]
fn allow_nullable_inputs_to_be_omitted_in_variable() { fn allow_nullable_inputs_to_be_omitted_in_variable() {
run_query( run_query(
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#, r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNullableStringInput"), result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)) Some(&Value::scalar(r#"None"#))
); );
}, },
); );
@ -506,10 +413,10 @@ fn allow_nullable_inputs_to_be_omitted_in_variable() {
fn allow_nullable_inputs_to_be_explicitly_null() { fn allow_nullable_inputs_to_be_explicitly_null() {
run_query( run_query(
r#"{ fieldWithNullableStringInput(input: null) }"#, r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNullableStringInput"), result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)) Some(&Value::scalar(r#"None"#))
); );
}, },
); );
@ -522,10 +429,10 @@ fn allow_nullable_inputs_to_be_set_to_null_in_variable() {
vec![("value".to_owned(), InputValue::null())] vec![("value".to_owned(), InputValue::null())]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNullableStringInput"), result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#)) Some(&Value::scalar(r#"None"#))
); );
}, },
); );
@ -535,13 +442,13 @@ fn allow_nullable_inputs_to_be_set_to_null_in_variable() {
fn allow_nullable_inputs_to_be_set_to_value_in_variable() { fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
run_variable_query( run_variable_query(
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#, r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
vec![("value".to_owned(), InputValue::string("a"))] vec![("value".to_owned(), InputValue::scalar("a"))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNullableStringInput"), result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"Some("a")"#)) Some(&Value::scalar(r#"Some("a")"#))
); );
}, },
); );
@ -551,10 +458,10 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
fn allow_nullable_inputs_to_be_set_to_value_directly() { fn allow_nullable_inputs_to_be_set_to_value_directly() {
run_query( run_query(
r#"{ fieldWithNullableStringInput(input: "a") }"#, r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNullableStringInput"), result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"Some("a")"#)) Some(&Value::scalar(r#"Some("a")"#))
); );
}, },
); );
@ -602,13 +509,13 @@ fn does_not_allow_non_nullable_input_to_be_set_to_null_in_variable() {
fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() { fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
run_variable_query( run_variable_query(
r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#, r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#,
vec![("value".to_owned(), InputValue::string("a"))] vec![("value".to_owned(), InputValue::scalar("a"))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNonNullableStringInput"), result.get_field_value("fieldWithNonNullableStringInput"),
Some(&Value::string(r#""a""#)) Some(&Value::scalar(r#""a""#))
); );
}, },
); );
@ -618,10 +525,10 @@ fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
fn allow_non_nullable_inputs_to_be_set_to_value_directly() { fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
run_query( run_query(
r#"{ fieldWithNonNullableStringInput(input: "a") }"#, r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithNonNullableStringInput"), result.get_field_value("fieldWithNonNullableStringInput"),
Some(&Value::string(r#""a""#)) Some(&Value::scalar(r#""a""#))
); );
}, },
); );
@ -634,8 +541,11 @@ fn allow_lists_to_be_null() {
vec![("input".to_owned(), InputValue::null())] vec![("input".to_owned(), InputValue::null())]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result: &Object<DefaultScalarValue>| {
assert_eq!(result.get_field_value("list"), Some(&Value::string(r#"None"#))); assert_eq!(
result.get_field_value("list"),
Some(&Value::scalar(r#"None"#))
);
}, },
); );
} }
@ -646,13 +556,13 @@ fn allow_lists_to_contain_values() {
r#"query q($input: [String]) { list(input: $input) }"#, r#"query q($input: [String]) { list(input: $input) }"#,
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![InputValue::string("A")]), InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("list"), result.get_field_value("list"),
Some(&Value::string(r#"Some([Some("A")])"#)) Some(&Value::scalar(r#"Some([Some("A")])"#))
); );
}, },
); );
@ -665,16 +575,16 @@ fn allow_lists_to_contain_null() {
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![ InputValue::list(vec![
InputValue::string("A"), InputValue::scalar("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::scalar("B"),
]), ]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("list"), result.get_field_value("list"),
Some(&Value::string(r#"Some([Some("A"), None, Some("B")])"#)) Some(&Value::scalar(r#"Some([Some("A"), None, Some("B")])"#))
); );
}, },
); );
@ -706,11 +616,14 @@ fn allow_non_null_lists_to_contain_values() {
r#"query q($input: [String]!) { nnList(input: $input) }"#, r#"query q($input: [String]!) { nnList(input: $input) }"#,
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![InputValue::string("A")]), InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!(result.get_field_value("nnList"), Some(&Value::string(r#"[Some("A")]"#))); assert_eq!(
result.get_field_value("nnList"),
Some(&Value::scalar(r#"[Some("A")]"#))
);
}, },
); );
} }
@ -721,16 +634,16 @@ fn allow_non_null_lists_to_contain_null() {
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![ InputValue::list(vec![
InputValue::string("A"), InputValue::scalar("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::scalar("B"),
]), ]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("nnList"), result.get_field_value("nnList"),
Some(&Value::string(r#"[Some("A"), None, Some("B")]"#)) Some(&Value::scalar(r#"[Some("A"), None, Some("B")]"#))
); );
}, },
); );
@ -744,7 +657,10 @@ fn allow_lists_of_non_null_to_be_null() {
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!(result.get_field_value("listNn"), Some(&Value::string(r#"None"#))); assert_eq!(
result.get_field_value("listNn"),
Some(&Value::scalar(r#"None"#))
);
}, },
); );
} }
@ -755,11 +671,14 @@ fn allow_lists_of_non_null_to_contain_values() {
r#"query q($input: [String!]) { listNn(input: $input) }"#, r#"query q($input: [String!]) { listNn(input: $input) }"#,
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![InputValue::string("A")]), InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!(result.get_field_value("listNn"), Some(&Value::string(r#"Some(["A"])"#))); assert_eq!(
result.get_field_value("listNn"),
Some(&Value::scalar(r#"Some(["A"])"#))
);
}, },
); );
} }
@ -772,12 +691,12 @@ fn does_not_allow_lists_of_non_null_to_contain_null() {
let vars = vec![( let vars = vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![ InputValue::list(vec![
InputValue::string("A"), InputValue::scalar("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::scalar("B"),
]), ]),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -797,12 +716,12 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() {
let vars = vec![( let vars = vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![ InputValue::list(vec![
InputValue::string("A"), InputValue::scalar("A"),
InputValue::null(), InputValue::null(),
InputValue::string("B"), InputValue::scalar("B"),
]), ]),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -840,11 +759,14 @@ fn allow_non_null_lists_of_non_null_to_contain_values() {
r#"query q($input: [String!]!) { nnListNn(input: $input) }"#, r#"query q($input: [String!]!) { nnListNn(input: $input) }"#,
vec![( vec![(
"input".to_owned(), "input".to_owned(),
InputValue::list(vec![InputValue::string("A")]), InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter() )].into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!(result.get_field_value("nnListNn"), Some(&Value::string(r#"["A"]"#))); assert_eq!(
result.get_field_value("nnListNn"),
Some(&Value::scalar(r#"["A"]"#))
);
}, },
); );
} }
@ -856,9 +778,9 @@ fn does_not_allow_invalid_types_to_be_used_as_values() {
let query = r#"query q($input: TestType!) { fieldWithObjectInput(input: $input) }"#; let query = r#"query q($input: TestType!) { fieldWithObjectInput(input: $input) }"#;
let vars = vec![( let vars = vec![(
"value".to_owned(), "value".to_owned(),
InputValue::list(vec![InputValue::string("A"), InputValue::string("B")]), InputValue::list(vec![InputValue::scalar("A"), InputValue::scalar("B")]),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -877,9 +799,9 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
let query = r#"query q($input: UnknownType!) { fieldWithObjectInput(input: $input) }"#; let query = r#"query q($input: UnknownType!) { fieldWithObjectInput(input: $input) }"#;
let vars = vec![( let vars = vec![(
"value".to_owned(), "value".to_owned(),
InputValue::list(vec![InputValue::string("A"), InputValue::string("B")]), InputValue::list(vec![InputValue::scalar("A"), InputValue::scalar("B")]),
)].into_iter() )].into_iter()
.collect(); .collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -896,7 +818,7 @@ fn default_argument_when_not_provided() {
run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| { run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithDefaultArgumentValue"), result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#)) Some(&Value::scalar(r#""Hello World""#))
); );
}); });
} }
@ -908,7 +830,7 @@ fn default_argument_when_nullable_variable_not_provided() {
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithDefaultArgumentValue"), result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#)) Some(&Value::scalar(r#""Hello World""#))
); );
}, },
); );
@ -924,7 +846,7 @@ fn default_argument_when_nullable_variable_set_to_null() {
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("fieldWithDefaultArgumentValue"), result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#)) Some(&Value::scalar(r#""Hello World""#))
); );
}, },
); );
@ -935,14 +857,14 @@ fn nullable_input_object_arguments_successful_without_variables() {
run_query(r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#, |result| { run_query(r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#, |result| {
assert_eq!( assert_eq!(
result.get_field_value("exampleInput"), result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: Some("abc"), b: 123"#)) Some(&Value::scalar(r#"a: Some("abc"), b: 123"#))
); );
}); });
run_query(r#"{ exampleInput(arg: {a: null, b: 1}) }"#, |result| { run_query(r#"{ exampleInput(arg: {a: null, b: 1}) }"#, |result| {
assert_eq!( assert_eq!(
result.get_field_value("exampleInput"), result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)) Some(&Value::scalar(r#"a: None, b: 1"#))
); );
}); });
} }
@ -951,13 +873,13 @@ fn nullable_input_object_arguments_successful_without_variables() {
fn nullable_input_object_arguments_successful_with_variables() { fn nullable_input_object_arguments_successful_with_variables() {
run_variable_query( run_variable_query(
r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#, r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#,
vec![("var".to_owned(), InputValue::int(123))] vec![("var".to_owned(), InputValue::scalar(123))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("exampleInput"), result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 123"#)) Some(&Value::scalar(r#"a: None, b: 123"#))
); );
}, },
); );
@ -970,7 +892,7 @@ fn nullable_input_object_arguments_successful_with_variables() {
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("exampleInput"), result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)) Some(&Value::scalar(r#"a: None, b: 1"#))
); );
}, },
); );
@ -981,7 +903,7 @@ fn nullable_input_object_arguments_successful_with_variables() {
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("exampleInput"), result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#)) Some(&Value::scalar(r#"a: None, b: 1"#))
); );
}, },
); );
@ -1066,19 +988,19 @@ fn input_object_with_default_values() {
run_query(r#"{ inputWithDefaults(arg: {a: 1}) }"#, |result| { run_query(r#"{ inputWithDefaults(arg: {a: 1}) }"#, |result| {
assert_eq!( assert_eq!(
result.get_field_value("inputWithDefaults"), result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)) Some(&Value::scalar(r#"a: 1"#))
); );
}); });
run_variable_query( run_variable_query(
r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#, r#"query q($var: Int!) { inputWithDefaults(arg: {a: $var}) }"#,
vec![("var".to_owned(), InputValue::int(1))] vec![("var".to_owned(), InputValue::scalar(1))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("inputWithDefaults"), result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)) Some(&Value::scalar(r#"a: 1"#))
); );
}, },
); );
@ -1089,20 +1011,20 @@ fn input_object_with_default_values() {
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("inputWithDefaults"), result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#)) Some(&Value::scalar(r#"a: 1"#))
); );
}, },
); );
run_variable_query( run_variable_query(
r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#, r#"query q($var: Int = 1) { inputWithDefaults(arg: {a: $var}) }"#,
vec![("var".to_owned(), InputValue::int(2))] vec![("var".to_owned(), InputValue::scalar(2))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("inputWithDefaults"), result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 2"#)) Some(&Value::scalar(r#"a: 2"#))
); );
}, },
); );
@ -1115,26 +1037,26 @@ mod integers {
fn positive_and_negative_should_work() { fn positive_and_negative_should_work() {
run_variable_query( run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#, r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(1))] vec![("var".to_owned(), InputValue::scalar(1))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("integerInput"), result.get_field_value("integerInput"),
Some(&Value::string(r#"value: 1"#)) Some(&Value::scalar(r#"value: 1"#))
); );
}, },
); );
run_variable_query( run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#, r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(-1))] vec![("var".to_owned(), InputValue::scalar(-1))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("integerInput"), result.get_field_value("integerInput"),
Some(&Value::string(r#"value: -1"#)) Some(&Value::scalar(r#"value: -1"#))
); );
}, },
); );
@ -1145,7 +1067,7 @@ mod integers {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![("var".to_owned(), InputValue::float(10.0))] let vars = vec![("var".to_owned(), InputValue::scalar(10.0))]
.into_iter() .into_iter()
.collect(); .collect();
@ -1165,7 +1087,7 @@ mod integers {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let query = r#"query q($var: Int!) { integerInput(value: $var) }"#;
let vars = vec![("var".to_owned(), InputValue::string("10"))] let vars = vec![("var".to_owned(), InputValue::scalar("10"))]
.into_iter() .into_iter()
.collect(); .collect();
@ -1188,13 +1110,13 @@ mod floats {
fn float_values_should_work() { fn float_values_should_work() {
run_variable_query( run_variable_query(
r#"query q($var: Float!) { floatInput(value: $var) }"#, r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::float(10.0))] vec![("var".to_owned(), InputValue::scalar(10.0))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("floatInput"), result.get_field_value("floatInput"),
Some(&Value::string(r#"value: 10"#)) Some(&Value::scalar(r#"value: 10"#))
); );
}, },
); );
@ -1204,13 +1126,13 @@ mod floats {
fn coercion_from_integers_should_work() { fn coercion_from_integers_should_work() {
run_variable_query( run_variable_query(
r#"query q($var: Float!) { floatInput(value: $var) }"#, r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(-1))] vec![("var".to_owned(), InputValue::scalar(-1))]
.into_iter() .into_iter()
.collect(), .collect(),
|result| { |result| {
assert_eq!( assert_eq!(
result.get_field_value("floatInput"), result.get_field_value("floatInput"),
Some(&Value::string(r#"value: -1"#)) Some(&Value::scalar(r#"value: -1"#))
); );
}, },
); );
@ -1221,7 +1143,7 @@ mod floats {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
let query = r#"query q($var: Float!) { floatInput(value: $var) }"#; let query = r#"query q($var: Float!) { floatInput(value: $var) }"#;
let vars = vec![("var".to_owned(), InputValue::string("10"))] let vars = vec![("var".to_owned(), InputValue::scalar("10"))]
.into_iter() .into_iter()
.collect(); .collect();

View file

@ -2,11 +2,12 @@
pub mod graphiql; pub mod graphiql;
use serde::ser; use serde::de::Deserialize;
use serde::ser::SerializeMap; use serde::ser::{self, Serialize, SerializeMap};
use ast::InputValue; use ast::InputValue;
use executor::ExecutionError; use executor::ExecutionError;
use value::{DefaultScalarValue, ScalarRefValue, ScalarValue};
use {FieldError, GraphQLError, GraphQLType, RootNode, Value, Variables}; use {FieldError, GraphQLError, GraphQLType, RootNode, Value, Variables};
/// The expected structure of the decoded JSON document for either POST or GET requests. /// The expected structure of the decoded JSON document for either POST or GET requests.
@ -17,19 +18,26 @@ use {FieldError, GraphQLError, GraphQLType, RootNode, Value, Variables};
/// For GET, you will need to parse the query string and extract "query", /// For GET, you will need to parse the query string and extract "query",
/// "operationName", and "variables" manually. /// "operationName", and "variables" manually.
#[derive(Deserialize, Clone, Serialize, PartialEq, Debug)] #[derive(Deserialize, Clone, Serialize, PartialEq, Debug)]
pub struct GraphQLRequest { pub struct GraphQLRequest<S = DefaultScalarValue>
where
S: ScalarValue,
{
query: String, query: String,
#[serde(rename = "operationName")] #[serde(rename = "operationName")]
operation_name: Option<String>, operation_name: Option<String>,
variables: Option<InputValue>, #[serde(bound(deserialize = "InputValue<S>: Deserialize<'de> + Serialize"))]
variables: Option<InputValue<S>>,
} }
impl GraphQLRequest { impl<S> GraphQLRequest<S>
where
S: ScalarValue,
{
fn operation_name(&self) -> Option<&str> { fn operation_name(&self) -> Option<&str> {
self.operation_name.as_ref().map(|oper_name| &**oper_name) self.operation_name.as_ref().map(|oper_name| &**oper_name)
} }
fn variables(&self) -> Variables { fn variables(&self) -> Variables<S> {
self.variables self.variables
.as_ref() .as_ref()
.and_then(|iv| { .and_then(|iv| {
@ -38,16 +46,15 @@ impl GraphQLRequest {
.map(|(k, v)| (k.to_owned(), v.clone())) .map(|(k, v)| (k.to_owned(), v.clone()))
.collect() .collect()
}) })
}) }).unwrap_or_default()
.unwrap_or_default()
} }
/// Construct a new GraphQL request from parts /// Construct a new GraphQL request from parts
pub fn new( pub fn new(
query: String, query: String,
operation_name: Option<String>, operation_name: Option<String>,
variables: Option<InputValue>, variables: Option<InputValue<S>>,
) -> GraphQLRequest { ) -> Self {
GraphQLRequest { GraphQLRequest {
query: query, query: query,
operation_name: operation_name, operation_name: operation_name,
@ -61,12 +68,14 @@ impl GraphQLRequest {
/// top level of this crate. /// top level of this crate.
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, CtxT, QueryT, MutationT>(
&'a self, &'a self,
root_node: &RootNode<QueryT, MutationT>, root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT, context: &CtxT,
) -> GraphQLResponse<'a> ) -> GraphQLResponse<'a, S>
where where
QueryT: GraphQLType<Context = CtxT>, S: ScalarValue,
MutationT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
GraphQLResponse(::execute( GraphQLResponse(::execute(
&self.query, &self.query,
@ -83,11 +92,16 @@ impl GraphQLRequest {
/// This struct implements Serialize, so you can simply serialize this /// This struct implements Serialize, so you can simply serialize this
/// to JSON and send it over the wire. Use the `is_ok` method to determine /// to JSON and send it over the wire. Use the `is_ok` method to determine
/// whether to send a 200 or 400 HTTP status code. /// whether to send a 200 or 400 HTTP status code.
pub struct GraphQLResponse<'a>(Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>); pub struct GraphQLResponse<'a, S = DefaultScalarValue>(
Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>,
);
impl<'a> GraphQLResponse<'a> { impl<'a, S> GraphQLResponse<'a, S>
where
S: ScalarValue,
{
/// Constructs an error response outside of the normal execution flow /// Constructs an error response outside of the normal execution flow
pub fn error(error: FieldError) -> Self { pub fn error(error: FieldError<S>) -> Self {
GraphQLResponse(Ok((Value::null(), vec![ExecutionError::at_origin(error)]))) GraphQLResponse(Ok((Value::null(), vec![ExecutionError::at_origin(error)])))
} }
@ -100,7 +114,13 @@ impl<'a> GraphQLResponse<'a> {
} }
} }
impl<'a> ser::Serialize for GraphQLResponse<'a> { impl<'a, T> Serialize for GraphQLResponse<'a, T>
where
T: Serialize + ScalarValue,
Value<T>: Serialize,
ExecutionError<T>: Serialize,
GraphQLError<'a>: Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,

View file

@ -15,35 +15,53 @@
*/ */
use chrono::prelude::*; use chrono::prelude::*;
use parser::{ParseError, ScalarToken, Token};
use value::{ParseScalarResult, ParseScalarValue};
use Value; use Value;
#[doc(hidden)] #[doc(hidden)]
pub static RFC3339_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S%.f%:z"; pub static RFC3339_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S%.f%:z";
graphql_scalar!(DateTime<FixedOffset> as "DateTimeFixedOffset" { graphql_scalar!(DateTime<FixedOffset> as "DateTimeFixedOffset" where Scalar = <S>{
description: "DateTime" description: "DateTime"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self.to_rfc3339()) Value::scalar(self.to_rfc3339())
} }
from_input_value(v: &InputValue) -> Option<DateTime<FixedOffset>> { from_input_value(v: &InputValue) -> Option<DateTime<FixedOffset>> {
v.as_string_value() v.as_scalar_value::<String>()
.and_then(|s| DateTime::parse_from_rfc3339(s).ok()) .and_then(|s| DateTime::parse_from_rfc3339(s).ok())
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::String(value) = value {
Ok(S::from(value.to_owned()))
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
graphql_scalar!(DateTime<Utc> as "DateTimeUtc" { graphql_scalar!(DateTime<Utc> as "DateTimeUtc" where Scalar = <S>{
description: "DateTime" description: "DateTime"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self.to_rfc3339()) Value::scalar(self.to_rfc3339())
} }
from_input_value(v: &InputValue) -> Option<DateTime<Utc>> { from_input_value(v: &InputValue) -> Option<DateTime<Utc>> {
v.as_string_value() v.as_scalar_value::<String>()
.and_then(|s| (s.parse::<DateTime<Utc>>().ok())) .and_then(|s| (s.parse::<DateTime<Utc>>().ok()))
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::String(value) = value {
Ok(S::from(value.to_owned()))
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
// Don't use `Date` as the docs say: // Don't use `Date` as the docs say:
@ -51,40 +69,53 @@ graphql_scalar!(DateTime<Utc> as "DateTimeUtc" {
// inherent lack of precision required for the time zone resolution. // inherent lack of precision required for the time zone resolution.
// For serialization and deserialization uses, it is best to use // For serialization and deserialization uses, it is best to use
// `NaiveDate` instead." // `NaiveDate` instead."
graphql_scalar!(NaiveDate { graphql_scalar!(NaiveDate where Scalar = <S>{
description: "NaiveDate" description: "NaiveDate"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self.format("%Y-%m-%d").to_string()) Value::scalar(self.format("%Y-%m-%d").to_string())
} }
from_input_value(v: &InputValue) -> Option<NaiveDate> { from_input_value(v: &InputValue) -> Option<NaiveDate> {
v.as_string_value() v.as_scalar_value::<String>()
.and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) .and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok())
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::String(value) = value {
Ok(S::from(value.to_owned()))
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
/// JSON numbers (i.e. IEEE doubles) are not precise enough for nanosecond /// JSON numbers (i.e. IEEE doubles) are not precise enough for nanosecond
/// datetimes. Values will be truncated to microsecond resolution. /// datetimes. Values will be truncated to microsecond resolution.
graphql_scalar!(NaiveDateTime { graphql_scalar!(NaiveDateTime where Scalar = <S> {
description: "NaiveDateTime" description: "NaiveDateTime"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::float(self.timestamp() as f64) Value::scalar(self.timestamp() as f64)
} }
from_input_value(v: &InputValue) -> Option<NaiveDateTime> { from_input_value(v: &InputValue) -> Option<NaiveDateTime> {
v.as_float_value() v.as_scalar_value::<f64>()
.and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0)) .and_then(|f| NaiveDateTime::from_timestamp_opt(*f as i64, 0))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<f64 as ParseScalarValue<S>>::from_str(value)
} }
}); });
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use chrono::prelude::*; use chrono::prelude::*;
use value::DefaultScalarValue;
fn datetime_fixedoffset_test(raw: &'static str) { fn datetime_fixedoffset_test(raw: &'static str) {
let input = ::InputValue::String(raw.to_string()); let input: ::InputValue<DefaultScalarValue> = ::InputValue::scalar(raw.to_string());
let parsed: DateTime<FixedOffset> = ::FromInputValue::from_input_value(&input).unwrap(); let parsed: DateTime<FixedOffset> = ::FromInputValue::from_input_value(&input).unwrap();
let expected = DateTime::parse_from_rfc3339(raw).unwrap(); let expected = DateTime::parse_from_rfc3339(raw).unwrap();
@ -108,7 +139,7 @@ mod test {
} }
fn datetime_utc_test(raw: &'static str) { fn datetime_utc_test(raw: &'static str) {
let input = ::InputValue::String(raw.to_string()); let input: ::InputValue<DefaultScalarValue> = ::InputValue::scalar(raw.to_string());
let parsed: DateTime<Utc> = ::FromInputValue::from_input_value(&input).unwrap(); let parsed: DateTime<Utc> = ::FromInputValue::from_input_value(&input).unwrap();
let expected = DateTime::parse_from_rfc3339(raw) let expected = DateTime::parse_from_rfc3339(raw)
@ -135,7 +166,8 @@ mod test {
#[test] #[test]
fn naivedate_from_input_value() { fn naivedate_from_input_value() {
let input = ::InputValue::String("1996-12-19".to_string()); let input: ::InputValue<DefaultScalarValue> =
::InputValue::scalar("1996-12-19".to_string());
let y = 1996; let y = 1996;
let m = 12; let m = 12;
let d = 19; let d = 19;
@ -153,7 +185,7 @@ mod test {
#[test] #[test]
fn naivedatetime_from_input_value() { fn naivedatetime_from_input_value() {
let raw = 1_000_000_000_f64; let raw = 1_000_000_000_f64;
let input = ::InputValue::Float(raw); let input: ::InputValue<DefaultScalarValue> = ::InputValue::scalar(raw);
let parsed: NaiveDateTime = ::FromInputValue::from_input_value(&input).unwrap(); let parsed: NaiveDateTime = ::FromInputValue::from_input_value(&input).unwrap();
let expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap(); let expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap();
@ -171,11 +203,11 @@ mod integration_test {
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::Value; use value::{Value};
#[test] #[test]
fn test_serialization() { fn test_serialization() {
struct Root {} struct Root;
graphql_object!(Root: () |&self| { graphql_object!(Root: () |&self| {
field exampleNaiveDate() -> NaiveDate { field exampleNaiveDate() -> NaiveDate {
NaiveDate::from_ymd(2015, 3, 14) NaiveDate::from_ymd(2015, 3, 14)
@ -200,7 +232,7 @@ mod integration_test {
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema= RootNode::new(Root, EmptyMutation::<()>::new());
let (result, errs) = let (result, errs) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -211,18 +243,18 @@ mod integration_test {
result, result,
Value::object( Value::object(
vec![ vec![
("exampleNaiveDate", Value::string("2015-03-14")), ("exampleNaiveDate", Value::scalar("2015-03-14")),
("exampleNaiveDateTime", Value::float(1467969011.0)), ("exampleNaiveDateTime", Value::scalar(1467969011.0)),
( (
"exampleDateTimeFixedOffset", "exampleDateTimeFixedOffset",
Value::string("1996-12-19T16:39:57-08:00"), Value::scalar("1996-12-19T16:39:57-08:00"),
), ),
( (
"exampleDateTimeUtc", "exampleDateTimeUtc",
Value::string("1970-01-01T00:01:01+00:00"), Value::scalar("1970-01-01T00:01:01+00:00"),
), ),
].into_iter() ].into_iter()
.collect() .collect()
) )
); );
} }

View file

@ -8,14 +8,17 @@ use ast::InputValue;
use executor::ExecutionError; use executor::ExecutionError;
use parser::{ParseError, SourcePosition, Spanning}; use parser::{ParseError, SourcePosition, Spanning};
use validation::RuleError; use validation::RuleError;
use {GraphQLError, Object, Value}; use {GraphQLError, Object, ScalarValue, Value};
#[derive(Serialize)] #[derive(Serialize)]
struct SerializeHelper { struct SerializeHelper {
message: &'static str, message: &'static str,
} }
impl ser::Serialize for ExecutionError { impl<T> ser::Serialize for ExecutionError<T>
where
T: ScalarValue,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,
@ -51,92 +54,192 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
GraphQLError::ValidationError(ref errs) => errs.serialize(serializer), GraphQLError::ValidationError(ref errs) => errs.serialize(serializer),
GraphQLError::NoOperationProvided => [SerializeHelper { GraphQLError::NoOperationProvided => [SerializeHelper {
message: "Must provide an operation", message: "Must provide an operation",
}].serialize(serializer), }]
.serialize(serializer),
GraphQLError::MultipleOperationsProvided => [SerializeHelper { GraphQLError::MultipleOperationsProvided => [SerializeHelper {
message: "Must provide operation name \ message: "Must provide operation name \
if query contains multiple operations", if query contains multiple operations",
}].serialize(serializer), }]
.serialize(serializer),
GraphQLError::UnknownOperationName => [SerializeHelper { GraphQLError::UnknownOperationName => [SerializeHelper {
message: "Unknown operation", message: "Unknown operation",
}].serialize(serializer), }]
.serialize(serializer),
} }
} }
} }
impl<'de> de::Deserialize<'de> for InputValue { impl<'de, S> de::Deserialize<'de> for InputValue<S>
fn deserialize<D>(deserializer: D) -> Result<InputValue, D::Error> where
S: ScalarValue,
{
fn deserialize<D>(deserializer: D) -> Result<InputValue<S>, D::Error>
where where
D: de::Deserializer<'de>, D: de::Deserializer<'de>,
{ {
struct InputValueVisitor; struct InputValueVisitor<S: ScalarValue>(S::Visitor);
impl<'de> de::Visitor<'de> for InputValueVisitor { impl<S: ScalarValue> Default for InputValueVisitor<S> {
type Value = InputValue; fn default() -> Self {
InputValueVisitor(S::Visitor::default())
}
}
impl<'de, S> de::Visitor<'de> for InputValueVisitor<S>
where
S: ScalarValue,
{
type Value = InputValue<S>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid input value") formatter.write_str("a valid input value")
} }
fn visit_bool<E>(self, value: bool) -> Result<InputValue, E> { fn visit_bool<E>(self, value: bool) -> Result<InputValue<S>, E>
Ok(InputValue::boolean(value))
}
fn visit_i64<E>(self, value: i64) -> Result<InputValue, E>
where where
E: de::Error, E: de::Error,
{ {
if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) { self.0.visit_bool(value).map(InputValue::Scalar)
Ok(InputValue::int(value as i32)) }
} else {
// Browser's JSON.stringify serialize all numbers having no fn visit_i8<E>(self, value: i8) -> Result<InputValue<S>, E>
// fractional part as integers (no decimal point), so we where
// must parse large integers as floating point otherwise E: de::Error,
// we would error on transferring large floating point {
// numbers. self.0.visit_i8(value).map(InputValue::Scalar)
Ok(InputValue::float(value as f64)) }
fn visit_i16<E>(self, value: i16) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_i16(value).map(InputValue::Scalar)
}
fn visit_i32<E>(self, value: i32) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_i32(value).map(InputValue::Scalar)
}
fn visit_i64<E>(self, value: i64) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_i64(value).map(InputValue::Scalar)
}
serde_if_integer128! {
fn visit_i128<E>(self, value: i128) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_i128(value).map(InputValue::Scalar)
} }
} }
fn visit_u64<E>(self, value: u64) -> Result<InputValue, E> fn visit_u8<E>(self, value: u8) -> Result<InputValue<S>, E>
where where
E: de::Error, E: de::Error,
{ {
if value <= i32::max_value() as u64 { self.0.visit_u8(value).map(InputValue::Scalar)
self.visit_i64(value as i64) }
} else {
// Browser's JSON.stringify serialize all numbers having no fn visit_u16<E>(self, value: u16) -> Result<InputValue<S>, E>
// fractional part as integers (no decimal point), so we where
// must parse large integers as floating point otherwise E: de::Error,
// we would error on transferring large floating point {
// numbers. self.0.visit_u16(value).map(InputValue::Scalar)
Ok(InputValue::float(value as f64)) }
fn visit_u32<E>(self, value: u32) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_u32(value).map(InputValue::Scalar)
}
fn visit_u64<E>(self, value: u64) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_u64(value).map(InputValue::Scalar)
}
serde_if_integer128!{
fn visit_u128<E>(self, value: u128) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_u128(value).map(InputValue::Scalar)
} }
} }
fn visit_f64<E>(self, value: f64) -> Result<InputValue, E> { fn visit_f32<E>(self, value: f32) -> Result<InputValue<S>, E>
Ok(InputValue::float(value))
}
fn visit_str<E>(self, value: &str) -> Result<InputValue, E>
where where
E: de::Error, E: de::Error,
{ {
self.visit_string(value.into()) self.0.visit_f32(value).map(InputValue::Scalar)
} }
fn visit_string<E>(self, value: String) -> Result<InputValue, E> { fn visit_f64<E>(self, value: f64) -> Result<InputValue<S>, E>
Ok(InputValue::string(value)) where
E: de::Error,
{
self.0.visit_f64(value).map(InputValue::Scalar)
} }
fn visit_none<E>(self) -> Result<InputValue, E> { fn visit_char<E>(self, value: char) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_char(value).map(InputValue::Scalar)
}
fn visit_str<E>(self, value: &str) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_str(value).map(InputValue::Scalar)
}
fn visit_string<E>(self, value: String) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_string(value).map(InputValue::Scalar)
}
fn visit_bytes<E>(self, bytes: &[u8]) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_bytes(bytes).map(InputValue::Scalar)
}
fn visit_byte_buf<E>(self, bytes: Vec<u8>) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_byte_buf(bytes).map(InputValue::Scalar)
}
fn visit_none<E>(self) -> Result<InputValue<S>, E>
where
E: de::Error,
{
Ok(InputValue::null()) Ok(InputValue::null())
} }
fn visit_unit<E>(self) -> Result<InputValue, E> { fn visit_unit<E>(self) -> Result<InputValue<S>, E>
where
E: de::Error,
{
Ok(InputValue::null()) Ok(InputValue::null())
} }
fn visit_seq<V>(self, mut visitor: V) -> Result<InputValue, V::Error> fn visit_seq<V>(self, mut visitor: V) -> Result<InputValue<S>, V::Error>
where where
V: de::SeqAccess<'de>, V: de::SeqAccess<'de>,
{ {
@ -149,40 +252,45 @@ impl<'de> de::Deserialize<'de> for InputValue {
Ok(InputValue::list(values)) Ok(InputValue::list(values))
} }
fn visit_map<V>(self, mut visitor: V) -> Result<InputValue, V::Error> fn visit_map<V>(self, mut visitor: V) -> Result<InputValue<S>, V::Error>
where where
V: de::MapAccess<'de>, V: de::MapAccess<'de>,
{ {
let mut values: IndexMap<String, InputValue> = IndexMap::new(); let mut object = IndexMap::<String, InputValue<S>>::with_capacity(
visitor.size_hint().unwrap_or(0),
);
while let Some((key, value)) = visitor.next_entry()? { while let Some((key, value)) = visitor.next_entry()? {
values.insert(key, value); object.insert(key, value);
} }
Ok(InputValue::object(values)) Ok(InputValue::object(object))
} }
} }
deserializer.deserialize_any(InputValueVisitor) deserializer.deserialize_any(InputValueVisitor::default())
} }
} }
impl ser::Serialize for InputValue { impl<T> ser::Serialize for InputValue<T>
where
T: ScalarValue,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,
{ {
match *self { match *self {
InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(), InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(),
InputValue::Int(v) => serializer.serialize_i64(i64::from(v)), InputValue::Scalar(ref s) => s.serialize(serializer),
InputValue::Float(v) => serializer.serialize_f64(v), InputValue::Enum(ref v) => serializer.serialize_str(v),
InputValue::String(ref v) | InputValue::Enum(ref v) => serializer.serialize_str(v), InputValue::List(ref v) => v
InputValue::Boolean(v) => serializer.serialize_bool(v), .iter()
InputValue::List(ref v) => v.iter()
.map(|x| x.item.clone()) .map(|x| x.item.clone())
.collect::<Vec<_>>() .collect::<Vec<_>>()
.serialize(serializer), .serialize(serializer),
InputValue::Object(ref v) => v.iter() InputValue::Object(ref v) => v
.iter()
.map(|&(ref k, ref v)| (k.item.clone(), v.item.clone())) .map(|&(ref k, ref v)| (k.item.clone(), v.item.clone()))
.collect::<IndexMap<_, _>>() .collect::<IndexMap<_, _>>()
.serialize(serializer), .serialize(serializer),
@ -250,7 +358,10 @@ impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
} }
} }
impl ser::Serialize for Object { impl<T> ser::Serialize for Object<T>
where
T: ser::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,
@ -266,17 +377,17 @@ impl ser::Serialize for Object {
} }
} }
impl ser::Serialize for Value { impl<T> ser::Serialize for Value<T>
where
T: ser::Serialize,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where
S: ser::Serializer, S: ser::Serializer,
{ {
match *self { match *self {
Value::Null => serializer.serialize_unit(), Value::Null => serializer.serialize_unit(),
Value::Int(v) => serializer.serialize_i64(i64::from(v)), Value::Scalar(ref s) => s.serialize(serializer),
Value::Float(v) => serializer.serialize_f64(v),
Value::String(ref v) => serializer.serialize_str(v),
Value::Boolean(v) => serializer.serialize_bool(v),
Value::List(ref v) => v.serialize(serializer), Value::List(ref v) => v.serialize(serializer),
Value::Object(ref v) => v.serialize(serializer), Value::Object(ref v) => v.serialize(serializer),
} }
@ -289,27 +400,27 @@ mod tests {
use ast::InputValue; use ast::InputValue;
use serde_json::from_str; use serde_json::from_str;
use serde_json::to_string; use serde_json::to_string;
use value::{DefaultScalarValue, Object};
use {FieldError, Value}; use {FieldError, Value};
use ::value::Object;
#[test] #[test]
fn int() { fn int() {
assert_eq!( assert_eq!(
from_str::<InputValue>("1235").unwrap(), from_str::<InputValue<DefaultScalarValue>>("1235").unwrap(),
InputValue::int(1235) InputValue::scalar(1235)
); );
} }
#[test] #[test]
fn float() { fn float() {
assert_eq!( assert_eq!(
from_str::<InputValue>("2.0").unwrap(), from_str::<InputValue<DefaultScalarValue>>("2.0").unwrap(),
InputValue::float(2.0) InputValue::scalar(2.0)
); );
// large value without a decimal part is also float // large value without a decimal part is also float
assert_eq!( assert_eq!(
from_str::<InputValue>("123567890123").unwrap(), from_str::<InputValue<DefaultScalarValue>>("123567890123").unwrap(),
InputValue::float(123567890123.0) InputValue::scalar(123567890123.0)
); );
} }
@ -323,8 +434,8 @@ mod tests {
#[test] #[test]
fn error_extensions() { fn error_extensions() {
let mut obj = Object::with_capacity(1); let mut obj: Object<DefaultScalarValue> = Object::with_capacity(1);
obj.add_field("foo".to_string(), Value::String("bar".to_string())); obj.add_field("foo".to_string(), Value::scalar("bar"));
assert_eq!( assert_eq!(
to_string(&ExecutionError::at_origin(FieldError::new( to_string(&ExecutionError::at_origin(FieldError::new(
"foo error", "foo error",

View file

@ -1,18 +1,23 @@
use url::Url; use url::Url;
use value::{ParseScalarResult, ParseScalarValue};
use Value; use Value;
graphql_scalar!(Url { graphql_scalar!(Url where Scalar = <S>{
description: "Url" description: "Url"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self.as_str()) Value::scalar(self.as_str().to_owned())
} }
from_input_value(v: &InputValue) -> Option<Url> { from_input_value(v: &InputValue) -> Option<Url> {
v.as_string_value() v.as_scalar_value::<String>()
.and_then(|s| Url::parse(s).ok()) .and_then(|s| Url::parse(s).ok())
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<String as ParseScalarValue<S>>::from_str(value)
}
}); });
#[cfg(test)] #[cfg(test)]
@ -22,7 +27,7 @@ mod test {
#[test] #[test]
fn url_from_input_value() { fn url_from_input_value() {
let raw = "https://example.net/"; let raw = "https://example.net/";
let input = ::InputValue::String(raw.to_string()); let input: ::InputValue = ::InputValue::scalar(raw.to_string());
let parsed: Url = ::FromInputValue::from_input_value(&input).unwrap(); let parsed: Url = ::FromInputValue::from_input_value(&input).unwrap();
let url = Url::parse(raw).unwrap(); let url = Url::parse(raw).unwrap();

View file

@ -1,28 +1,39 @@
use uuid::Uuid; use uuid::Uuid;
use parser::{ParseError, ScalarToken, Token};
use value::ParseScalarResult;
use Value; use Value;
graphql_scalar!(Uuid { graphql_scalar!(Uuid where Scalar = <S> {
description: "Uuid" description: "Uuid"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self.to_string()) Value::scalar(self.to_string())
} }
from_input_value(v: &InputValue) -> Option<Uuid> { from_input_value(v: &InputValue) -> Option<Uuid> {
v.as_string_value() v.as_scalar_value::<String>()
.and_then(|s| Uuid::parse_str(s).ok()) .and_then(|s| Uuid::parse_str(s).ok())
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::String(value) = value {
Ok(S::from(value.to_owned()))
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use uuid::Uuid; use uuid::Uuid;
use value::DefaultScalarValue;
#[test] #[test]
fn uuid_from_input_value() { fn uuid_from_input_value() {
let raw = "123e4567-e89b-12d3-a456-426655440000"; let raw = "123e4567-e89b-12d3-a456-426655440000";
let input = ::InputValue::String(raw.to_string()); let input: ::InputValue<DefaultScalarValue> = ::InputValue::scalar(raw.to_string());
let parsed: Uuid = ::FromInputValue::from_input_value(&input).unwrap(); let parsed: Uuid = ::FromInputValue::from_input_value(&input).unwrap();
let id = Uuid::parse_str(raw).unwrap(); let id = Uuid::parse_str(raw).unwrap();

View file

@ -90,7 +90,9 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected.
*/ */
#![warn(missing_docs)] #![warn(missing_docs)]
extern crate serde; #[doc(hidden)]
#[macro_use]
pub extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
@ -111,13 +113,28 @@ extern crate url;
extern crate uuid; extern crate uuid;
// Depend on juniper_codegen and re-export everything in it. // Depend on juniper_codegen and re-export everything in it.
// This allows users to just depend on juniper and get the derive functionality automatically. // This allows users to just depend on juniper and get the derive
// functionality automatically.
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] #[macro_use]
extern crate juniper_codegen; extern crate juniper_codegen;
#[doc(hidden)] #[doc(hidden)]
pub use juniper_codegen::*; pub use juniper_codegen::*;
// This macro is used as abstraction to make custom derives work
// in juniper itself and outside of juniper
// This macro needs to be here because it is used a derive in value::scalar
// The tests in macros are using a macro from the value module, and because
// rust macros needs to be defined before they are used it would cause problems
// to move this macro somewhere else.
#[macro_export]
#[doc(hidden)]
macro_rules! __juniper_use_everything {
() => {
pub use $crate::*;
};
}
#[macro_use] #[macro_use]
mod value; mod value;
#[macro_use] #[macro_use]
@ -125,7 +142,7 @@ mod macros;
mod ast; mod ast;
mod executor; mod executor;
pub mod parser; pub mod parser;
mod schema; pub(crate) mod schema;
mod types; mod types;
mod util; mod util;
mod validation; mod validation;
@ -159,13 +176,15 @@ pub use executor::{
Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult, FromContext, Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult, FromContext,
IntoFieldError, IntoResolvable, Registry, Variables, IntoFieldError, IntoResolvable, Registry, Variables,
}; };
pub use schema::meta;
pub use schema::model::RootNode; pub use schema::model::RootNode;
pub use types::base::{Arguments, GraphQLType, TypeKind}; pub use types::base::{Arguments, GraphQLType, TypeKind};
pub use types::scalars::{EmptyMutation, ID}; pub use types::scalars::{EmptyMutation, ID};
pub use validation::RuleError; pub use validation::RuleError;
pub use value::{Value, Object}; pub use value::{
DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue,
pub use schema::meta; Value,
};
/// An error that prevented query execution /// An error that prevented query execution
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -179,19 +198,20 @@ pub enum GraphQLError<'a> {
} }
/// Execute a query in a provided schema /// Execute a query in a provided schema
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, S, CtxT, QueryT, MutationT>(
document_source: &'a str, document_source: &'a str,
operation_name: Option<&str>, operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>, root_node: &'a RootNode<QueryT, MutationT, S>,
variables: &Variables, variables: &Variables<S>,
context: &CtxT, context: &CtxT,
) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>> ) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
where where
QueryT: GraphQLType<Context = CtxT>, S: ScalarValue,
MutationT: GraphQLType<Context = CtxT>, for<'b> &'b S: ScalarRefValue<'b>,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
{ {
let document = parse_document_source(document_source)?; let document = parse_document_source(document_source, &root_node.schema)?;
{ {
let errors = validate_input_values(variables, &document, &root_node.schema); let errors = validate_input_values(variables, &document, &root_node.schema);

View file

@ -1,147 +0,0 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! __graphql__args {
// Internal type conversion
( @as_expr, $e:expr) => { $e };
( @as_pattern, $p:pat) => { $p };
( @assign_arg_vars, $args:ident, $executorvar:ident, , $($rest:tt)* ) => {
__graphql__args!(@assign_arg_vars, $args, $executorvar, $($rest)*);
};
( @assign_arg_vars, $args:ident, $executorvar:ident, ) => {
();
};
(
@assign_arg_vars,
$args:ident, $executorvar:ident, &$exec:ident $($rest:tt)*
) => {
let __graphql__args!(@as_pattern, $exec) = &$executorvar;
__graphql__args!(@assign_arg_vars, $args, $executorvar, $($rest)*);
};
(
@assign_arg_vars,
$args:ident, $executorvar:ident,
$name:ident $(= $default:tt)* : $ty:ty $(as $desc:tt)*, $($rest:tt)*
) => {
let $name: $ty = $args
.get(&$crate::to_camel_case(__graphql__stringify!($name)))
.expect("Argument missing - validation must have failed");
__graphql__args!(@assign_arg_vars, $args, $executorvar, $($rest)*);
};
(
@assign_arg_vars,
$args:ident, $executorvar:ident,
$name:ident $(= $default:tt)* : $ty:ty $(as $desc:expr)*
) => {
let $name: $ty = $args
.get(&$crate::to_camel_case(__graphql__stringify!($name)))
.expect("Argument missing - validation must have failed");
};
( @apply_args, $reg:expr, $base:expr, $info:expr, ( ) ) => {
$base
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( , $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base,
$info,
( $($rest)* ))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( &executor $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base,
$info,
( $($rest)* ))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( $name:ident = $default:tt : $t:ty )
) => {
$base.argument($reg.arg_with_default::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)),
&__graphql__args!(@as_expr, $default), $info))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( $name:ident = $default:tt : $t:ty , $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base.argument($reg.arg_with_default::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)),
&__graphql__args!(@as_expr, $default), $info)),
$info,
( $($rest)* ))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr,
( $name:ident = $default:tt : $t:ty as $desc:tt $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base.argument($reg.arg_with_default::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)),
&__graphql__args!(@as_expr, $default), $info)
.description($desc)),
$info,
( $($rest)* ))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( $name:ident : $t:ty )
) => {
$base.argument($reg.arg::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( $name:ident : $t:ty , $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base.argument($reg.arg::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)),
$info,
( $($rest)* ))
};
(
@apply_args,
$reg:expr, $base:expr, $info:expr, ( $name:ident : $t:ty as $desc:tt $( $rest:tt )* )
) => {
__graphql__args!(
@apply_args,
$reg,
$base.argument(
$reg.arg::<$t>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc)),
$info,
( $($rest)* ))
};
}

View file

@ -0,0 +1,627 @@
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_impl_trait {
(
impl< < DefaultScalarValue > $(, $other: tt)* > $impl_trait:tt for $name:ty {
$($body:tt)+
}
) => {
impl<$($other,)*> $crate::$impl_trait<$crate::DefaultScalarValue> for $name {
$($body)+
}
};
(
impl< <$generic:tt $(: $bound: tt)*> $(, $other: tt)* > $impl_trait:tt for $name:ty {
$($body:tt)+
}
) => {
impl<$($other,)* $generic $(: $bound)*> $crate::$impl_trait<$generic> for $name
where
$generic: $crate::ScalarValue,
for<'__b> &'__b $generic: $crate::ScalarRefValue<'__b>,
{
$($body)+
}
};
(
impl<$scalar:ty $(, $other: tt )*> $impl_trait:tt for $name:ty {
$($body:tt)+
}
) => {
impl<$($other, )*> $crate::$impl_trait<$scalar> for $name {
$($body)+
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_insert_generic {
(<DefaultScalarValue>) => {$crate::DefaultScalarValue};
(
<$generic:tt $(: $bound: tt)*>
) => {
$generic
};
(
$scalar: ty
) => {
$scalar
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_parse_object_header {
(
callback = $callback:ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)* as $outname: tt
where Scalar = <$generic:tt $(: $bound:tt)*> $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {<$generic $(: $bound)*>},
},
rest = $($items)*
);
};
(
callback = $callback:ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)* as $outname: tt
where Scalar = $scalar: ty $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {$scalar},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)* as $outname: tt $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {<DefaultScalarValue>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)* as $outname: tt
where Scalar = <$generic:tt $(: $bound:tt)*> $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {<$generic $(:$bound)*>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)* as $outname: tt
where Scalar = $scalar: ty $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {$scalar},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)* as $outname: tt $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {$outname},
scalar = {<DefaultScalarValue>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)*
where Scalar = <$generic:tt $(: $bound:tt)*> $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {<$generic $(:$bounds)*>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)*
where Scalar = $scalar: ty $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {$scalar},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = <$($lifetime:tt),*> $name: ty $(: $ctxt: ty)* $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {<DefaultScalarValue>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)*
where Scalar = <$generic:tt $(: $bound:tt)*> $(| &$mainself:ident |)*
{
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {<$generic $(: $bound)*>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)* where Scalar = $scalar: ty $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {$scalar},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $name: ty $(: $ctxt: ty)* $(| &$mainself:ident |)* {
$($items: tt)*
}
) => {
$callback!(
@parse,
meta = {
lifetimes = [],
name = $name,
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
outname = {stringify!($name)},
scalar = {<DefaultScalarValue>},
},
rest = $($items)*
);
};
(
callback = $callback: ident,
rest = $($rest:tt)*
) => {
compile_error!("Invalid syntax");
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_parse_field_list {
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {$($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest =
) => {
$success_callback!(
@generate,
meta = {$($meta)*},
items = [$({$($items)*},)*],
);
};
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {$($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest = , $($rest: tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {$($meta)*},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
@parse_description,
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {
$(lifetimes = [$($lifetime:tt,)*],)*
$(name = $name:ty,)*
$(ctx = $ctxt: ty,)*
$(main_self = $mainself: ident,)*
$(outname = {$($outname:tt)*},)*
$(scalar = {$($scalar:tt)*},)*
$(description = $_desciption: tt,)*
$(additional = {$($other: tt)*},)*
},
items = [$({$($items: tt)*},)*],
rest = $desc: tt $($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {
$(lifetimes = [$($lifetime,)*],)*
$(name = $name,)*
$(ctx = $ctxt,)*
$(main_self = $mainself,)*
$(outname = {$($outname)*},)*
$(scalar = {$($scalar)*},)*
description = $desc,
$(additional = {$($other)*},)*
},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = { $($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest = description: $($rest:tt)*
) => {
__juniper_parse_field_list!(
@parse_description,
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {$($meta)*},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {$($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest = field deprecated $reason:tt $name: ident (
$(&$executor: tt)* $(,)*
$($arg_name:ident $(= $default_value: tt)* : $arg_ty: ty $(as $arg_des: expr)*),* $(,)*
) -> $return_ty: ty $(as $desc: tt)* $body: block
$($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {$($meta)*},
items = [$({$($items)*},)* {
name = $name,
body = $body,
return_ty = $return_ty,
args = [
$({
arg_name = $arg_name,
arg_ty = $arg_ty,
$(arg_description = $arg_desc,)*
$(arg_default = $default_value,)*
},)*
],
$(decs = $desc,)*
deprecated = $reason,
$(executor_var = $executor,)*
},],
rest = $($rest)*
);
};
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {$($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest = field $name: ident (
$(&$executor: ident)* $(,)*
$($arg_name:ident $(= $default_value: tt)* : $arg_ty: ty $(as $arg_desc: expr)*),* $(,)*
) -> $return_ty: ty $(as $desc: tt)* $body: block
$($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {$($meta)*},
items = [$({$($items)*},)* {
name = $name,
body = $body,
return_ty = $return_ty,
args = [
$({
arg_name = $arg_name,
arg_ty = $arg_ty,
$(arg_description = $arg_desc,)*
$(arg_default = $default_value,)*
},)*
],
$(decs = $desc,)*
$(executor_var = $executor,)*
},],
rest = $($rest)*
);
};
(
success_callback = $success_callback: ident,
additional_parser = {
callback = $callback: ident,
header = {$($header:tt)*},
},
meta = {$($meta:tt)*},
items = [$({$($items: tt)*},)*],
rest = $($rest:tt)*
) => {
$callback!(
$($header)*
success_callback = $success_callback,
additional_parser = {
callback = $callback,
header = {$($header)*},
},
meta = {$($meta)*},
items = [$({$($items)*},)*],
rest = $($rest)*
);
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_parse_instance_resolver {
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {
lifetimes = [$($lifetime:tt,)*],
name = $name:ty,
ctx = $ctxt:ty,
main_self = $mainself:ident,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
$(description = $desciption:tt,)*
$(additional = {
$(resolver = {$($ignored_resolver:tt)*},)*
},)*
},
items = [$({$($items: tt)*},)*],
rest = instance_resolvers: |&$context: ident| {
$( $srctype:ty => $resolver:expr ),* $(,)*
} $($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
ctx = $ctxt,
main_self = $mainself,
outname = {$($outname)*},
scalar = {$($scalar)*},
$(description = $desciption,)*
additional = {
resolver = {
context = $context,
items = [
$({
src = $srctype,
resolver = $resolver,
},)*
],
},
},
},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = {
lifetimes = [$($lifetime:tt,)*],
name = $name:ty,
ctx = $ctxt:ty,
main_self = $mainself:ident,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
$(description = $desciption:tt,)*
$(additional = {
$(resolver = {$($ignored_resolver:tt)*},)*
},)*
},
items = [$({$($items: tt)*},)*],
rest = instance_resolvers: |$(&)* _| {$( $srctype:ty => $resolver:expr ),* $(,)*} $($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
ctx = $ctxt,
main_self = $mainself,
outname = {$($outname)*},
scalar = {$($scalar)*},
$(description = $desciption,)*
additional = {
resolver = {
items = [
$({
src = $srctype,
resolver = $resolver,
},)*
],
},
},
},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! __juniper_create_arg {
(
registry = $reg: ident,
info = $info: ident,
arg_ty = $arg_ty: ty,
arg_name = $arg_name: ident,
$(description = $arg_description: expr,)*
) => {
$reg.arg::<$arg_ty>(
&$crate::to_camel_case(stringify!($arg_name)),
$info,
)
$(.description($arg_description))*
};
(
registry = $reg: ident,
info = $info: ident,
arg_ty = $arg_ty: ty,
arg_name = $arg_name: ident,
$(description = $arg_description: expr,)*
default = $arg_default: expr,
) => {
$reg.arg_with_default::<$arg_ty>(
&$crate::to_camel_case(stringify!($arg_name)),
&($arg_default),
$info,
)
$(.description($arg_description))*
};
}

View file

@ -1,103 +0,0 @@
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! __graphql__build_field_matches {
// field deprecated <reason> <name>(...) -> <type> as <description> { ... }
(
$resolveargs:tt,
( $( $acc:tt )* ),
field deprecated $_reason:tt
$name:ident
$args:tt -> $t:ty
as $desc:tt
$body:block
$( $rest:tt )*
) => {
__graphql__build_field_matches!(
$resolveargs,
(($name; $args; $t; $body) $( $acc )*),
$( $rest )*);
};
// field deprecated <reason> <name>(...) -> <type> { ... }
(
$resolveargs:tt,
( $( $acc:tt )* ),
field deprecated $_reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
__graphql__build_field_matches!(
$resolveargs,
(($name; $args; $t; $body) $( $acc )*),
$( $rest )*);
};
// field <name>(...) -> <type> as <description> { ... }
(
$resolveargs:tt,
( $( $acc:tt )* ),
field $name:ident
$args:tt -> $t:ty
as $desc:tt
$body:block
$( $rest:tt )*
) => {
__graphql__build_field_matches!(
$resolveargs,
(($name; $args; $t; $body) $( $acc )*),
$( $rest )*);
};
// field <name>(...) -> <type> { ... }
(
$resolveargs:tt,
( $( $acc:tt )* ), field $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
__graphql__build_field_matches!(
$resolveargs,
(($name; $args; $t; $body) $( $acc )*),
$( $rest )*);
};
( $resolveargs:tt, $acc:tt, description : $value:tt $( $rest:tt )*) => {
__graphql__build_field_matches!($resolveargs, $acc, $( $rest )*);
};
( $resolveargs:tt, $acc:tt, interfaces : $value:tt $( $rest:tt )*) => {
__graphql__build_field_matches!($resolveargs, $acc, $( $rest )*);
};
( $resolveargs:tt,
$acc:tt,
instance_resolvers : | $execvar:pat | $resolvers:tt $( $rest:tt )*
) => {
__graphql__build_field_matches!($resolveargs, $acc, $( $rest )*);
};
( $resolveargs:tt, $acc:tt, , $( $rest:tt )*) => {
__graphql__build_field_matches!($resolveargs, $acc, $( $rest )*);
};
(
($outname:tt, $selfvar:ident, $fieldvar:ident, $argsvar:ident, $executorvar:ident),
( $( ( $name:ident; ( $($args:tt)* ); $t:ty; $body:block ) )* ),
) => {
$(
if $fieldvar == &$crate::to_camel_case(__graphql__stringify!($name)) {
let result: $t = (||{
__graphql__args!(
@assign_arg_vars,
$argsvar, $executorvar, $($args)*
);
$body
})();
return ($crate::IntoResolvable::into(result, $executorvar.context())).and_then(
|res| match res {
Some((ctx, r)) =>
$executorvar.replaced_context(ctx).resolve_with_ctx(&(), &r),
None => Ok($crate::Value::null()),
})
}
)*
__graphql__panic!("Field {} not found on type {}", $fieldvar, $outname);
};
}

View file

@ -87,246 +87,195 @@ graphql_interface!(<'a> &'a Character: Database as "Character" |&self| {
*/ */
#[macro_export(local_inner_macros)] #[macro_export(local_inner_macros)]
macro_rules! graphql_interface { macro_rules! graphql_interface {
( @as_item, $i:item) => { $i };
( @as_expr, $e:expr) => { $e };
// field deprecated <reason> <name>(...) -> <type> as <description> { ... }
(
@ gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
field deprecated $reason:tt
$name:ident
$args:tt -> $t:ty as $desc:tt
$body:block
$( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc)
.deprecated($reason),
$info,
$args));
graphql_interface!(@ gather_meta, ($reg, $acc, $info, $descr), $( $rest )*);
};
// field deprecated <reason> <name>(...) -> <type> { ... }
(
@ gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
field deprecated $reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.deprecated($reason),
$info,
$args));
graphql_interface!(@ gather_meta, ($reg, $acc, $info, $descr), $( $rest )*);
};
// field <name>(...) -> <type> as <description> { ... }
(
@gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
field $name:ident $args:tt -> $t:ty as $desc:tt $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc),
$info,
$args));
graphql_interface!(@ gather_meta, ($reg, $acc, $info, $descr), $( $rest )*);
};
// field <name>(...) -> <type> { ... }
(
@ gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
field $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info),
$info,
$args));
graphql_interface!(@ gather_meta, ($reg, $acc, $info, $descr), $( $rest )*);
};
// description: <description>
(
@ gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
description : $value:tt $( $rest:tt )*
) => {
$descr = Some(graphql_interface!(@as_expr, $value));
graphql_interface!(@gather_meta, ($reg, $acc, $info, $descr), $( $rest )*)
};
// instance_resolvers: | <ctxtvar> | [...]
(
@ gather_meta,
($reg:expr, $acc:expr, $info:expr, $descr:expr),
instance_resolvers : | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
$(
let _ = $reg.get_type::<$srctype>(&());
)*
graphql_interface!(@gather_meta, ($reg, $acc, $info, $descr), $( $rest )*)
};
// instance_resolvers: | <ctxtvar> | [...]
(
@ concrete_type_name,
($outname:tt, $ctxtarg:ident, $ctxttype:ty),
instance_resolvers : | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
let $ctxtvar = &$ctxtarg;
$(
if ($resolver as Option<$srctype>).is_some() {
return (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned();
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname);
};
// instance_resolvers: | <ctxtvar> |
(
@ resolve_into_type,
($outname:tt, $typenamearg:ident, $execarg:ident, $ctxttype:ty),
instance_resolvers : | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
let $ctxtvar = &$execarg.context();
$(
if $typenamearg == (<$srctype as $crate::GraphQLType>::name(&())).unwrap() {
return $execarg.resolve(&(), &$resolver);
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname);
};
( @ $mfn:ident, $args:tt, $first:tt $($rest:tt)* ) => {
graphql_interface!(@ $mfn, $args, $($rest)*);
};
( @ $mfn:ident, $buildargs:tt, ) => {};
( (
( $($lifetime:tt),* ) $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @generate,
$( $items:tt )* meta = {
} lifetimes = [$($lifetimes:tt,)*],
name = $name:ty,
ctx = $ctx:ty,
main_self = $main_self:ident,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
$(description = $desciption:tt,)*
additional = {
resolver = {
$(context = $resolver_ctx: ident,)*
items = [
$({
src = $resolver_src: ty,
resolver = $resolver_expr: expr,
},)*
],
},
},
},
items = [$({
name = $fn_name: ident,
body = $body: block,
return_ty = $return_ty: ty,
args = [$({
arg_name = $arg_name : ident,
arg_ty = $arg_ty: ty,
$(arg_description = $arg_description: expr,)*
$(arg_default = $arg_default: expr,)*
},)*],
$(decs = $fn_description: expr,)*
$(deprecated = $deprecated: expr,)*
$(executor_var = $executor: ident,)*
},)*],
) => { ) => {
graphql_interface!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { __juniper_impl_trait!(
type Context = $ctxt; impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
type TypeInfo = (); type Context = $ctx;
type TypeInfo = ();
fn name(_: &()) -> Option<&str> { fn name(_ : &Self::TypeInfo) -> Option<&str> {
Some($outname) Some($($outname)*)
}
#[allow(unused_assignments)]
#[allow(unused_mut)]
fn meta<'r>(
info: &(),
registry: &mut $crate::Registry<'r>
) -> $crate::meta::MetaType<'r> {
let mut fields = Vec::new();
let mut description = None;
graphql_interface!(
@ gather_meta, (registry, fields, info, description), $($items)*
);
let mut mt = registry.build_interface_type::<$name>(&(), &fields);
if let Some(description) = description {
mt = mt.description(description);
} }
mt.into_meta() fn meta<'r>(
} info: &Self::TypeInfo,
registry: &mut $crate::Registry<'r, __juniper_insert_generic!($($scalar)+)>
) -> $crate::meta::MetaType<'r, __juniper_insert_generic!($($scalar)+)>
where for<'__b> &'__b __juniper_insert_generic!($($scalar)+): $crate::ScalarRefValue<'__b>,
__juniper_insert_generic!($($scalar)+): 'r
{
// Ensure all child types are registered
$(
let _ = registry.get_type::<$resolver_src>(info);
)*
let fields = &[$(
registry.field_convert::<$return_ty, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($fn_name)),
info
)
$(.description($fn_description))*
$(.deprecated($deprecated))*
$(.argument(
__juniper_create_arg!(
registry = registry,
info = info,
arg_ty = $arg_ty,
arg_name = $arg_name,
$(description = $arg_description,)*
$(default = $arg_default,)*
)
))*,
)*];
registry.build_interface_type::<$name>(
info, fields
)
$(.description($desciption))*
.into_meta()
}
#[allow(unused_variables)]
#[allow(unused_mut)]
fn resolve_field(
&$mainself,
info: &(),
field: &str,
args: &$crate::Arguments,
mut executor: &$crate::Executor<Self::Context>
) -> $crate::ExecutionResult {
__graphql__build_field_matches!(
($outname, $mainself, field, args, executor),
(),
$($items)*);
}
fn concrete_type_name(&$mainself, context: &Self::Context, _info: &()) -> String { #[allow(unused_variables)]
graphql_interface!( fn resolve_field(
@ concrete_type_name, &$main_self,
($outname, context, $ctxt), info: &Self::TypeInfo,
$($items)*); field: &str,
} args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>,
executor: &$crate::Executor<Self::Context, __juniper_insert_generic!($($scalar)+)>
) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> {
$(
if field == &$crate::to_camel_case(__graphql__stringify!($fn_name)) {
let result: $return_ty = (|| {
$(
let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(stringify!($arg_name)))
.expect(__graphql__concat!(
"Argument ",
__graphql__stringify!($arg_name),
" missing - validation must have failed"
));
)*
$(
let $executor = &executor;
)*
$body
})();
fn resolve_into_type( return $crate::IntoResolvable::into(result, executor.context())
&$mainself, .and_then(|res| {
_: &(), match res {
type_name: &str, Some((ctx, r)) => {
_: Option<&[$crate::Selection]>, executor.replaced_context(ctx)
executor: &$crate::Executor<Self::Context>, .resolve_with_ctx(&(), &r)
) }
-> $crate::ExecutionResult None => Ok($crate::Value::null())
{ }
graphql_interface!( });
@ resolve_into_type, }
($outname, type_name, executor, $ctxt), )*
$($items)*);
__graphql__panic!("Field {} not found on type {}", field, $($outname)*)
}
#[allow(unused_variables)]
fn concrete_type_name(&$main_self, context: &Self::Context, _info: &Self::TypeInfo) -> String {
$(let $resolver_ctx = &context;)*
$(
if ($resolver_expr as ::std::option::Option<$resolver_src>).is_some() {
return
<$resolver_src as $crate::GraphQLType<_>>::name(&()).unwrap().to_owned();
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*);
}
fn resolve_into_type(
&$main_self,
_info: &Self::TypeInfo,
type_name: &str,
_: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>,
executor: &$crate::Executor<Self::Context, __juniper_insert_generic!($($scalar)*)>,
) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)*)> {
$(let $resolver_ctx = &executor.context();)*
$(
if type_name == (<$resolver_src as $crate::GraphQLType<_>>::name(&())).unwrap() {
return executor.resolve(&(), &$resolver_expr);
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*);
}
} }
}); );
}; };
( (
<$($lifetime:tt),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @parse,
$( $items:tt )* meta = {$($meta:tt)*},
} rest = $($rest:tt)*
) => { ) => {
graphql_interface!( __juniper_parse_field_list!(
($($lifetime),*) $name : $ctxt as $outname | &$mainself | { $( $items )* }); success_callback = graphql_interface,
additional_parser = {
callback = __juniper_parse_instance_resolver,
header = {},
},
meta = {$($meta)*},
items = [],
rest = $($rest)*
);
};
(@$($stuff:tt)*) => {
__graphql__compile_error!("Invalid syntax for `graphql_interface!`");
}; };
( (
$name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { $($rest:tt)*
$( $items:tt )*
}
) => { ) => {
graphql_interface!(() $name : $ctxt as $outname | &$mainself | { $( $items )* }); __juniper_parse_object_header!(
}; callback = graphql_interface,
rest = $($rest)*
);
}
(
$name:ty : $ctxt:ty | &$mainself:ident | {
$( $items:tt )*
}
) => {
graphql_interface!(() $name : $ctxt as (__graphql__stringify!($name)) | &$mainself | { $( $items )* });
};
} }

View file

@ -14,10 +14,18 @@ macro_rules! __graphql__stringify {
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! __graphql__vec { macro_rules! __graphql__concat {
($($t:tt)*) => ( vec!($($t)*) ); ($($t:tt)*) => ( concat!($($t)*) );
} }
#[doc(hidden)]
#[macro_export]
macro_rules! __graphql__compile_error {
($t:expr) => ( compile_error!($t) );
}
#[macro_use]
mod common;
#[macro_use] #[macro_use]
mod object; mod object;
#[macro_use] #[macro_use]
@ -25,10 +33,6 @@ mod interface;
#[macro_use] #[macro_use]
mod scalar; mod scalar;
#[macro_use] #[macro_use]
mod args;
#[macro_use]
mod field;
#[macro_use]
mod union; mod union;
#[cfg(test)] #[cfg(test)]

View file

@ -120,8 +120,8 @@ even have to be backed by a trait!
## Emitting errors ## Emitting errors
`FieldResult<T>` is a type alias for `Result<T, FieldError>`, where `FieldResult<T, S = DefaultScalarValue>` is a type alias for `Result<T, FieldError<S>>`, where
`FieldResult` is a tuple that contains an error message and optionally a `FieldError` is a tuple that contains an error message and optionally a
JSON-like data structure. In the end, errors that fields emit are serialized JSON-like data structure. In the end, errors that fields emit are serialized
into strings in the response. However, the execution system will keep track of into strings in the response. However, the execution system will keep track of
the source of all errors, and will continue executing despite some fields the source of all errors, and will continue executing despite some fields
@ -149,17 +149,52 @@ graphql_object!(User: () |&self| {
# fn main() { } # fn main() { }
``` ```
## Specify scalar value representation
Sometimes it is necessary to use a other scalar value representation as the default
one provided by `DefaultScalarValue`.
It is possible to specify a specific scalar value type using the `where Scalar = Type`
syntax.
Additionally it is possible to use a generic parameter for the scalar value type
(in such a way that the type implements `GraphQLType` for all possible scalar value
representation). Similary to the specific type case the syntax here is
`where Scalar = <S>` where `S` is a freely choosable type parameter, that also could
be used as type parameter to the implementing type.
Example for using a generic scalar value type
```rust
# #[macro_use] extern crate juniper;
struct User { id: String }
graphql_object!(User: () where Scalar = <S> |&self| {
field id() -> &String {
&self.id
}
});
# fn main() { }
```
# Syntax # Syntax
The top-most syntax of this macro defines which type to expose, the context The top-most syntax of this macro defines which type to expose, the context
type, which lifetime parameters or generics to define, and which name to use in type, which lifetime parameters or generics to define, which name to use in
the GraphQL schema. It takes one of the following two forms: the GraphQL schema and which scalar value type is used. It takes the
following form:
```text ```text
ExposedType: ContextType as "ExposedName" |&self| { items... } <Generics> ExposedType: ContextType as "ExposedName" where Scalar = <S> |&self| { items... }
<Generics> ExposedType: ContextType as "ExposedName" |&self| { items... } <Generics> ExposedType: ContextType as "ExposedName" where Scalar = SpecificType |&self| { items... }
``` ```
The following parts are optional:
* `<Generics>`, if not set no generics are defined
* `as "ExposedName"`, if not set `ExposedType` is used as name
* `where Scalar = <S>` / `where Scalar = SpecificType` if not set `DefaultScalarValue`
is used as scalar value
## Items ## Items
Each item within the brackets of the top level declaration has its own syntax. Each item within the brackets of the top level declaration has its own syntax.
@ -239,220 +274,214 @@ arg_name = ("default".to_owned()): String
*/ */
#[macro_export(local_inner_macros)] #[macro_export(local_inner_macros)]
macro_rules! graphql_object { macro_rules! graphql_object {
( @as_item, $i:item) => { $i };
( @as_expr, $e:expr) => { $e };
// field deprecated <reason> <name>(...) -> <type> as <description> { ... }
( (
@gather_object_meta, @generate,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr, meta = {
field deprecated lifetimes = [$($lifetimes:tt,)*],
$reason:tt name = $name: ty,
$name:ident ctx = $ctx: ty,
$args:tt -> $t:ty main_self = $main_self: ident,
as $desc:tt outname = {$($outname: tt)*},
$body:block scalar = {$($scalar:tt)*},
$( $rest:tt )* $(description = $desciption: expr,)*
$(additional = {
$(interfaces = [$($interface:ty,)*],)*
},)*
},
items = [$({
name = $fn_name: ident,
body = $body: block,
return_ty = $return_ty: ty,
args = [$({
arg_name = $arg_name : ident,
arg_ty = $arg_ty: ty,
$(arg_description = $arg_description: expr,)*
$(arg_default = $arg_default: expr,)*
},)*],
$(decs = $fn_description: expr,)*
$(deprecated = $deprecated: expr,)*
$(executor_var = $executor: ident,)*
},)*],
) => { ) => {
$acc.push(__graphql__args!( __juniper_impl_trait!(
@apply_args, impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
$reg, type Context = $ctx;
$reg.field_convert::<$t, _, Self::Context>( type TypeInfo = ();
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc)
.deprecated($reason),
$info,
$args));
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*); fn name(_ : &Self::TypeInfo) -> Option<&str> {
}; Some($($outname)*)
// field deprecated <reason> <name>(...) -> <type> { ... }
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
field deprecated $reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.deprecated($reason),
$info,
$args));
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*);
};
// field <name>(...) -> <type> as <description> { ... }
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
field $name:ident $args:tt -> $t:ty as $desc:tt $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc),
$info,
$args));
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*);
};
// field <name>(...) -> <type> { ... }
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
field $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )*
) => {
$acc.push(__graphql__args!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info),
$info,
$args));
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*);
};
// description: <description>
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
description : $value:tt $( $rest:tt )*
) => {
$descr = Some(graphql_object!(@as_expr, $value));
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*)
};
// interfaces: [...]
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
interfaces : $value:tt $( $rest:tt )*
) => {
graphql_object!(@assign_interfaces, $reg, $ifaces, $value);
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*)
};
// eat commas
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr, , $( $rest:tt )*
) => {
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*)
};
// base case
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
) => {};
( @assign_interfaces, $reg:expr, $tgt:expr, [ $($t:ty,)* ] ) => {
$tgt = Some(__graphql__vec![
$($reg.get_type::<$t>(&())),*
]);
};
( @assign_interfaces, $reg:expr, $tgt:expr, [ $($t:ty),* ] ) => {
$tgt = Some(__graphql__vec![
$($reg.get_type::<$t>(&())),*
]);
};
(
( $($lifetime:tt)* );
$name:ty; $ctxt:ty; $outname:expr; $mainself:ident; $($items:tt)*
) => {
graphql_object!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name {
type Context = $ctxt;
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
Some($outname)
}
#[allow(unused_assignments)]
#[allow(unused_mut)]
fn meta<'r>(
info: &(),
registry: &mut $crate::Registry<'r>
) -> $crate::meta::MetaType<'r> {
let mut fields = Vec::new();
let mut description = None;
let mut interfaces: Option<Vec<$crate::Type>> = None;
graphql_object!(
@gather_object_meta,
registry, fields, info, description, interfaces, $($items)*
);
let mut mt = registry.build_object_type::<$name>(info, &fields);
if let Some(description) = description {
mt = mt.description(description);
} }
if let Some(interfaces) = interfaces { fn meta<'r>(
mt = mt.interfaces(&interfaces); info: &Self::TypeInfo,
registry: &mut $crate::Registry<'r, __juniper_insert_generic!($($scalar)+)>
) -> $crate::meta::MetaType<'r, __juniper_insert_generic!($($scalar)+)>
where for<'__b> &'__b __juniper_insert_generic!($($scalar)+): $crate::ScalarRefValue<'__b>,
__juniper_insert_generic!($($scalar)+): 'r
{
let fields = &[$(
registry.field_convert::<$return_ty, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($fn_name)),
info
)
$(.description($fn_description))*
$(.deprecated($deprecated))*
$(.argument(
__juniper_create_arg!(
registry = registry,
info = info,
arg_ty = $arg_ty,
arg_name = $arg_name,
$(description = $arg_description,)*
$(default = $arg_default,)*
)
))*,
)*];
registry.build_object_type::<$name>(
info, fields
)
$(.description($desciption))*
$($(.interfaces(&[
$(registry.get_type::<$interface>(&()),)*
]))*)*
.into_meta()
} }
mt.into_meta() fn concrete_type_name(&self, _: &Self::Context, _: &Self::TypeInfo) -> String {
} $($outname)*.to_owned()
}
fn concrete_type_name(&self, _: &Self::Context, _: &()) -> String { #[allow(unused_variables)]
$outname.to_owned() fn resolve_field(
} &$main_self,
info: &Self::TypeInfo,
field: &str,
args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>,
executor: &$crate::Executor<Self::Context, __juniper_insert_generic!($($scalar)+)>
) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> {
$(
if field == &$crate::to_camel_case(__graphql__stringify!($fn_name)) {
let result: $return_ty = (|| {
$(
let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(__graphql__stringify!($arg_name)))
.expect(__graphql__concat!(
"Argument ",
__graphql__stringify!($arg_name),
" missing - validation must have failed"
));
)*
$(
let $executor = &executor;
)*
$body
})();
#[allow(unused_variables)] return $crate::IntoResolvable::into(result, executor.context())
#[allow(unused_mut)] .and_then(|res| {
fn resolve_field( match res {
&$mainself, Some((ctx, r)) => {
info: &(), executor.replaced_context(ctx)
field: &str, .resolve_with_ctx(&(), &r)
args: &$crate::Arguments, }
executor: &$crate::Executor<Self::Context> None => Ok($crate::Value::null())
) }
-> $crate::ExecutionResult });
{ }
__graphql__build_field_matches!( )*
($outname, $mainself, field, args, executor),
(), __graphql__panic!("Field {} not found on type {}", field, $($outname)*);
$($items)*); }
} }
}); );
}; };
( (
<$( $lifetime:tt ),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @parse_interfaces,
$( $items:tt )* success_callback = $success_callback: ident,
} additional_parser = {$($additional:tt)*},
meta = {
lifetimes = [$($lifetime:tt,)*],
name = $name:ty,
ctx = $ctxt: ty,
main_self = $mainself: ident,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
$(description = $desciption: tt,)*
$(additional = {
$(interfaces = [$($_interface:ty,)*],)*
},)*
},
items = [$({$($items: tt)*},)*],
rest = [$($interface: ty),+] $($rest:tt)*
) => { ) => {
graphql_object!( __juniper_parse_field_list!(
( $($lifetime),* ); $name; $ctxt; $outname; $mainself; $( $items )*); success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = {
lifetimes = [$($lifetime,)*],
name = $name,
ctx = $ctxt,
main_self = $mainself,
outname = {$($outname)*},
scalar = {$($scalar)*},
$(description = $desciption,)*
additional = {
interfaces = [$($interface,)*],
},
},
items = [$({$($items)*},)*],
rest = $($rest)*
);
}; };
( (
$name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @parse_interfaces,
$( $items:tt )* success_callback = $success_callback: ident,
} additional_parser = {$($additional:tt)*},
meta = { $($meta:tt)* },
items = [$({$($items: tt)*},)*],
rest = interfaces: $($rest:tt)*
) => { ) => {
graphql_object!( graphql_object!(
( ); $name; $ctxt; $outname; $mainself; $( $items )*); @parse_interfaces,
success_callback = $success_callback,
additional_parser = {$($additional)*},
meta = { $($meta)* },
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
@parse,
meta = {$($meta:tt)*},
rest = $($rest:tt)*
) => {
__juniper_parse_field_list!(
success_callback = graphql_object,
additional_parser = {
callback = graphql_object,
header = {@parse_interfaces,},
},
meta = {$($meta)*},
items = [],
rest = $($rest)*
);
};
(@$($stuff:tt)*) => {
__graphql__compile_error!("Invalid syntax for `graphql_object!`");
}; };
( (
$name:ty : $ctxt:ty | &$mainself:ident | { $($rest:tt)*
$( $items:tt )*
}
) => { ) => {
graphql_object!( __juniper_parse_object_header!(
( ); $name; $ctxt; (__graphql__stringify!($name)); $mainself; $( $items )*); callback = graphql_object,
}; rest = $($rest)*
);
}
} }

View file

@ -11,9 +11,14 @@ custom scalars will be transferred as strings. You therefore need to ensure that
the client library you are sending data to can parse the custom value into a the client library you are sending data to can parse the custom value into a
datatype appropriate for that platform. datatype appropriate for that platform.
By default the trait is implemented in terms of the default scalar value
representation provided by juniper. If that does not fit your needs it is
possible to use the same syntax as on `graphql_object!` to specify a custom
representation.
```rust ```rust
# #[macro_use] extern crate juniper; # #[macro_use] extern crate juniper;
# use juniper::{Value, FieldResult}; # use juniper::{Value, FieldResult, ParseScalarValue, ParseScalarResult};
struct UserID(String); struct UserID(String);
graphql_scalar!(UserID { graphql_scalar!(UserID {
@ -26,6 +31,10 @@ graphql_scalar!(UserID {
from_input_value(v: &InputValue) -> Option<UserID> { from_input_value(v: &InputValue) -> Option<UserID> {
v.as_string_value().map(|s| UserID(s.to_owned())) v.as_string_value().map(|s| UserID(s.to_owned()))
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<String as ParseScalarValue>::from_str(value)
}
}); });
# fn main() { } # fn main() { }
@ -38,119 +47,297 @@ usable as arguments and default values.
*/ */
#[macro_export(local_inner_macros)] #[macro_export(local_inner_macros)]
macro_rules! graphql_scalar { macro_rules! graphql_scalar {
( @as_expr, $e:expr) => { $e }; ( @as_expr $e:expr) => { $e };
// Calls $val.$func($arg) if $arg is not None
( @maybe_apply, None, $func:ident, $val:expr ) => { $val };
( @maybe_apply, $arg:tt, $func:ident, $val:expr ) => { $val.$func($arg) };
// Each of the @parse match arms accumulates data up to a call to @generate
//
// ( $name, $outname, $descr ): the name of the Rust type and the name of the
// GraphQL scalar (as a string), and the description of the scalar (as a
// string or none).
//
// ( $resolve_selfvar, $resolve_body ): the "self" argument and body for the
// resolve() method on GraphQLType and the to_input_value() method on ToInputValue.
//
// ( $fiv_arg, $fiv_result, $fiv_body ): the method argument, result type,
// and body for the from() method on FromInputValue.
( (
@generate, @generate,
( $name:ty, $outname:expr, $descr:tt ), meta = {
( name = $name:ty,
( $resolve_selfvar:ident, $resolve_retval:ty, $resolve_body:block ), outname = {$($outname:tt)+},
( $fiv_arg:ident, $fiv_result:ty, $fiv_body:block ) scalar = {$($scalar:tt)+},
) $(description = $descr:tt,)*
},
resolve = {
self_var = $resolve_self_var:ident,
body = $resolve_body: block,
return_type = $resolve_retun_type: ty,
},
from_input_value = {
arg = $from_input_value_arg: ident,
result = $from_input_value_result: ty,
body = $from_input_value_body: block,
},
from_str = {
value_arg = $from_str_arg: ident,
result = $from_str_result: ty,
body = $from_str_body: block,
lifetime = $from_str_lt: tt,
},
) => { ) => {
impl $crate::GraphQLType for $name { __juniper_impl_trait!(
type Context = (); impl <$($scalar)+> GraphQLType for $name {
type TypeInfo = (); type Context = ();
type TypeInfo = ();
fn name(_: &()) -> Option<&str> { fn name(_: &Self::TypeInfo) -> Option<&str> {
Some(graphql_scalar!( @as_expr, $outname )) Some(graphql_scalar!(@as_expr $($outname)+))
} }
fn meta<'r>( fn meta<'r>(
info: &(), info: &Self::TypeInfo,
registry: &mut $crate::Registry<'r> registry: &mut $crate::Registry<'r, __juniper_insert_generic!($($scalar)+)>
) -> $crate::meta::MetaType<'r> { ) -> $crate::meta::MetaType<'r, __juniper_insert_generic!($($scalar)+)>
graphql_scalar!( where for<'__b> &'__b __juniper_insert_generic!($($scalar)+): $crate::ScalarRefValue<'__b>,
@maybe_apply, $descr, description, __juniper_insert_generic!($($scalar)+): 'r
registry.build_scalar_type::<Self>(info)) {
.into_meta() let meta = registry.build_scalar_type::<Self>(info);
} $(
let meta = meta.description($descr);
)*
meta.into_meta()
}
fn resolve( fn resolve(
&$resolve_selfvar, &$resolve_self_var,
_: &(), _: &(),
_: Option<&[$crate::Selection]>, _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)+)>]>,
_: &$crate::Executor<Self::Context>) -> $resolve_retval { _: &$crate::Executor<
$resolve_body Self::Context,
} __juniper_insert_generic!($($scalar)+)
} >) -> $crate::Value<__juniper_insert_generic!($($scalar)+)> {
$resolve_body
}
});
impl $crate::ToInputValue for $name { __juniper_impl_trait!(
fn to_input_value(&$resolve_selfvar) -> $crate::InputValue { impl<$($scalar)+> ToInputValue for $name {
$crate::ToInputValue::to_input_value(&$resolve_body) fn to_input_value(&$resolve_self_var) -> $crate::InputValue<__juniper_insert_generic!($($scalar)+)> {
let v = $resolve_body;
$crate::ToInputValue::to_input_value(&v)
}
} }
} );
impl $crate::FromInputValue for $name { __juniper_impl_trait!(
fn from_input_value($fiv_arg: &$crate::InputValue) -> $fiv_result { impl<$($scalar)+> FromInputValue for $name {
$fiv_body fn from_input_value(
$from_input_value_arg: &$crate::InputValue<__juniper_insert_generic!($($scalar)+)>
) -> $from_input_value_result {
$from_input_value_body
}
} }
} );
__juniper_impl_trait!(
impl<$($scalar)+> ParseScalarValue for $name {
fn from_str<$from_str_lt>($from_str_arg: $crate::parser::ScalarToken<$from_str_lt>) -> $from_str_result {
$from_str_body
}
}
);
}; };
// No more items to parse // No more items to parse
( (
@parse, @parse_functions,
$meta:tt, meta = {
$acc:tt, name = $name:ty,
outname = {$($outname:tt)+},
scalar = {$($scalar:tt)+},
$(description = $descr:tt,)*
},
resolve = {$($resolve_body:tt)+},
from_input_value = {$($from_input_value_body:tt)+},
from_str = {$($from_str_body:tt)+},
rest =
) => { ) => {
graphql_scalar!( @generate, $meta, $acc ); graphql_scalar!(
@generate,
meta = {
name = $name,
outname = {$($outname)+},
scalar = {$($scalar)+},
$(description = $descr,)*
},
resolve = {$($resolve_body)+},
from_input_value = {$($from_input_value_body)+},
from_str = {$($from_str_body)+},
);
}; };
(
@parse_functions,
meta = {
name = $name:ty,
outname = {$($outname:tt)+},
scalar = {$($scalar:tt)+},
$(description = $descr:tt,)*
},
$(from_input_value = {$($from_input_value_body:tt)+})*,
$(from_str = {$($from_str_body:tt)+})*,
rest =
) => {
__graphql__compile_error!("Missing resolve function");
};
(
@parse_functions,
meta = {
name = $name:ty,
outname = {$($outname:tt)+},
scalar = {$($scalar:tt)+},
$(description = $descr:tt,)*
},
resolve = {$($resolve_body:tt)+},
$(from_str = {$($from_str_body:tt)+})*,
rest =
) => {
__graphql__compile_error!("Missing from_input_value function");
};
(
@parse_functions,
meta = {
name = $name:ty,
outname = {$($outname:tt)+},
scalar = {$($scalar:tt)+},
$(description = $descr:tt,)*
},
resolve = {$($resolve_body:tt)+},
from_input_value = {$($from_input_value_body:tt)+},
rest =
) =>{
__graphql__compile_error!("Missing from_str function");
};
// resolve(&self) -> Value { ... } // resolve(&self) -> Value { ... }
( (
@parse, @parse_functions,
$meta:tt, meta = {$($meta:tt)*},
( $_ignored:tt, $fiv:tt ), $(resolve = {$($resolve_body:tt)+},)*
resolve(&$selfvar:ident) -> $retval:ty $body:block $($rest:tt)* $(from_input_value = {$($from_input_value_body:tt)+},)*
$(from_str = {$($from_str_body:tt)+},)*
rest = resolve(&$selfvar:ident) -> $return_ty:ty $body:block $($rest:tt)*
) => { ) => {
graphql_scalar!( @parse, $meta, ( ($selfvar, $retval, $body), $fiv ), $($rest)* ); graphql_scalar!(
@parse_functions,
meta = {$($meta)*},
resolve = {
self_var = $selfvar,
body = $body,
return_type = $return_ty,
},
$(from_input_value = {$($from_input_value_body)+},)*
$(from_str = {$($from_str_body)+},)*
rest = $($rest)*
);
}; };
// from_input_value(arg: &InputValue) -> ... { ... } // from_input_value(arg: &InputValue) -> ... { ... }
( (
@parse, @parse_functions,
$meta:tt, meta = { $($meta:tt)* },
( $resolve:tt, $_ignored:tt ), $(resolve = {$($resolve_body:tt)+})*,
from_input_value($arg:ident: &InputValue) -> $result:ty $body:block $($rest:tt)* $(from_input_value = {$($from_input_value_body:tt)+},)*
$(from_str = {$($from_str_body:tt)+},)*
rest = from_input_value($arg:ident: &InputValue) -> $result:ty $body:block $($rest:tt)*
) => { ) => {
graphql_scalar!( @parse, $meta, ( $resolve, ( $arg, $result, $body ) ), $($rest)* ); graphql_scalar!(
@parse_functions,
meta = { $($meta)* },
$(resolve = {$($resolve_body)+},)*
from_input_value = {
arg = $arg,
result = $result,
body = $body,
},
$(from_str = {$($from_str_body)+},)*
rest = $($rest)*
);
};
// from_str(value: &str) -> Result<S, ParseError>
(
@parse_functions,
meta = { $($meta:tt)* },
$(resolve = {$($resolve_body:tt)+},)*
$(from_input_value = {$($from_input_value_body:tt)+},)*
$(from_str = {$($from_str_body:tt)+},)*
rest = from_str<$from_str_lt: tt>($value_arg:ident: ScalarToken<$ignored_lt2: tt>) -> $result:ty $body:block $($rest:tt)*
) => {
graphql_scalar!(
@parse_functions,
meta = { $($meta)* },
$(resolve = {$($resolve_body)+},)*
$(from_input_value = {$($from_input_value_body)+},)*
from_str = {
value_arg = $value_arg,
result = $result,
body = $body,
lifetime = $from_str_lt,
},
rest = $($rest)*
);
}; };
// description: <description> // description: <description>
( (
@parse, @parse_functions,
( $name:ty, $outname:expr, $_ignored:tt ), meta = {
$acc:tt, name = $name:ty,
description: $descr:tt $($rest:tt)* outname = {$($outname:tt)+},
scalar = {$($scalar:tt)+},
},
$(resolve = {$($resolve_body:tt)+},)*
$(from_input_value = {$($from_input_value_body:tt)+},)*
$(from_str = {$($from_str_body:tt)+},)*
rest = description: $descr:tt $($rest:tt)*
) => { ) => {
graphql_scalar!( @parse, ( $name, $outname, $descr ), $acc, $($rest)* ); graphql_scalar!(
@parse_functions,
meta = {
name = $name,
outname = {$($outname)+},
scalar = {$($scalar)+},
description = $descr,
},
$(resolve = {$($resolve_body)+},)*
$(from_input_value = {$($from_input_value_body)+},)*
$(from_str = {$($from_str_body)+},)*
rest = $($rest)*
);
}; };
// Entry point: (
// RustName as "GraphQLName" { ... } @parse,
( $name:ty as $outname:tt { $( $items:tt )* }) => { meta = {
graphql_scalar!( @parse, ( $name, $outname, None ), ( None, None ), $($items)* ); lifetimes = [],
name = $name: ty,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
},
rest = $($rest:tt)*
) => {
graphql_scalar!(
@parse_functions,
meta = {
name = $name,
outname = {$($outname)*},
scalar = {$($scalar)*},
},
rest = $($rest)*
);
}; };
// Entry point (@$($stuff:tt)*) => {
// RustName { ... } __graphql__compile_error!("Invalid syntax for `graphql_scalar!`");
( $name:ty { $( $items:tt )* }) => {
graphql_scalar!( @parse, ( $name, __graphql__stringify!($name), None ), ( None, None ), $($items)* );
}; };
($($rest:tt)*) => {
__juniper_parse_object_header!(
callback = graphql_scalar,
rest = $($rest)*
);
}
} }

View file

@ -1,7 +1,7 @@
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::Value; use value::{DefaultScalarValue, Value};
struct Root; struct Root;
@ -20,7 +20,6 @@ Syntax to validate:
*/ */
#[derive(GraphQLInputObject)] #[derive(GraphQLInputObject)]
#[graphql(_internal)]
struct Point { struct Point {
x: i32, x: i32,
} }
@ -78,7 +77,7 @@ graphql_object!(Root: () |&self| {
fn run_args_info_query<F>(field_name: &str, f: F) fn run_args_info_query<F>(field_name: &str, f: F)
where where
F: Fn(&Vec<Value>) -> (), F: Fn(&Vec<Value<DefaultScalarValue>>) -> (),
{ {
let doc = r#" let doc = r#"
{ {
@ -130,10 +129,10 @@ where
.expect("Field not an object") .expect("Field not an object")
.get_field_value("name") .get_field_value("name")
.expect("name field missing from field") .expect("name field missing from field")
.as_string_value() .as_scalar_value::<String>()
.expect("name is not a string") == field_name .expect("name is not a string")
}) == field_name
.next() }).next()
.expect("Field not found") .expect("Field not found")
.as_object_value() .as_object_value()
.expect("Field is not an object"); .expect("Field is not an object");
@ -173,7 +172,7 @@ fn introspect_field_exec_arg_and_more() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg")), ("name", Value::scalar("arg")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -184,15 +183,15 @@ fn introspect_field_exec_arg_and_more() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -206,7 +205,7 @@ fn introspect_field_single_arg() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg")), ("name", Value::scalar("arg")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -217,15 +216,15 @@ fn introspect_field_single_arg() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -239,7 +238,7 @@ fn introspect_field_multi_args() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -250,22 +249,22 @@ fn introspect_field_multi_args() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -276,15 +275,15 @@ fn introspect_field_multi_args() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -298,7 +297,7 @@ fn introspect_field_multi_args_trailing_comma() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -309,22 +308,22 @@ fn introspect_field_multi_args_trailing_comma() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
@ -335,15 +334,15 @@ fn introspect_field_multi_args_trailing_comma() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -357,8 +356,8 @@ fn introspect_field_single_arg_descr() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg")), ("name", Value::scalar("arg")),
("description", Value::string("The arg")), ("description", Value::scalar("The arg")),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
"type", "type",
@ -368,15 +367,15 @@ fn introspect_field_single_arg_descr() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -390,8 +389,8 @@ fn introspect_field_multi_args_descr() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::string("The first arg")), ("description", Value::scalar("The first arg")),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
"type", "type",
@ -401,23 +400,23 @@ fn introspect_field_multi_args_descr() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::string("The second arg")), ("description", Value::scalar("The second arg")),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
"type", "type",
@ -427,15 +426,15 @@ fn introspect_field_multi_args_descr() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -449,8 +448,8 @@ fn introspect_field_multi_args_descr_trailing_comma() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::string("The first arg")), ("description", Value::scalar("The first arg")),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
"type", "type",
@ -460,23 +459,23 @@ fn introspect_field_multi_args_descr_trailing_comma() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::string("The second arg")), ("description", Value::scalar("The second arg")),
("defaultValue", Value::null()), ("defaultValue", Value::null()),
( (
"type", "type",
@ -486,15 +485,15 @@ fn introspect_field_multi_args_descr_trailing_comma() {
( (
"ofType", "ofType",
Value::object( Value::object(
vec![("name", Value::string("Int"))].into_iter().collect(), vec![("name", Value::scalar("Int"))].into_iter().collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -508,19 +507,19 @@ fn introspect_field_arg_with_default() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg")), ("name", Value::scalar("arg")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -534,38 +533,38 @@ fn introspect_field_multi_args_with_default() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::string("456")), ("defaultValue", Value::scalar("456")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -579,38 +578,38 @@ fn introspect_field_multi_args_with_default_trailing_comma() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::null()), ("description", Value::null()),
("defaultValue", Value::string("456")), ("defaultValue", Value::scalar("456")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -624,19 +623,19 @@ fn introspect_field_arg_with_default_descr() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg")), ("name", Value::scalar("arg")),
("description", Value::string("The arg")), ("description", Value::scalar("The arg")),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -650,38 +649,38 @@ fn introspect_field_multi_args_with_default_descr() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::string("The first arg")), ("description", Value::scalar("The first arg")),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::string("The second arg")), ("description", Value::scalar("The second arg")),
("defaultValue", Value::string("456")), ("defaultValue", Value::scalar("456")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -695,38 +694,38 @@ fn introspect_field_multi_args_with_default_trailing_comma_descr() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::string("The first arg")), ("description", Value::scalar("The first arg")),
("defaultValue", Value::string("123")), ("defaultValue", Value::scalar("123")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
("description", Value::string("The second arg")), ("description", Value::scalar("The second arg")),
("defaultValue", Value::string("456")), ("defaultValue", Value::scalar("456")),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Int")), ("ofType", Value::null())] vec![("name", Value::scalar("Int")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });
@ -740,41 +739,41 @@ fn introspect_field_args_with_complex_default() {
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg1")), ("name", Value::scalar("arg1")),
("description", Value::string("A string default argument")), ("description", Value::scalar("A string default argument")),
("defaultValue", Value::string(r#""test""#)), ("defaultValue", Value::scalar(r#""test""#)),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("String")), ("ofType", Value::null())] vec![("name", Value::scalar("String")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
assert!( assert!(
args.contains(&Value::object( args.contains(&Value::object(
vec![ vec![
("name", Value::string("arg2")), ("name", Value::scalar("arg2")),
( (
"description", "description",
Value::string("An input object default argument"), Value::scalar("An input object default argument"),
), ),
("defaultValue", Value::string(r#"{x: 1}"#)), ("defaultValue", Value::scalar(r#"{x: 1}"#)),
( (
"type", "type",
Value::object( Value::object(
vec![("name", Value::string("Point")), ("ofType", Value::null())] vec![("name", Value::scalar("Point")), ("ofType", Value::null())]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
)) ))
); );
}); });

View file

@ -2,9 +2,10 @@ use ast::InputValue;
use executor::FieldResult; use executor::FieldResult;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Object, Value}; use value::{DefaultScalarValue, Object, Value};
struct Interface; struct Interface;
#[derive(Debug)]
struct Root; struct Root;
/* /*
@ -57,7 +58,7 @@ graphql_interface!(Interface: () |&self| {
fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F) fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
let doc = r#" let doc = r#"
query ($typeName: String!) { query ($typeName: String!) {
@ -72,7 +73,7 @@ where
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![("typeName".to_owned(), InputValue::string(type_name))] let vars = vec![("typeName".to_owned(), InputValue::scalar(type_name))]
.into_iter() .into_iter()
.collect(); .collect();
@ -103,10 +104,10 @@ where
.expect("Field not an object") .expect("Field not an object")
.get_field_value("name") .get_field_value("name")
.expect("name field missing from field") .expect("name field missing from field")
.as_string_value() .as_scalar_value::<String>()
.expect("name is not a string") == field_name .expect("name is not a string")
}) == field_name
.next() }).next()
.expect("Field not found") .expect("Field not found")
.as_object_value() .as_object_value()
.expect("Field is not an object"); .expect("Field is not an object");
@ -121,12 +122,12 @@ fn introspect_object_field_simple() {
run_field_info_query("Root", "simple", |field| { run_field_info_query("Root", "simple", |field| {
assert_eq!( assert_eq!(
field.get_field_value("name"), field.get_field_value("name"),
Some(&Value::string("simple")) Some(&Value::scalar("simple"))
); );
assert_eq!(field.get_field_value("description"), Some(&Value::null())); assert_eq!(field.get_field_value("description"), Some(&Value::null()));
assert_eq!( assert_eq!(
field.get_field_value("isDeprecated"), field.get_field_value("isDeprecated"),
Some(&Value::boolean(false)) Some(&Value::scalar(false))
); );
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
@ -140,12 +141,12 @@ fn introspect_interface_field_simple() {
run_field_info_query("Interface", "simple", |field| { run_field_info_query("Interface", "simple", |field| {
assert_eq!( assert_eq!(
field.get_field_value("name"), field.get_field_value("name"),
Some(&Value::string("simple")) Some(&Value::scalar("simple"))
); );
assert_eq!(field.get_field_value("description"), Some(&Value::null())); assert_eq!(field.get_field_value("description"), Some(&Value::null()));
assert_eq!( assert_eq!(
field.get_field_value("isDeprecated"), field.get_field_value("isDeprecated"),
Some(&Value::boolean(false)) Some(&Value::scalar(false))
); );
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
@ -159,39 +160,60 @@ fn introspect_object_field_description() {
run_field_info_query("Root", "description", |field| { run_field_info_query("Root", "description", |field| {
assert_eq!( assert_eq!(
field.get_field_value("name"), field.get_field_value("name"),
Some(&Value::string("description")) Some(&Value::scalar("description"))
); );
assert_eq!( assert_eq!(
field.get_field_value("description"), field.get_field_value("description"),
Some(&Value::string("Field description")) Some(&Value::scalar("Field description"))
);
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(false))
);
assert_eq!(
field.get_field_value("deprecationReason"),
Some(&Value::null())
); );
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get_field_value("deprecationReason"), Some(&Value::null()));
}); });
} }
#[test] #[test]
fn introspect_interface_field_description() { fn introspect_interface_field_description() {
run_field_info_query("Interface", "description", |field| { run_field_info_query("Interface", "description", |field| {
assert_eq!(field.get_field_value("name"), Some(&Value::string("description"))); assert_eq!(
field.get_field_value("name"),
Some(&Value::scalar("description"))
);
assert_eq!( assert_eq!(
field.get_field_value("description"), field.get_field_value("description"),
Some(&Value::string("Field description")) Some(&Value::scalar("Field description"))
);
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(false))
);
assert_eq!(
field.get_field_value("deprecationReason"),
Some(&Value::null())
); );
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get_field_value("deprecationReason"), Some(&Value::null()));
}); });
} }
#[test] #[test]
fn introspect_object_field_deprecated() { fn introspect_object_field_deprecated() {
run_field_info_query("Root", "deprecated", |field| { run_field_info_query("Root", "deprecated", |field| {
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecated"))); assert_eq!(
field.get_field_value("name"),
Some(&Value::scalar("deprecated"))
);
assert_eq!(field.get_field_value("description"), Some(&Value::null())); assert_eq!(field.get_field_value("description"), Some(&Value::null()));
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true))); assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
);
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason")) Some(&Value::scalar("Deprecation reason"))
); );
}); });
} }
@ -199,12 +221,18 @@ fn introspect_object_field_deprecated() {
#[test] #[test]
fn introspect_interface_field_deprecated() { fn introspect_interface_field_deprecated() {
run_field_info_query("Interface", "deprecated", |field| { run_field_info_query("Interface", "deprecated", |field| {
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecated"))); assert_eq!(
field.get_field_value("name"),
Some(&Value::scalar("deprecated"))
);
assert_eq!(field.get_field_value("description"), Some(&Value::null())); assert_eq!(field.get_field_value("description"), Some(&Value::null()));
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true))); assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
);
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason")) Some(&Value::scalar("Deprecation reason"))
); );
}); });
} }
@ -212,15 +240,21 @@ fn introspect_interface_field_deprecated() {
#[test] #[test]
fn introspect_object_field_deprecated_descr() { fn introspect_object_field_deprecated_descr() {
run_field_info_query("Root", "deprecatedDescr", |field| { run_field_info_query("Root", "deprecatedDescr", |field| {
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecatedDescr"))); assert_eq!(
field.get_field_value("name"),
Some(&Value::scalar("deprecatedDescr"))
);
assert_eq!( assert_eq!(
field.get_field_value("description"), field.get_field_value("description"),
Some(&Value::string("Field description")) Some(&Value::scalar("Field description"))
);
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
); );
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason")) Some(&Value::scalar("Deprecation reason"))
); );
}); });
} }
@ -228,15 +262,21 @@ fn introspect_object_field_deprecated_descr() {
#[test] #[test]
fn introspect_interface_field_deprecated_descr() { fn introspect_interface_field_deprecated_descr() {
run_field_info_query("Interface", "deprecatedDescr", |field| { run_field_info_query("Interface", "deprecatedDescr", |field| {
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecatedDescr"))); assert_eq!(
field.get_field_value("name"),
Some(&Value::scalar("deprecatedDescr"))
);
assert_eq!( assert_eq!(
field.get_field_value("description"), field.get_field_value("description"),
Some(&Value::string("Field description")) Some(&Value::scalar("Field description"))
);
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
); );
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!( assert_eq!(
field.get_field_value("deprecationReason"), field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason")) Some(&Value::scalar("Deprecation reason"))
); );
}); });
} }

View file

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use ast::InputValue; use ast::InputValue;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{Value, Object, DefaultScalarValue};
/* /*
@ -129,7 +129,7 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F) fn run_type_info_query<F>(type_name: &str, f: F)
where where
F: Fn(&Object, &Vec<Value>) -> (), F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{ {
let doc = r#" let doc = r#"
query ($typeName: String!) { query ($typeName: String!) {
@ -143,7 +143,7 @@ where
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![("typeName".to_owned(), InputValue::string(type_name))] let vars = vec![("typeName".to_owned(), InputValue::scalar(type_name))]
.into_iter() .into_iter()
.collect(); .collect();
@ -175,13 +175,13 @@ fn introspect_custom_name() {
run_type_info_query("ACustomNamedInterface", |object, fields| { run_type_info_query("ACustomNamedInterface", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("ACustomNamedInterface")) Some(&Value::scalar("ACustomNamedInterface"))
); );
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -192,12 +192,12 @@ fn introspect_custom_name() {
#[test] #[test]
fn introspect_with_lifetime() { fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |object, fields| { run_type_info_query("WithLifetime", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("WithLifetime"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("WithLifetime")));
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -208,12 +208,12 @@ fn introspect_with_lifetime() {
#[test] #[test]
fn introspect_with_generics() { fn introspect_with_generics() {
run_type_info_query("WithGenerics", |object, fields| { run_type_info_query("WithGenerics", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("WithGenerics"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("WithGenerics")));
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -224,15 +224,15 @@ fn introspect_with_generics() {
#[test] #[test]
fn introspect_description_first() { fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |object, fields| { run_type_info_query("DescriptionFirst", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("DescriptionFirst"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("DescriptionFirst")));
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -243,15 +243,15 @@ fn introspect_description_first() {
#[test] #[test]
fn introspect_fields_first() { fn introspect_fields_first() {
run_type_info_query("FieldsFirst", |object, fields| { run_type_info_query("FieldsFirst", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("FieldsFirst"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("FieldsFirst")));
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -262,15 +262,15 @@ fn introspect_fields_first() {
#[test] #[test]
fn introspect_interfaces_first() { fn introspect_interfaces_first() {
run_type_info_query("InterfacesFirst", |object, fields| { run_type_info_query("InterfacesFirst", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("InterfacesFirst"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("InterfacesFirst")));
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -283,16 +283,16 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| { run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing")) Some(&Value::scalar("CommasWithTrailing"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -303,15 +303,15 @@ fn introspect_commas_with_trailing() {
#[test] #[test]
fn introspect_commas_on_meta() { fn introspect_commas_on_meta() {
run_type_info_query("CommasOnMeta", |object, fields| { run_type_info_query("CommasOnMeta", |object, fields| {
assert_eq!(object.get_field_value("name"), Some(&Value::string("CommasOnMeta"))); assert_eq!(object.get_field_value("name"), Some(&Value::scalar("CommasOnMeta")));
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -324,16 +324,16 @@ fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |object, fields| { run_type_info_query("ResolversWithTrailingComma", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma")) Some(&Value::scalar("ResolversWithTrailingComma"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
fields.contains(&Value::object( fields.contains(&Value::object(
vec![("name", Value::string("simple"))] vec![("name", Value::scalar("simple"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))

View file

@ -4,7 +4,7 @@ use ast::InputValue;
use executor::{Context, FieldResult}; use executor::{Context, FieldResult};
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Object, Value}; use value::{DefaultScalarValue, Object, Value};
/* /*
@ -143,7 +143,7 @@ graphql_object!(<'a> Root: InnerContext as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F) fn run_type_info_query<F>(type_name: &str, f: F)
where where
F: Fn(&Object, &Vec<Value>) -> (), F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{ {
let doc = r#" let doc = r#"
query ($typeName: String!) { query ($typeName: String!) {
@ -169,7 +169,7 @@ where
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<InnerContext>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<InnerContext>::new());
let vars = vec![("typeName".to_owned(), InputValue::string(type_name))] let vars = vec![("typeName".to_owned(), InputValue::scalar(type_name))]
.into_iter() .into_iter()
.collect(); .collect();
@ -202,7 +202,7 @@ fn introspect_custom_name() {
run_type_info_query("ACustomNamedType", |object, fields| { run_type_info_query("ACustomNamedType", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("ACustomNamedType")) Some(&Value::scalar("ACustomNamedType"))
); );
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!( assert_eq!(
@ -222,7 +222,7 @@ fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |object, fields| { run_type_info_query("WithLifetime", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("WithLifetime")) Some(&Value::scalar("WithLifetime"))
); );
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!( assert_eq!(
@ -242,7 +242,7 @@ fn introspect_with_generics() {
run_type_info_query("WithGenerics", |object, fields| { run_type_info_query("WithGenerics", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("WithGenerics")) Some(&Value::scalar("WithGenerics"))
); );
assert_eq!(object.get_field_value("description"), Some(&Value::null())); assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!( assert_eq!(
@ -262,20 +262,20 @@ fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |object, fields| { run_type_info_query("DescriptionFirst", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("DescriptionFirst")) Some(&Value::scalar("DescriptionFirst"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert_eq!( assert_eq!(
object.get_field_value("interfaces"), object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![ vec![
("name", Value::string("Interface")), ("name", Value::scalar("Interface")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
)])) )]))
); );
@ -291,20 +291,20 @@ fn introspect_fields_first() {
run_type_info_query("FieldsFirst", |object, fields| { run_type_info_query("FieldsFirst", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("FieldsFirst")) Some(&Value::scalar("FieldsFirst"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert_eq!( assert_eq!(
object.get_field_value("interfaces"), object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![ vec![
("name", Value::string("Interface")), ("name", Value::scalar("Interface")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
)])) )]))
); );
@ -320,20 +320,20 @@ fn introspect_interfaces_first() {
run_type_info_query("InterfacesFirst", |object, fields| { run_type_info_query("InterfacesFirst", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("InterfacesFirst")) Some(&Value::scalar("InterfacesFirst"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert_eq!( assert_eq!(
object.get_field_value("interfaces"), object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![ vec![
("name", Value::string("Interface")), ("name", Value::scalar("Interface")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
)])) )]))
); );
@ -349,20 +349,20 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| { run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing")) Some(&Value::scalar("CommasWithTrailing"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert_eq!( assert_eq!(
object.get_field_value("interfaces"), object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![ vec![
("name", Value::string("Interface")), ("name", Value::scalar("Interface")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
)])) )]))
); );
@ -378,20 +378,20 @@ fn introspect_commas_on_meta() {
run_type_info_query("CommasOnMeta", |object, fields| { run_type_info_query("CommasOnMeta", |object, fields| {
assert_eq!( assert_eq!(
object.get_field_value("name"), object.get_field_value("name"),
Some(&Value::string("CommasOnMeta")) Some(&Value::scalar("CommasOnMeta"))
); );
assert_eq!( assert_eq!(
object.get_field_value("description"), object.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert_eq!( assert_eq!(
object.get_field_value("interfaces"), object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object( Some(&Value::list(vec![Value::object(
vec![ vec![
("name", Value::string("Interface")), ("name", Value::scalar("Interface")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
)])) )]))
); );

View file

@ -1,7 +1,7 @@
use executor::Variables; use executor::Variables;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{Value, Object, DefaultScalarValue, ParseScalarValue, ParseScalarResult};
struct DefaultName(i32); struct DefaultName(i32);
struct OtherOrder(i32); struct OtherOrder(i32);
@ -19,45 +19,62 @@ Syntax to validate:
*/ */
graphql_scalar!(DefaultName { graphql_scalar!(DefaultName where Scalar = <S> {
resolve(&self) -> Value { resolve(&self) -> Value {
Value::int(self.0) Value::scalar(self.0)
} }
from_input_value(v: &InputValue) -> Option<DefaultName> { from_input_value(v: &InputValue) -> Option<DefaultName> {
v.as_int_value().map(|i| DefaultName(i)) v.as_scalar_value::<i32>().map(|i| DefaultName(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
<i32 as ParseScalarValue<S>>::from_str(value)
} }
}); });
graphql_scalar!(OtherOrder { graphql_scalar!(OtherOrder {
from_input_value(v: &InputValue) -> Option<OtherOrder> { resolve(&self) -> Value {
v.as_int_value().map(|i| OtherOrder(i)) Value::scalar(self.0)
} }
resolve(&self) -> Value { from_input_value(v: &InputValue) -> Option<OtherOrder> {
Value::int(self.0) v.as_scalar_value::<i32>().map(|i| OtherOrder(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
<i32 as ParseScalarValue>::from_str(value)
} }
}); });
graphql_scalar!(Named as "ANamedScalar" { graphql_scalar!(Named as "ANamedScalar" where Scalar = DefaultScalarValue {
resolve(&self) -> Value { resolve(&self) -> Value {
Value::int(self.0) Value::scalar(self.0)
} }
from_input_value(v: &InputValue) -> Option<Named> { from_input_value(v: &InputValue) -> Option<Named> {
v.as_int_value().map(|i| Named(i)) v.as_scalar_value::<i32>().map(|i| Named(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> {
<i32 as ParseScalarValue>::from_str(value)
} }
}); });
graphql_scalar!(ScalarDescription { graphql_scalar!(ScalarDescription {
description: "A sample scalar, represented as an integer" description: "A sample scalar, represented as an integer"
resolve(&self) -> Value { resolve(&self) -> Value {
Value::int(self.0) Value::scalar(self.0)
} }
from_input_value(v: &InputValue) -> Option<ScalarDescription> { from_input_value(v: &InputValue) -> Option<ScalarDescription> {
v.as_int_value().map(|i| ScalarDescription(i)) v.as_scalar_value::<i32>().map(|i| ScalarDescription(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<i32 as ParseScalarValue>::from_str(value)
} }
}); });
@ -70,7 +87,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F) fn run_type_info_query<F>(doc: &str, f: F)
where where
F: Fn(&Object) -> (), F: Fn(&Object<DefaultScalarValue>) -> (),
{ {
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -95,13 +112,18 @@ where
#[test] #[test]
fn path_in_resolve_return_type() { fn path_in_resolve_return_type() {
struct ResolvePath(i32); struct ResolvePath(i32);
graphql_scalar!(ResolvePath { graphql_scalar!(ResolvePath {
resolve(&self) -> self::Value { resolve(&self) -> self::Value {
Value::int(self.0) Value::scalar(self.0)
} }
from_input_value(v: &InputValue) -> Option<ResolvePath> { from_input_value(v: &InputValue) -> Option<ResolvePath> {
v.as_int_value().map(|i| ResolvePath(i)) v.as_scalar_value::<i32>().map(|i| ResolvePath(*i))
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<i32 as ParseScalarValue>::from_str(value)
} }
}); });
} }
@ -118,7 +140,7 @@ fn default_name_introspection() {
"#; "#;
run_type_info_query(doc, |type_info| { run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("DefaultName"))); assert_eq!(type_info.get_field_value("name"), Some(&Value::scalar("DefaultName")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
}); });
} }
@ -135,7 +157,7 @@ fn other_order_introspection() {
"#; "#;
run_type_info_query(doc, |type_info| { run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("OtherOrder"))); assert_eq!(type_info.get_field_value("name"), Some(&Value::scalar("OtherOrder")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
}); });
} }
@ -152,7 +174,7 @@ fn named_introspection() {
"#; "#;
run_type_info_query(doc, |type_info| { run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("ANamedScalar"))); assert_eq!(type_info.get_field_value("name"), Some(&Value::scalar("ANamedScalar")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null())); assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
}); });
} }
@ -171,11 +193,11 @@ fn scalar_description_introspection() {
run_type_info_query(doc, |type_info| { run_type_info_query(doc, |type_info| {
assert_eq!( assert_eq!(
type_info.get_field_value("name"), type_info.get_field_value("name"),
Some(&Value::string("ScalarDescription")) Some(&Value::scalar("ScalarDescription"))
); );
assert_eq!( assert_eq!(
type_info.get_field_value("description"), type_info.get_field_value("description"),
Some(&Value::string("A sample scalar, represented as an integer")) Some(&Value::scalar("A sample scalar, represented as an integer"))
); );
}); });
} }

View file

@ -3,7 +3,7 @@ use std::marker::PhantomData;
use ast::InputValue; use ast::InputValue;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::{Value, Object}; use value::{Value, Object, DefaultScalarValue};
/* /*
@ -110,7 +110,7 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F) fn run_type_info_query<F>(type_name: &str, f: F)
where where
F: Fn(&Object, &Vec<Value>) -> (), F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{ {
let doc = r#" let doc = r#"
query ($typeName: String!) { query ($typeName: String!) {
@ -124,7 +124,7 @@ where
} }
"#; "#;
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
let vars = vec![("typeName".to_owned(), InputValue::string(type_name))] let vars = vec![("typeName".to_owned(), InputValue::scalar(type_name))]
.into_iter() .into_iter()
.collect(); .collect();
@ -154,12 +154,12 @@ where
#[test] #[test]
fn introspect_custom_name() { fn introspect_custom_name() {
run_type_info_query("ACustomNamedUnion", |union, possible_types| { run_type_info_query("ACustomNamedUnion", |union, possible_types| {
assert_eq!(union.get_field_value("name"), Some(&Value::string("ACustomNamedUnion"))); assert_eq!(union.get_field_value("name"), Some(&Value::scalar("ACustomNamedUnion")));
assert_eq!(union.get_field_value("description"), Some(&Value::null())); assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -170,12 +170,12 @@ fn introspect_custom_name() {
#[test] #[test]
fn introspect_with_lifetime() { fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |union, possible_types| { run_type_info_query("WithLifetime", |union, possible_types| {
assert_eq!(union.get_field_value("name"), Some(&Value::string("WithLifetime"))); assert_eq!(union.get_field_value("name"), Some(&Value::scalar("WithLifetime")));
assert_eq!(union.get_field_value("description"), Some(&Value::null())); assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -186,12 +186,12 @@ fn introspect_with_lifetime() {
#[test] #[test]
fn introspect_with_generics() { fn introspect_with_generics() {
run_type_info_query("WithGenerics", |union, possible_types| { run_type_info_query("WithGenerics", |union, possible_types| {
assert_eq!(union.get_field_value("name"), Some(&Value::string("WithGenerics"))); assert_eq!(union.get_field_value("name"), Some(&Value::scalar("WithGenerics")));
assert_eq!(union.get_field_value("description"), Some(&Value::null())); assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -202,15 +202,15 @@ fn introspect_with_generics() {
#[test] #[test]
fn introspect_description_first() { fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |union, possible_types| { run_type_info_query("DescriptionFirst", |union, possible_types| {
assert_eq!(union.get_field_value("name"), Some(&Value::string("DescriptionFirst"))); assert_eq!(union.get_field_value("name"), Some(&Value::scalar("DescriptionFirst")));
assert_eq!( assert_eq!(
union.get_field_value("description"), union.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -221,15 +221,15 @@ fn introspect_description_first() {
#[test] #[test]
fn introspect_resolvers_first() { fn introspect_resolvers_first() {
run_type_info_query("ResolversFirst", |union, possible_types| { run_type_info_query("ResolversFirst", |union, possible_types| {
assert_eq!(union.get_field_value("name"), Some(&Value::string("ResolversFirst"))); assert_eq!(union.get_field_value("name"), Some(&Value::scalar("ResolversFirst")));
assert_eq!( assert_eq!(
union.get_field_value("description"), union.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -242,16 +242,16 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |union, possible_types| { run_type_info_query("CommasWithTrailing", |union, possible_types| {
assert_eq!( assert_eq!(
union.get_field_value("name"), union.get_field_value("name"),
Some(&Value::string("CommasWithTrailing")) Some(&Value::scalar("CommasWithTrailing"))
); );
assert_eq!( assert_eq!(
union.get_field_value("description"), union.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))
@ -264,16 +264,16 @@ fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |union, possible_types| { run_type_info_query("ResolversWithTrailingComma", |union, possible_types| {
assert_eq!( assert_eq!(
union.get_field_value("name"), union.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma")) Some(&Value::scalar("ResolversWithTrailingComma"))
); );
assert_eq!( assert_eq!(
union.get_field_value("description"), union.get_field_value("description"),
Some(&Value::string("A description")) Some(&Value::scalar("A description"))
); );
assert!( assert!(
possible_types.contains(&Value::object( possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))] vec![("name", Value::scalar("Concrete"))]
.into_iter() .into_iter()
.collect(), .collect(),
)) ))

View file

@ -19,164 +19,119 @@ resolvers.
*/ */
#[macro_export(local_inner_macros)] #[macro_export(local_inner_macros)]
macro_rules! graphql_union { macro_rules! graphql_union {
( @as_item, $i:item) => { $i };
( @as_expr, $e:expr) => { $e };
( @as_path, $p:path) => { $p };
( @as_type, $t:ty) => { $t };
// description: <description>
(
@ gather_meta,
($reg:expr, $acc:expr, $descr:expr),
description : $value:tt $( $rest:tt )*
) => {
$descr = Some(graphql_interface!(@as_expr, $value));
graphql_union!(@ gather_meta, ($reg, $acc, $descr), $( $rest )*)
};
// Gathering meta for instance resolvers
// instance_resolvers: | <ctxtvar> | [...]
(
@ gather_meta,
($reg:expr, $acc:expr, $descr:expr),
instance_resolvers: | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
$acc = __graphql__vec![
$(
$reg.get_type::<$srctype>(&())
),*
];
graphql_union!(@ gather_meta, ($reg, $acc, $descr), $( $rest )*)
};
// To generate the "concrete type name" resolver, syntax case:
// instance_resolvers: | <ctxtvar> | [...]
(
@ concrete_type_name,
($outname:tt, $ctxtarg:ident, $ctxttype:ty),
instance_resolvers: | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
let $ctxtvar = &$ctxtarg;
$(
if let Some(_) = $resolver as Option<$srctype> {
return (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned();
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname);
};
// To generate the "resolve into type" resolver, syntax case:
// instance_resolvers: | <ctxtvar> | [...]
(
@ resolve_into_type,
($outname:tt, $typenamearg:ident, $execarg:ident, $ctxttype:ty),
instance_resolvers: | $ctxtvar:pat
| { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )*
) => {
let $ctxtvar = &$execarg.context();
$(
if $typenamearg == (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned() {
return $execarg.resolve(&(), &$resolver);
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname);
};
// eat commas
( @ $mfn:ident, $args:tt, , $($rest:tt)* ) => {
graphql_union!(@ $mfn, $args, $($rest)*);
};
// eat one tt
( @ $mfn:ident, $args:tt, $item:tt $($rest:tt)* ) => {
graphql_union!(@ $mfn, $args, $($rest)*);
};
// end case
( @ $mfn:ident, $args:tt, ) => {};
( (
( $($lifetime:tt),* ) $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @generate,
$( $items:tt )* meta = {
} lifetimes = [$($lifetimes:tt,)*],
name = $name:ty,
ctx = $ctx:ty,
main_self = $main_self:ident,
outname = {$($outname:tt)*},
scalar = {$($scalar:tt)*},
$(description = $desciption:tt,)*
additional = {
resolver = {
$(context = $resolver_ctx: ident,)*
items = [
$({
src = $resolver_src: ty,
resolver = $resolver_expr: expr,
},)*
],
},
},
},
items = [],
) => { ) => {
graphql_union!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { __juniper_impl_trait!(
type Context = $ctxt; impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
type TypeInfo = (); type Context = $ctx;
type TypeInfo = ();
fn name(_: &()) -> Option<&str> { fn name(_ : &Self::TypeInfo) -> Option<&str> {
Some($outname) Some($($outname)*)
}
#[allow(unused_assignments)]
#[allow(unused_mut)]
fn meta<'r>(_: &(), registry: &mut $crate::Registry<'r>) -> $crate::meta::MetaType<'r> {
let mut types;
let mut description = None;
graphql_union!(@ gather_meta, (registry, types, description), $($items)*);
let mut mt = registry.build_union_type::<$name>(&(), &types);
if let Some(description) = description {
mt = mt.description(description);
} }
mt.into_meta() fn meta<'r>(
} info: &Self::TypeInfo,
registry: &mut $crate::Registry<'r, __juniper_insert_generic!($($scalar)+)>
) -> $crate::meta::MetaType<'r, __juniper_insert_generic!($($scalar)+)>
where for<'__b> &'__b __juniper_insert_generic!($($scalar)+): $crate::ScalarRefValue<'__b>,
__juniper_insert_generic!($($scalar)+): 'r
{
let types = &[
$(
registry.get_type::<$resolver_src>(&()),
)*
];
registry.build_union_type::<$name>(
info, types
)
$(.description($desciption))*
.into_meta()
}
fn concrete_type_name(&$mainself, context: &Self::Context, _: &()) -> String { #[allow(unused_variables)]
graphql_union!( fn concrete_type_name(&$main_self, context: &Self::Context, _info: &Self::TypeInfo) -> String {
@ concrete_type_name, $(let $resolver_ctx = &context;)*
($outname, context, $ctxt),
$($items)*);
}
fn resolve_into_type( $(
&$mainself, if ($resolver_expr as ::std::option::Option<$resolver_src>).is_some() {
_: &(), return
type_name: &str, <$resolver_src as $crate::GraphQLType<_>>::name(&()).unwrap().to_owned();
_: Option<&[$crate::Selection]>, }
executor: &$crate::Executor<Self::Context>, )*
)
-> $crate::ExecutionResult __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*);
{ }
graphql_union!(
@ resolve_into_type, fn resolve_into_type(
($outname, type_name, executor, $ctxt), &$main_self,
$($items)*); _info: &Self::TypeInfo,
type_name: &str,
_: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>,
executor: &$crate::Executor<Self::Context, __juniper_insert_generic!($($scalar)*)>,
) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)*)> {
$(let $resolver_ctx = &executor.context();)*
$(
if type_name == (<$resolver_src as $crate::GraphQLType<_>>::name(&())).unwrap() {
return executor.resolve(&(), &$resolver_expr);
}
)*
__graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*);
}
} }
}); );
}; };
(
<$($lifetime:tt),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | {
$( $items:tt )*
}
) => {
graphql_union!(
($($lifetime),*) $name : $ctxt as $outname | &$mainself | { $( $items )* });
};
( (
$name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { @parse,
$( $items:tt )* meta = {$($meta:tt)*},
} rest = $($rest:tt)*
) => { ) => {
graphql_union!(() $name : $ctxt as $outname | &$mainself | { $( $items )* }); __juniper_parse_field_list!(
success_callback = graphql_union,
additional_parser = {
callback = __juniper_parse_instance_resolver,
header = {},
},
meta = {$($meta)*},
items = [],
rest = $($rest)*
);
};
(@$($stuff:tt)*) => {
__graphql__compile_error!("Invalid syntax for `graphql_union!`");
}; };
( ($($rest: tt)*) => {
$name:ty : $ctxt:ty | &$mainself:ident | { __juniper_parse_object_header!(
$( $items:tt )* callback = graphql_union,
} rest = $($rest)*
) => { );
graphql_union!(() $name : $ctxt as (__graphql__stringify!($name)) | &$mainself | { $( $items )* });
}; };
} }

View file

@ -10,19 +10,34 @@ use parser::{
Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token, Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token,
UnlocatedParseResult, UnlocatedParseResult,
}; };
use schema::meta::{Argument, Field as MetaField};
use schema::model::SchemaType;
use value::ScalarValue;
#[doc(hidden)] #[doc(hidden)]
pub fn parse_document_source(s: &str) -> UnlocatedParseResult<Document> { pub fn parse_document_source<'a, 'b, S>(
s: &'a str,
schema: &'b SchemaType<'b, S>,
) -> UnlocatedParseResult<'a, Document<'a, S>>
where
S: ScalarValue,
{
let mut lexer = Lexer::new(s); let mut lexer = Lexer::new(s);
let mut parser = Parser::new(&mut lexer).map_err(|s| s.map(ParseError::LexerError))?; let mut parser = Parser::new(&mut lexer).map_err(|s| s.map(ParseError::LexerError))?;
parse_document(&mut parser) parse_document(&mut parser, schema)
} }
fn parse_document<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Document<'a>> { fn parse_document<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
) -> UnlocatedParseResult<'a, Document<'a, S>>
where
S: ScalarValue,
{
let mut defs = Vec::new(); let mut defs = Vec::new();
loop { loop {
defs.push(parse_definition(parser)?); defs.push(parse_definition(parser, schema)?);
if parser.peek().item == Token::EndOfFile { if parser.peek().item == Token::EndOfFile {
return Ok(defs); return Ok(defs);
@ -30,19 +45,35 @@ fn parse_document<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Docum
} }
} }
fn parse_definition<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Definition<'a>> { fn parse_definition<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
) -> UnlocatedParseResult<'a, Definition<'a, S>>
where
S: ScalarValue,
{
match parser.peek().item { match parser.peek().item {
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => { Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Ok(
Ok(Definition::Operation(parse_operation_definition(parser)?)) Definition::Operation(parse_operation_definition(parser, schema)?),
} ),
Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(parser)?)), Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(
parser, schema,
)?)),
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)), _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Operation<'a>> { fn parse_operation_definition<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, Operation<'a, S>>
where
S: ScalarValue,
{
if parser.peek().item == Token::CurlyOpen { if parser.peek().item == Token::CurlyOpen {
let selection_set = parse_selection_set(parser)?; let fields = schema.concrete_query_type().fields(schema);
let fields = fields.as_ref().map(|c| c as &[_]);
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&selection_set.start, &selection_set.start,
@ -58,13 +89,20 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
} else { } else {
let start_pos = parser.peek().start.clone(); let start_pos = parser.peek().start.clone();
let operation_type = parse_operation_type(parser)?; let operation_type = parse_operation_type(parser)?;
let op = match operation_type.item {
OperationType::Query => Some(schema.concrete_query_type()),
OperationType::Mutation => schema.concrete_mutation_type(),
};
let fields = op.and_then(|m| m.fields(schema));
let fields = fields.as_ref().map(|c| c as &[_]);
let name = match parser.peek().item { let name = match parser.peek().item {
Token::Name(_) => Some(parser.expect_name()?), Token::Name(_) => Some(parser.expect_name()?),
_ => None, _ => None,
}; };
let variable_definitions = parse_variable_definitions(parser)?; let variable_definitions = parse_variable_definitions(parser, schema)?;
let directives = parse_directives(parser)?; let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser)?; let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&start_pos, &start_pos,
@ -80,7 +118,13 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
} }
} }
fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fragment<'a>> { fn parse_fragment_definition<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, Fragment<'a, S>>
where
S: ScalarValue,
{
let Spanning { let Spanning {
start: start_pos, .. start: start_pos, ..
} = parser.expect(&Token::Name("fragment"))?; } = parser.expect(&Token::Name("fragment"))?;
@ -95,8 +139,14 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
parser.expect(&Token::Name("on"))?; parser.expect(&Token::Name("on"))?;
let type_cond = parser.expect_name()?; let type_cond = parser.expect_name()?;
let directives = parse_directives(parser)?;
let selection_set = parse_selection_set(parser)?; let fields = schema
.concrete_type_by_name(type_cond.item)
.and_then(|m| m.fields(schema));
let fields = fields.as_ref().map(|c| c as &[_]);
let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&start_pos, &start_pos,
@ -110,28 +160,58 @@ fn parse_fragment_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Fra
)) ))
} }
fn parse_optional_selection_set<'a>( fn parse_optional_selection_set<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
) -> OptionParseResult<'a, Vec<Selection<'a>>> { schema: &'b SchemaType<'b, S>,
fields: Option<&[&MetaField<'b, S>]>,
) -> OptionParseResult<'a, Vec<Selection<'a, S>>>
where
S: ScalarValue,
{
if parser.peek().item == Token::CurlyOpen { if parser.peek().item == Token::CurlyOpen {
Ok(Some(parse_selection_set(parser)?)) Ok(Some(parse_selection_set(parser, schema, fields)?))
} else { } else {
Ok(None) Ok(None)
} }
} }
fn parse_selection_set<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Vec<Selection<'a>>> { fn parse_selection_set<'a, 'b, S>(
parser.unlocated_delimited_nonempty_list(&Token::CurlyOpen, parse_selection, &Token::CurlyClose) parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
fields: Option<&[&MetaField<'b, S>]>,
) -> ParseResult<'a, Vec<Selection<'a, S>>>
where
S: ScalarValue,
{
parser.unlocated_delimited_nonempty_list(
&Token::CurlyOpen,
|p| parse_selection(p, schema, fields),
&Token::CurlyClose,
)
} }
fn parse_selection<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selection<'a>> { fn parse_selection<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
fields: Option<&[&MetaField<'b, S>]>,
) -> UnlocatedParseResult<'a, Selection<'a, S>>
where
S: ScalarValue,
{
match parser.peek().item { match parser.peek().item {
Token::Ellipsis => parse_fragment(parser), Token::Ellipsis => parse_fragment(parser, schema, fields),
_ => parse_field(parser).map(Selection::Field), _ => parse_field(parser, schema, fields).map(Selection::Field),
} }
} }
fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selection<'a>> { fn parse_fragment<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
fields: Option<&[&MetaField<'b, S>]>,
) -> UnlocatedParseResult<'a, Selection<'a, S>>
where
S: ScalarValue,
{
let Spanning { let Spanning {
start: ref start_pos, start: ref start_pos,
.. ..
@ -141,8 +221,13 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
Token::Name("on") => { Token::Name("on") => {
parser.next()?; parser.next()?;
let name = parser.expect_name()?; let name = parser.expect_name()?;
let directives = parse_directives(parser)?;
let selection_set = parse_selection_set(parser)?; let fields = schema
.concrete_type_by_name(name.item)
.and_then(|m| m.fields(schema));
let fields = fields.as_ref().map(|c| c as &[_]);
let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Selection::InlineFragment(Spanning::start_end( Ok(Selection::InlineFragment(Spanning::start_end(
&start_pos.clone(), &start_pos.clone(),
@ -155,7 +240,7 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
))) )))
} }
Token::CurlyOpen => { Token::CurlyOpen => {
let selection_set = parse_selection_set(parser)?; let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Selection::InlineFragment(Spanning::start_end( Ok(Selection::InlineFragment(Spanning::start_end(
&start_pos.clone(), &start_pos.clone(),
@ -169,7 +254,7 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
} }
Token::Name(_) => { Token::Name(_) => {
let frag_name = parser.expect_name()?; let frag_name = parser.expect_name()?;
let directives = parse_directives(parser)?; let directives = parse_directives(parser, schema)?;
Ok(Selection::FragmentSpread(Spanning::start_end( Ok(Selection::FragmentSpread(Spanning::start_end(
&start_pos.clone(), &start_pos.clone(),
@ -184,8 +269,8 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
))) )))
} }
Token::At => { Token::At => {
let directives = parse_directives(parser)?; let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser)?; let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Selection::InlineFragment(Spanning::start_end( Ok(Selection::InlineFragment(Spanning::start_end(
&start_pos.clone(), &start_pos.clone(),
@ -201,7 +286,14 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
} }
} }
fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> { fn parse_field<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
fields: Option<&[&MetaField<'b, S>]>,
) -> ParseResult<'a, Field<'a, S>>
where
S: ScalarValue,
{
let mut alias = Some(parser.expect_name()?); let mut alias = Some(parser.expect_name()?);
let name = if parser.skip(&Token::Colon)?.is_some() { let name = if parser.skip(&Token::Colon)?.is_some() {
@ -210,9 +302,21 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
alias.take().unwrap() alias.take().unwrap()
}; };
let arguments = parse_arguments(parser)?; let field = fields.and_then(|f| f.iter().find(|f| f.name == name.item));
let directives = parse_directives(parser)?; let args = field
let selection_set = parse_optional_selection_set(parser)?; .as_ref()
.and_then(|f| f.arguments.as_ref().map(|a| a as &[_]));
let fields = field
.as_ref()
.and_then(|f| schema.lookup_type(&f.field_type))
.and_then(|m| m.fields(schema));
let fields = fields.as_ref().map(|c| c as &[_]);
let arguments = parse_arguments(parser, schema, args)?;
let directives = parse_directives(parser, schema)?;
let selection_set = parse_optional_selection_set(parser, schema, fields)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&alias.as_ref().unwrap_or(&name).start.clone(), &alias.as_ref().unwrap_or(&name).start.clone(),
@ -233,26 +337,45 @@ fn parse_field<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Field<'a>> {
)) ))
} }
fn parse_arguments<'a>(parser: &mut Parser<'a>) -> OptionParseResult<'a, Arguments<'a>> { fn parse_arguments<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
arguments: Option<&[Argument<'b, S>]>,
) -> OptionParseResult<'a, Arguments<'a, S>>
where
S: ScalarValue,
{
if parser.peek().item != Token::ParenOpen { if parser.peek().item != Token::ParenOpen {
Ok(None) Ok(None)
} else { } else {
Ok(Some( Ok(Some(
parser parser
.delimited_nonempty_list(&Token::ParenOpen, parse_argument, &Token::ParenClose)? .delimited_nonempty_list(
.map(|args| Arguments { &Token::ParenOpen,
|p| parse_argument(p, schema, arguments),
&Token::ParenClose,
)?.map(|args| Arguments {
items: args.into_iter().map(|s| s.item).collect(), items: args.into_iter().map(|s| s.item).collect(),
}), }),
)) ))
} }
} }
fn parse_argument<'a>( fn parse_argument<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
) -> ParseResult<'a, (Spanning<&'a str>, Spanning<InputValue>)> { schema: &'b SchemaType<'b, S>,
arguments: Option<&[Argument<'b, S>]>,
) -> ParseResult<'a, (Spanning<&'a str>, Spanning<InputValue<S>>)>
where
S: ScalarValue,
{
let name = parser.expect_name()?; let name = parser.expect_name()?;
let tpe = arguments
.and_then(|args| args.iter().find(|a| a.name == name.item))
.and_then(|arg| schema.lookup_type(&arg.arg_type));
parser.expect(&Token::Colon)?; parser.expect(&Token::Colon)?;
let value = parse_value_literal(parser, false)?; let value = parse_value_literal(parser, false, schema, tpe)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&name.start.clone(), &name.start.clone(),
@ -269,9 +392,13 @@ fn parse_operation_type<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Operatio
} }
} }
fn parse_variable_definitions<'a>( fn parse_variable_definitions<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
) -> OptionParseResult<'a, VariableDefinitions<'a>> { schema: &'b SchemaType<'b, S>,
) -> OptionParseResult<'a, VariableDefinitions<'a, S>>
where
S: ScalarValue,
{
if parser.peek().item != Token::ParenOpen { if parser.peek().item != Token::ParenOpen {
Ok(None) Ok(None)
} else { } else {
@ -279,28 +406,32 @@ fn parse_variable_definitions<'a>(
parser parser
.delimited_nonempty_list( .delimited_nonempty_list(
&Token::ParenOpen, &Token::ParenOpen,
parse_variable_definition, |p| parse_variable_definition(p, schema),
&Token::ParenClose, &Token::ParenClose,
)? )?.map(|defs| VariableDefinitions {
.map(|defs| VariableDefinitions {
items: defs.into_iter().map(|s| s.item).collect(), items: defs.into_iter().map(|s| s.item).collect(),
}), }),
)) ))
} }
} }
fn parse_variable_definition<'a>( fn parse_variable_definition<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
) -> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a>)> { schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, (Spanning<&'a str>, VariableDefinition<'a, S>)>
where
S: ScalarValue,
{
let Spanning { let Spanning {
start: start_pos, .. start: start_pos, ..
} = parser.expect(&Token::Dollar)?; } = parser.expect(&Token::Dollar)?;
let var_name = parser.expect_name()?; let var_name = parser.expect_name()?;
parser.expect(&Token::Colon)?; parser.expect(&Token::Colon)?;
let var_type = parse_type(parser)?; let var_type = parse_type(parser)?;
let tpe = schema.lookup_type(&var_type.item);
let default_value = if parser.skip(&Token::Equals)?.is_some() { let default_value = if parser.skip(&Token::Equals)?.is_some() {
Some(parse_value_literal(parser, true)?) Some(parse_value_literal(parser, true, schema, tpe)?)
} else { } else {
None None
}; };
@ -321,27 +452,44 @@ fn parse_variable_definition<'a>(
)) ))
} }
fn parse_directives<'a>( fn parse_directives<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
) -> OptionParseResult<'a, Vec<Spanning<Directive<'a>>>> { schema: &'b SchemaType<'b, S>,
) -> OptionParseResult<'a, Vec<Spanning<Directive<'a, S>>>>
where
S: ScalarValue,
{
if parser.peek().item != Token::At { if parser.peek().item != Token::At {
Ok(None) Ok(None)
} else { } else {
let mut items = Vec::new(); let mut items = Vec::new();
while parser.peek().item == Token::At { while parser.peek().item == Token::At {
items.push(parse_directive(parser)?); items.push(parse_directive(parser, schema)?);
} }
Ok(Spanning::spanning(items)) Ok(Spanning::spanning(items))
} }
} }
fn parse_directive<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Directive<'a>> { fn parse_directive<'a, 'b, S>(
parser: &mut Parser<'a>,
schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, Directive<'a, S>>
where
S: ScalarValue,
{
let Spanning { let Spanning {
start: start_pos, .. start: start_pos, ..
} = parser.expect(&Token::At)?; } = parser.expect(&Token::At)?;
let name = parser.expect_name()?; let name = parser.expect_name()?;
let arguments = parse_arguments(parser)?;
let directive = schema.directive_by_name(name.item);
let arguments = parse_arguments(
parser,
schema,
directive.as_ref().map(|d| &d.arguments as &[_]),
)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&start_pos, &start_pos,

View file

@ -16,14 +16,23 @@ pub struct Lexer<'a> {
has_reached_eof: bool, has_reached_eof: bool,
} }
/// A single scalar value literal
///
/// This is only used for tagging how the lexer has interpreted a value literal
#[derive(Debug, PartialEq, Clone, Copy)]
#[allow(missing_docs)]
pub enum ScalarToken<'a> {
String(&'a str),
Float(&'a str),
Int(&'a str)
}
/// A single token in the input source /// A single token in the input source
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq, Clone, Copy)]
#[allow(missing_docs)] #[allow(missing_docs)]
pub enum Token<'a> { pub enum Token<'a> {
Name(&'a str), Name(&'a str),
Int(i32), Scalar(ScalarToken<'a>),
Float(f64),
String(String),
ExclamationMark, ExclamationMark,
Dollar, Dollar,
ParenOpen, ParenOpen,
@ -180,7 +189,7 @@ impl<'a> Lexer<'a> {
} }
fn scan_name(&mut self) -> LexerResult<'a> { fn scan_name(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone(); let start_pos = self.position;
let (start_idx, start_ch) = self.next_char().ok_or(Spanning::zero_width( let (start_idx, start_ch) = self.next_char().ok_or(Spanning::zero_width(
&self.position, &self.position,
LexerError::UnexpectedEndOfFile, LexerError::UnexpectedEndOfFile,
@ -206,102 +215,58 @@ impl<'a> Lexer<'a> {
} }
fn scan_string(&mut self) -> LexerResult<'a> { fn scan_string(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone(); let start_pos = self.position;
let (_, start_ch) = self.next_char().ok_or(Spanning::zero_width( let (start_idx, start_ch) = self.next_char().ok_or(Spanning::zero_width(
&self.position, &self.position,
LexerError::UnexpectedEndOfFile, LexerError::UnexpectedEndOfFile,
))?; ))?;
assert!(start_ch == '"'); if start_ch != '"' {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnterminatedString,
));
}
let mut acc = String::new(); let mut escaped = false;
let mut old_pos = self.position;
while let Some((_, ch)) = self.peek_char() { while let Some((idx, ch)) = self.next_char() {
if ch == '"' { match ch {
self.next_char(); 'b' |'f' |'n'|'r' |'t'|'\\'|'/'| '"' if escaped => {
return Ok(Spanning::start_end( escaped = false;
&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((_, 'u')) => {
let start_pos = self.position.clone();
self.next_char();
acc.push(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),
));
}
None => {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnterminatedString,
));
}
} }
if let Some((_, ch)) = self.peek_char() { 'u' if escaped => {
if ch == 'n' {} self.scan_escaped_unicode(&old_pos)?;
} else { escaped = false;
},
c if escaped => {
return Err(Spanning::zero_width( return Err(Spanning::zero_width(
&old_pos,
LexerError::UnknownEscapeSequence(format!("\\{}", c))
))
}
'\\' => escaped = true,
'"' if !escaped => {
return Ok(Spanning::start_end(
&start_pos,
&self.position, &self.position,
LexerError::UnterminatedString, Token::Scalar(ScalarToken::String(&self.source[start_idx+1..idx])),
)); ));
} }
} else if ch == '\n' || ch == '\r' { '\n' | '\r' => {
return Err(Spanning::zero_width( return Err(Spanning::zero_width(
&self.position, &old_pos,
LexerError::UnterminatedString, LexerError::UnterminatedString,
)); ));
} else if !is_source_char(ch) { }
return Err(Spanning::zero_width( c if !is_source_char(c) => {
&self.position, return Err(Spanning::zero_width(
LexerError::UnknownCharacterInString(ch), &old_pos,
)); LexerError::UnknownCharacterInString(ch),
} else { ));
self.next_char(); }
acc.push(ch); _ => {}
} }
old_pos = self.position;
} }
Err(Spanning::zero_width( Err(Spanning::zero_width(
@ -313,7 +278,7 @@ impl<'a> Lexer<'a> {
fn scan_escaped_unicode( fn scan_escaped_unicode(
&mut self, &mut self,
start_pos: &SourcePosition, start_pos: &SourcePosition,
) -> Result<char, Spanning<LexerError>> { ) -> Result<(), Spanning<LexerError>> {
let (start_idx, _) = self.peek_char().ok_or(Spanning::zero_width( let (start_idx, _) = self.peek_char().ok_or(Spanning::zero_width(
&self.position, &self.position,
LexerError::UnterminatedString, LexerError::UnterminatedString,
@ -356,127 +321,118 @@ impl<'a> Lexer<'a> {
start_pos, start_pos,
LexerError::UnknownEscapeSequence("\\u".to_owned() + escape), LexerError::UnknownEscapeSequence("\\u".to_owned() + escape),
) )
}) }).map(|_|())
} }
fn scan_number(&mut self) -> LexerResult<'a> { fn scan_number(&mut self) -> LexerResult<'a> {
let start_pos = self.position.clone(); let start_pos = self.position;
let int_part = self.scan_integer_part()?; let (start_idx, _) = self.peek_char().ok_or(Spanning::zero_width(
let mut frac_part = None;
let mut exp_part = None;
if let Some((_, '.')) = self.peek_char() {
self.next_char();
frac_part = Some(self.scan_digits()?);
}
if let Some((_, ch)) = self.peek_char() {
if ch == 'e' || ch == 'E' {
self.next_char();
let mut is_negative = false;
if let Some((_, ch)) = self.peek_char() {
if ch == '-' {
self.next_char();
is_negative = true;
} else if ch == '+' {
self.next_char();
}
}
exp_part = Some(if is_negative { -1 } else { 1 } * self.scan_digits()?);
}
}
let mantissa = frac_part
.map(|f| f64::from(f))
.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| f64::from(e)).map(|e| 10f64.powf(e));
Ok(Spanning::start_end(
&start_pos,
&self.position, &self.position,
match (mantissa, exp) { LexerError::UnexpectedEndOfFile,
(None, None) => Token::Int(int_part), ))?;
(None, Some(exp)) => Token::Float((f64::from(int_part)) * exp),
(Some(mantissa), None) => Token::Float((f64::from(int_part)) + mantissa), let mut last_idx = start_idx;
(Some(mantissa), Some(exp)) => { let mut last_char = '1';
Token::Float(((f64::from(int_part)) + mantissa) * exp) let mut is_float = false;
let mut end_idx = loop {
if let Some((idx, ch)) = self.peek_char() {
if ch.is_digit(10) || (ch == '-' && last_idx == start_idx) {
if ch == '0' && last_char == '0' && last_idx == start_idx {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedCharacter('0'),
));
}
self.next_char();
last_char = ch;
} else if last_char == '-' {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedCharacter(ch),
));
} else {
break idx;
} }
}, last_idx = idx;
))
}
fn scan_integer_part(&mut self) -> Result<i32, Spanning<LexerError>> {
let is_negative = {
let (_, init_ch) = self.peek_char().ok_or(Spanning::zero_width(
&self.position,
LexerError::UnexpectedEndOfFile,
))?;
if init_ch == '-' {
self.next_char();
true
} else { } else {
false break last_idx + 1;
} }
}; };
let (_, ch) = self.peek_char().ok_or(Spanning::zero_width( if let Some((start_idx, '.')) = self.peek_char() {
&self.position, is_float = true;
LexerError::UnexpectedEndOfFile, let mut last_idx = start_idx;
))?;
if ch == '0' {
self.next_char(); self.next_char();
end_idx = loop {
match self.peek_char() { if let Some((idx, ch)) = self.peek_char() {
Some((_, '0')) => Err(Spanning::zero_width( if ch.is_digit(10) {
&self.position, self.next_char();
LexerError::UnexpectedCharacter(ch), } else if last_idx == start_idx {
)), return Err(Spanning::zero_width(
_ => Ok(0), &self.position,
} LexerError::UnexpectedCharacter(ch),
} else { ));
Ok(self.scan_digits()? * if is_negative { -1 } else { 1 }) } else {
break idx;
}
last_idx = idx;
} else if last_idx == start_idx {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedEndOfFile,
));
} else {
break last_idx + 1;
}
};
} }
} if let Some((start_idx, ch)) = self.peek_char() {
if ch == 'e' || ch == 'E' {
fn scan_digits(&mut self) -> Result<i32, Spanning<LexerError>> { is_float = true;
let start_pos = self.position.clone();
let (start_idx, ch) = self.peek_char().ok_or(Spanning::zero_width(
&self.position,
LexerError::UnexpectedEndOfFile,
))?;
let mut end_idx = start_idx;
if !ch.is_digit(10) {
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedCharacter(ch),
));
}
while let Some((idx, ch)) = self.peek_char() {
if !ch.is_digit(10) {
break;
} else {
self.next_char(); self.next_char();
end_idx = idx; let mut last_idx = start_idx;
end_idx = loop {
if let Some((idx, ch)) = self.peek_char() {
if ch.is_digit(10) || (last_idx == start_idx && (ch == '-' || ch == '+')) {
self.next_char();
} else if last_idx == start_idx {
// 1e is not a valid floating point number
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedCharacter(ch),
));
} else {
break idx;
}
last_idx = idx;
} else if last_idx == start_idx {
// 1e is not a valid floting point number
return Err(Spanning::zero_width(
&self.position,
LexerError::UnexpectedEndOfFile,
));
} else {
break last_idx + 1;
}
};
} }
} }
let number = &self.source[start_idx..end_idx];
let end_pos = &self.position;
i32::from_str_radix(&self.source[start_idx..end_idx + 1], 10) let token = if is_float {
.map_err(|_| Spanning::zero_width(&start_pos, LexerError::InvalidNumber)) Token::Scalar(ScalarToken::Float(number))
}else {
Token::Scalar(ScalarToken::Int(number))
};
Ok(Spanning::start_end(
&start_pos,
end_pos,
token,
))
} }
} }
@ -529,11 +485,10 @@ impl<'a> fmt::Display for Token<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
Token::Name(name) => write!(f, "{}", name), Token::Name(name) => write!(f, "{}", name),
Token::Int(i) => write!(f, "{}", i), Token::Scalar(ScalarToken::Int(s)) | Token::Scalar(ScalarToken::Float(s)) => write!(f, "{}", s),
Token::Float(v) => write!(f, "{}", v), Token::Scalar(ScalarToken::String(s)) => {
Token::String(ref s) => {
write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
} },
Token::ExclamationMark => write!(f, "!"), Token::ExclamationMark => write!(f, "!"),
Token::Dollar => write!(f, "$"), Token::Dollar => write!(f, "$"),
Token::ParenOpen => write!(f, "("), Token::ParenOpen => write!(f, "("),

View file

@ -11,6 +11,6 @@ mod tests;
pub use self::document::parse_document_source; pub use self::document::parse_document_source;
pub use self::lexer::{Lexer, LexerError, Token}; pub use self::lexer::{Lexer, LexerError, Token, ScalarToken};
pub use self::parser::{OptionParseResult, ParseError, ParseResult, Parser, UnlocatedParseResult}; pub use self::parser::{OptionParseResult, ParseError, ParseResult, Parser, UnlocatedParseResult};
pub use self::utils::{SourcePosition, Spanning}; pub use self::utils::{SourcePosition, Spanning};

View file

@ -182,8 +182,8 @@ impl<'a> Parser<'a> {
item: Token::EndOfFile, item: Token::EndOfFile,
.. ..
} => Err(Spanning::start_end( } => Err(Spanning::start_end(
&self.peek().start.clone(), &self.peek().start,
&self.peek().end.clone(), &self.peek().end,
ParseError::UnexpectedEndOfFile, ParseError::UnexpectedEndOfFile,
)), )),
_ => Err(self.next()?.map(ParseError::UnexpectedToken)), _ => Err(self.next()?.map(ParseError::UnexpectedToken)),

View file

@ -3,13 +3,25 @@ use ast::{
}; };
use parser::document::parse_document_source; use parser::document::parse_document_source;
use parser::{ParseError, SourcePosition, Spanning, Token}; use parser::{ParseError, SourcePosition, Spanning, Token};
use schema::model::SchemaType;
use validation::test_harness::{MutationRoot, QueryRoot};
use value::{ScalarRefValue, ScalarValue, DefaultScalarValue};
fn parse_document(s: &str) -> Document { fn parse_document<S>(s: &str) -> Document<S>
parse_document_source(s).expect(&format!("Parse error on input {:#?}", s)) where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
parse_document_source(s, &SchemaType::new::<QueryRoot, MutationRoot>(&(), &()))
.expect(&format!("Parse error on input {:#?}", s))
} }
fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> { fn parse_document_error<'a, S>(s: &'a str) -> Spanning<ParseError<'a>>
match parse_document_source(s) { where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
match parse_document_source::<S>(s, &SchemaType::new::<QueryRoot, MutationRoot>(&(), &())) {
Ok(doc) => panic!("*No* parse error on input {:#?} =>\n{:#?}", s, doc), Ok(doc) => panic!("*No* parse error on input {:#?} =>\n{:#?}", s, doc),
Err(err) => err, Err(err) => err,
} }
@ -18,7 +30,7 @@ fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> {
#[test] #[test]
fn simple_ast() { fn simple_ast() {
assert_eq!( assert_eq!(
parse_document( parse_document::<DefaultScalarValue>(
r#" r#"
{ {
node(id: 4) { node(id: 4) {
@ -59,7 +71,7 @@ fn simple_ast() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(40, 2, 25), &SourcePosition::new(40, 2, 25),
&SourcePosition::new(41, 2, 26), &SourcePosition::new(41, 2, 26),
InputValue::int(4), InputValue::scalar(4),
), ),
)], )],
}, },
@ -107,7 +119,7 @@ fn simple_ast() {
#[test] #[test]
fn errors() { fn errors() {
assert_eq!( assert_eq!(
parse_document_error("{"), parse_document_error::<DefaultScalarValue>("{"),
Spanning::zero_width( Spanning::zero_width(
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
ParseError::UnexpectedEndOfFile ParseError::UnexpectedEndOfFile
@ -115,7 +127,7 @@ fn errors() {
); );
assert_eq!( assert_eq!(
parse_document_error("{ ...MissingOn }\nfragment MissingOn Type"), parse_document_error::<DefaultScalarValue>("{ ...MissingOn }\nfragment MissingOn Type"),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(36, 1, 19), &SourcePosition::new(36, 1, 19),
&SourcePosition::new(40, 1, 23), &SourcePosition::new(40, 1, 23),
@ -124,7 +136,7 @@ fn errors() {
); );
assert_eq!( assert_eq!(
parse_document_error("{ ...on }"), parse_document_error::<DefaultScalarValue>("{ ...on }"),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(8, 0, 8), &SourcePosition::new(8, 0, 8),
&SourcePosition::new(9, 0, 9), &SourcePosition::new(9, 0, 9),

View file

@ -1,4 +1,4 @@
use parser::{Lexer, LexerError, SourcePosition, Spanning, Token}; use parser::{Lexer, LexerError, SourcePosition, Spanning, Token, ScalarToken};
fn tokenize_to_vec<'a>(s: &'a str) -> Vec<Spanning<Token<'a>>> { fn tokenize_to_vec<'a>(s: &'a str) -> Vec<Spanning<Token<'a>>> {
let mut tokens = Vec::new(); let mut tokens = Vec::new();
@ -148,7 +148,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(8, 0, 8), &SourcePosition::new(8, 0, 8),
Token::String("simple".to_owned()) Token::Scalar(ScalarToken::String("simple"))
) )
); );
@ -157,7 +157,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(15, 0, 15), &SourcePosition::new(15, 0, 15),
Token::String(" white space ".to_owned()) Token::Scalar(ScalarToken::String(" white space "))
) )
); );
@ -166,7 +166,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(10, 0, 10), &SourcePosition::new(10, 0, 10),
Token::String("quote \"".to_owned()) Token::Scalar(ScalarToken::String(r#"quote \""#))
) )
); );
@ -175,7 +175,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(20, 0, 20), &SourcePosition::new(20, 0, 20),
Token::String("escaped \n\r\u{0008}\t\u{000c}".to_owned()) Token::Scalar(ScalarToken::String(r#"escaped \n\r\b\t\f"#))
) )
); );
@ -184,7 +184,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(15, 0, 15), &SourcePosition::new(15, 0, 15),
Token::String("slashes \\ /".to_owned()) Token::Scalar(ScalarToken::String(r#"slashes \\ \/"#))
) )
); );
@ -193,7 +193,7 @@ fn strings() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(34, 0, 34), &SourcePosition::new(34, 0, 34),
Token::String("unicode \u{1234}\u{5678}\u{90ab}\u{cdef}".to_owned()) Token::Scalar(ScalarToken::String(r#"unicode \u1234\u5678\u90AB\uCDEF"#))
) )
); );
} }
@ -327,17 +327,16 @@ fn numbers() {
source: &str, source: &str,
start: SourcePosition, start: SourcePosition,
end: SourcePosition, end: SourcePosition,
expected: f64, expected: &str,
) { ) {
let parsed = tokenize_single(source); let parsed = tokenize_single(source);
assert_eq!(parsed.start, start); assert_eq!(parsed.start, start);
assert_eq!(parsed.end, end); assert_eq!(parsed.end, end);
match parsed.item { match parsed.item {
Token::Float(actual) => { Token::Scalar(ScalarToken::Float(actual)) => {
let relative_error = ((expected - actual) / actual).abs();
assert!( assert!(
relative_error.abs() < 0.001, expected == actual,
"[expected] {} != {} [actual]", "[expected] {} != {} [actual]",
expected, expected,
actual actual
@ -352,7 +351,7 @@ fn numbers() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
Token::Int(4) Token::Scalar(ScalarToken::Int("4"))
) )
); );
@ -360,14 +359,14 @@ fn numbers() {
"4.123", "4.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
4.123, "4.123",
); );
assert_float_token_eq( assert_float_token_eq(
"4.0", "4.0",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(3, 0, 3), SourcePosition::new(3, 0, 3),
4.0, "4.0",
); );
assert_eq!( assert_eq!(
@ -375,7 +374,7 @@ fn numbers() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2), &SourcePosition::new(2, 0, 2),
Token::Int(-4) Token::Scalar(ScalarToken::Int("-4"))
) )
); );
@ -384,7 +383,7 @@ fn numbers() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
Token::Int(9) Token::Scalar(ScalarToken::Int("9"))
) )
); );
@ -393,7 +392,7 @@ fn numbers() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
Token::Int(0) Token::Scalar(ScalarToken::Int("0"))
) )
); );
@ -401,77 +400,77 @@ fn numbers() {
"-4.123", "-4.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
-4.123, "-4.123",
); );
assert_float_token_eq( assert_float_token_eq(
"0.123", "0.123",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
0.123, "0.123",
); );
assert_float_token_eq( assert_float_token_eq(
"123e4", "123e4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
123e4, "123e4",
); );
assert_float_token_eq( assert_float_token_eq(
"123E4", "123E4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5), SourcePosition::new(5, 0, 5),
123e4, "123E4",
); );
assert_float_token_eq( assert_float_token_eq(
"123e-4", "123e-4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
123e-4, "123e-4",
); );
assert_float_token_eq( assert_float_token_eq(
"123e+4", "123e+4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6), SourcePosition::new(6, 0, 6),
123e4, "123e+4",
); );
assert_float_token_eq( assert_float_token_eq(
"-1.123e4", "-1.123e4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8), SourcePosition::new(8, 0, 8),
-1.123e4, "-1.123e4",
); );
assert_float_token_eq( assert_float_token_eq(
"-1.123E4", "-1.123E4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8), SourcePosition::new(8, 0, 8),
-1.123e4, "-1.123E4",
); );
assert_float_token_eq( assert_float_token_eq(
"-1.123e-4", "-1.123e-4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e-4, "-1.123e-4",
); );
assert_float_token_eq( assert_float_token_eq(
"-1.123e+4", "-1.123e+4",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e4, "-1.123e+4",
); );
assert_float_token_eq( assert_float_token_eq(
"-1.123e45", "-1.123e45",
SourcePosition::new(0, 0, 0), SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9), SourcePosition::new(9, 0, 9),
-1.123e45, "-1.123e45",
); );
} }
@ -653,19 +652,19 @@ fn punctuation_error() {
fn display() { fn display() {
assert_eq!(format!("{}", Token::Name("identifier")), "identifier"); assert_eq!(format!("{}", Token::Name("identifier")), "identifier");
assert_eq!(format!("{}", Token::Int(123)), "123"); assert_eq!(format!("{}", Token::Scalar(ScalarToken::Int("123"))), "123");
assert_eq!(format!("{}", Token::Float(4.5)), "4.5"); assert_eq!(format!("{}", Token::Scalar(ScalarToken::Float("4.5"))), "4.5");
assert_eq!( assert_eq!(
format!("{}", Token::String("some string".to_owned())), format!("{}", Token::Scalar(ScalarToken::String("some string"))),
"\"some string\"" "\"some string\""
); );
assert_eq!( assert_eq!(
format!( format!(
"{}", "{}",
Token::String("string with \\ escape and \" quote".to_owned()) Token::Scalar(ScalarToken::String("string with \\ escape and \" quote"))
), ),
"\"string with \\\\ escape and \\\" quote\"" "\"string with \\\\ escape and \\\" quote\""
); );

View file

@ -1,60 +1,121 @@
use indexmap::IndexMap; use indexmap::IndexMap;
use ast::InputValue; use ast::{FromInputValue, InputValue, Type};
use parser::value::parse_value_literal; use parser::value::parse_value_literal;
use parser::{Lexer, Parser, SourcePosition, Spanning}; use parser::{Lexer, Parser, SourcePosition, Spanning};
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
fn parse_value(s: &str) -> Spanning<InputValue> { use schema::meta::{MetaType, ScalarMeta, EnumMeta, EnumValue, InputObjectMeta, Argument};
use schema::model::SchemaType;
use types::scalars::EmptyMutation;
#[derive(GraphQLEnum)]
enum Enum {
EnumValue
}
#[derive(GraphQLInputObject)]
struct Bar {
foo: String,
}
#[derive(GraphQLInputObject)]
struct Foo {
key: i32,
other: Bar,
}
struct Query;
graphql_object!(Query: () where Scalar = <S> |&self| {
field int_field() -> i32 {
42
}
field float_field() -> f64 {
3.14
}
field string_field() -> String {
"".into()
}
field bool_field() -> bool {
true
}
field enum_field(_foo: Foo) -> Enum {
Enum::EnumValue
}
});
fn scalar_meta<T>(name: &'static str) -> MetaType<DefaultScalarValue>
where
T: FromInputValue<DefaultScalarValue> + ParseScalarValue<DefaultScalarValue> + 'static,
{
MetaType::Scalar(ScalarMeta::new::<T>(name.into()))
}
fn parse_value<S>(s: &str, meta: &MetaType<S>) -> Spanning<InputValue<S>>
where
S: ScalarValue,
for<'a> &'a S: ScalarRefValue<'a>,
{
let mut lexer = Lexer::new(s); 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));
let schema = SchemaType::new::<Query, EmptyMutation<()>>(&(), &());
parse_value_literal(&mut parser, false).expect(&format!("Parse error on input {:#?}", s)) parse_value_literal(&mut parser, false, &schema, Some(meta))
.expect(&format!("Parse error on input {:#?}", s))
} }
#[test] #[test]
fn input_value_literals() { fn input_value_literals() {
assert_eq!( assert_eq!(
parse_value("123"), parse_value::<DefaultScalarValue>("123", &scalar_meta::<i32>("Int")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(3, 0, 3), &SourcePosition::new(3, 0, 3),
InputValue::int(123) InputValue::scalar(123)
) )
); );
assert_eq!( assert_eq!(
parse_value("123.45"), parse_value::<DefaultScalarValue>("123.45", &scalar_meta::<f64>("Float")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(6, 0, 6), &SourcePosition::new(6, 0, 6),
InputValue::float(123.45) InputValue::scalar(123.45)
) )
); );
assert_eq!( assert_eq!(
parse_value("true"), parse_value::<DefaultScalarValue>("true", &scalar_meta::<bool>("Bool")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(4, 0, 4), &SourcePosition::new(4, 0, 4),
InputValue::boolean(true) InputValue::scalar(true)
) )
); );
assert_eq!( assert_eq!(
parse_value("false"), parse_value::<DefaultScalarValue>("false", &scalar_meta::<bool>("Bool")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(5, 0, 5), &SourcePosition::new(5, 0, 5),
InputValue::boolean(false) InputValue::scalar(false)
) )
); );
assert_eq!( assert_eq!(
parse_value(r#""test""#), parse_value::<DefaultScalarValue>(r#""test""#, &scalar_meta::<String>("String")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(6, 0, 6), &SourcePosition::new(6, 0, 6),
InputValue::string("test") InputValue::scalar("test")
) )
); );
let values = &[EnumValue::new("enum_value")];
let e: EnumMeta<DefaultScalarValue> = EnumMeta::new::<Enum>("TestEnum".into(), values);
assert_eq!( assert_eq!(
parse_value("enum_value"), parse_value::<DefaultScalarValue>("enum_value", &MetaType::Enum(e)),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(10, 0, 10), &SourcePosition::new(10, 0, 10),
@ -62,7 +123,7 @@ fn input_value_literals() {
) )
); );
assert_eq!( assert_eq!(
parse_value("$variable"), parse_value::<DefaultScalarValue>("$variable", &scalar_meta::<i32>("Int")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(9, 0, 9), &SourcePosition::new(9, 0, 9),
@ -70,7 +131,7 @@ fn input_value_literals() {
) )
); );
assert_eq!( assert_eq!(
parse_value("[]"), parse_value::<DefaultScalarValue>("[]", &scalar_meta::<i32>("Int")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2), &SourcePosition::new(2, 0, 2),
@ -78,7 +139,7 @@ fn input_value_literals() {
) )
); );
assert_eq!( assert_eq!(
parse_value("[1, [2, 3]]"), parse_value::<DefaultScalarValue>("[1, [2, 3]]", &scalar_meta::<i32>("Int")),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(11, 0, 11), &SourcePosition::new(11, 0, 11),
@ -86,7 +147,7 @@ fn input_value_literals() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(1, 0, 1), &SourcePosition::new(1, 0, 1),
&SourcePosition::new(2, 0, 2), &SourcePosition::new(2, 0, 2),
InputValue::int(1), InputValue::scalar(1),
), ),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(4, 0, 4), &SourcePosition::new(4, 0, 4),
@ -95,28 +156,35 @@ fn input_value_literals() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(5, 0, 5), &SourcePosition::new(5, 0, 5),
&SourcePosition::new(6, 0, 6), &SourcePosition::new(6, 0, 6),
InputValue::int(2), InputValue::scalar(2),
), ),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(8, 0, 8), &SourcePosition::new(8, 0, 8),
&SourcePosition::new(9, 0, 9), &SourcePosition::new(9, 0, 9),
InputValue::int(3), InputValue::scalar(3),
), ),
]), ]),
), ),
]) ])
) )
); );
let fields = [ Argument::new("key", Type::NonNullNamed("Int".into())),
Argument::new("other", Type::NonNullNamed("Bar".into()))];
let meta = &MetaType::InputObject(InputObjectMeta::new::<Foo>("foo".into(), &fields));
assert_eq!( assert_eq!(
parse_value("{}"), parse_value::<DefaultScalarValue>("{}", meta),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2), &SourcePosition::new(2, 0, 2),
InputValue::object(IndexMap::<String, InputValue>::new()) InputValue::object(IndexMap::<String, InputValue<DefaultScalarValue>>::new())
) )
); );
assert_eq!( assert_eq!(
parse_value(r#"{key: 123, other: {foo: "bar"}}"#), parse_value::<DefaultScalarValue>(
r#"{key: 123, other: {foo: "bar"}}"#,
meta
),
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(0, 0, 0), &SourcePosition::new(0, 0, 0),
&SourcePosition::new(31, 0, 31), &SourcePosition::new(31, 0, 31),
@ -130,7 +198,7 @@ fn input_value_literals() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(6, 0, 6), &SourcePosition::new(6, 0, 6),
&SourcePosition::new(9, 0, 9), &SourcePosition::new(9, 0, 9),
InputValue::int(123), InputValue::scalar(123),
), ),
), ),
( (
@ -151,7 +219,7 @@ fn input_value_literals() {
Spanning::start_end( Spanning::start_end(
&SourcePosition::new(24, 0, 24), &SourcePosition::new(24, 0, 24),
&SourcePosition::new(29, 0, 29), &SourcePosition::new(29, 0, 29),
InputValue::string("bar"), InputValue::scalar("bar"),
), ),
)]), )]),
), ),

View file

@ -1,5 +1,4 @@
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher};
/// A reference to a line and column in an input source file /// A reference to a line and column in an input source file
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
@ -14,8 +13,8 @@ pub struct SourcePosition {
/// A "span" is a range of characters in the input source, starting at the /// A "span" is a range of characters in the input source, starting at the
/// character pointed by the `start` field and ending just before the `end` /// character pointed by the `start` field and ending just before the `end`
/// marker. /// marker.
#[derive(Debug)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct Spanning<T: fmt::Debug> { pub struct Spanning<T> {
/// The wrapped item /// The wrapped item
pub item: T, pub item: T,
@ -28,7 +27,7 @@ pub struct Spanning<T: fmt::Debug> {
pub end: SourcePosition, pub end: SourcePosition,
} }
impl<T: fmt::Debug> Spanning<T> { impl<T> Spanning<T> {
#[doc(hidden)] #[doc(hidden)]
pub fn zero_width(pos: &SourcePosition, item: T) -> Spanning<T> { pub fn zero_width(pos: &SourcePosition, item: T) -> Spanning<T> {
Spanning { Spanning {
@ -94,45 +93,6 @@ impl<T: fmt::Debug> Spanning<T> {
} }
} }
impl<T> Clone for Spanning<T>
where
T: Clone + fmt::Debug,
{
fn clone(&self) -> Self {
Spanning {
start: self.start.clone(),
end: self.end.clone(),
item: self.item.clone(),
}
}
}
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
}
}
impl<T> Eq for Spanning<T>
where
T: Eq + 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);
self.item.hash(state);
}
}
impl SourcePosition { impl SourcePosition {
#[doc(hidden)] #[doc(hidden)]
pub fn new(index: usize, line: usize, col: usize) -> SourcePosition { pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {

View file

@ -1,99 +1,176 @@
use ast::InputValue; use ast::InputValue;
use parser::{ParseError, ParseResult, Parser, Spanning, Token}; use parser::{ParseError, ParseResult, Parser, ScalarToken, SourcePosition, Spanning, Token};
use schema::meta::{InputObjectMeta, MetaType};
use schema::model::SchemaType;
use value::ScalarValue;
pub fn parse_value_literal<'a>( pub fn parse_value_literal<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
is_const: bool, is_const: bool,
) -> ParseResult<'a, InputValue> { schema: &'b SchemaType<'b, S>,
match *parser.peek() { tpe: Option<&MetaType<'b, S>>,
Spanning { ) -> ParseResult<'a, InputValue<S>>
item: Token::BracketOpen, where
.. S: ScalarValue,
} => parse_list_literal(parser, is_const), {
Spanning { match (parser.peek(), tpe) {
item: Token::CurlyOpen, (
.. &Spanning {
} => parse_object_literal(parser, is_const), item: Token::BracketOpen,
Spanning { ..
item: Token::Dollar, },
.. _,
} if !is_const => ) => parse_list_literal(parser, is_const, schema, tpe),
(
&Spanning {
item: Token::CurlyOpen,
..
},
None,
) => parse_object_literal(parser, is_const, schema, None),
(
&Spanning {
item: Token::CurlyOpen,
..
},
Some(&MetaType::InputObject(ref o)),
) => parse_object_literal(parser, is_const, schema, Some(o)),
(
&Spanning {
item: Token::Dollar,
..
},
_,
)
if !is_const =>
{ {
parse_variable_literal(parser) parse_variable_literal(parser)
} }
Spanning { (
item: Token::Int(i), &Spanning {
.. item: Token::Scalar(_),
} => Ok(parser.next()?.map(|_| InputValue::int(i))), ..
Spanning { },
item: Token::Float(f), Some(&MetaType::Scalar(ref s)),
.. ) => {
} => Ok(parser.next()?.map(|_| InputValue::float(f))), if let Spanning {
Spanning { item: Token::Scalar(scalar),
item: Token::String(_), start,
.. end,
} => Ok(parser.next()?.map(|t| { } = parser.next()?
if let Token::String(s) = t { {
InputValue::string(s) (s.parse_fn)(scalar)
.map(|s| Spanning::start_end(&start, &end, InputValue::Scalar(s)))
.or_else(|_| parse_scalar_literal_by_infered_type(scalar, &start, &end, schema))
} else { } else {
panic!("Internal parser error"); unreachable!()
} }
})), }
Spanning { (
item: Token::Name("true"), &Spanning {
.. item: Token::Scalar(_),
} => Ok(parser.next()?.map(|_| InputValue::boolean(true))), ..
Spanning { },
item: Token::Name("false"), _,
.. ) => {
} => Ok(parser.next()?.map(|_| InputValue::boolean(false))), if let Spanning {
Spanning { item: Token::Scalar(token),
item: Token::Name("null"), start,
.. end,
} => Ok(parser.next()?.map(|_| InputValue::null())), } = parser.next()?
Spanning { {
item: Token::Name(name), parse_scalar_literal_by_infered_type(token, &start, &end, schema)
.. } else {
} => Ok(parser unreachable!()
}
}
(
&Spanning {
item: Token::Name("true"),
..
},
_,
) => Ok(parser.next()?.map(|_| InputValue::scalar(true))),
(
&Spanning {
item: Token::Name("false"),
..
},
_,
) => Ok(parser.next()?.map(|_| InputValue::scalar(false))),
(
&Spanning {
item: Token::Name("null"),
..
},
_,
) => Ok(parser.next()?.map(|_| InputValue::null())),
(
&Spanning {
item: Token::Name(name),
..
},
_,
) => Ok(parser
.next()? .next()?
.map(|_| InputValue::enum_value(name.to_owned()))), .map(|_| InputValue::enum_value(name.to_owned()))),
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)), _ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
} }
} }
fn parse_list_literal<'a>(parser: &mut Parser<'a>, is_const: bool) -> ParseResult<'a, InputValue> { fn parse_list_literal<'a, 'b, S>(
parser: &mut Parser<'a>,
is_const: bool,
schema: &'b SchemaType<'b, S>,
tpe: Option<&MetaType<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
S: ScalarValue,
{
Ok(parser Ok(parser
.delimited_list( .delimited_list(
&Token::BracketOpen, &Token::BracketOpen,
|p| parse_value_literal(p, is_const), |p| parse_value_literal(p, is_const, schema, tpe),
&Token::BracketClose, &Token::BracketClose,
)? )?.map(InputValue::parsed_list))
.map(InputValue::parsed_list))
} }
fn parse_object_literal<'a>( fn parse_object_literal<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
is_const: bool, is_const: bool,
) -> ParseResult<'a, InputValue> { schema: &'b SchemaType<'b, S>,
object_tpe: Option<&InputObjectMeta<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
S: ScalarValue,
{
Ok(parser Ok(parser
.delimited_list( .delimited_list(
&Token::CurlyOpen, &Token::CurlyOpen,
|p| parse_object_field(p, is_const), |p| parse_object_field(p, is_const, schema, object_tpe),
&Token::CurlyClose, &Token::CurlyClose,
)? )?.map(|items| InputValue::parsed_object(items.into_iter().map(|s| s.item).collect())))
.map(|items| InputValue::parsed_object(items.into_iter().map(|s| s.item).collect())))
} }
fn parse_object_field<'a>( fn parse_object_field<'a, 'b, S>(
parser: &mut Parser<'a>, parser: &mut Parser<'a>,
is_const: bool, is_const: bool,
) -> ParseResult<'a, (Spanning<String>, Spanning<InputValue>)> { schema: &'b SchemaType<'b, S>,
object_meta: Option<&InputObjectMeta<'b, S>>,
) -> ParseResult<'a, (Spanning<String>, Spanning<InputValue<S>>)>
where
S: ScalarValue,
{
let key = parser.expect_name()?; let key = parser.expect_name()?;
let tpe = object_meta
.and_then(|o| o.input_fields.iter().find(|f| f.name == key.item))
.and_then(|f| schema.lookup_type(&f.arg_type));
parser.expect(&Token::Colon)?; parser.expect(&Token::Colon)?;
let value = parse_value_literal(parser, is_const)?; let value = parse_value_literal(parser, is_const, schema, tpe)?;
Ok(Spanning::start_end( Ok(Spanning::start_end(
&key.start.clone(), &key.start.clone(),
@ -102,7 +179,10 @@ fn parse_object_field<'a>(
)) ))
} }
fn parse_variable_literal<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, InputValue> { fn parse_variable_literal<'a, S>(parser: &mut Parser<'a>) -> ParseResult<'a, InputValue<S>>
where
S: ScalarValue,
{
let Spanning { let Spanning {
start: start_pos, .. start: start_pos, ..
} = parser.expect(&Token::Dollar)?; } = parser.expect(&Token::Dollar)?;
@ -118,3 +198,43 @@ fn parse_variable_literal<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, InputV
InputValue::variable(name), InputValue::variable(name),
)) ))
} }
fn parse_scalar_literal_by_infered_type<'a, 'b, S>(
token: ScalarToken<'a>,
start: &SourcePosition,
end: &SourcePosition,
schema: &'b SchemaType<'b, S>,
) -> ParseResult<'a, InputValue<S>>
where
S: ScalarValue,
{
match token {
ScalarToken::String(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("String") {
(s.parse_fn)(token)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else {
panic!("There needs to be a String type")
}
}
ScalarToken::Int(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Int") {
(s.parse_fn)(token)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else {
panic!("There needs to be a Int type")
}
}
ScalarToken::Float(_) => {
if let Some(&MetaType::Scalar(ref s)) = schema.concrete_type_by_name("Float") {
(s.parse_fn)(token)
.map(|s| Spanning::start_end(start, end, InputValue::Scalar(s)))
.map_err(|e| Spanning::start_end(start, end, e))
} else {
panic!("There needs to be a Float type")
}
}
}
}

View file

@ -4,16 +4,19 @@ use std::borrow::Cow;
use std::fmt; use std::fmt;
use ast::{FromInputValue, InputValue, Type}; use ast::{FromInputValue, InputValue, Type};
use parser::{ParseError, ScalarToken};
use schema::model::SchemaType;
use types::base::TypeKind; use types::base::TypeKind;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
/// Scalar type metadata /// Scalar type metadata
pub struct ScalarMeta<'a> { pub struct ScalarMeta<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: Cow<'a, str>, pub name: Cow<'a, str>,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>, pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError<'b>>,
} }
/// List type metadata /// List type metadata
@ -32,38 +35,37 @@ pub struct NullableMeta<'a> {
/// Object type metadata /// Object type metadata
#[derive(Debug)] #[derive(Debug)]
pub struct ObjectMeta<'a> { pub struct ObjectMeta<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: Cow<'a, str>, pub name: Cow<'a, str>,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] #[doc(hidden)]
pub fields: Vec<Field<'a>>, pub fields: Vec<Field<'a, S>>,
#[doc(hidden)] #[doc(hidden)]
pub interface_names: Vec<String>, pub interface_names: Vec<String>,
} }
/// Enum type metadata /// Enum type metadata
pub struct EnumMeta<'a> { pub struct EnumMeta<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: Cow<'a, str>, pub name: Cow<'a, str>,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] #[doc(hidden)]
pub values: Vec<EnumValue>, pub values: Vec<EnumValue>,
#[doc(hidden)] pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>,
} }
/// Interface type metadata /// Interface type metadata
#[derive(Debug)] #[derive(Debug)]
pub struct InterfaceMeta<'a> { pub struct InterfaceMeta<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: Cow<'a, str>, pub name: Cow<'a, str>,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] #[doc(hidden)]
pub fields: Vec<Field<'a>>, pub fields: Vec<Field<'a, S>>,
} }
/// Union type metadata /// Union type metadata
@ -78,15 +80,14 @@ pub struct UnionMeta<'a> {
} }
/// Input object metadata /// Input object metadata
pub struct InputObjectMeta<'a> { pub struct InputObjectMeta<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: Cow<'a, str>, pub name: Cow<'a, str>,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] #[doc(hidden)]
pub input_fields: Vec<Argument<'a>>, pub input_fields: Vec<Argument<'a, S>>,
#[doc(hidden)] pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>,
} }
/// A placeholder for not-yet-registered types /// A placeholder for not-yet-registered types
@ -101,36 +102,36 @@ pub struct PlaceholderMeta<'a> {
/// Generic type metadata /// Generic type metadata
#[derive(Debug)] #[derive(Debug)]
pub enum MetaType<'a> { pub enum MetaType<'a, S = DefaultScalarValue> {
#[doc(hidden)] #[doc(hidden)]
Scalar(ScalarMeta<'a>), Scalar(ScalarMeta<'a, S>),
#[doc(hidden)] #[doc(hidden)]
List(ListMeta<'a>), List(ListMeta<'a>),
#[doc(hidden)] #[doc(hidden)]
Nullable(NullableMeta<'a>), Nullable(NullableMeta<'a>),
#[doc(hidden)] #[doc(hidden)]
Object(ObjectMeta<'a>), Object(ObjectMeta<'a, S>),
#[doc(hidden)] #[doc(hidden)]
Enum(EnumMeta<'a>), Enum(EnumMeta<'a, S>),
#[doc(hidden)] #[doc(hidden)]
Interface(InterfaceMeta<'a>), Interface(InterfaceMeta<'a, S>),
#[doc(hidden)] #[doc(hidden)]
Union(UnionMeta<'a>), Union(UnionMeta<'a>),
#[doc(hidden)] #[doc(hidden)]
InputObject(InputObjectMeta<'a>), InputObject(InputObjectMeta<'a, S>),
#[doc(hidden)] #[doc(hidden)]
Placeholder(PlaceholderMeta<'a>), Placeholder(PlaceholderMeta<'a>),
} }
/// Metadata for a field /// Metadata for a field
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Field<'a> { pub struct Field<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: String, pub name: String,
#[doc(hidden)] #[doc(hidden)]
pub description: Option<String>, pub description: Option<String>,
#[doc(hidden)] #[doc(hidden)]
pub arguments: Option<Vec<Argument<'a>>>, pub arguments: Option<Vec<Argument<'a, S>>>,
#[doc(hidden)] #[doc(hidden)]
pub field_type: Type<'a>, pub field_type: Type<'a>,
#[doc(hidden)] #[doc(hidden)]
@ -139,7 +140,7 @@ pub struct Field<'a> {
/// Metadata for an argument to a field /// Metadata for an argument to a field
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Argument<'a> { pub struct Argument<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub name: String, pub name: String,
#[doc(hidden)] #[doc(hidden)]
@ -147,7 +148,7 @@ pub struct Argument<'a> {
#[doc(hidden)] #[doc(hidden)]
pub arg_type: Type<'a>, pub arg_type: Type<'a>,
#[doc(hidden)] #[doc(hidden)]
pub default_value: Option<InputValue>, pub default_value: Option<InputValue<S>>,
} }
/// Metadata for a single value in an enum /// Metadata for a single value in an enum
@ -168,7 +169,7 @@ pub struct EnumValue {
pub deprecation_reason: Option<String>, pub deprecation_reason: Option<String>,
} }
impl<'a> MetaType<'a> { impl<'a, S> MetaType<'a, S> {
/// Access the name of the type, if applicable /// Access the name of the type, if applicable
/// ///
/// Lists, non-null wrappers, and placeholders don't have names. /// Lists, non-null wrappers, and placeholders don't have names.
@ -232,7 +233,7 @@ impl<'a> MetaType<'a> {
/// Access a field's meta data given its name /// Access a field's meta data given its name
/// ///
/// Only objects and interfaces have fields. This method always returns `None` for other types. /// Only objects and interfaces have fields. This method always returns `None` for other types.
pub fn field_by_name(&self, name: &str) -> Option<&Field> { pub fn field_by_name(&self, name: &str) -> Option<&Field<S>> {
match *self { match *self {
MetaType::Object(ObjectMeta { ref fields, .. }) MetaType::Object(ObjectMeta { ref fields, .. })
| MetaType::Interface(InterfaceMeta { ref fields, .. }) => { | MetaType::Interface(InterfaceMeta { ref fields, .. }) => {
@ -245,7 +246,7 @@ impl<'a> MetaType<'a> {
/// Access an input field's meta data given its name /// Access an input field's meta data given its name
/// ///
/// Only input objects have input fields. This method always returns `None` for other types. /// Only input objects have input fields. This method always returns `None` for other types.
pub fn input_field_by_name(&self, name: &str) -> Option<&Argument> { pub fn input_field_by_name(&self, name: &str) -> Option<&Argument<S>> {
match *self { match *self {
MetaType::InputObject(InputObjectMeta { MetaType::InputObject(InputObjectMeta {
ref input_fields, .. ref input_fields, ..
@ -283,7 +284,7 @@ impl<'a> MetaType<'a> {
/// `true` if it can be parsed as the provided type. /// `true` if it can be parsed as the provided type.
/// ///
/// Only scalars, enums, and input objects have parse functions. /// Only scalars, enums, and input objects have parse functions.
pub fn input_value_parse_fn(&self) -> Option<&Box<Fn(&InputValue) -> bool + Send + Sync>> { pub fn input_value_parse_fn(&self) -> Option<for<'b> fn(&'b InputValue<S>) -> bool> {
match *self { match *self {
MetaType::Scalar(ScalarMeta { MetaType::Scalar(ScalarMeta {
ref try_parse_fn, .. ref try_parse_fn, ..
@ -293,7 +294,7 @@ impl<'a> MetaType<'a> {
}) })
| MetaType::InputObject(InputObjectMeta { | MetaType::InputObject(InputObjectMeta {
ref try_parse_fn, .. ref try_parse_fn, ..
}) => Some(try_parse_fn), }) => Some(*try_parse_fn),
_ => None, _ => None,
} }
} }
@ -337,30 +338,54 @@ impl<'a> MetaType<'a> {
_ => false, _ => false,
} }
} }
pub(crate) fn fields<'b>(&self, schema: &'b SchemaType<S>) -> Option<Vec<&'b Field<'b, S>>> {
schema.lookup_type(&self.as_type()).and_then(|tpe| {
match *tpe {
MetaType::Interface(ref i) => Some(i.fields.iter().collect()),
MetaType::Object(ref o) => Some(o.fields.iter().collect()),
MetaType::Union(ref u) => Some(
u.of_type_names
.iter()
.filter_map(|n| schema.concrete_type_by_name(n))
.filter_map(|t| t.fields(schema))
.flat_map(|f| f)
.collect(),
),
_ => None,
}
})
}
} }
impl<'a> ScalarMeta<'a> { impl<'a, S> ScalarMeta<'a, S>
where
S: ScalarValue + 'a,
{
/// Build a new scalar type metadata with the specified name /// Build a new scalar type metadata with the specified name
pub fn new<T: FromInputValue>(name: Cow<'a, str>) -> ScalarMeta<'a> { pub fn new<T>(name: Cow<'a, str>) -> Self
where
T: FromInputValue<S> + ParseScalarValue<S> + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
{
ScalarMeta { ScalarMeta {
name: name, name: name,
description: None, description: None,
try_parse_fn: Box::new(|v: &InputValue| { try_parse_fn: try_parse_fn::<S, T>,
<T as FromInputValue>::from_input_value(v).is_some() parse_fn: <T as ParseScalarValue<S>>::from_str,
}),
} }
} }
/// Set the description for the given scalar type /// Set the description for the given scalar type
/// ///
/// If a description already was set prior to calling this method, it will be overwritten. /// If a description already was set prior to calling this method, it will be overwritten.
pub fn description(mut self, description: &str) -> ScalarMeta<'a> { pub fn description(mut self, description: &str) -> ScalarMeta<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
/// Wrap the scalar in a generic meta type /// Wrap the scalar in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Scalar(self) MetaType::Scalar(self)
} }
} }
@ -372,7 +397,7 @@ impl<'a> ListMeta<'a> {
} }
/// Wrap the list in a generic meta type /// Wrap the list in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::List(self) MetaType::List(self)
} }
} }
@ -384,14 +409,17 @@ impl<'a> NullableMeta<'a> {
} }
/// Wrap the nullable type in a generic meta type /// Wrap the nullable type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::Nullable(self) MetaType::Nullable(self)
} }
} }
impl<'a> ObjectMeta<'a> { impl<'a, S> ObjectMeta<'a, S>
where
S: ScalarValue,
{
/// Build a new object type with the specified name and fields /// Build a new object type with the specified name and fields
pub fn new(name: Cow<'a, str>, fields: &[Field<'a>]) -> ObjectMeta<'a> { pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> Self {
ObjectMeta { ObjectMeta {
name: name, name: name,
description: None, description: None,
@ -403,7 +431,7 @@ impl<'a> ObjectMeta<'a> {
/// Set the description for the object /// Set the description for the object
/// ///
/// If a description was provided prior to calling this method, it will be overwritten. /// If a description was provided prior to calling this method, it will be overwritten.
pub fn description(mut self, description: &str) -> ObjectMeta<'a> { pub fn description(mut self, description: &str) -> ObjectMeta<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
@ -412,7 +440,7 @@ impl<'a> ObjectMeta<'a> {
/// ///
/// If a list of interfaces already was provided prior to calling this method, they will be /// If a list of interfaces already was provided prior to calling this method, they will be
/// overwritten. /// overwritten.
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a> { pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a, S> {
self.interface_names = interfaces self.interface_names = interfaces
.iter() .iter()
.map(|t| t.innermost_name().to_owned()) .map(|t| t.innermost_name().to_owned())
@ -421,41 +449,49 @@ impl<'a> ObjectMeta<'a> {
} }
/// Wrap this object type in a generic meta type /// Wrap this object type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Object(self) MetaType::Object(self)
} }
} }
impl<'a> EnumMeta<'a> { impl<'a, S> EnumMeta<'a, S>
where
S: ScalarValue + 'a,
{
/// Build a new enum type with the specified name and possible values /// Build a new enum type with the specified name and possible values
pub fn new<T: FromInputValue>(name: Cow<'a, str>, values: &[EnumValue]) -> EnumMeta<'a> { pub fn new<T>(name: Cow<'a, str>, values: &[EnumValue]) -> Self
where
T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
EnumMeta { EnumMeta {
name: name, name: name,
description: None, description: None,
values: values.to_vec(), values: values.to_vec(),
try_parse_fn: Box::new(|v: &InputValue| { try_parse_fn: try_parse_fn::<S, T>,
<T as FromInputValue>::from_input_value(v).is_some()
}),
} }
} }
/// Set the description of the type /// Set the description of the type
/// ///
/// If a description was provided prior to calling this method, it will be overwritten /// If a description was provided prior to calling this method, it will be overwritten
pub fn description(mut self, description: &str) -> EnumMeta<'a> { pub fn description(mut self, description: &str) -> EnumMeta<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
/// Wrap this enum type in a generic meta type /// Wrap this enum type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Enum(self) MetaType::Enum(self)
} }
} }
impl<'a> InterfaceMeta<'a> { impl<'a, S> InterfaceMeta<'a, S>
where
S: ScalarValue,
{
/// Build a new interface type with the specified name and fields /// Build a new interface type with the specified name and fields
pub fn new(name: Cow<'a, str>, fields: &[Field<'a>]) -> InterfaceMeta<'a> { pub fn new(name: Cow<'a, str>, fields: &[Field<'a, S>]) -> InterfaceMeta<'a, S> {
InterfaceMeta { InterfaceMeta {
name: name, name: name,
description: None, description: None,
@ -466,13 +502,13 @@ impl<'a> InterfaceMeta<'a> {
/// Set the description of the type /// Set the description of the type
/// ///
/// If a description was provided prior to calling this method, it will be overwritten. /// If a description was provided prior to calling this method, it will be overwritten.
pub fn description(mut self, description: &str) -> InterfaceMeta<'a> { pub fn description(mut self, description: &str) -> InterfaceMeta<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
/// Wrap this interface type in a generic meta type /// Wrap this interface type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::Interface(self) MetaType::Interface(self)
} }
} }
@ -499,46 +535,47 @@ impl<'a> UnionMeta<'a> {
} }
/// Wrap this union type in a generic meta type /// Wrap this union type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta<S>(self) -> MetaType<'a, S> {
MetaType::Union(self) MetaType::Union(self)
} }
} }
impl<'a> InputObjectMeta<'a> { impl<'a, S> InputObjectMeta<'a, S>
where
S: ScalarValue,
{
/// Build a new input type with the specified name and input fields /// Build a new input type with the specified name and input fields
pub fn new<T: FromInputValue>( pub fn new<T: FromInputValue<S>>(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self
name: Cow<'a, str>, where
input_fields: &[Argument<'a>], for<'b> &'b S: ScalarRefValue<'b>,
) -> InputObjectMeta<'a> { {
InputObjectMeta { InputObjectMeta {
name: name, name: name,
description: None, description: None,
input_fields: input_fields.to_vec(), input_fields: input_fields.to_vec(),
try_parse_fn: Box::new(|v: &InputValue| { try_parse_fn: try_parse_fn::<S, T>,
<T as FromInputValue>::from_input_value(v).is_some()
}),
} }
} }
/// Set the description of the type /// Set the description of the type
/// ///
/// If a description was provided prior to calling this method, it will be overwritten. /// If a description was provided prior to calling this method, it will be overwritten.
pub fn description(mut self, description: &str) -> InputObjectMeta<'a> { pub fn description(mut self, description: &str) -> InputObjectMeta<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
/// Wrap this union type in a generic meta type /// Wrap this union type in a generic meta type
pub fn into_meta(self) -> MetaType<'a> { pub fn into_meta(self) -> MetaType<'a, S> {
MetaType::InputObject(self) MetaType::InputObject(self)
} }
} }
impl<'a> Field<'a> { impl<'a, S> Field<'a, S> {
/// Set the description of the field /// Set the description of the field
/// ///
/// This overwrites the description if any was previously set. /// This overwrites the description if any was previously set.
pub fn description(mut self, description: &str) -> Field<'a> { pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
@ -546,7 +583,7 @@ impl<'a> Field<'a> {
/// Add an argument to the field /// Add an argument to the field
/// ///
/// Arguments are unordered and can't contain duplicates by name. /// Arguments are unordered and can't contain duplicates by name.
pub fn argument(mut self, argument: Argument<'a>) -> Field<'a> { pub fn argument(mut self, argument: Argument<'a, S>) -> Self {
match self.arguments { match self.arguments {
None => { None => {
self.arguments = Some(vec![argument]); self.arguments = Some(vec![argument]);
@ -562,15 +599,15 @@ impl<'a> Field<'a> {
/// Set the deprecation reason /// Set the deprecation reason
/// ///
/// This overwrites the deprecation reason if any was previously set. /// This overwrites the deprecation reason if any was previously set.
pub fn deprecated(mut self, reason: &str) -> Field<'a> { pub fn deprecated(mut self, reason: &str) -> Self {
self.deprecation_reason = Some(reason.to_owned()); self.deprecation_reason = Some(reason.to_owned());
self self
} }
} }
impl<'a> Argument<'a> { impl<'a, S> Argument<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub fn new(name: &str, arg_type: Type<'a>) -> Argument<'a> { pub fn new(name: &str, arg_type: Type<'a>) -> Self {
Argument { Argument {
name: name.to_owned(), name: name.to_owned(),
description: None, description: None,
@ -582,7 +619,7 @@ impl<'a> Argument<'a> {
/// Set the description of the argument /// Set the description of the argument
/// ///
/// This overwrites the description if any was previously set. /// This overwrites the description if any was previously set.
pub fn description(mut self, description: &str) -> Argument<'a> { pub fn description(mut self, description: &str) -> Self {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
@ -590,7 +627,7 @@ impl<'a> Argument<'a> {
/// Set the default value of the argument /// Set the default value of the argument
/// ///
/// This overwrites the description if any was previously set. /// This overwrites the description if any was previously set.
pub fn default_value(mut self, default_value: InputValue) -> Argument<'a> { pub fn default_value(mut self, default_value: InputValue<S>) -> Self {
self.default_value = Some(default_value); self.default_value = Some(default_value);
self self
} }
@ -623,7 +660,7 @@ impl EnumValue {
} }
} }
impl<'a> fmt::Debug for ScalarMeta<'a> { impl<'a, S: fmt::Debug> fmt::Debug for ScalarMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("ScalarMeta") fmt.debug_struct("ScalarMeta")
.field("name", &self.name) .field("name", &self.name)
@ -632,7 +669,7 @@ impl<'a> fmt::Debug for ScalarMeta<'a> {
} }
} }
impl<'a> fmt::Debug for EnumMeta<'a> { impl<'a, S: fmt::Debug> fmt::Debug for EnumMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("EnumMeta") fmt.debug_struct("EnumMeta")
.field("name", &self.name) .field("name", &self.name)
@ -642,7 +679,7 @@ impl<'a> fmt::Debug for EnumMeta<'a> {
} }
} }
impl<'a> fmt::Debug for InputObjectMeta<'a> { impl<'a, S: fmt::Debug> fmt::Debug for InputObjectMeta<'a, S> {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.debug_struct("InputObjectMeta") fmt.debug_struct("InputObjectMeta")
.field("name", &self.name) .field("name", &self.name)
@ -651,3 +688,11 @@ impl<'a> fmt::Debug for InputObjectMeta<'a> {
.finish() .finish()
} }
} }
fn try_parse_fn<S, T>(v: &InputValue<S>) -> bool
where
T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
<T as FromInputValue<S>>::from_input_value(v).is_some()
}

View file

@ -7,12 +7,18 @@ use executor::{Context, Registry};
use schema::meta::{Argument, InterfaceMeta, MetaType, ObjectMeta, PlaceholderMeta, UnionMeta}; use schema::meta::{Argument, InterfaceMeta, MetaType, ObjectMeta, PlaceholderMeta, UnionMeta};
use types::base::GraphQLType; use types::base::GraphQLType;
use types::name::Name; use types::name::Name;
use value::{DefaultScalarValue, ScalarRefValue, ScalarValue};
/// Root query node of a schema /// Root query node of a schema
/// ///
/// This brings the mutation and query types together, and provides the /// This brings the mutation and query types together, and provides the
/// predefined metadata fields. /// predefined metadata fields.
pub struct RootNode<'a, QueryT: GraphQLType, MutationT: GraphQLType> { #[derive(Debug)]
pub struct RootNode<'a, QueryT: GraphQLType<S>, MutationT: GraphQLType<S>, S = DefaultScalarValue>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
#[doc(hidden)] #[doc(hidden)]
pub query_type: QueryT, pub query_type: QueryT,
#[doc(hidden)] #[doc(hidden)]
@ -22,35 +28,37 @@ pub struct RootNode<'a, QueryT: GraphQLType, MutationT: GraphQLType> {
#[doc(hidden)] #[doc(hidden)]
pub mutation_info: MutationT::TypeInfo, pub mutation_info: MutationT::TypeInfo,
#[doc(hidden)] #[doc(hidden)]
pub schema: SchemaType<'a>, pub schema: SchemaType<'a, S>,
} }
/// Metadata for a schema /// Metadata for a schema
pub struct SchemaType<'a> { #[derive(Debug)]
types: FnvHashMap<Name, MetaType<'a>>, pub struct SchemaType<'a, S> {
pub(crate) types: FnvHashMap<Name, MetaType<'a, S>>,
query_type_name: String, query_type_name: String,
mutation_type_name: Option<String>, mutation_type_name: Option<String>,
directives: FnvHashMap<String, DirectiveType<'a>>, directives: FnvHashMap<String, DirectiveType<'a, S>>,
} }
impl<'a> Context for SchemaType<'a> {} impl<'a, S> Context for SchemaType<'a, S> {}
#[derive(Clone)] #[derive(Clone)]
pub enum TypeType<'a> { pub enum TypeType<'a, S: 'a> {
Concrete(&'a MetaType<'a>), Concrete(&'a MetaType<'a, S>),
NonNull(Box<TypeType<'a>>), NonNull(Box<TypeType<'a, S>>),
List(Box<TypeType<'a>>), List(Box<TypeType<'a, S>>),
} }
pub struct DirectiveType<'a> { #[derive(Debug)]
pub struct DirectiveType<'a, S> {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub locations: Vec<DirectiveLocation>, pub locations: Vec<DirectiveLocation>,
pub arguments: Vec<Argument<'a>>, pub arguments: Vec<Argument<'a, S>>,
} }
#[derive(GraphQLEnum, Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug, GraphQLEnum)]
#[graphql(name = "__DirectiveLocation", _internal)] #[graphql(name = "__DirectiveLocation")]
pub enum DirectiveLocation { pub enum DirectiveLocation {
Query, Query,
Mutation, Mutation,
@ -63,24 +71,31 @@ pub enum DirectiveLocation {
InlineFragment, InlineFragment,
} }
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT> impl<'a, QueryT, MutationT, S> RootNode<'a, QueryT, MutationT, S>
where where
QueryT: GraphQLType<TypeInfo = ()>, S: ScalarValue + 'a,
MutationT: GraphQLType<TypeInfo = ()>, QueryT: GraphQLType<S, TypeInfo = ()>,
MutationT: GraphQLType<S, TypeInfo = ()>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
/// Construct a new root node from query and mutation nodes /// Construct a new root node from query and mutation nodes
/// ///
/// If the schema should not support mutations, use the /// If the schema should not support mutations, use the
/// `new` constructor instead. /// `new` constructor instead.
pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> RootNode<'a, QueryT, MutationT> { pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> Self
where
for<'b> &'b S: ScalarRefValue<'b>,
{
RootNode::new_with_info(query_obj, mutation_obj, (), ()) RootNode::new_with_info(query_obj, mutation_obj, (), ())
} }
} }
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT> impl<'a, S, QueryT, MutationT> RootNode<'a, QueryT, MutationT, S>
where where
QueryT: GraphQLType, QueryT: GraphQLType<S>,
MutationT: GraphQLType, MutationT: GraphQLType<S>,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
/// Construct a new root node from query and mutation nodes, /// Construct a new root node from query and mutation nodes,
/// while also providing type info objects for the query and /// while also providing type info objects for the query and
@ -90,7 +105,10 @@ where
mutation_obj: MutationT, mutation_obj: MutationT,
query_info: QueryT::TypeInfo, query_info: QueryT::TypeInfo,
mutation_info: MutationT::TypeInfo, mutation_info: MutationT::TypeInfo,
) -> RootNode<'a, QueryT, MutationT> { ) -> Self
where
for<'b> &'b S: ScalarRefValue<'b>,
{
RootNode { RootNode {
query_type: query_obj, query_type: query_obj,
mutation_type: mutation_obj, mutation_type: mutation_obj,
@ -101,14 +119,16 @@ where
} }
} }
impl<'a> SchemaType<'a> { impl<'a, S> SchemaType<'a, S> {
pub fn new<QueryT, MutationT>( pub fn new<QueryT, MutationT>(
query_info: &QueryT::TypeInfo, query_info: &QueryT::TypeInfo,
mutation_info: &MutationT::TypeInfo, mutation_info: &MutationT::TypeInfo,
) -> SchemaType<'a> ) -> Self
where where
QueryT: GraphQLType, S: ScalarValue + 'a,
MutationT: GraphQLType, QueryT: GraphQLType<S>,
MutationT: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let mut directives = FnvHashMap::default(); let mut directives = FnvHashMap::default();
let query_type_name: String; let query_type_name: String;
@ -119,12 +139,14 @@ impl<'a> SchemaType<'a> {
.get_type::<QueryT>(query_info) .get_type::<QueryT>(query_info)
.innermost_name() .innermost_name()
.to_owned(); .to_owned();
mutation_type_name = registry mutation_type_name = registry
.get_type::<MutationT>(mutation_info) .get_type::<MutationT>(mutation_info)
.innermost_name() .innermost_name()
.to_owned(); .to_owned();
registry.get_type::<SchemaType>(&()); registry.get_type::<SchemaType<S>>(&());
directives.insert("skip".to_owned(), DirectiveType::new_skip(&mut registry)); directives.insert("skip".to_owned(), DirectiveType::new_skip(&mut registry));
directives.insert( directives.insert(
"include".to_owned(), "include".to_owned(),
@ -132,9 +154,9 @@ impl<'a> SchemaType<'a> {
); );
let mut meta_fields = vec![ let mut meta_fields = vec![
registry.field::<SchemaType>("__schema", &()), registry.field::<SchemaType<S>>("__schema", &()),
registry registry
.field::<TypeType>("__type", &()) .field::<TypeType<S>>("__type", &())
.argument(registry.arg::<String>("name", &())), .argument(registry.arg::<String>("name", &())),
]; ];
@ -153,7 +175,6 @@ impl<'a> SchemaType<'a> {
panic!("Type {:?} is still a placeholder type", of_type); panic!("Type {:?} is still a placeholder type", of_type);
} }
} }
SchemaType { SchemaType {
types: registry.types, types: registry.types,
query_type_name: query_type_name, query_type_name: query_type_name,
@ -166,19 +187,28 @@ impl<'a> SchemaType<'a> {
} }
} }
pub fn add_directive(&mut self, directive: DirectiveType<'a>) { pub fn add_directive(&mut self, directive: DirectiveType<'a, S>) {
self.directives.insert(directive.name.clone(), directive); self.directives.insert(directive.name.clone(), directive);
} }
pub fn type_by_name(&self, name: &str) -> Option<TypeType> { pub fn type_by_name(&self, name: &str) -> Option<TypeType<S>> {
self.types.get(name).map(|t| TypeType::Concrete(t)) self.types.get(name).map(|t| TypeType::Concrete(t))
} }
pub fn concrete_type_by_name(&self, name: &str) -> Option<&MetaType> { pub fn concrete_type_by_name(&self, name: &str) -> Option<&MetaType<S>> {
self.types.get(name) self.types.get(name)
} }
pub fn query_type(&self) -> TypeType { pub(crate) fn lookup_type(&self, tpe: &Type) -> Option<&MetaType<S>> {
match *tpe {
Type::NonNullNamed(ref name) | Type::Named(ref name) => {
self.concrete_type_by_name(name)
}
Type::List(ref inner) | Type::NonNullList(ref inner) => self.lookup_type(inner),
}
}
pub fn query_type(&self) -> TypeType<S> {
TypeType::Concrete( TypeType::Concrete(
self.types self.types
.get(&self.query_type_name) .get(&self.query_type_name)
@ -186,13 +216,13 @@ impl<'a> SchemaType<'a> {
) )
} }
pub fn concrete_query_type(&self) -> &MetaType { pub fn concrete_query_type(&self) -> &MetaType<S> {
self.types self.types
.get(&self.query_type_name) .get(&self.query_type_name)
.expect("Query type does not exist in schema") .expect("Query type does not exist in schema")
} }
pub fn mutation_type(&self) -> Option<TypeType> { pub fn mutation_type(&self) -> Option<TypeType<S>> {
if let Some(ref mutation_type_name) = self.mutation_type_name { if let Some(ref mutation_type_name) = self.mutation_type_name {
Some( Some(
self.type_by_name(mutation_type_name) self.type_by_name(mutation_type_name)
@ -203,22 +233,22 @@ impl<'a> SchemaType<'a> {
} }
} }
pub fn concrete_mutation_type(&self) -> Option<&MetaType> { pub fn concrete_mutation_type(&self) -> Option<&MetaType<S>> {
self.mutation_type_name.as_ref().map(|name| { self.mutation_type_name.as_ref().map(|name| {
self.concrete_type_by_name(name) self.concrete_type_by_name(name)
.expect("Mutation type does not exist in schema") .expect("Mutation type does not exist in schema")
}) })
} }
pub fn type_list(&self) -> Vec<TypeType> { pub fn type_list(&self) -> Vec<TypeType<S>> {
self.types.values().map(|t| TypeType::Concrete(t)).collect() self.types.values().map(|t| TypeType::Concrete(t)).collect()
} }
pub fn concrete_type_list(&self) -> Vec<&MetaType> { pub fn concrete_type_list(&self) -> Vec<&MetaType<S>> {
self.types.values().collect() self.types.values().collect()
} }
pub fn make_type(&self, t: &Type) -> TypeType { pub fn make_type(&self, t: &Type) -> TypeType<S> {
match *t { match *t {
Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new( Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new(
self.type_by_name(n).expect("Type not found in schema"), self.type_by_name(n).expect("Type not found in schema"),
@ -231,21 +261,22 @@ impl<'a> SchemaType<'a> {
} }
} }
pub fn directive_list(&self) -> Vec<&DirectiveType> { pub fn directive_list(&self) -> Vec<&DirectiveType<S>> {
self.directives.values().collect() self.directives.values().collect()
} }
pub fn directive_by_name(&self, name: &str) -> Option<&DirectiveType> { pub fn directive_by_name(&self, name: &str) -> Option<&DirectiveType<S>> {
self.directives.get(name) self.directives.get(name)
} }
pub fn type_overlap(&self, t1: &MetaType, t2: &MetaType) -> bool { pub fn type_overlap(&self, t1: &MetaType<S>, t2: &MetaType<S>) -> bool {
if (t1 as *const MetaType) == (t2 as *const MetaType) { if (t1 as *const MetaType<S>) == (t2 as *const MetaType<S>) {
return true; return true;
} }
match (t1.is_abstract(), t2.is_abstract()) { match (t1.is_abstract(), t2.is_abstract()) {
(true, true) => self.possible_types(t1) (true, true) => self
.possible_types(t1)
.iter() .iter()
.any(|t| self.is_possible_type(t2, t)), .any(|t| self.is_possible_type(t2, t)),
(true, false) => self.is_possible_type(t1, t2), (true, false) => self.is_possible_type(t1, t2),
@ -254,7 +285,7 @@ impl<'a> SchemaType<'a> {
} }
} }
pub fn possible_types(&self, t: &MetaType) -> Vec<&MetaType> { pub fn possible_types(&self, t: &MetaType<S>) -> Vec<&MetaType<S>> {
match *t { match *t {
MetaType::Union(UnionMeta { MetaType::Union(UnionMeta {
ref of_type_names, .. ref of_type_names, ..
@ -262,7 +293,8 @@ impl<'a> SchemaType<'a> {
.iter() .iter()
.flat_map(|t| self.concrete_type_by_name(t)) .flat_map(|t| self.concrete_type_by_name(t))
.collect(), .collect(),
MetaType::Interface(InterfaceMeta { ref name, .. }) => self.concrete_type_list() MetaType::Interface(InterfaceMeta { ref name, .. }) => self
.concrete_type_list()
.into_iter() .into_iter()
.filter(|t| match **t { .filter(|t| match **t {
MetaType::Object(ObjectMeta { MetaType::Object(ObjectMeta {
@ -270,16 +302,19 @@ impl<'a> SchemaType<'a> {
.. ..
}) => interface_names.iter().any(|iname| iname == name), }) => interface_names.iter().any(|iname| iname == name),
_ => false, _ => false,
}) }).collect(),
.collect(),
_ => panic!("Can't retrieve possible types from non-abstract meta type"), _ => panic!("Can't retrieve possible types from non-abstract meta type"),
} }
} }
pub fn is_possible_type(&self, abstract_type: &MetaType, possible_type: &MetaType) -> bool { pub fn is_possible_type(
&self,
abstract_type: &MetaType<S>,
possible_type: &MetaType<S>,
) -> bool {
self.possible_types(abstract_type) self.possible_types(abstract_type)
.into_iter() .into_iter()
.any(|t| (t as *const MetaType) == (possible_type as *const MetaType)) .any(|t| (t as *const MetaType<S>) == (possible_type as *const MetaType<S>))
} }
pub fn is_subtype<'b>(&self, sub_type: &Type<'b>, super_type: &Type<'b>) -> bool { pub fn is_subtype<'b>(&self, sub_type: &Type<'b>, super_type: &Type<'b>) -> bool {
@ -318,9 +353,9 @@ impl<'a> SchemaType<'a> {
} }
} }
impl<'a> TypeType<'a> { impl<'a, S> TypeType<'a, S> {
#[inline] #[inline]
pub fn to_concrete(&self) -> Option<&'a MetaType> { pub fn to_concrete(&self) -> Option<&'a MetaType<S>> {
match *self { match *self {
TypeType::Concrete(t) => Some(t), TypeType::Concrete(t) => Some(t),
_ => None, _ => None,
@ -328,7 +363,7 @@ impl<'a> TypeType<'a> {
} }
#[inline] #[inline]
pub fn innermost_concrete(&self) -> &'a MetaType { pub fn innermost_concrete(&self) -> &'a MetaType<S> {
match *self { match *self {
TypeType::Concrete(t) => t, TypeType::Concrete(t) => t,
TypeType::NonNull(ref n) | TypeType::List(ref n) => n.innermost_concrete(), TypeType::NonNull(ref n) | TypeType::List(ref n) => n.innermost_concrete(),
@ -336,7 +371,7 @@ impl<'a> TypeType<'a> {
} }
#[inline] #[inline]
pub fn list_contents(&self) -> Option<&TypeType<'a>> { pub fn list_contents(&self) -> Option<&TypeType<'a, S>> {
match *self { match *self {
TypeType::List(ref n) => Some(n), TypeType::List(ref n) => Some(n),
TypeType::NonNull(ref n) => n.list_contents(), TypeType::NonNull(ref n) => n.list_contents(),
@ -353,12 +388,15 @@ impl<'a> TypeType<'a> {
} }
} }
impl<'a> DirectiveType<'a> { impl<'a, S> DirectiveType<'a, S>
where
S: ScalarValue + 'a,
{
pub fn new( pub fn new(
name: &str, name: &str,
locations: &[DirectiveLocation], locations: &[DirectiveLocation],
arguments: &[Argument<'a>], arguments: &[Argument<'a, S>],
) -> DirectiveType<'a> { ) -> DirectiveType<'a, S> {
DirectiveType { DirectiveType {
name: name.to_owned(), name: name.to_owned(),
description: None, description: None,
@ -367,7 +405,11 @@ impl<'a> DirectiveType<'a> {
} }
} }
fn new_skip(registry: &mut Registry<'a>) -> DirectiveType<'a> { fn new_skip(registry: &mut Registry<'a, S>) -> DirectiveType<'a, S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
Self::new( Self::new(
"skip", "skip",
&[ &[
@ -379,7 +421,11 @@ impl<'a> DirectiveType<'a> {
) )
} }
fn new_include(registry: &mut Registry<'a>) -> DirectiveType<'a> { fn new_include(registry: &mut Registry<'a, S>) -> DirectiveType<'a, S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
Self::new( Self::new(
"include", "include",
&[ &[
@ -391,7 +437,7 @@ impl<'a> DirectiveType<'a> {
) )
} }
pub fn description(mut self, description: &str) -> DirectiveType<'a> { pub fn description(mut self, description: &str) -> DirectiveType<'a, S> {
self.description = Some(description.to_owned()); self.description = Some(description.to_owned());
self self
} }
@ -410,7 +456,7 @@ impl fmt::Display for DirectiveLocation {
} }
} }
impl<'a> fmt::Display for TypeType<'a> { impl<'a, S> fmt::Display for TypeType<'a, S> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self { match *self {
TypeType::Concrete(t) => f.write_str(t.name().unwrap()), TypeType::Concrete(t) => f.write_str(t.name().unwrap()),

View file

@ -1,7 +1,7 @@
use ast::Selection;
use executor::{ExecutionResult, Executor, Registry}; use executor::{ExecutionResult, Executor, Registry};
use types::base::{Arguments, GraphQLType, TypeKind}; use types::base::{Arguments, GraphQLType, TypeKind};
use value::Value; use value::{ScalarRefValue, ScalarValue, Value};
use ast::Selection;
use schema::meta::{ use schema::meta::{
Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, MetaType, ObjectMeta, Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, MetaType, ObjectMeta,
@ -9,10 +9,12 @@ use schema::meta::{
}; };
use schema::model::{DirectiveLocation, DirectiveType, RootNode, SchemaType, TypeType}; use schema::model::{DirectiveLocation, DirectiveType, RootNode, SchemaType, TypeType};
impl<'a, CtxT, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT> impl<'a, CtxT, S, QueryT, MutationT> GraphQLType<S> for RootNode<'a, QueryT, MutationT, S>
where where
QueryT: GraphQLType<Context = CtxT>, S: ScalarValue,
MutationT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = QueryT::TypeInfo; type TypeInfo = QueryT::TypeInfo;
@ -21,7 +23,11 @@ where
QueryT::name(info) QueryT::name(info)
} }
fn meta<'r>(info: &QueryT::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &QueryT::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
QueryT::meta(info, registry) QueryT::meta(info, registry)
} }
@ -29,9 +35,9 @@ where
&self, &self,
info: &QueryT::TypeInfo, info: &QueryT::TypeInfo,
field: &str, field: &str,
args: &Arguments, args: &Arguments<S>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
match field { match field {
"__schema" => executor "__schema" => executor
.replaced_context(&self.schema) .replaced_context(&self.schema)
@ -49,11 +55,11 @@ where
fn resolve( fn resolve(
&self, &self,
info: &Self::TypeInfo, info: &Self::TypeInfo,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context>, executor: &Executor<Self::Context, S>,
) -> Value { ) -> Value<S> {
use value::Object;
use types::base::resolve_selection_set_into; use types::base::resolve_selection_set_into;
use value::Object;
if let Some(selection_set) = selection_set { if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len()); let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) { if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
@ -67,33 +73,37 @@ where
} }
} }
graphql_object!(<'a> SchemaType<'a>: SchemaType<'a> as "__Schema" |&self| { graphql_object!(<'a> SchemaType<'a, S>: SchemaType<'a, S> as "__Schema"
field types() -> Vec<TypeType> { where Scalar = <S: 'a> |&self|
{
field types() -> Vec<TypeType<S>> {
self.type_list() self.type_list()
.into_iter() .into_iter()
.filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false)) .filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false))
.collect() .collect()
} }
field query_type() -> TypeType { field query_type() -> TypeType<S> {
self.query_type() self.query_type()
} }
field mutation_type() -> Option<TypeType> { field mutation_type() -> Option<TypeType<S>> {
self.mutation_type() self.mutation_type()
} }
// Included for compatibility with the introspection query in GraphQL.js // Included for compatibility with the introspection query in GraphQL.js
field subscription_type() -> Option<TypeType> { field subscription_type() -> Option<TypeType<S>> {
None None
} }
field directives() -> Vec<&DirectiveType> { field directives() -> Vec<&DirectiveType<S>> {
self.directive_list() self.directive_list()
} }
}); });
graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| { graphql_object!(<'a> TypeType<'a, S>: SchemaType<'a, S> as "__Type"
where Scalar = <S: 'a> |&self|
{
field name() -> Option<&str> { field name() -> Option<&str> {
match *self { match *self {
TypeType::Concrete(t) => t.name(), TypeType::Concrete(t) => t.name(),
@ -116,7 +126,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| {
} }
} }
field fields(include_deprecated = false: bool) -> Option<Vec<&Field>> { field fields(include_deprecated = false: bool) -> Option<Vec<&Field<S>>> {
match *self { match *self {
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) | TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) |
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) =>
@ -129,14 +139,14 @@ graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| {
} }
} }
field of_type() -> Option<&Box<TypeType>> { field of_type() -> Option<&Box<TypeType<S>>> {
match *self { match *self {
TypeType::Concrete(_) => None, TypeType::Concrete(_) => None,
TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l), TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l),
} }
} }
field input_fields() -> Option<&Vec<Argument>> { field input_fields() -> Option<&Vec<Argument<S>>> {
match *self { match *self {
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) => TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) =>
Some(input_fields), Some(input_fields),
@ -144,7 +154,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| {
} }
} }
field interfaces(&executor) -> Option<Vec<TypeType>> { field interfaces(&executor) -> Option<Vec<TypeType<S>>> {
match *self { match *self {
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => { TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
let schema = executor.context(); let schema = executor.context();
@ -157,7 +167,7 @@ graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| {
} }
} }
field possible_types(&executor) -> Option<Vec<TypeType>> { field possible_types(&executor) -> Option<Vec<TypeType<S>>> {
let schema = executor.context(); let schema = executor.context();
match *self { match *self {
TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => { TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => {
@ -198,7 +208,9 @@ graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| {
} }
}); });
graphql_object!(<'a> Field<'a>: SchemaType<'a> as "__Field" |&self| { graphql_object!(<'a> Field<'a, S>: SchemaType<'a, S> as "__Field"
where Scalar = <S: 'a> |&self|
{
field name() -> &String { field name() -> &String {
&self.name &self.name
} }
@ -207,11 +219,11 @@ graphql_object!(<'a> Field<'a>: SchemaType<'a> as "__Field" |&self| {
&self.description &self.description
} }
field args() -> Vec<&Argument> { field args() -> Vec<&Argument<S>> {
self.arguments.as_ref().map_or_else(Vec::new, |v| v.iter().collect()) self.arguments.as_ref().map_or_else(Vec::new, |v| v.iter().collect())
} }
field type(&executor) -> TypeType { field type(&executor) -> TypeType<S> {
executor.context().make_type(&self.field_type) executor.context().make_type(&self.field_type)
} }
@ -224,7 +236,9 @@ graphql_object!(<'a> Field<'a>: SchemaType<'a> as "__Field" |&self| {
} }
}); });
graphql_object!(<'a> Argument<'a>: SchemaType<'a> as "__InputValue" |&self| { graphql_object!(<'a> Argument<'a, S>: SchemaType<'a, S> as "__InputValue"
where Scalar = <S: 'a> |&self|
{
field name() -> &String { field name() -> &String {
&self.name &self.name
} }
@ -233,7 +247,7 @@ graphql_object!(<'a> Argument<'a>: SchemaType<'a> as "__InputValue" |&self| {
&self.description &self.description
} }
field type(&executor) -> TypeType { field type(&executor) -> TypeType<S> {
executor.context().make_type(&self.arg_type) executor.context().make_type(&self.arg_type)
} }
@ -242,7 +256,7 @@ graphql_object!(<'a> Argument<'a>: SchemaType<'a> as "__InputValue" |&self| {
} }
}); });
graphql_object!(EnumValue: () as "__EnumValue" |&self| { graphql_object!(EnumValue: () as "__EnumValue" where Scalar = <S> |&self| {
field name() -> &String { field name() -> &String {
&self.name &self.name
} }
@ -260,8 +274,10 @@ graphql_object!(EnumValue: () as "__EnumValue" |&self| {
} }
}); });
graphql_object!(<'a> DirectiveType<'a>: SchemaType<'a> as "__Directive" |&self| { graphql_object!(<'a> DirectiveType<'a, S>: SchemaType<'a, S> as "__Directive"
field name() -> &String { where Scalar = <S: 'a> |&self|
{
field name() -> &String {
&self.name &self.name
} }
@ -273,7 +289,7 @@ graphql_object!(<'a> DirectiveType<'a>: SchemaType<'a> as "__Directive" |&self|
&self.locations &self.locations
} }
field args() -> &Vec<Argument> { field args() -> &Vec<Argument<S>> {
&self.arguments &self.arguments
} }

View file

@ -29,13 +29,13 @@ fn test_query_type_name() {
vec![( vec![(
"queryType", "queryType",
Value::object( Value::object(
vec![("name", Value::string("Query"))].into_iter().collect(), vec![("name", Value::scalar("Query"))].into_iter().collect(),
), ),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -59,9 +59,9 @@ fn test_specific_type_name() {
Value::object( Value::object(
vec![( vec![(
"__type", "__type",
Value::object(vec![("name", Value::string("Droid"))].into_iter().collect()), Value::object(vec![("name", Value::scalar("Droid"))].into_iter().collect()),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -89,13 +89,13 @@ fn test_specific_object_type_name_and_kind() {
"__type", "__type",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Droid")), ("name", Value::scalar("Droid")),
("kind", Value::string("OBJECT")), ("kind", Value::scalar("OBJECT")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -123,13 +123,13 @@ fn test_specific_interface_type_name_and_kind() {
"__type", "__type",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Character")), ("name", Value::scalar("Character")),
("kind", Value::string("INTERFACE")), ("kind", Value::scalar("INTERFACE")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -157,16 +157,16 @@ fn test_documentation() {
"__type", "__type",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Droid")), ("name", Value::scalar("Droid")),
( (
"description", "description",
Value::string("A mechanical creature in the Star Wars universe."), Value::scalar("A mechanical creature in the Star Wars universe."),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -212,10 +212,9 @@ fn test_possible_types() {
.expect("possible type not an object") .expect("possible type not an object")
.get_field_value("name") .get_field_value("name")
.expect("'name' not present in type") .expect("'name' not present in type")
.as_string_value() .as_scalar_value::<String>()
.expect("'name' not a string") .expect("'name' not a string") as &str
}) }).collect::<HashSet<_>>();
.collect::<HashSet<_>>();
assert_eq!(possible_types, vec!["Human", "Droid"].into_iter().collect()); assert_eq!(possible_types, vec!["Human", "Droid"].into_iter().collect());
} }

View file

@ -3,7 +3,6 @@
use std::collections::HashMap; use std::collections::HashMap;
#[derive(GraphQLEnum, Copy, Clone, Eq, PartialEq, Debug)] #[derive(GraphQLEnum, Copy, Clone, Eq, PartialEq, Debug)]
#[graphql(_internal)]
pub enum Episode { pub enum Episode {
#[graphql(name = "NEW_HOPE")] #[graphql(name = "NEW_HOPE")]
NewHope, NewHope,

View file

@ -22,9 +22,9 @@ fn test_hero_name() {
Value::object( Value::object(
vec![( vec![(
"hero", "hero",
Value::object(vec![("name", Value::string("R2-D2"))].into_iter().collect()), Value::object(vec![("name", Value::scalar("R2-D2"))].into_iter().collect()),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -51,13 +51,13 @@ fn test_hero_field_order() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("id", Value::string("2001")), ("id", Value::scalar("2001")),
("name", Value::string("R2-D2")), ("name", Value::scalar("R2-D2")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -78,13 +78,13 @@ fn test_hero_field_order() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("name", Value::string("R2-D2")), ("name", Value::scalar("R2-D2")),
("id", Value::string("2001")), ("id", Value::scalar("2001")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -114,33 +114,33 @@ fn test_hero_name_and_friends() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("id", Value::string("2001")), ("id", Value::scalar("2001")),
("name", Value::string("R2-D2")), ("name", Value::scalar("R2-D2")),
( (
"friends", "friends",
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![("name", Value::string("Luke Skywalker"))] vec![("name", Value::scalar("Luke Skywalker"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("Han Solo"))] vec![("name", Value::scalar("Han Solo"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("Leia Organa"))] vec![("name", Value::scalar("Leia Organa"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -174,61 +174,61 @@ fn test_hero_name_and_friends_and_friends_of_friends() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("id", Value::string("2001")), ("id", Value::scalar("2001")),
("name", Value::string("R2-D2")), ("name", Value::scalar("R2-D2")),
( (
"friends", "friends",
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![ vec![
("name", Value::string("Luke Skywalker")), ("name", Value::scalar("Luke Skywalker")),
( (
"appearsIn", "appearsIn",
Value::list(vec![ Value::list(vec![
Value::string("NEW_HOPE"), Value::scalar("NEW_HOPE"),
Value::string("EMPIRE"), Value::scalar("EMPIRE"),
Value::string("JEDI"), Value::scalar("JEDI"),
]), ]),
), ),
( (
"friends", "friends",
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![("name", Value::string("Han Solo"))] vec![("name", Value::scalar("Han Solo"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![( vec![(
"name", "name",
Value::string("Leia Organa"), Value::scalar("Leia Organa"),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("C-3PO"))] vec![("name", Value::scalar("C-3PO"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("R2-D2"))] vec![("name", Value::scalar("R2-D2"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![ vec![
("name", Value::string("Han Solo")), ("name", Value::scalar("Han Solo")),
( (
"appearsIn", "appearsIn",
Value::list(vec![ Value::list(vec![
Value::string("NEW_HOPE"), Value::scalar("NEW_HOPE"),
Value::string("EMPIRE"), Value::scalar("EMPIRE"),
Value::string("JEDI"), Value::scalar("JEDI"),
]), ]),
), ),
( (
@ -237,36 +237,36 @@ fn test_hero_name_and_friends_and_friends_of_friends() {
Value::object( Value::object(
vec![( vec![(
"name", "name",
Value::string("Luke Skywalker"), Value::scalar("Luke Skywalker"),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![( vec![(
"name", "name",
Value::string("Leia Organa"), Value::scalar("Leia Organa"),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("R2-D2"))] vec![("name", Value::scalar("R2-D2"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![ vec![
("name", Value::string("Leia Organa")), ("name", Value::scalar("Leia Organa")),
( (
"appearsIn", "appearsIn",
Value::list(vec![ Value::list(vec![
Value::string("NEW_HOPE"), Value::scalar("NEW_HOPE"),
Value::string("EMPIRE"), Value::scalar("EMPIRE"),
Value::string("JEDI"), Value::scalar("JEDI"),
]), ]),
), ),
( (
@ -275,37 +275,37 @@ fn test_hero_name_and_friends_and_friends_of_friends() {
Value::object( Value::object(
vec![( vec![(
"name", "name",
Value::string("Luke Skywalker"), Value::scalar("Luke Skywalker"),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("Han Solo"))] vec![("name", Value::scalar("Han Solo"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("C-3PO"))] vec![("name", Value::scalar("C-3PO"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("R2-D2"))] vec![("name", Value::scalar("R2-D2"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
]), ]),
), ),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -325,12 +325,12 @@ fn test_query_name() {
vec![( vec![(
"human", "human",
Value::object( Value::object(
vec![("name", Value::string("Luke Skywalker"))] vec![("name", Value::scalar("Luke Skywalker"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -350,12 +350,12 @@ fn test_query_alias_single() {
vec![( vec![(
"luke", "luke",
Value::object( Value::object(
vec![("name", Value::string("Luke Skywalker"))] vec![("name", Value::scalar("Luke Skywalker"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -380,7 +380,7 @@ fn test_query_alias_multiple() {
( (
"luke", "luke",
Value::object( Value::object(
vec![("name", Value::string("Luke Skywalker"))] vec![("name", Value::scalar("Luke Skywalker"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
@ -388,13 +388,13 @@ fn test_query_alias_multiple() {
( (
"leia", "leia",
Value::object( Value::object(
vec![("name", Value::string("Leia Organa"))] vec![("name", Value::scalar("Leia Organa"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -425,24 +425,24 @@ fn test_query_alias_multiple_with_fragment() {
"luke", "luke",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Luke Skywalker")), ("name", Value::scalar("Luke Skywalker")),
("homePlanet", Value::string("Tatooine")), ("homePlanet", Value::scalar("Tatooine")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
( (
"leia", "leia",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Leia Organa")), ("name", Value::scalar("Leia Organa")),
("homePlanet", Value::string("Alderaan")), ("homePlanet", Value::scalar("Alderaan")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
), ),
].into_iter() ].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -455,7 +455,7 @@ fn test_query_name_variable() {
let database = Database::new(); let database = Database::new();
let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
let vars = vec![("someId".to_owned(), InputValue::string("1000"))] let vars = vec![("someId".to_owned(), InputValue::scalar("1000"))]
.into_iter() .into_iter()
.collect(); .collect();
@ -466,12 +466,12 @@ fn test_query_name_variable() {
vec![( vec![(
"human", "human",
Value::object( Value::object(
vec![("name", Value::string("Luke Skywalker"))] vec![("name", Value::scalar("Luke Skywalker"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -484,7 +484,7 @@ fn test_query_name_invalid_variable() {
let database = Database::new(); let database = Database::new();
let schema = RootNode::new(&database, EmptyMutation::<Database>::new()); let schema = RootNode::new(&database, EmptyMutation::<Database>::new());
let vars = vec![("someId".to_owned(), InputValue::string("some invalid id"))] let vars = vec![("someId".to_owned(), InputValue::scalar("some invalid id"))]
.into_iter() .into_iter()
.collect(); .collect();
@ -514,27 +514,27 @@ fn test_query_friends_names() {
"friends", "friends",
Value::list(vec![ Value::list(vec![
Value::object( Value::object(
vec![("name", Value::string("Han Solo"))] vec![("name", Value::scalar("Han Solo"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("Leia Organa"))] vec![("name", Value::scalar("Leia Organa"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("C-3PO"))].into_iter().collect(), vec![("name", Value::scalar("C-3PO"))].into_iter().collect(),
), ),
Value::object( Value::object(
vec![("name", Value::string("R2-D2"))].into_iter().collect(), vec![("name", Value::scalar("R2-D2"))].into_iter().collect(),
), ),
]), ]),
)].into_iter() )].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -566,14 +566,14 @@ fn test_query_inline_fragments_droid() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("name", Value::string("R2-D2")), ("name", Value::scalar("R2-D2")),
("__typename", Value::string("Droid")), ("__typename", Value::scalar("Droid")),
("primaryFunction", Value::string("Astromech")), ("primaryFunction", Value::scalar("Astromech")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -601,13 +601,13 @@ fn test_query_inline_fragments_human() {
"hero", "hero",
Value::object( Value::object(
vec![ vec![
("name", Value::string("Luke Skywalker")), ("name", Value::scalar("Luke Skywalker")),
("__typename", Value::string("Human")), ("__typename", Value::scalar("Human")),
].into_iter() ].into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))
@ -632,12 +632,12 @@ fn test_object_typename() {
vec![( vec![(
"human", "human",
Value::object( Value::object(
vec![("__typename", Value::string("Human"))] vec![("__typename", Value::scalar("Human"))]
.into_iter() .into_iter()
.collect(), .collect(),
), ),
)].into_iter() )].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))

View file

@ -5,7 +5,7 @@ use schema::meta::MetaType;
use schema::model::RootNode; use schema::model::RootNode;
use types::base::{Arguments, GraphQLType}; use types::base::{Arguments, GraphQLType};
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
use value::Value; use value::{ScalarRefValue, ScalarValue, Value};
pub struct NodeTypeInfo { pub struct NodeTypeInfo {
name: String, name: String,
@ -16,7 +16,11 @@ pub struct Node {
attributes: IndexMap<String, String>, attributes: IndexMap<String, String>,
} }
impl GraphQLType for Node { impl<S> GraphQLType<S> for Node
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = NodeTypeInfo; type TypeInfo = NodeTypeInfo;
@ -24,8 +28,12 @@ impl GraphQLType for Node {
Some(&info.name) Some(&info.name)
} }
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
let fields = info.attribute_names where
S: 'r,
{
let fields = info
.attribute_names
.iter() .iter()
.map(|name| registry.field::<String>(name, &())) .map(|name| registry.field::<String>(name, &()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
@ -39,9 +47,9 @@ impl GraphQLType for Node {
&self, &self,
_: &Self::TypeInfo, _: &Self::TypeInfo,
field_name: &str, field_name: &str,
_: &Arguments, _: &Arguments<S>,
executor: &Executor<Self::Context>, executor: &Executor<Self::Context, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
executor.resolve(&(), &self.attributes.get(field_name).unwrap()) executor.resolve(&(), &self.attributes.get(field_name).unwrap())
} }
} }
@ -64,18 +72,18 @@ fn test_node() {
node.attributes.insert("foo".to_string(), "1".to_string()); node.attributes.insert("foo".to_string(), "1".to_string());
node.attributes.insert("bar".to_string(), "2".to_string()); node.attributes.insert("bar".to_string(), "2".to_string());
node.attributes.insert("baz".to_string(), "3".to_string()); node.attributes.insert("baz".to_string(), "3".to_string());
let schema = RootNode::new_with_info(node, EmptyMutation::new(), node_info, ()); let schema: RootNode<_, _> = RootNode::new_with_info(node, EmptyMutation::new(), node_info, ());
assert_eq!( assert_eq!(
::execute(doc, None, &schema, &Variables::new(), &()), ::execute(doc, None, &schema, &Variables::new(), &()),
Ok(( Ok((
Value::object( Value::object(
vec![ vec![
("foo", Value::string("1")), ("foo", Value::scalar("1")),
("bar", Value::string("2")), ("bar", Value::scalar("2")),
("baz", Value::string("3")), ("baz", Value::scalar("3")),
].into_iter() ].into_iter()
.collect() .collect()
), ),
vec![] vec![]
)) ))

View file

@ -2,7 +2,7 @@ use indexmap::IndexMap;
use ast::{Directive, FromInputValue, InputValue, Selection}; use ast::{Directive, FromInputValue, InputValue, Selection};
use executor::Variables; use executor::Variables;
use value::{Object, Value}; use value::{DefaultScalarValue, Object, ScalarRefValue, ScalarValue, Value};
use executor::{ExecutionResult, Executor, Registry}; use executor::{ExecutionResult, Executor, Registry};
use parser::Spanning; use parser::Spanning;
@ -12,9 +12,8 @@ use schema::meta::{Argument, MetaType};
/// ///
/// The GraphQL specification defines a number of type kinds - the meta type /// The GraphQL specification defines a number of type kinds - the meta type
/// of a type. /// of a type.
#[derive(GraphQLEnum, Clone, Eq, PartialEq, Debug)] #[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)]
// Note: _internal flag needed to make derive work in juniper crate itself. #[graphql(name = "__TypeKind")]
#[graphql(name = "__TypeKind", _internal)]
pub enum TypeKind { pub enum TypeKind {
/// ## Scalar types /// ## Scalar types
/// ///
@ -68,16 +67,20 @@ pub enum TypeKind {
} }
/// Field argument container /// Field argument container
pub struct Arguments<'a> { #[derive(Debug)]
args: Option<IndexMap<&'a str, InputValue>>, pub struct Arguments<'a, S = DefaultScalarValue> {
args: Option<IndexMap<&'a str, InputValue<S>>>,
} }
impl<'a> Arguments<'a> { impl<'a, S> Arguments<'a, S>
where
S: ScalarValue,
{
#[doc(hidden)] #[doc(hidden)]
pub fn new( pub fn new(
mut args: Option<IndexMap<&'a str, InputValue>>, mut args: Option<IndexMap<&'a str, InputValue<S>>>,
meta_args: &'a Option<Vec<Argument>>, meta_args: &'a Option<Vec<Argument<S>>>,
) -> Arguments<'a> { ) -> Self {
if meta_args.is_some() && args.is_none() { if meta_args.is_some() && args.is_none() {
args = Some(IndexMap::new()); args = Some(IndexMap::new());
} }
@ -106,7 +109,8 @@ impl<'a> Arguments<'a> {
/// succeeeds. /// succeeeds.
pub fn get<T>(&self, key: &str) -> Option<T> pub fn get<T>(&self, key: &str) -> Option<T>
where where
T: FromInputValue, T: FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
match self.args { match self.args {
Some(ref args) => match args.get(key) { Some(ref args) => match args.get(key) {
@ -144,16 +148,20 @@ root:
```rust ```rust
use juniper::{GraphQLType, Registry, FieldResult, Context, use juniper::{GraphQLType, Registry, FieldResult, Context,
Arguments, Executor, ExecutionResult}; Arguments, Executor, ExecutionResult,
DefaultScalarValue};
use juniper::meta::MetaType; use juniper::meta::MetaType;
# use std::collections::HashMap; # use std::collections::HashMap;
#[derive(Debug)]
struct User { id: String, name: String, friend_ids: Vec<String> } struct User { id: String, name: String, friend_ids: Vec<String> }
#[derive(Debug)]
struct Database { users: HashMap<String, User> } struct Database { users: HashMap<String, User> }
impl Context for Database {} impl Context for Database {}
impl GraphQLType for User { impl GraphQLType for User
{
type Context = Database; type Context = Database;
type TypeInfo = (); type TypeInfo = ();
@ -161,7 +169,9 @@ impl GraphQLType for User {
Some("User") Some("User")
} }
fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r>
where DefaultScalarValue: 'r,
{
// First, we need to define all fields and their types on this type. // First, we need to define all fields and their types on this type.
// //
// If we need arguments, want to implement interfaces, or want to add // If we need arguments, want to implement interfaces, or want to add
@ -219,7 +229,11 @@ impl GraphQLType for User {
``` ```
*/ */
pub trait GraphQLType: Sized { pub trait GraphQLType<S = DefaultScalarValue>: Sized
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// The expected context type for this GraphQL type /// The expected context type for this GraphQL type
/// ///
/// The context is threaded through query execution to all affected nodes, /// The context is threaded through query execution to all affected nodes,
@ -242,7 +256,9 @@ pub trait GraphQLType: Sized {
fn name(info: &Self::TypeInfo) -> Option<&str>; fn name(info: &Self::TypeInfo) -> Option<&str>;
/// The meta type representing this GraphQL type. /// The meta type representing this GraphQL type.
fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r>; fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r;
/// Resolve the value of a single field on this type. /// Resolve the value of a single field on this type.
/// ///
@ -257,9 +273,9 @@ pub trait GraphQLType: Sized {
&self, &self,
info: &Self::TypeInfo, info: &Self::TypeInfo,
field_name: &str, field_name: &str,
arguments: &Arguments, arguments: &Arguments<S>,
executor: &Executor<Self::Context>, executor: &Executor<Self::Context, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
panic!("resolve_field must be implemented by object types"); panic!("resolve_field must be implemented by object types");
} }
@ -274,9 +290,9 @@ pub trait GraphQLType: Sized {
&self, &self,
info: &Self::TypeInfo, info: &Self::TypeInfo,
type_name: &str, type_name: &str,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context>, executor: &Executor<Self::Context, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
if Self::name(info).unwrap() == type_name { if Self::name(info).unwrap() == type_name {
Ok(self.resolve(info, selection_set, executor)) Ok(self.resolve(info, selection_set, executor))
} else { } else {
@ -305,9 +321,9 @@ pub trait GraphQLType: Sized {
fn resolve( fn resolve(
&self, &self,
info: &Self::TypeInfo, info: &Self::TypeInfo,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context>, executor: &Executor<Self::Context, S>,
) -> Value { ) -> Value<S> {
if let Some(selection_set) = selection_set { if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len()); let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) { if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
@ -321,15 +337,17 @@ pub trait GraphQLType: Sized {
} }
} }
pub(crate) fn resolve_selection_set_into<T, CtxT>( pub(crate) fn resolve_selection_set_into<T, CtxT, S>(
instance: &T, instance: &T,
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: &[Selection], selection_set: &[Selection<S>],
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
result: &mut Object, result: &mut Object<S>,
) -> bool ) -> bool
where where
T: GraphQLType<Context = CtxT>, T: GraphQLType<S, Context = CtxT>,
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let meta_type = executor let meta_type = executor
.schema() .schema()
@ -337,8 +355,7 @@ where
T::name(info) T::name(info)
.expect("Resolving named type's selection set") .expect("Resolving named type's selection set")
.as_ref(), .as_ref(),
) ).expect("Type not found in schema");
.expect("Type not found in schema");
for selection in selection_set { for selection in selection_set {
match *selection { match *selection {
@ -356,7 +373,7 @@ where
if f.name.item == "__typename" { if f.name.item == "__typename" {
result.add_field( result.add_field(
response_name, response_name,
Value::string(instance.concrete_type_name(executor.context(), info)), Value::scalar(instance.concrete_type_name(executor.context(), info)),
); );
continue; continue;
} }
@ -387,8 +404,7 @@ where
.iter() .iter()
.map(|&(ref k, ref v)| { .map(|&(ref k, ref v)| {
(k.item, v.item.clone().into_const(exec_vars)) (k.item, v.item.clone().into_const(exec_vars))
}) }).collect()
.collect()
}), }),
&meta_field.arguments, &meta_field.arguments,
), ),
@ -477,7 +493,11 @@ where
true true
} }
fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables) -> bool { fn is_excluded<S>(directives: &Option<Vec<Spanning<Directive<S>>>>, vars: &Variables<S>) -> bool
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
if let Some(ref directives) = *directives { if let Some(ref directives) = *directives {
for &Spanning { for &Spanning {
item: ref directive, item: ref directive,
@ -502,8 +522,10 @@ fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables)
false false
} }
fn merge_key_into(result: &mut Object, response_name: &str, value: Value) { fn merge_key_into<S>(result: &mut Object<S>, response_name: &str, value: Value<S>) {
if let Some(&mut (_, ref mut e)) = result.iter_mut().find(|&&mut (ref key, _)| key == response_name) if let Some(&mut (_, ref mut e)) = result
.iter_mut()
.find(|&&mut (ref key, _)| key == response_name)
{ {
match *e { match *e {
Value::Object(ref mut dest_obj) => { Value::Object(ref mut dest_obj) => {
@ -533,7 +555,7 @@ fn merge_key_into(result: &mut Object, response_name: &str, value: Value) {
result.add_field(response_name, value); result.add_field(response_name, value);
} }
fn merge_maps(dest: &mut Object, src: Object) { fn merge_maps<S>(dest: &mut Object<S>, src: Object<S>) {
for (key, value) in src { for (key, value) in src {
if dest.contains_field(&key) { if dest.contains_field(&key) {
merge_key_into(dest, &key, value); merge_key_into(dest, &key, value);

View file

@ -1,13 +1,15 @@
use ast::{FromInputValue, InputValue, Selection, ToInputValue}; use ast::{FromInputValue, InputValue, Selection, ToInputValue};
use schema::meta::MetaType; use schema::meta::MetaType;
use value::Value; use value::{ScalarRefValue, ScalarValue, Value};
use executor::{Executor, Registry}; use executor::{Executor, Registry};
use types::base::GraphQLType; use types::base::GraphQLType;
impl<T, CtxT> GraphQLType for Option<T> impl<S, T, CtxT> GraphQLType<S> for Option<T>
where where
T: GraphQLType<Context = CtxT>, S: ScalarValue,
T: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
@ -16,16 +18,20 @@ where
None None
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_nullable_type::<T>(info).into_meta() registry.build_nullable_type::<T>(info).into_meta()
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> Value { ) -> Value<S> {
match *self { match *self {
Some(ref obj) => executor.resolve_into_value(info, obj), Some(ref obj) => executor.resolve_into_value(info, obj),
None => Value::null(), None => Value::null(),
@ -33,11 +39,15 @@ where
} }
} }
impl<T> FromInputValue for Option<T> impl<S, T> FromInputValue<S> for Option<T>
where where
T: FromInputValue, T: FromInputValue<S>,
S: ScalarValue,
{ {
fn from_input_value(v: &InputValue) -> Option<Option<T>> { fn from_input_value<'a>(v: &'a InputValue<S>) -> Option<Option<T>>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
match v { match v {
&InputValue::Null => Some(None), &InputValue::Null => Some(None),
v => match v.convert() { v => match v.convert() {
@ -48,11 +58,12 @@ where
} }
} }
impl<T> ToInputValue for Option<T> impl<S, T> ToInputValue<S> for Option<T>
where where
T: ToInputValue, T: ToInputValue<S>,
S: ScalarValue,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
match *self { match *self {
Some(ref v) => v.to_input_value(), Some(ref v) => v.to_input_value(),
None => InputValue::null(), None => InputValue::null(),
@ -60,9 +71,11 @@ where
} }
} }
impl<T, CtxT> GraphQLType for Vec<T> impl<S, T, CtxT> GraphQLType<S> for Vec<T>
where where
T: GraphQLType<Context = CtxT>, T: GraphQLType<S, Context = CtxT>,
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
@ -71,25 +84,33 @@ where
None None
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_list_type::<T>(info).into_meta() registry.build_list_type::<T>(info).into_meta()
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> Value { ) -> Value<S> {
resolve_into_list(executor, info, self.iter()) resolve_into_list(executor, info, self.iter())
} }
} }
impl<T> FromInputValue for Vec<T> impl<T, S> FromInputValue<S> for Vec<T>
where where
T: FromInputValue, T: FromInputValue<S>,
S: ScalarValue,
{ {
fn from_input_value(v: &InputValue) -> Option<Vec<T>> { fn from_input_value<'a>(v: &'a InputValue<S>) -> Option<Vec<T>>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
match *v { match *v {
InputValue::List(ref ls) => { InputValue::List(ref ls) => {
let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect(); let v: Vec<_> = ls.iter().filter_map(|i| i.item.convert()).collect();
@ -109,18 +130,21 @@ where
} }
} }
impl<T> ToInputValue for Vec<T> impl<T, S> ToInputValue<S> for Vec<T>
where where
T: ToInputValue, T: ToInputValue<S>,
S: ScalarValue,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
InputValue::list(self.iter().map(|v| v.to_input_value()).collect()) InputValue::list(self.iter().map(|v| v.to_input_value()).collect())
} }
} }
impl<'a, T, CtxT> GraphQLType for &'a [T] impl<'a, S, T, CtxT> GraphQLType<S> for &'a [T]
where where
T: GraphQLType<Context = CtxT>, S: ScalarValue,
T: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
@ -129,33 +153,44 @@ where
None None
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_list_type::<T>(info).into_meta() registry.build_list_type::<T>(info).into_meta()
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
_: Option<&[Selection]>, _: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> Value { ) -> Value<S> {
resolve_into_list(executor, info, self.iter()) resolve_into_list(executor, info, self.iter())
} }
} }
impl<'a, T> ToInputValue for &'a [T] impl<'a, T, S> ToInputValue<S> for &'a [T]
where where
T: ToInputValue, T: ToInputValue<S>,
S: ScalarValue,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
InputValue::list(self.iter().map(|v| v.to_input_value()).collect()) InputValue::list(self.iter().map(|v| v.to_input_value()).collect())
} }
} }
fn resolve_into_list<T, I>(executor: &Executor<T::Context>, info: &T::TypeInfo, iter: I) -> Value fn resolve_into_list<S, T, I>(
executor: &Executor<T::Context, S>,
info: &T::TypeInfo,
iter: I,
) -> Value<S>
where where
S: ScalarValue,
I: Iterator<Item = T> + ExactSizeIterator, I: Iterator<Item = T> + ExactSizeIterator,
T: GraphQLType, T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{ {
let stop_on_null = executor let stop_on_null = executor
.current_type() .current_type()

View file

@ -1,14 +1,17 @@
use ast::{FromInputValue, InputValue, Selection, ToInputValue}; use ast::{FromInputValue, InputValue, Selection, ToInputValue};
use std::fmt::Debug;
use std::sync::Arc; use std::sync::Arc;
use value::Value;
use executor::{ExecutionResult, Executor, Registry}; use executor::{ExecutionResult, Executor, Registry};
use schema::meta::MetaType; use schema::meta::MetaType;
use types::base::{Arguments, GraphQLType}; use types::base::{Arguments, GraphQLType};
use value::{ScalarRefValue, ScalarValue, Value};
impl<T, CtxT> GraphQLType for Box<T> impl<S, T, CtxT> GraphQLType<S> for Box<T>
where where
T: GraphQLType<Context = CtxT>, S: ScalarValue,
T: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
@ -17,7 +20,11 @@ where
T::name(info) T::name(info)
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
T::meta(info, registry) T::meta(info, registry)
} }
@ -25,9 +32,9 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
name: &str, name: &str,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_into_type(info, name, selection_set, executor) (**self).resolve_into_type(info, name, selection_set, executor)
} }
@ -35,46 +42,53 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
field: &str, field: &str,
args: &Arguments, args: &Arguments<S>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_field(info, field, args, executor) (**self).resolve_field(info, field, args, executor)
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> Value { ) -> Value<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }
impl<T> FromInputValue for Box<T> impl<T, S> FromInputValue<S> for Box<T>
where where
T: FromInputValue, S: ScalarValue,
T: FromInputValue<S>,
{ {
fn from_input_value(v: &InputValue) -> Option<Box<T>> { fn from_input_value<'a>(v: &'a InputValue<S>) -> Option<Box<T>>
match <T as FromInputValue>::from_input_value(v) { where
for<'b> &'b S: ScalarRefValue<'b>
{
match <T as FromInputValue<S>>::from_input_value(v) {
Some(v) => Some(Box::new(v)), Some(v) => Some(Box::new(v)),
None => None, None => None,
} }
} }
} }
impl<T> ToInputValue for Box<T> impl<T, S> ToInputValue<S> for Box<T>
where where
T: ToInputValue, S: Debug,
T: ToInputValue<S>,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
(**self).to_input_value() (**self).to_input_value()
} }
} }
impl<'a, T, CtxT> GraphQLType for &'a T impl<'a, S, T, CtxT> GraphQLType<S> for &'a T
where where
T: GraphQLType<Context = CtxT>, S: ScalarValue,
T: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>
{ {
type Context = CtxT; type Context = CtxT;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
@ -83,7 +97,11 @@ where
T::name(info) T::name(info)
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
T::meta(info, registry) T::meta(info, registry)
} }
@ -91,9 +109,9 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
name: &str, name: &str,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_into_type(info, name, selection_set, executor) (**self).resolve_into_type(info, name, selection_set, executor)
} }
@ -101,43 +119,50 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
field: &str, field: &str,
args: &Arguments, args: &Arguments<S>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_field(info, field, args, executor) (**self).resolve_field(info, field, args, executor)
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<CtxT, S>,
) -> Value { ) -> Value<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }
impl<'a, T> ToInputValue for &'a T impl<'a, T, S> ToInputValue<S> for &'a T
where where
T: ToInputValue, S: Debug,
T: ToInputValue<S>,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
(**self).to_input_value() (**self).to_input_value()
} }
} }
impl<T, CtxT> GraphQLType for Arc<T> impl<S, T> GraphQLType<S> for Arc<T>
where where
T: GraphQLType<Context = CtxT>, S: ScalarValue,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>
{ {
type Context = CtxT; type Context = T::Context;
type TypeInfo = T::TypeInfo; type TypeInfo = T::TypeInfo;
fn name(info: &T::TypeInfo) -> Option<&str> { fn name(info: &T::TypeInfo) -> Option<&str> {
T::name(info) T::name(info)
} }
fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(info: &T::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
T::meta(info, registry) T::meta(info, registry)
} }
@ -145,9 +170,9 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
name: &str, name: &str,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<T::Context, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_into_type(info, name, selection_set, executor) (**self).resolve_into_type(info, name, selection_set, executor)
} }
@ -155,27 +180,28 @@ where
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
field: &str, field: &str,
args: &Arguments, args: &Arguments<S>,
executor: &Executor<CtxT>, executor: &Executor<T::Context, S>,
) -> ExecutionResult { ) -> ExecutionResult<S> {
(**self).resolve_field(info, field, args, executor) (**self).resolve_field(info, field, args, executor)
} }
fn resolve( fn resolve(
&self, &self,
info: &T::TypeInfo, info: &T::TypeInfo,
selection_set: Option<&[Selection]>, selection_set: Option<&[Selection<S>]>,
executor: &Executor<CtxT>, executor: &Executor<T::Context, S>,
) -> Value { ) -> Value<S> {
(**self).resolve(info, selection_set, executor) (**self).resolve(info, selection_set, executor)
} }
} }
impl<T> ToInputValue for Arc<T> impl<T, S> ToInputValue<S> for Arc<T>
where where
T: ToInputValue, S: Debug,
T: ToInputValue<S>,
{ {
fn to_input_value(&self) -> InputValue { fn to_input_value(&self) -> InputValue<S> {
(**self).to_input_value() (**self).to_input_value()
} }
} }

View file

@ -1,14 +1,15 @@
use std::convert::From; use std::convert::From;
use std::fmt::Debug;
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; use std::ops::Deref;
use std::{char, u32};
use ast::{FromInputValue, InputValue, Selection, ToInputValue}; use ast::{FromInputValue, InputValue, Selection, ToInputValue};
use value::Value;
use schema::meta::MetaType;
use executor::{Executor, Registry}; use executor::{Executor, Registry};
use parser::{LexerError, ParseError, ScalarToken, Token};
use schema::meta::MetaType;
use types::base::GraphQLType; use types::base::GraphQLType;
use value::{ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue, Value};
/// An ID as defined by the GraphQL specification /// An ID as defined by the GraphQL specification
/// ///
@ -30,34 +31,135 @@ impl Deref for ID {
} }
} }
graphql_scalar!(ID as "ID" { graphql_scalar!(ID as "ID" where Scalar = <S>{
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(&self.0) Value::scalar(self.0.clone())
} }
from_input_value(v: &InputValue) -> Option<ID> { from_input_value(v: &InputValue) -> Option<ID> {
match *v { match *v {
InputValue::String(ref s) => Some(ID(s.to_owned())), InputValue::Scalar(ref s) => {
InputValue::Int(i) => Some(ID(format!("{}", i))), s.as_string().or_else(|| s.as_int().map(|i| i.to_string()))
.map(ID)
}
_ => None _ => None
} }
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
match value {
ScalarToken::String(value) | ScalarToken::Int(value) => {
Ok(S::from(value.to_owned()))
}
_ => Err(ParseError::UnexpectedToken(Token::Scalar(value))),
}
}
}); });
graphql_scalar!(String as "String" { graphql_scalar!(String as "String" where Scalar = <S>{
resolve(&self) -> Value { resolve(&self) -> Value {
Value::string(self) Value::scalar(self.clone())
} }
from_input_value(v: &InputValue) -> Option<String> { from_input_value(v: &InputValue) -> Option<String> {
match *v { match *v {
InputValue::String(ref s) => Some(s.clone()), InputValue::Scalar(ref s) => s.as_string(),
_ => None, _ => None,
} }
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::String(value) = value {
let mut ret = String::with_capacity(value.len());
let mut char_iter = value.chars();
while let Some(ch) = char_iter.next() {
match ch {
'\\' => {
match char_iter.next() {
Some('"') => {ret.push('"');}
Some('/') => {ret.push('/');}
Some('n') => {ret.push('\n');}
Some('r') => {ret.push('\r');}
Some('t') => {ret.push('\t');}
Some('\\') => {ret.push('\\');}
Some('f') => {ret.push('\u{000c}');}
Some('b') => {ret.push('\u{0008}');}
Some('u') => {
ret.push(parse_unicode_codepoint(&mut char_iter)?);
}
Some(s) => return Err(ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\{}", s)))),
None => return Err(ParseError::LexerError(LexerError::UnterminatedString)),
}
},
ch => {ret.push(ch);}
}
}
Ok(ret.into())
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
impl<'a> GraphQLType for &'a str { fn parse_unicode_codepoint<'a, I>(char_iter: &mut I) -> Result<char, ParseError<'a>>
where
I: Iterator<Item = char>,
{
let escaped_code_point = char_iter
.next()
.ok_or_else(|| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(String::from("\\u")))
}).and_then(|c1| {
char_iter
.next()
.map(|c2| format!("{}{}", c1, c2))
.ok_or_else(|| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\u{}", c1)))
})
}).and_then(|mut s| {
char_iter
.next()
.ok_or_else(|| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
"\\u{}",
s.clone()
)))
}).map(|c2| {
s.push(c2);
s
})
}).and_then(|mut s| {
char_iter
.next()
.ok_or_else(|| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
"\\u{}",
s.clone()
)))
}).map(|c2| {
s.push(c2);
s
})
})?;
let code_point = u32::from_str_radix(&escaped_code_point, 16).map_err(|_| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
"\\u{}",
escaped_code_point
)))
})?;
char::from_u32(code_point).ok_or_else(|| {
ParseError::LexerError(LexerError::UnknownEscapeSequence(format!(
"\\u{}",
escaped_code_point
)))
})
}
impl<'a, S> GraphQLType<S> for &'a str
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -65,62 +167,106 @@ impl<'a> GraphQLType for &'a str {
Some("String") Some("String")
} }
fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_scalar_type::<String>(&()).into_meta() registry.build_scalar_type::<String>(&()).into_meta()
} }
fn resolve(&self, _: &(), _: Option<&[Selection]>, _: &Executor<Self::Context>) -> Value { fn resolve(
Value::string(self) &self,
_: &(),
_: Option<&[Selection<S>]>,
_: &Executor<Self::Context, S>,
) -> Value<S> {
Value::scalar(String::from(*self))
} }
} }
impl<'a> ToInputValue for &'a str { impl<'a, S> ToInputValue<S> for &'a str
fn to_input_value(&self) -> InputValue { where
InputValue::string(self) S: ScalarValue,
{
fn to_input_value(&self) -> InputValue<S> {
InputValue::scalar(String::from(*self))
} }
} }
graphql_scalar!(bool as "Boolean" { graphql_scalar!(bool as "Boolean" where Scalar = <S>{
resolve(&self) -> Value { resolve(&self) -> Value {
Value::boolean(*self) Value::scalar(*self)
} }
from_input_value(v: &InputValue) -> Option<bool> { from_input_value(v: &InputValue) -> Option<bool> {
match *v { match *v {
InputValue::Boolean(b) => Some(b), InputValue::Scalar(ref b) => b.as_boolean(),
_ => None, _ => None,
} }
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S > {
// Bools are parsed on it's own. This should not hit this code path
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}); });
graphql_scalar!(i32 as "Int" { graphql_scalar!(i32 as "Int" where Scalar = <S>{
resolve(&self) -> Value { resolve(&self) -> Value {
Value::int(*self) Value::scalar(*self)
} }
from_input_value(v: &InputValue) -> Option<i32> { from_input_value(v: &InputValue) -> Option<i32> {
match *v { match *v {
InputValue::Int(i) => Some(i), InputValue::Scalar(ref i) => i.as_int(),
_ => None, _ => None,
} }
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
if let ScalarToken::Int(v) = value {
v.parse()
.map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
.map(|s: i32| s.into())
} else {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}); });
graphql_scalar!(f64 as "Float" { graphql_scalar!(f64 as "Float" where Scalar = <S>{
resolve(&self) -> Value { resolve(&self) -> Value {
Value::float(*self) Value::scalar(*self)
} }
from_input_value(v: &InputValue) -> Option<f64> { from_input_value(v: &InputValue) -> Option<f64> {
match *v { match *v {
InputValue::Int(i) => Some(f64::from(i)), InputValue::Scalar(ref s) => s.as_float(),
InputValue::Float(f) => Some(f),
_ => None, _ => None,
} }
} }
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
match value {
ScalarToken::Int(v) | ScalarToken::Float(v) => {
v.parse()
.map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value)))
.map(|s: f64| s.into())
}
ScalarToken::String(_) => {
Err(ParseError::UnexpectedToken(Token::Scalar(value)))
}
}
}
}); });
impl GraphQLType for () { impl<S> GraphQLType<S> for ()
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -128,21 +274,38 @@ impl GraphQLType for () {
Some("__Unit") Some("__Unit")
} }
fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_scalar_type::<Self>(&()).into_meta() registry.build_scalar_type::<Self>(&()).into_meta()
} }
} }
impl FromInputValue for () { impl<S> ParseScalarValue<S> for ()
fn from_input_value(_: &InputValue) -> Option<()> { where
S: ScalarValue,
{
fn from_str<'a>(_value: ScalarToken<'a>) -> ParseScalarResult<'a, S> {
Ok(S::from(0))
}
}
impl<S: Debug> FromInputValue<S> for () {
fn from_input_value<'a>(_: &'a InputValue<S>) -> Option<()>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
None None
} }
} }
/// Utility type to define read-only schemas /// Utility type to define read-only schemas
/// ///
/// If you instantiate `RootNode` with this as the mutation, no mutation will be /// If you instantiate `RootNode` with this as the mutation, no mutation will be
/// generated for the schema. /// generated for the schema.
#[derive(Debug)]
pub struct EmptyMutation<T> { pub struct EmptyMutation<T> {
phantom: PhantomData<T>, phantom: PhantomData<T>,
} }
@ -156,7 +319,11 @@ impl<T> EmptyMutation<T> {
} }
} }
impl<T> GraphQLType for EmptyMutation<T> { impl<S, T> GraphQLType<S> for EmptyMutation<T>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = T; type Context = T;
type TypeInfo = (); type TypeInfo = ();
@ -164,7 +331,11 @@ impl<T> GraphQLType for EmptyMutation<T> {
Some("_EmptyMutation") Some("_EmptyMutation")
} }
fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
for<'b> &'b S: ScalarRefValue<'b>,
{
registry.build_object_type::<Self>(&(), &[]).into_meta() registry.build_object_type::<Self>(&(), &[]).into_meta()
} }
} }
@ -172,6 +343,8 @@ impl<T> GraphQLType for EmptyMutation<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::ID; use super::ID;
use parser::ScalarToken;
use value::{DefaultScalarValue, ParseScalarValue};
#[test] #[test]
fn test_id_from_string() { fn test_id_from_string() {
@ -185,4 +358,26 @@ mod tests {
let id = ID(String::from("foo")); let id = ID(String::from("foo"));
assert_eq!(id.len(), 3); assert_eq!(id.len(), 3);
} }
#[test]
fn parse_strings() {
fn parse_string(s: &str, expected: &str) {
let s =
<String as ParseScalarValue<DefaultScalarValue>>::from_str(ScalarToken::String(s));
assert!(s.is_ok(), "A parsing error occurred: {:?}", s);
let s: Option<String> = s.unwrap().into();
assert!(s.is_some(), "No string returned");
assert_eq!(s.unwrap(), expected);
}
parse_string("simple", "simple");
parse_string(" white space ", " white space ");
parse_string(r#"quote \""#, "quote \"");
parse_string(r#"escaped \n\r\b\t\f"#, "escaped \n\r\u{0008}\t\u{000c}");
parse_string(r#"slashes \\ \/"#, "slashes \\ /");
parse_string(
r#"unicode \u1234\u5678\u90AB\uCDEF"#,
"unicode \u{1234}\u{5678}\u{90ab}\u{cdef}",
);
}
} }

View file

@ -2,12 +2,16 @@ use ast::InputValue;
use schema::meta::{EnumMeta, InputObjectMeta, MetaType}; use schema::meta::{EnumMeta, InputObjectMeta, MetaType};
use schema::model::{SchemaType, TypeType}; use schema::model::{SchemaType, TypeType};
use std::collections::HashSet; use std::collections::HashSet;
use value::ScalarValue;
pub fn is_valid_literal_value( pub fn is_valid_literal_value<S>(
schema: &SchemaType, schema: &SchemaType<S>,
arg_type: &TypeType, arg_type: &TypeType<S>,
arg_value: &InputValue, arg_value: &InputValue<S>,
) -> bool { ) -> bool
where
S: ScalarValue,
{
match *arg_type { match *arg_type {
TypeType::NonNull(ref inner) => if arg_value.is_null() { TypeType::NonNull(ref inner) => if arg_value.is_null() {
false false
@ -23,7 +27,7 @@ pub fn is_valid_literal_value(
TypeType::Concrete(t) => { TypeType::Concrete(t) => {
// Even though InputValue::String can be parsed into an enum, they // Even though InputValue::String can be parsed into an enum, they
// are not valid as enum *literals* in a GraphQL query. // are not valid as enum *literals* in a GraphQL query.
if let (&InputValue::String(_), Some(&MetaType::Enum(EnumMeta { .. }))) = if let (&InputValue::Scalar(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
(arg_value, arg_type.to_concrete()) (arg_value, arg_type.to_concrete())
{ {
return false; return false;
@ -31,10 +35,7 @@ pub fn is_valid_literal_value(
match *arg_value { match *arg_value {
InputValue::Null | InputValue::Variable(_) => true, InputValue::Null | InputValue::Variable(_) => true,
ref v @ InputValue::Int(_) ref v @ InputValue::Scalar(_)
| ref v @ InputValue::Float(_)
| ref v @ InputValue::String(_)
| ref v @ InputValue::Boolean(_)
| ref v @ InputValue::Enum(_) => if let Some(parse_fn) = t.input_value_parse_fn() { | ref v @ InputValue::Enum(_) => if let Some(parse_fn) = t.input_value_parse_fn() {
parse_fn(v) parse_fn(v)
} else { } else {

View file

@ -1,4 +1,5 @@
use std::collections::HashSet; use std::collections::HashSet;
use std::fmt::Debug;
use ast::{Definition, Document, Type}; use ast::{Definition, Document, Type};
@ -15,14 +16,14 @@ pub struct RuleError {
} }
#[doc(hidden)] #[doc(hidden)]
pub struct ValidatorContext<'a> { pub struct ValidatorContext<'a, S: Debug + 'a> {
pub schema: &'a SchemaType<'a>, pub schema: &'a SchemaType<'a, S>,
errors: Vec<RuleError>, errors: Vec<RuleError>,
type_stack: Vec<Option<&'a MetaType<'a>>>, type_stack: Vec<Option<&'a MetaType<'a, S>>>,
type_literal_stack: Vec<Option<Type<'a>>>, type_literal_stack: Vec<Option<Type<'a>>>,
input_type_stack: Vec<Option<&'a MetaType<'a>>>, input_type_stack: Vec<Option<&'a MetaType<'a, S>>>,
input_type_literal_stack: Vec<Option<Type<'a>>>, input_type_literal_stack: Vec<Option<Type<'a>>>,
parent_type_stack: Vec<Option<&'a MetaType<'a>>>, parent_type_stack: Vec<Option<&'a MetaType<'a, S>>>,
fragment_names: HashSet<&'a str>, fragment_names: HashSet<&'a str>,
} }
@ -49,9 +50,9 @@ impl RuleError {
} }
} }
impl<'a> ValidatorContext<'a> { impl<'a, S: Debug> ValidatorContext<'a, S> {
#[doc(hidden)] #[doc(hidden)]
pub fn new(schema: &'a SchemaType, document: &Document<'a>) -> ValidatorContext<'a> { pub fn new(schema: &'a SchemaType<S>, document: &Document<'a, S>) -> ValidatorContext<'a, S> {
ValidatorContext { ValidatorContext {
errors: Vec::new(), errors: Vec::new(),
schema: schema, schema: schema,
@ -89,7 +90,7 @@ impl<'a> ValidatorContext<'a> {
#[doc(hidden)] #[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 where
F: FnOnce(&mut ValidatorContext<'a>) -> R, F: FnOnce(&mut ValidatorContext<'a, S>) -> R,
{ {
if let Some(t) = t { if let Some(t) = t {
self.type_stack self.type_stack
@ -111,7 +112,7 @@ impl<'a> ValidatorContext<'a> {
#[doc(hidden)] #[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 where
F: FnOnce(&mut ValidatorContext<'a>) -> R, F: FnOnce(&mut ValidatorContext<'a, S>) -> R,
{ {
self.parent_type_stack self.parent_type_stack
.push(*self.type_stack.last().unwrap_or(&None)); .push(*self.type_stack.last().unwrap_or(&None));
@ -124,7 +125,7 @@ impl<'a> ValidatorContext<'a> {
#[doc(hidden)] #[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 where
F: FnOnce(&mut ValidatorContext<'a>) -> R, F: FnOnce(&mut ValidatorContext<'a, S>) -> R,
{ {
if let Some(t) = t { if let Some(t) = t {
self.input_type_stack self.input_type_stack
@ -144,7 +145,7 @@ impl<'a> ValidatorContext<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn current_type(&self) -> Option<&'a MetaType<'a>> { pub fn current_type(&self) -> Option<&'a MetaType<'a, S>> {
*self.type_stack.last().unwrap_or(&None) *self.type_stack.last().unwrap_or(&None)
} }
@ -157,7 +158,7 @@ impl<'a> ValidatorContext<'a> {
} }
#[doc(hidden)] #[doc(hidden)]
pub fn parent_type(&self) -> Option<&'a MetaType<'a>> { pub fn parent_type(&self) -> Option<&'a MetaType<'a, S>> {
*self.parent_type_stack.last().unwrap_or(&None) *self.parent_type_stack.last().unwrap_or(&None)
} }

View file

@ -7,6 +7,7 @@ use parser::SourcePosition;
use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta}; use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta};
use schema::model::{SchemaType, TypeType}; use schema::model::{SchemaType, TypeType};
use validation::RuleError; use validation::RuleError;
use value::{ScalarRefValue, ScalarValue};
#[derive(Debug)] #[derive(Debug)]
enum Path<'a> { enum Path<'a> {
@ -15,31 +16,38 @@ enum Path<'a> {
ObjectField(&'a str, &'a Path<'a>), ObjectField(&'a str, &'a Path<'a>),
} }
pub fn validate_input_values( pub fn validate_input_values<S>(
values: &Variables, values: &Variables<S>,
document: &Document, document: &Document<S>,
schema: &SchemaType, schema: &SchemaType<S>,
) -> Vec<RuleError> { ) -> Vec<RuleError>
let mut errors: Vec<RuleError> = vec![]; where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut errs = vec![];
for def in document { for def in document {
if let Definition::Operation(ref op) = *def { if let Definition::Operation(ref op) = *def {
if let Some(ref vars) = op.item.variable_definitions { if let Some(ref vars) = op.item.variable_definitions {
validate_var_defs(values, &vars.item, schema, &mut errors); validate_var_defs(values, &vars.item, schema, &mut errs);
} }
} }
} }
errors.sort(); errs.sort();
errors errs
} }
fn validate_var_defs( fn validate_var_defs<S>(
values: &Variables, values: &Variables<S>,
var_defs: &VariableDefinitions, var_defs: &VariableDefinitions<S>,
schema: &SchemaType, schema: &SchemaType<S>,
errors: &mut Vec<RuleError>, errors: &mut Vec<RuleError>,
) { ) where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
for &(ref name, ref def) in var_defs.iter() { for &(ref name, ref def) in var_defs.iter() {
let raw_type_name = def.var_type.item.innermost_name(); let raw_type_name = def.var_type.item.innermost_name();
match schema.concrete_type_by_name(raw_type_name) { match schema.concrete_type_by_name(raw_type_name) {
@ -70,14 +78,18 @@ fn validate_var_defs(
} }
} }
fn unify_value<'a>( fn unify_value<'a, S>(
var_name: &str, var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue<S>,
meta_type: &TypeType<'a>, meta_type: &TypeType<'a, S>,
schema: &SchemaType, schema: &SchemaType<S>,
path: Path<'a>, path: Path<'a>,
) -> Vec<RuleError> { ) -> Vec<RuleError>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut errors: Vec<RuleError> = vec![]; let mut errors: Vec<RuleError> = vec![];
match *meta_type { match *meta_type {
@ -155,13 +167,16 @@ fn unify_value<'a>(
errors errors
} }
fn unify_scalar<'a>( fn unify_scalar<'a, S>(
var_name: &str, var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue<S>,
meta: &ScalarMeta, meta: &ScalarMeta<S>,
path: &Path<'a>, path: &Path<'a>,
) -> Vec<RuleError> { ) -> Vec<RuleError>
where
S: fmt::Debug,
{
let mut errors: Vec<RuleError> = vec![]; let mut errors: Vec<RuleError> = vec![];
if !(meta.try_parse_fn)(value) { if !(meta.try_parse_fn)(value) {
@ -191,17 +206,32 @@ fn unify_scalar<'a>(
errors errors
} }
fn unify_enum<'a>( fn unify_enum<'a, S>(
var_name: &str, var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue<S>,
meta: &EnumMeta, meta: &EnumMeta<S>,
path: &Path<'a>, path: &Path<'a>,
) -> Vec<RuleError> { ) -> Vec<RuleError>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut errors: Vec<RuleError> = vec![]; let mut errors: Vec<RuleError> = vec![];
match *value { match *value {
InputValue::String(ref name) | InputValue::Enum(ref name) => { InputValue::Scalar(ref scalar) if scalar.is_type::<String>() => {
if let Some(ref name) = <&S as Into<Option<&String>>>::into(scalar) {
if !meta.values.iter().any(|ev| &ev.name == *name) {
errors.push(unification_error(
var_name,
var_pos,
path,
&format!(r#"Invalid value for enum "{}""#, meta.name),
))
}
}
}
InputValue::Enum(ref name) => {
if !meta.values.iter().any(|ev| &ev.name == name) { if !meta.values.iter().any(|ev| &ev.name == name) {
errors.push(unification_error( errors.push(unification_error(
var_name, var_name,
@ -221,14 +251,18 @@ fn unify_enum<'a>(
errors errors
} }
fn unify_input_object<'a>( fn unify_input_object<'a, S>(
var_name: &str, var_name: &str,
var_pos: &SourcePosition, var_pos: &SourcePosition,
value: &InputValue, value: &InputValue<S>,
meta: &InputObjectMeta, meta: &InputObjectMeta<S>,
schema: &SchemaType, schema: &SchemaType<S>,
path: &Path<'a>, path: &Path<'a>,
) -> Vec<RuleError> { ) -> Vec<RuleError>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut errors: Vec<RuleError> = vec![]; let mut errors: Vec<RuleError> = vec![];
if let Some(ref obj) = value.to_object_value() { if let Some(ref obj) = value.to_object_value() {
@ -282,7 +316,10 @@ fn unify_input_object<'a>(
errors errors
} }
fn is_absent_or_null(v: Option<&InputValue>) -> bool { fn is_absent_or_null<S>(v: Option<&InputValue<S>>) -> bool
where
S: ScalarValue,
{
v.map_or(true, InputValue::is_null) v.map_or(true, InputValue::is_null)
} }

View file

@ -8,12 +8,12 @@ mod traits;
mod visitor; mod visitor;
#[cfg(test)] #[cfg(test)]
mod test_harness; pub(crate) mod test_harness;
pub use self::context::{RuleError, ValidatorContext}; pub use self::context::{RuleError, ValidatorContext};
pub use self::input_value::validate_input_values; pub use self::input_value::validate_input_values;
pub use self::multi_visitor::{MultiVisitor, MultiVisitorNil}; pub use self::multi_visitor::MultiVisitorNil;
pub use self::rules::visit_all_rules; pub(crate) use self::rules::visit_all_rules;
pub use self::traits::Visitor; pub use self::traits::Visitor;
pub use self::visitor::visit; pub use self::visitor::visit;

View file

@ -4,250 +4,260 @@ use ast::{
}; };
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[doc(hidden)] #[doc(hidden)]
pub trait MultiVisitor<'a> { pub struct MultiVisitorNil;
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, f: F);
fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self> impl MultiVisitorNil {
where pub fn with<V>(self, visitor: V) -> MultiVisitorCons<V, Self> {
Self: Sized,
{
MultiVisitorCons(visitor, self) MultiVisitorCons(visitor, self)
} }
} }
#[doc(hidden)]
pub struct MultiVisitorNil;
impl<'a> MultiVisitor<'a> for MultiVisitorNil {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, _: F) {}
}
#[doc(hidden)] #[doc(hidden)]
pub struct MultiVisitorCons<A, B>(A, B); pub struct MultiVisitorCons<A, B>(A, B);
impl<'a, A: Visitor<'a>, B: MultiVisitor<'a>> MultiVisitor<'a> for MultiVisitorCons<A, B> { impl<A, B> MultiVisitorCons<A, B> {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, mut f: F) { pub fn with<V>(self, visitor: V) -> MultiVisitorCons<V, Self> {
f(&mut self.0); MultiVisitorCons(visitor, self)
self.1.visit_all(f);
} }
} }
impl<'a, M> Visitor<'a> for M impl<'a, S> Visitor<'a, S> for MultiVisitorNil where S: ScalarValue {}
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));
}
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { impl<'a, A, B, S> Visitor<'a, S> for MultiVisitorCons<A, B>
self.visit_all(|v| v.exit_document(ctx, doc)); where
S: ScalarValue,
A: Visitor<'a, S> + 'a,
B: Visitor<'a, S> + 'a,
{
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>) {
self.0.enter_document(ctx, doc);
self.1.enter_document(ctx, doc);
}
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>) {
self.0.exit_document(ctx, doc);
self.1.exit_document(ctx, doc);
} }
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
self.visit_all(|v| v.enter_operation_definition(ctx, op)); self.0.enter_operation_definition(ctx, op);
self.1.enter_operation_definition(ctx, op);
} }
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
self.visit_all(|v| v.exit_operation_definition(ctx, op)); self.0.exit_operation_definition(ctx, op);
self.1.exit_operation_definition(ctx, op);
} }
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
self.visit_all(|v| v.enter_fragment_definition(ctx, f)); self.0.enter_fragment_definition(ctx, f);
self.1.enter_fragment_definition(ctx, f);
} }
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
self.visit_all(|v| v.exit_fragment_definition(ctx, f)); self.0.exit_fragment_definition(ctx, f);
self.1.exit_fragment_definition(ctx, f);
} }
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
def: &'a (Spanning<&'a str>, VariableDefinition), def: &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
self.visit_all(|v| v.enter_variable_definition(ctx, def)); self.0.enter_variable_definition(ctx, def);
self.1.enter_variable_definition(ctx, def);
} }
fn exit_variable_definition( fn exit_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
def: &'a (Spanning<&'a str>, VariableDefinition), def: &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
self.visit_all(|v| v.exit_variable_definition(ctx, def)); self.0.exit_variable_definition(ctx, def);
self.1.exit_variable_definition(ctx, def);
} }
fn enter_directive(&mut self, ctx: &mut ValidatorContext<'a>, d: &'a Spanning<Directive>) { fn enter_directive(
self.visit_all(|v| v.enter_directive(ctx, d)); &mut self,
ctx: &mut ValidatorContext<'a, S>,
d: &'a Spanning<Directive<S>>,
) {
self.0.enter_directive(ctx, d);
self.1.enter_directive(ctx, d);
} }
fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a>, d: &'a Spanning<Directive>) { fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a, S>, d: &'a Spanning<Directive<S>>) {
self.visit_all(|v| v.exit_directive(ctx, d)); self.0.exit_directive(ctx, d);
self.1.exit_directive(ctx, d);
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>), arg: &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
self.visit_all(|v| v.enter_argument(ctx, arg)); self.0.enter_argument(ctx, arg);
self.1.enter_argument(ctx, arg);
} }
fn exit_argument( fn exit_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>), arg: &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
self.visit_all(|v| v.exit_argument(ctx, arg)); self.0.exit_argument(ctx, arg);
self.1.exit_argument(ctx, arg);
} }
fn enter_selection_set(&mut self, ctx: &mut ValidatorContext<'a>, s: &'a Vec<Selection>) { fn enter_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec<Selection<S>>) {
self.visit_all(|v| v.enter_selection_set(ctx, s)); self.0.enter_selection_set(ctx, s);
self.1.enter_selection_set(ctx, s);
} }
fn exit_selection_set(&mut self, ctx: &mut ValidatorContext<'a>, s: &'a Vec<Selection>) { fn exit_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec<Selection<S>>) {
self.visit_all(|v| v.exit_selection_set(ctx, s)); self.0.exit_selection_set(ctx, s);
self.1.exit_selection_set(ctx, s);
} }
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<Field>) { fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning<Field<S>>) {
self.visit_all(|v| v.enter_field(ctx, f)); self.0.enter_field(ctx, f);
self.1.enter_field(ctx, f);
} }
fn exit_field(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning<Field>) { fn exit_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning<Field<S>>) {
self.visit_all(|v| v.exit_field(ctx, f)); self.0.exit_field(ctx, f);
self.1.exit_field(ctx, f);
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
s: &'a Spanning<FragmentSpread>, s: &'a Spanning<FragmentSpread<S>>,
) { ) {
self.visit_all(|v| v.enter_fragment_spread(ctx, s)); self.0.enter_fragment_spread(ctx, s);
self.1.enter_fragment_spread(ctx, s);
} }
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
s: &'a Spanning<FragmentSpread>, s: &'a Spanning<FragmentSpread<S>>,
) { ) {
self.visit_all(|v| v.exit_fragment_spread(ctx, s)); self.0.exit_fragment_spread(ctx, s);
self.1.exit_fragment_spread(ctx, s);
} }
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a Spanning<InlineFragment>, f: &'a Spanning<InlineFragment<S>>,
) { ) {
self.visit_all(|v| v.enter_inline_fragment(ctx, f)); self.0.enter_inline_fragment(ctx, f);
self.1.enter_inline_fragment(ctx, f);
} }
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a Spanning<InlineFragment>, f: &'a Spanning<InlineFragment<S>>,
) { ) {
self.visit_all(|v| v.exit_inline_fragment(ctx, f)); self.0.exit_inline_fragment(ctx, f);
self.1.exit_inline_fragment(ctx, f);
} }
fn enter_null_value(&mut self, ctx: &mut ValidatorContext<'a>, n: Spanning<()>) { fn enter_null_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<()>) {
self.visit_all(|v| v.enter_null_value(ctx, n.clone())); self.0.enter_null_value(ctx, n);
self.1.enter_null_value(ctx, n);
} }
fn exit_null_value(&mut self, ctx: &mut ValidatorContext<'a>, n: Spanning<()>) { fn exit_null_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<()>) {
self.visit_all(|v| v.exit_null_value(ctx, n.clone())); self.0.exit_null_value(ctx, n);
self.1.exit_null_value(ctx, n);
} }
fn enter_int_value(&mut self, ctx: &mut ValidatorContext<'a>, i: Spanning<i32>) { fn enter_scalar_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<&'a S>) {
self.visit_all(|v| v.enter_int_value(ctx, i.clone())); self.0.enter_scalar_value(ctx, n);
self.1.enter_scalar_value(ctx, n);
} }
fn exit_int_value(&mut self, ctx: &mut ValidatorContext<'a>, i: Spanning<i32>) { fn exit_scalar_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<&'a S>) {
self.visit_all(|v| v.exit_int_value(ctx, i.clone())); self.0.exit_scalar_value(ctx, n);
self.1.exit_scalar_value(ctx, n);
} }
fn enter_float_value(&mut self, ctx: &mut ValidatorContext<'a>, f: Spanning<f64>) { fn enter_enum_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
self.visit_all(|v| v.enter_float_value(ctx, f.clone())); self.0.enter_enum_value(ctx, s);
self.1.enter_enum_value(ctx, s);
} }
fn exit_float_value(&mut self, ctx: &mut ValidatorContext<'a>, f: Spanning<f64>) { fn exit_enum_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
self.visit_all(|v| v.exit_float_value(ctx, f.clone())); self.0.exit_enum_value(ctx, s);
self.1.exit_enum_value(ctx, s);
} }
fn enter_string_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { fn enter_variable_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
self.visit_all(|v| v.enter_string_value(ctx, s.clone())); self.0.enter_variable_value(ctx, s);
self.1.enter_variable_value(ctx, s);
} }
fn exit_string_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { fn exit_variable_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
self.visit_all(|v| v.exit_string_value(ctx, s.clone())); self.0.exit_variable_value(ctx, s);
} self.1.exit_variable_value(ctx, s);
fn enter_boolean_value(&mut self, ctx: &mut ValidatorContext<'a>, b: Spanning<bool>) {
self.visit_all(|v| v.enter_boolean_value(ctx, b.clone()));
}
fn exit_boolean_value(&mut self, ctx: &mut ValidatorContext<'a>, b: Spanning<bool>) {
self.visit_all(|v| v.exit_boolean_value(ctx, b.clone()));
}
fn enter_enum_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) {
self.visit_all(|v| v.enter_enum_value(ctx, s.clone()));
}
fn exit_enum_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) {
self.visit_all(|v| v.exit_enum_value(ctx, s.clone()));
}
fn enter_variable_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) {
self.visit_all(|v| v.enter_variable_value(ctx, s.clone()));
}
fn exit_variable_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) {
self.visit_all(|v| v.exit_variable_value(ctx, s.clone()));
} }
fn enter_list_value( fn enter_list_value(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
l: Spanning<&'a Vec<Spanning<InputValue>>>, l: Spanning<&'a Vec<Spanning<InputValue<S>>>>,
) { ) {
self.visit_all(|v| v.enter_list_value(ctx, l.clone())); self.0.enter_list_value(ctx, l);
self.1.enter_list_value(ctx, l);
} }
fn exit_list_value( fn exit_list_value(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
l: Spanning<&'a Vec<Spanning<InputValue>>>, l: Spanning<&'a Vec<Spanning<InputValue<S>>>>,
) { ) {
self.visit_all(|v| v.exit_list_value(ctx, l.clone())); self.0.exit_list_value(ctx, l);
self.1.exit_list_value(ctx, l);
} }
fn enter_object_value( fn enter_object_value(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
self.visit_all(|v| v.enter_object_value(ctx, o.clone())); self.0.enter_object_value(ctx, o);
self.1.enter_object_value(ctx, o);
} }
fn exit_object_value( fn exit_object_value(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
self.visit_all(|v| v.exit_object_value(ctx, o.clone())); self.0.exit_object_value(ctx, o);
self.1.exit_object_value(ctx, o);
} }
fn enter_object_field( fn enter_object_field(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a (Spanning<String>, Spanning<InputValue>), f: &'a (Spanning<String>, Spanning<InputValue<S>>),
) { ) {
self.visit_all(|v| v.enter_object_field(ctx, f)); self.0.enter_object_field(ctx, f);
self.1.enter_object_field(ctx, f);
} }
fn exit_object_field( fn exit_object_field(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
f: &'a (Spanning<String>, Spanning<InputValue>), f: &'a (Spanning<String>, Spanning<InputValue<S>>),
) { ) {
self.visit_all(|v| v.exit_object_field(ctx, f)); self.0.exit_object_field(ctx, f);
self.1.exit_object_field(ctx, f);
} }
} }

View file

@ -1,48 +1,56 @@
use ast::{Directive, Field, InputValue}; use ast::{Directive, Field, InputValue};
use parser::Spanning; use parser::Spanning;
use schema::meta::Argument; use schema::meta::Argument;
use std::fmt::Debug;
use types::utilities::is_valid_literal_value; use types::utilities::is_valid_literal_value;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct ArgumentsOfCorrectType<'a> { pub struct ArgumentsOfCorrectType<'a, S: Debug + 'a> {
current_args: Option<&'a Vec<Argument<'a>>>, current_args: Option<&'a Vec<Argument<'a, S>>>,
} }
pub fn factory<'a>() -> ArgumentsOfCorrectType<'a> { pub fn factory<'a, S: Debug>() -> ArgumentsOfCorrectType<'a, S> {
ArgumentsOfCorrectType { current_args: None } ArgumentsOfCorrectType { current_args: None }
} }
impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> { impl<'a, S> Visitor<'a, S> for ArgumentsOfCorrectType<'a, S>
where
S: ScalarValue,
{
fn enter_directive( fn enter_directive(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
directive: &'a Spanning<Directive>, directive: &'a Spanning<Directive<S>>,
) { ) {
self.current_args = ctx.schema self.current_args = ctx
.schema
.directive_by_name(directive.item.name.item) .directive_by_name(directive.item.name.item)
.map(|d| &d.arguments); .map(|d| &d.arguments);
} }
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) { fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Directive<S>>) {
self.current_args = None; self.current_args = None;
} }
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning<Field<S>>) {
self.current_args = ctx.parent_type() self.current_args = ctx
.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item)) .and_then(|t| t.field_by_name(field.item.name.item))
.and_then(|f| f.arguments.as_ref()); .and_then(|f| f.arguments.as_ref());
} }
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {
self.current_args = None; self.current_args = None;
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning<InputValue>), &(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
if let Some(argument_meta) = self.current_args if let Some(argument_meta) = self
.current_args
.and_then(|args| args.iter().find(|a| a.name == arg_name.item)) .and_then(|args| args.iter().find(|a| a.name == arg_name.item))
{ {
let meta_type = ctx.schema.make_type(&argument_meta.arg_type); let meta_type = ctx.schema.make_type(&argument_meta.arg_type);
@ -70,10 +78,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn good_null_value() { fn good_null_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -87,7 +96,7 @@ mod tests {
#[test] #[test]
fn null_into_int() { fn null_into_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -105,7 +114,7 @@ mod tests {
#[test] #[test]
fn good_int_value() { fn good_int_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -119,7 +128,7 @@ mod tests {
#[test] #[test]
fn good_boolean_value() { fn good_boolean_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -133,7 +142,7 @@ mod tests {
#[test] #[test]
fn good_string_value() { fn good_string_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -147,7 +156,7 @@ mod tests {
#[test] #[test]
fn good_float_value() { fn good_float_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -161,7 +170,7 @@ mod tests {
#[test] #[test]
fn int_into_float() { fn int_into_float() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -175,7 +184,7 @@ mod tests {
#[test] #[test]
fn int_into_id() { fn int_into_id() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -189,7 +198,7 @@ mod tests {
#[test] #[test]
fn string_into_id() { fn string_into_id() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -203,7 +212,7 @@ mod tests {
#[test] #[test]
fn good_enum_value() { fn good_enum_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -217,7 +226,7 @@ mod tests {
#[test] #[test]
fn int_into_string() { fn int_into_string() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -235,7 +244,7 @@ mod tests {
#[test] #[test]
fn float_into_string() { fn float_into_string() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -253,7 +262,7 @@ mod tests {
#[test] #[test]
fn boolean_into_string() { fn boolean_into_string() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -271,7 +280,7 @@ mod tests {
#[test] #[test]
fn unquoted_string_into_string() { fn unquoted_string_into_string() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -289,7 +298,7 @@ mod tests {
#[test] #[test]
fn string_into_int() { fn string_into_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -307,7 +316,7 @@ mod tests {
#[test] #[test]
fn unquoted_string_into_int() { fn unquoted_string_into_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -325,7 +334,7 @@ mod tests {
#[test] #[test]
fn simple_float_into_int() { fn simple_float_into_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -343,7 +352,7 @@ mod tests {
#[test] #[test]
fn float_into_int() { fn float_into_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -361,7 +370,7 @@ mod tests {
#[test] #[test]
fn string_into_float() { fn string_into_float() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -379,7 +388,7 @@ mod tests {
#[test] #[test]
fn boolean_into_float() { fn boolean_into_float() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -397,7 +406,7 @@ mod tests {
#[test] #[test]
fn unquoted_into_float() { fn unquoted_into_float() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -415,7 +424,7 @@ mod tests {
#[test] #[test]
fn int_into_boolean() { fn int_into_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -433,7 +442,7 @@ mod tests {
#[test] #[test]
fn float_into_boolean() { fn float_into_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -451,7 +460,7 @@ mod tests {
#[test] #[test]
fn string_into_boolean() { fn string_into_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -469,7 +478,7 @@ mod tests {
#[test] #[test]
fn unquoted_into_boolean() { fn unquoted_into_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -487,7 +496,7 @@ mod tests {
#[test] #[test]
fn float_into_id() { fn float_into_id() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -505,7 +514,7 @@ mod tests {
#[test] #[test]
fn boolean_into_id() { fn boolean_into_id() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -523,7 +532,7 @@ mod tests {
#[test] #[test]
fn unquoted_into_id() { fn unquoted_into_id() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -541,7 +550,7 @@ mod tests {
#[test] #[test]
fn int_into_enum() { fn int_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -559,7 +568,7 @@ mod tests {
#[test] #[test]
fn float_into_enum() { fn float_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -577,7 +586,7 @@ mod tests {
#[test] #[test]
fn string_into_enum() { fn string_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -595,7 +604,7 @@ mod tests {
#[test] #[test]
fn boolean_into_enum() { fn boolean_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -613,7 +622,7 @@ mod tests {
#[test] #[test]
fn unknown_enum_value_into_enum() { fn unknown_enum_value_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -631,7 +640,7 @@ mod tests {
#[test] #[test]
fn different_case_enum_value_into_enum() { fn different_case_enum_value_into_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -649,7 +658,7 @@ mod tests {
#[test] #[test]
fn good_list_value() { fn good_list_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -663,7 +672,7 @@ mod tests {
#[test] #[test]
fn empty_list_value() { fn empty_list_value() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -677,7 +686,7 @@ mod tests {
#[test] #[test]
fn single_value_into_list() { fn single_value_into_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -691,7 +700,7 @@ mod tests {
#[test] #[test]
fn incorrect_item_type() { fn incorrect_item_type() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -709,7 +718,7 @@ mod tests {
#[test] #[test]
fn single_value_of_incorrect_type() { fn single_value_of_incorrect_type() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -727,7 +736,7 @@ mod tests {
#[test] #[test]
fn arg_on_optional_arg() { fn arg_on_optional_arg() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -741,7 +750,7 @@ mod tests {
#[test] #[test]
fn no_arg_on_optional_arg() { fn no_arg_on_optional_arg() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -755,7 +764,7 @@ mod tests {
#[test] #[test]
fn multiple_args() { fn multiple_args() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -769,7 +778,7 @@ mod tests {
#[test] #[test]
fn multiple_args_reverse_order() { fn multiple_args_reverse_order() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -783,7 +792,7 @@ mod tests {
#[test] #[test]
fn no_args_on_multiple_optional() { fn no_args_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -797,7 +806,7 @@ mod tests {
#[test] #[test]
fn one_arg_on_multiple_optional() { fn one_arg_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -811,7 +820,7 @@ mod tests {
#[test] #[test]
fn second_arg_on_multiple_optional() { fn second_arg_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -825,7 +834,7 @@ mod tests {
#[test] #[test]
fn multiple_reqs_on_mixed_list() { fn multiple_reqs_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -839,7 +848,7 @@ mod tests {
#[test] #[test]
fn multiple_reqs_and_one_opt_on_mixed_list() { fn multiple_reqs_and_one_opt_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -853,7 +862,7 @@ mod tests {
#[test] #[test]
fn all_reqs_and_opts_on_mixed_list() { fn all_reqs_and_opts_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -867,7 +876,7 @@ mod tests {
#[test] #[test]
fn incorrect_value_type() { fn incorrect_value_type() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -891,7 +900,7 @@ mod tests {
#[test] #[test]
fn incorrect_value_and_missing_argument() { fn incorrect_value_and_missing_argument() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -909,7 +918,7 @@ mod tests {
#[test] #[test]
fn optional_arg_despite_required_field_in_type() { fn optional_arg_despite_required_field_in_type() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -923,7 +932,7 @@ mod tests {
#[test] #[test]
fn partial_object_only_required() { fn partial_object_only_required() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -937,7 +946,7 @@ mod tests {
#[test] #[test]
fn partial_object_required_field_can_be_falsy() { fn partial_object_required_field_can_be_falsy() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -951,7 +960,7 @@ mod tests {
#[test] #[test]
fn partial_object_including_required() { fn partial_object_including_required() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -965,7 +974,7 @@ mod tests {
#[test] #[test]
fn full_object() { fn full_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -985,7 +994,7 @@ mod tests {
#[test] #[test]
fn full_object_with_fields_in_different_order() { fn full_object_with_fields_in_different_order() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -1005,7 +1014,7 @@ mod tests {
#[test] #[test]
fn partial_object_missing_required() { fn partial_object_missing_required() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -1023,7 +1032,7 @@ mod tests {
#[test] #[test]
fn partial_object_invalid_field_type() { fn partial_object_invalid_field_type() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -1044,7 +1053,7 @@ mod tests {
#[test] #[test]
fn partial_object_unknown_field_arg() { fn partial_object_unknown_field_arg() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -1065,7 +1074,7 @@ mod tests {
#[test] #[test]
fn directive_with_valid_types() { fn directive_with_valid_types() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -1082,7 +1091,7 @@ mod tests {
#[test] #[test]
fn directive_with_incorrect_types() { fn directive_with_incorrect_types() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -2,18 +2,22 @@ use ast::VariableDefinition;
use parser::Spanning; use parser::Spanning;
use types::utilities::is_valid_literal_value; use types::utilities::is_valid_literal_value;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct DefaultValuesOfCorrectType {} pub struct DefaultValuesOfCorrectType;
pub fn factory() -> DefaultValuesOfCorrectType { pub fn factory() -> DefaultValuesOfCorrectType {
DefaultValuesOfCorrectType {} DefaultValuesOfCorrectType
} }
impl<'a> Visitor<'a> for DefaultValuesOfCorrectType { impl<'a, S> Visitor<'a, S> for DefaultValuesOfCorrectType
where
S: ScalarValue,
{
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition), &(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
if let Some(Spanning { if let Some(Spanning {
item: ref var_value, item: ref var_value,
@ -60,10 +64,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn variables_with_no_default_values() { fn variables_with_no_default_values() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query NullableValues($a: Int, $b: String, $c: ComplexInput) { query NullableValues($a: Int, $b: String, $c: ComplexInput) {
@ -75,7 +80,7 @@ mod tests {
#[test] #[test]
fn required_variables_without_default_values() { fn required_variables_without_default_values() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query RequiredValues($a: Int!, $b: String!) { query RequiredValues($a: Int!, $b: String!) {
@ -87,7 +92,7 @@ mod tests {
#[test] #[test]
fn variables_with_valid_default_values() { fn variables_with_valid_default_values() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query WithDefaultValues( query WithDefaultValues(
@ -103,7 +108,7 @@ mod tests {
#[test] #[test]
fn no_required_variables_with_default_values() { fn no_required_variables_with_default_values() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") { query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
@ -125,7 +130,7 @@ mod tests {
#[test] #[test]
fn variables_with_invalid_default_values() { fn variables_with_invalid_default_values() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query InvalidDefaultValues( query InvalidDefaultValues(
@ -155,7 +160,7 @@ mod tests {
#[test] #[test]
fn complex_variables_missing_required_field() { fn complex_variables_missing_required_field() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query MissingRequiredField($a: ComplexInput = {intField: 3}) { query MissingRequiredField($a: ComplexInput = {intField: 3}) {
@ -171,7 +176,7 @@ mod tests {
#[test] #[test]
fn list_variables_with_invalid_item() { fn list_variables_with_invalid_item() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query InvalidItem($a: [String] = ["one", 2]) { query InvalidItem($a: [String] = ["one", 2]) {

View file

@ -2,15 +2,24 @@ use ast::Field;
use parser::Spanning; use parser::Spanning;
use schema::meta::MetaType; use schema::meta::MetaType;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct FieldsOnCorrectType {} pub struct FieldsOnCorrectType;
pub fn factory() -> FieldsOnCorrectType { pub fn factory() -> FieldsOnCorrectType {
FieldsOnCorrectType {} FieldsOnCorrectType
} }
impl<'a> Visitor<'a> for FieldsOnCorrectType { impl<'a, S> Visitor<'a, S> for FieldsOnCorrectType
fn enter_field(&mut self, context: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { where
S: ScalarValue,
{
fn enter_field(
&mut self,
context: &mut ValidatorContext<'a, S>,
field: &'a Spanning<Field<S>>,
) {
{ {
if let Some(parent_type) = context.parent_type() { if let Some(parent_type) = context.parent_type() {
let field_name = &field.item.name; let field_name = &field.item.name;
@ -49,10 +58,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn selection_on_object() { fn selection_on_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectFieldSelection on Dog { fragment objectFieldSelection on Dog {
@ -65,7 +75,7 @@ mod tests {
#[test] #[test]
fn aliased_selection_on_object() { fn aliased_selection_on_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment aliasedObjectFieldSelection on Dog { fragment aliasedObjectFieldSelection on Dog {
@ -78,7 +88,7 @@ mod tests {
#[test] #[test]
fn selection_on_interface() { fn selection_on_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceFieldSelection on Pet { fragment interfaceFieldSelection on Pet {
@ -91,7 +101,7 @@ mod tests {
#[test] #[test]
fn aliased_selection_on_interface() { fn aliased_selection_on_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceFieldSelection on Pet { fragment interfaceFieldSelection on Pet {
@ -103,7 +113,7 @@ mod tests {
#[test] #[test]
fn lying_alias_selection() { fn lying_alias_selection() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment lyingAliasSelection on Dog { fragment lyingAliasSelection on Dog {
@ -115,7 +125,7 @@ mod tests {
#[test] #[test]
fn ignores_unknown_type() { fn ignores_unknown_type() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment unknownSelection on UnknownType { fragment unknownSelection on UnknownType {
@ -127,7 +137,7 @@ mod tests {
#[test] #[test]
fn nested_unknown_fields() { fn nested_unknown_fields() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment typeKnownAgain on Pet { fragment typeKnownAgain on Pet {
@ -153,7 +163,7 @@ mod tests {
#[test] #[test]
fn unknown_field_on_fragment() { fn unknown_field_on_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fieldNotDefined on Dog { fragment fieldNotDefined on Dog {
@ -169,7 +179,7 @@ mod tests {
#[test] #[test]
fn ignores_deeply_unknown_field() { fn ignores_deeply_unknown_field() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment deepFieldNotDefined on Dog { fragment deepFieldNotDefined on Dog {
@ -187,7 +197,7 @@ mod tests {
#[test] #[test]
fn unknown_subfield() { fn unknown_subfield() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment subFieldNotDefined on Human { fragment subFieldNotDefined on Human {
@ -205,7 +215,7 @@ mod tests {
#[test] #[test]
fn unknown_field_on_inline_fragment() { fn unknown_field_on_inline_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fieldNotDefined on Pet { fragment fieldNotDefined on Pet {
@ -223,7 +233,7 @@ mod tests {
#[test] #[test]
fn unknown_aliased_target() { fn unknown_aliased_target() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment aliasedFieldTargetNotDefined on Dog { fragment aliasedFieldTargetNotDefined on Dog {
@ -239,7 +249,7 @@ mod tests {
#[test] #[test]
fn unknown_aliased_lying_field_target() { fn unknown_aliased_lying_field_target() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment aliasedLyingFieldTargetNotDefined on Dog { fragment aliasedLyingFieldTargetNotDefined on Dog {
@ -255,7 +265,7 @@ mod tests {
#[test] #[test]
fn not_defined_on_interface() { fn not_defined_on_interface() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment notDefinedOnInterface on Pet { fragment notDefinedOnInterface on Pet {
@ -271,7 +281,7 @@ mod tests {
#[test] #[test]
fn defined_in_concrete_types_but_not_interface() { fn defined_in_concrete_types_but_not_interface() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment definedOnImplementorsButNotInterface on Pet { fragment definedOnImplementorsButNotInterface on Pet {
@ -287,7 +297,7 @@ mod tests {
#[test] #[test]
fn meta_field_on_union() { fn meta_field_on_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment definedOnImplementorsButNotInterface on Pet { fragment definedOnImplementorsButNotInterface on Pet {
@ -299,7 +309,7 @@ mod tests {
#[test] #[test]
fn fields_on_union() { fn fields_on_union() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment definedOnImplementorsQueriedOnUnion on CatOrDog { fragment definedOnImplementorsQueriedOnUnion on CatOrDog {
@ -315,7 +325,7 @@ mod tests {
#[test] #[test]
fn typename_on_union() { fn typename_on_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectFieldSelection on Pet { fragment objectFieldSelection on Pet {
@ -333,7 +343,7 @@ mod tests {
#[test] #[test]
fn valid_field_in_inline_fragment() { fn valid_field_in_inline_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectFieldSelection on Pet { fragment objectFieldSelection on Pet {

View file

@ -1,18 +1,22 @@
use ast::{Fragment, InlineFragment}; use ast::{Fragment, InlineFragment};
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct FragmentsOnCompositeTypes {} pub struct FragmentsOnCompositeTypes;
pub fn factory() -> FragmentsOnCompositeTypes { pub fn factory() -> FragmentsOnCompositeTypes {
FragmentsOnCompositeTypes {} FragmentsOnCompositeTypes
} }
impl<'a> Visitor<'a> for FragmentsOnCompositeTypes { impl<'a, S> Visitor<'a, S> for FragmentsOnCompositeTypes
where
S: ScalarValue,
{
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
context: &mut ValidatorContext<'a>, context: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
{ {
if let Some(current_type) = context.current_type() { if let Some(current_type) = context.current_type() {
@ -31,8 +35,8 @@ impl<'a> Visitor<'a> for FragmentsOnCompositeTypes {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
context: &mut ValidatorContext<'a>, context: &mut ValidatorContext<'a, S>,
f: &'a Spanning<InlineFragment>, f: &'a Spanning<InlineFragment<S>>,
) { ) {
{ {
if let Some(ref type_cond) = f.item.type_condition { if let Some(ref type_cond) = f.item.type_condition {
@ -71,10 +75,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn on_object() { fn on_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment validFragment on Dog { fragment validFragment on Dog {
@ -86,7 +91,7 @@ mod tests {
#[test] #[test]
fn on_interface() { fn on_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment validFragment on Pet { fragment validFragment on Pet {
@ -98,7 +103,7 @@ mod tests {
#[test] #[test]
fn on_object_inline() { fn on_object_inline() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment validFragment on Pet { fragment validFragment on Pet {
@ -112,7 +117,7 @@ mod tests {
#[test] #[test]
fn on_inline_without_type_cond() { fn on_inline_without_type_cond() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment validFragment on Pet { fragment validFragment on Pet {
@ -126,7 +131,7 @@ mod tests {
#[test] #[test]
fn on_union() { fn on_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment validFragment on CatOrDog { fragment validFragment on CatOrDog {
@ -138,7 +143,7 @@ mod tests {
#[test] #[test]
fn not_on_scalar() { fn not_on_scalar() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarFragment on Boolean { fragment scalarFragment on Boolean {
@ -154,7 +159,7 @@ mod tests {
#[test] #[test]
fn not_on_enum() { fn not_on_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarFragment on FurColor { fragment scalarFragment on FurColor {
@ -170,7 +175,7 @@ mod tests {
#[test] #[test]
fn not_on_input_object() { fn not_on_input_object() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment inputFragment on ComplexInput { fragment inputFragment on ComplexInput {
@ -186,7 +191,7 @@ mod tests {
#[test] #[test]
fn not_on_scalar_inline() { fn not_on_scalar_inline() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidFragment on Pet { fragment invalidFragment on Pet {

View file

@ -1,7 +1,9 @@
use ast::{Directive, Field, InputValue}; use ast::{Directive, Field, InputValue};
use parser::Spanning; use parser::Spanning;
use schema::meta::Argument; use schema::meta::Argument;
use std::fmt::Debug;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[derive(Debug)] #[derive(Debug)]
enum ArgumentPosition<'a> { enum ArgumentPosition<'a> {
@ -9,21 +11,25 @@ enum ArgumentPosition<'a> {
Field(&'a str, &'a str), Field(&'a str, &'a str),
} }
pub struct KnownArgumentNames<'a> { pub struct KnownArgumentNames<'a, S: Debug + 'a> {
current_args: Option<(ArgumentPosition<'a>, &'a Vec<Argument<'a>>)>, current_args: Option<(ArgumentPosition<'a>, &'a Vec<Argument<'a, S>>)>,
} }
pub fn factory<'a>() -> KnownArgumentNames<'a> { pub fn factory<'a, S: Debug>() -> KnownArgumentNames<'a, S> {
KnownArgumentNames { current_args: None } KnownArgumentNames { current_args: None }
} }
impl<'a> Visitor<'a> for KnownArgumentNames<'a> { impl<'a, S> Visitor<'a, S> for KnownArgumentNames<'a, S>
where
S: ScalarValue,
{
fn enter_directive( fn enter_directive(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
directive: &'a Spanning<Directive>, directive: &'a Spanning<Directive<S>>,
) { ) {
self.current_args = ctx.schema self.current_args = ctx
.schema
.directive_by_name(directive.item.name.item) .directive_by_name(directive.item.name.item)
.map(|d| { .map(|d| {
( (
@ -33,12 +39,13 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
}); });
} }
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) { fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Directive<S>>) {
self.current_args = None; self.current_args = None;
} }
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning<Field<S>>) {
self.current_args = ctx.parent_type() self.current_args = ctx
.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item)) .and_then(|t| t.field_by_name(field.item.name.item))
.and_then(|f| f.arguments.as_ref()) .and_then(|f| f.arguments.as_ref())
.map(|args| { .map(|args| {
@ -55,14 +62,14 @@ impl<'a> Visitor<'a> for KnownArgumentNames<'a> {
}); });
} }
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {
self.current_args = None; self.current_args = None;
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>), &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
if let Some((ref pos, args)) = self.current_args { if let Some((ref pos, args)) = self.current_args {
if args.iter().find(|a| a.name == arg_name.item).is_none() { if args.iter().find(|a| a.name == arg_name.item).is_none() {
@ -101,10 +108,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn single_arg_is_known() { fn single_arg_is_known() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment argOnRequiredArg on Dog { fragment argOnRequiredArg on Dog {
@ -116,7 +124,7 @@ mod tests {
#[test] #[test]
fn multiple_args_are_known() { fn multiple_args_are_known() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment multipleArgs on ComplicatedArgs { fragment multipleArgs on ComplicatedArgs {
@ -128,7 +136,7 @@ mod tests {
#[test] #[test]
fn ignores_args_of_unknown_fields() { fn ignores_args_of_unknown_fields() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment argOnUnknownField on Dog { fragment argOnUnknownField on Dog {
@ -140,7 +148,7 @@ mod tests {
#[test] #[test]
fn multiple_args_in_reverse_order_are_known() { fn multiple_args_in_reverse_order_are_known() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment multipleArgsReverseOrder on ComplicatedArgs { fragment multipleArgsReverseOrder on ComplicatedArgs {
@ -152,7 +160,7 @@ mod tests {
#[test] #[test]
fn no_args_on_optional_arg() { fn no_args_on_optional_arg() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment noArgOnOptionalArg on Dog { fragment noArgOnOptionalArg on Dog {
@ -164,7 +172,7 @@ mod tests {
#[test] #[test]
fn args_are_known_deeply() { fn args_are_known_deeply() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -185,7 +193,7 @@ mod tests {
#[test] #[test]
fn directive_args_are_known() { fn directive_args_are_known() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -197,7 +205,7 @@ mod tests {
#[test] #[test]
fn undirective_args_are_invalid() { fn undirective_args_are_invalid() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -213,7 +221,7 @@ mod tests {
#[test] #[test]
fn invalid_arg_name() { fn invalid_arg_name() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidArgName on Dog { fragment invalidArgName on Dog {
@ -229,7 +237,7 @@ mod tests {
#[test] #[test]
fn unknown_args_amongst_known_args() { fn unknown_args_amongst_known_args() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment oneGoodArgOneInvalidArg on Dog { fragment oneGoodArgOneInvalidArg on Dog {
@ -251,7 +259,7 @@ mod tests {
#[test] #[test]
fn unknown_args_deeply() { fn unknown_args_deeply() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -2,6 +2,7 @@ use ast::{Directive, Field, Fragment, FragmentSpread, InlineFragment, Operation,
use parser::Spanning; use parser::Spanning;
use schema::model::DirectiveLocation; use schema::model::DirectiveLocation;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct KnownDirectives { pub struct KnownDirectives {
location_stack: Vec<DirectiveLocation>, location_stack: Vec<DirectiveLocation>,
@ -13,11 +14,15 @@ pub fn factory() -> KnownDirectives {
} }
} }
impl<'a> Visitor<'a> for KnownDirectives { impl<'a, S> Visitor<'a, S> for KnownDirectives
where
S: ScalarValue,
{
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
self.location_stack.push(match op.item.operation_type { self.location_stack.push(match op.item.operation_type {
OperationType::Query => DirectiveLocation::Query, OperationType::Query => DirectiveLocation::Query,
@ -27,26 +32,26 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Operation>, _: &'a Spanning<Operation<S>>,
) { ) {
let top = self.location_stack.pop(); let top = self.location_stack.pop();
assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation)); assert!(top == Some(DirectiveLocation::Query) || top == Some(DirectiveLocation::Mutation));
} }
fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {
self.location_stack.push(DirectiveLocation::Field); self.location_stack.push(DirectiveLocation::Field);
} }
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {
let top = self.location_stack.pop(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::Field)); assert_eq!(top, Some(DirectiveLocation::Field));
} }
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Fragment>, _: &'a Spanning<Fragment<S>>,
) { ) {
self.location_stack self.location_stack
.push(DirectiveLocation::FragmentDefinition); .push(DirectiveLocation::FragmentDefinition);
@ -54,8 +59,8 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Fragment>, _: &'a Spanning<Fragment<S>>,
) { ) {
let top = self.location_stack.pop(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::FragmentDefinition)); assert_eq!(top, Some(DirectiveLocation::FragmentDefinition));
@ -63,16 +68,16 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<FragmentSpread>, _: &'a Spanning<FragmentSpread<S>>,
) { ) {
self.location_stack.push(DirectiveLocation::FragmentSpread); self.location_stack.push(DirectiveLocation::FragmentSpread);
} }
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<FragmentSpread>, _: &'a Spanning<FragmentSpread<S>>,
) { ) {
let top = self.location_stack.pop(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::FragmentSpread)); assert_eq!(top, Some(DirectiveLocation::FragmentSpread));
@ -80,16 +85,16 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<InlineFragment>, _: &'a Spanning<InlineFragment<S>>,
) { ) {
self.location_stack.push(DirectiveLocation::InlineFragment); self.location_stack.push(DirectiveLocation::InlineFragment);
} }
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<InlineFragment>, _: &'a Spanning<InlineFragment<S>>,
) { ) {
let top = self.location_stack.pop(); let top = self.location_stack.pop();
assert_eq!(top, Some(DirectiveLocation::InlineFragment)); assert_eq!(top, Some(DirectiveLocation::InlineFragment));
@ -97,8 +102,8 @@ impl<'a> Visitor<'a> for KnownDirectives {
fn enter_directive( fn enter_directive(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
directive: &'a Spanning<Directive>, directive: &'a Spanning<Directive<S>>,
) { ) {
let directive_name = &directive.item.name.item; let directive_name = &directive.item.name.item;
@ -143,10 +148,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use schema::model::DirectiveLocation; use schema::model::DirectiveLocation;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn with_no_directives() { fn with_no_directives() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -163,7 +169,7 @@ mod tests {
#[test] #[test]
fn with_known_directives() { fn with_known_directives() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -180,7 +186,7 @@ mod tests {
#[test] #[test]
fn with_unknown_directive() { fn with_unknown_directive() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -198,7 +204,7 @@ mod tests {
#[test] #[test]
fn with_many_unknown_directives() { fn with_many_unknown_directives() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -232,7 +238,7 @@ mod tests {
#[test] #[test]
fn with_well_placed_directives() { fn with_well_placed_directives() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo @onQuery { query Foo @onQuery {
@ -251,7 +257,7 @@ mod tests {
#[test] #[test]
fn with_misplaced_directives() { fn with_misplaced_directives() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo @include(if: true) { query Foo @include(if: true) {

View file

@ -1,18 +1,23 @@
use ast::FragmentSpread; use ast::FragmentSpread;
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct KnownFragmentNames {} pub struct KnownFragmentNames;
pub fn factory() -> KnownFragmentNames { pub fn factory() -> KnownFragmentNames {
KnownFragmentNames {} KnownFragmentNames
} }
impl<'a> Visitor<'a> for KnownFragmentNames { impl<'a, S> Visitor<'a, S> for KnownFragmentNames
where
S: ScalarValue,
{
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
context: &mut ValidatorContext<'a>, context: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
let spread_name = &spread.item.name; let spread_name = &spread.item.name;
if !context.is_known_fragment(spread_name.item) { if !context.is_known_fragment(spread_name.item) {
@ -34,10 +39,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn known() { fn known() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -67,7 +73,7 @@ mod tests {
#[test] #[test]
fn unknown() { fn unknown() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -1,18 +1,23 @@
use ast::{Fragment, InlineFragment, VariableDefinition}; use ast::{Fragment, InlineFragment, VariableDefinition};
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use std::fmt::Debug;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct KnownTypeNames {} pub struct KnownTypeNames;
pub fn factory() -> KnownTypeNames { pub fn factory() -> KnownTypeNames {
KnownTypeNames {} KnownTypeNames
} }
impl<'a> Visitor<'a> for KnownTypeNames { impl<'a, S> Visitor<'a, S> for KnownTypeNames
where
S: ScalarValue,
{
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<InlineFragment>, fragment: &'a Spanning<InlineFragment<S>>,
) { ) {
if let Some(ref type_cond) = fragment.item.type_condition { if let Some(ref type_cond) = fragment.item.type_condition {
validate_type(ctx, type_cond.item, &type_cond.start); validate_type(ctx, type_cond.item, &type_cond.start);
@ -21,8 +26,8 @@ impl<'a> Visitor<'a> for KnownTypeNames {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<Fragment>, fragment: &'a Spanning<Fragment<S>>,
) { ) {
let type_cond = &fragment.item.type_condition; let type_cond = &fragment.item.type_condition;
validate_type(ctx, type_cond.item, &type_cond.start); validate_type(ctx, type_cond.item, &type_cond.start);
@ -30,15 +35,19 @@ impl<'a> Visitor<'a> for KnownTypeNames {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(_, ref var_def): &'a (Spanning<&'a str>, VariableDefinition), &(_, ref var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
let type_name = var_def.var_type.item.innermost_name(); let type_name = var_def.var_type.item.innermost_name();
validate_type(ctx, type_name, &var_def.var_type.start); validate_type(ctx, type_name, &var_def.var_type.start);
} }
} }
fn validate_type<'a>(ctx: &mut ValidatorContext<'a>, type_name: &str, location: &SourcePosition) { fn validate_type<'a, S: Debug>(
ctx: &mut ValidatorContext<'a, S>,
type_name: &str,
location: &SourcePosition,
) {
if ctx.schema.type_by_name(type_name).is_none() { if ctx.schema.type_by_name(type_name).is_none() {
ctx.report_error(&error_message(type_name), &[location.clone()]); ctx.report_error(&error_message(type_name), &[location.clone()]);
} }
@ -54,10 +63,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn known_type_names_are_valid() { fn known_type_names_are_valid() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($var: String, $required: [String!]!) { query Foo($var: String, $required: [String!]!) {
@ -74,7 +84,7 @@ mod tests {
#[test] #[test]
fn unknown_type_names_are_invalid() { fn unknown_type_names_are_invalid() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($var: JumbledUpLetters) { query Foo($var: JumbledUpLetters) {

View file

@ -1,6 +1,7 @@
use ast::{Definition, Document, Operation}; use ast::{Definition, Document, Operation};
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct LoneAnonymousOperation { pub struct LoneAnonymousOperation {
operation_count: Option<usize>, operation_count: Option<usize>,
@ -12,22 +13,24 @@ pub fn factory() -> LoneAnonymousOperation {
} }
} }
impl<'a> Visitor<'a> for LoneAnonymousOperation { impl<'a, S> Visitor<'a, S> for LoneAnonymousOperation
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, doc: &'a Document) { where
S: ScalarValue,
{
fn enter_document(&mut self, _: &mut ValidatorContext<'a, S>, doc: &'a Document<S>) {
self.operation_count = Some( self.operation_count = Some(
doc.iter() doc.iter()
.filter(|d| match **d { .filter(|d| match **d {
Definition::Operation(_) => true, Definition::Operation(_) => true,
Definition::Fragment(_) => false, Definition::Fragment(_) => false,
}) }).count(),
.count(),
); );
} }
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
if let Some(operation_count) = self.operation_count { if let Some(operation_count) = self.operation_count {
if operation_count > 1 && op.item.name.is_none() { if operation_count > 1 && op.item.name.is_none() {
@ -47,10 +50,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn no_operations() { fn no_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Type { fragment fragA on Type {
@ -62,7 +66,7 @@ mod tests {
#[test] #[test]
fn one_anon_operation() { fn one_anon_operation() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -74,7 +78,7 @@ mod tests {
#[test] #[test]
fn multiple_named_operations() { fn multiple_named_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -90,7 +94,7 @@ mod tests {
#[test] #[test]
fn anon_operation_with_fragment() { fn anon_operation_with_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -105,7 +109,7 @@ mod tests {
#[test] #[test]
fn multiple_anon_operations() { fn multiple_anon_operations() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -124,7 +128,7 @@ mod tests {
#[test] #[test]
fn anon_operation_with_a_mutation() { fn anon_operation_with_a_mutation() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -24,10 +24,14 @@ mod variables_are_input_types;
mod variables_in_allowed_position; mod variables_in_allowed_position;
use ast::Document; use ast::Document;
use validation::{visit, MultiVisitor, MultiVisitorNil, ValidatorContext}; use std::fmt::Debug;
use validation::{visit, MultiVisitorNil, ValidatorContext};
use value::ScalarValue;
#[doc(hidden)] pub(crate) fn visit_all_rules<'a, S: Debug>(ctx: &mut ValidatorContext<'a, S>, doc: &'a Document<S>)
pub fn visit_all_rules<'a>(ctx: &mut ValidatorContext<'a>, doc: &'a Document) { where
S: ScalarValue,
{
let mut mv = MultiVisitorNil let mut mv = MultiVisitorNil
.with(self::arguments_of_correct_type::factory()) .with(self::arguments_of_correct_type::factory())
.with(self::default_values_of_correct_type::factory()) .with(self::default_values_of_correct_type::factory())
@ -54,5 +58,5 @@ pub fn visit_all_rules<'a>(ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
.with(self::variables_are_input_types::factory()) .with(self::variables_are_input_types::factory())
.with(self::variables_in_allowed_position::factory()); .with(self::variables_in_allowed_position::factory());
visit(&mut mv, ctx, doc); visit(&mut mv, ctx, doc)
} }

View file

@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use ast::{Document, Fragment, FragmentSpread}; use ast::{Document, Fragment, FragmentSpread};
use parser::Spanning; use parser::Spanning;
use validation::{RuleError, ValidatorContext, Visitor}; use validation::{RuleError, ValidatorContext, Visitor};
use value::ScalarValue;
pub struct NoFragmentCycles<'a> { pub struct NoFragmentCycles<'a> {
current_fragment: Option<&'a str>, current_fragment: Option<&'a str>,
@ -25,8 +26,12 @@ pub fn factory<'a>() -> NoFragmentCycles<'a> {
} }
} }
impl<'a> Visitor<'a> for NoFragmentCycles<'a> { impl<'a, S> Visitor<'a, S> for NoFragmentCycles<'a>
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _: &'a Document) { where
S: ScalarValue,
{
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {
assert!(self.current_fragment.is_none()); assert!(self.current_fragment.is_none());
let mut detector = CycleDetector { let mut detector = CycleDetector {
@ -48,8 +53,8 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<Fragment>, fragment: &'a Spanning<Fragment<S>>,
) { ) {
assert!(self.current_fragment.is_none()); assert!(self.current_fragment.is_none());
@ -60,8 +65,8 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<Fragment>, fragment: &'a Spanning<Fragment<S>>,
) { ) {
assert_eq!(Some(fragment.item.name.item), self.current_fragment); assert_eq!(Some(fragment.item.name.item), self.current_fragment);
self.current_fragment = None; self.current_fragment = None;
@ -69,8 +74,8 @@ impl<'a> Visitor<'a> for NoFragmentCycles<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let Some(current_fragment) = self.current_fragment { if let Some(current_fragment) = self.current_fragment {
self.spreads self.spreads
@ -131,10 +136,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn single_reference_is_valid() { fn single_reference_is_valid() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
@ -145,7 +151,7 @@ mod tests {
#[test] #[test]
fn spreading_twice_is_not_circular() { fn spreading_twice_is_not_circular() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB, ...fragB } fragment fragA on Dog { ...fragB, ...fragB }
@ -156,7 +162,7 @@ mod tests {
#[test] #[test]
fn spreading_twice_indirectly_is_not_circular() { fn spreading_twice_indirectly_is_not_circular() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB, ...fragC } fragment fragA on Dog { ...fragB, ...fragC }
@ -168,7 +174,7 @@ mod tests {
#[test] #[test]
fn double_spread_within_abstract_types() { fn double_spread_within_abstract_types() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment nameFragment on Pet { fragment nameFragment on Pet {
@ -186,7 +192,7 @@ mod tests {
#[test] #[test]
fn does_not_false_positive_on_unknown_fragment() { fn does_not_false_positive_on_unknown_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment nameFragment on Pet { fragment nameFragment on Pet {
@ -198,7 +204,7 @@ mod tests {
#[test] #[test]
fn spreading_recursively_within_field_fails() { fn spreading_recursively_within_field_fails() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Human { relatives { ...fragA } }, fragment fragA on Human { relatives { ...fragA } },
@ -212,7 +218,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_directly() { fn no_spreading_itself_directly() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragA } fragment fragA on Dog { ...fragA }
@ -226,7 +232,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_directly_within_inline_fragment() { fn no_spreading_itself_directly_within_inline_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Pet { fragment fragA on Pet {
@ -244,7 +250,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_indirectly() { fn no_spreading_itself_indirectly() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
@ -259,7 +265,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_indirectly_reports_opposite_order() { fn no_spreading_itself_indirectly_reports_opposite_order() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragB on Dog { ...fragA } fragment fragB on Dog { ...fragA }
@ -274,7 +280,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_indirectly_within_inline_fragment() { fn no_spreading_itself_indirectly_within_inline_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Pet { fragment fragA on Pet {
@ -297,7 +303,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_deeply() { fn no_spreading_itself_deeply() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }
@ -318,7 +324,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_deeply_two_paths() { fn no_spreading_itself_deeply_two_paths() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB, ...fragC } fragment fragA on Dog { ...fragB, ...fragC }
@ -334,7 +340,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_deeply_two_paths_alt_traversal_order() { fn no_spreading_itself_deeply_two_paths_alt_traversal_order() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragC } fragment fragA on Dog { ...fragC }
@ -350,7 +356,7 @@ mod tests {
#[test] #[test]
fn no_spreading_itself_deeply_and_immediately() { fn no_spreading_itself_deeply_and_immediately() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Dog { ...fragB } fragment fragA on Dog { ...fragB }

View file

@ -2,6 +2,7 @@ use ast::{Document, Fragment, FragmentSpread, InputValue, Operation, VariableDef
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use validation::{RuleError, ValidatorContext, Visitor}; use validation::{RuleError, ValidatorContext, Visitor};
use value::ScalarValue;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Scope<'a> { pub enum Scope<'a> {
@ -55,8 +56,11 @@ impl<'a> NoUndefinedVariables<'a> {
} }
} }
impl<'a> Visitor<'a> for NoUndefinedVariables<'a> { impl<'a, S> Visitor<'a, S> for NoUndefinedVariables<'a>
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _: &'a Document) { where
S: ScalarValue,
{
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {
for (op_name, &(ref pos, ref def_vars)) in &self.defined_variables { for (op_name, &(ref pos, ref def_vars)) in &self.defined_variables {
let mut unused = Vec::new(); let mut unused = Vec::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
@ -75,16 +79,15 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
&error_message(var.item, *op_name), &error_message(var.item, *op_name),
&[var.start.clone(), pos.clone()], &[var.start.clone(), pos.clone()],
) )
}) }).collect(),
.collect(),
); );
} }
} }
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
let op_name = op.item.name.as_ref().map(|s| s.item); let op_name = op.item.name.as_ref().map(|s| s.item);
self.current_scope = Some(Scope::Operation(op_name)); self.current_scope = Some(Scope::Operation(op_name));
@ -94,16 +97,16 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
self.current_scope = Some(Scope::Fragment(f.item.name.item)); self.current_scope = Some(Scope::Fragment(f.item.name.item));
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.spreads self.spreads
@ -115,8 +118,8 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition), &(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
if let Some(Scope::Operation(ref name)) = self.current_scope { if let Some(Scope::Operation(ref name)) = self.current_scope {
if let Some(&mut (_, ref mut vars)) = self.defined_variables.get_mut(name) { if let Some(&mut (_, ref mut vars)) = self.defined_variables.get_mut(name) {
@ -127,21 +130,22 @@ impl<'a> Visitor<'a> for NoUndefinedVariables<'a> {
fn enter_argument( fn enter_argument(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>), &(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.used_variables self.used_variables
.entry(scope.clone()) .entry(scope.clone())
.or_insert_with(Vec::new) .or_insert_with(Vec::new)
.append(&mut value .append(
.item &mut value
.referenced_variables() .item
.iter() .referenced_variables()
.map(|&var_name| { .iter()
Spanning::start_end(&value.start.clone(), &value.end.clone(), var_name) .map(|&var_name| {
}) Spanning::start_end(&value.start.clone(), &value.end.clone(), var_name)
.collect()); }).collect(),
);
} }
} }
} }
@ -163,10 +167,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn all_variables_defined() { fn all_variables_defined() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -178,7 +183,7 @@ mod tests {
#[test] #[test]
fn all_variables_deeply_defined() { fn all_variables_deeply_defined() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -194,7 +199,7 @@ mod tests {
#[test] #[test]
fn all_variables_deeply_defined_in_inline_fragments_defined() { fn all_variables_deeply_defined_in_inline_fragments_defined() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -214,7 +219,7 @@ mod tests {
#[test] #[test]
fn all_variables_in_fragments_deeply_defined() { fn all_variables_in_fragments_deeply_defined() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -239,7 +244,7 @@ mod tests {
#[test] #[test]
fn variable_within_single_fragment_defined_in_multiple_operations() { fn variable_within_single_fragment_defined_in_multiple_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -257,7 +262,7 @@ mod tests {
#[test] #[test]
fn variable_within_fragments_defined_in_operations() { fn variable_within_fragments_defined_in_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -278,7 +283,7 @@ mod tests {
#[test] #[test]
fn variable_within_recursive_fragment_defined() { fn variable_within_recursive_fragment_defined() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -295,7 +300,7 @@ mod tests {
#[test] #[test]
fn variable_not_defined() { fn variable_not_defined() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -314,7 +319,7 @@ mod tests {
#[test] #[test]
fn variable_not_defined_by_unnamed_query() { fn variable_not_defined_by_unnamed_query() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -333,7 +338,7 @@ mod tests {
#[test] #[test]
fn multiple_variables_not_defined() { fn multiple_variables_not_defined() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {
@ -361,7 +366,7 @@ mod tests {
#[test] #[test]
fn variable_in_fragment_not_defined_by_unnamed_query() { fn variable_in_fragment_not_defined_by_unnamed_query() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -383,7 +388,7 @@ mod tests {
#[test] #[test]
fn variable_in_fragment_not_defined_by_operation() { fn variable_in_fragment_not_defined_by_operation() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String) { query Foo($a: String, $b: String) {
@ -415,7 +420,7 @@ mod tests {
#[test] #[test]
fn multiple_variables_in_fragments_not_defined() { fn multiple_variables_in_fragments_not_defined() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {
@ -456,7 +461,7 @@ mod tests {
#[test] #[test]
fn single_variable_in_fragment_not_defined_by_multiple_operations() { fn single_variable_in_fragment_not_defined_by_multiple_operations() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -490,7 +495,7 @@ mod tests {
#[test] #[test]
fn variables_in_fragment_not_defined_by_multiple_operations() { fn variables_in_fragment_not_defined_by_multiple_operations() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {
@ -524,7 +529,7 @@ mod tests {
#[test] #[test]
fn variable_in_fragment_used_by_other_operation() { fn variable_in_fragment_used_by_other_operation() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {
@ -561,7 +566,7 @@ mod tests {
#[test] #[test]
fn multiple_undefined_variables_produce_multiple_errors() { fn multiple_undefined_variables_produce_multiple_errors() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {

View file

@ -3,6 +3,7 @@ use std::collections::{HashMap, HashSet};
use ast::{Definition, Document, Fragment, FragmentSpread, Operation}; use ast::{Definition, Document, Fragment, FragmentSpread, Operation};
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Scope<'a> { pub enum Scope<'a> {
@ -42,8 +43,11 @@ impl<'a> NoUnusedFragments<'a> {
} }
} }
impl<'a> Visitor<'a> for NoUnusedFragments<'a> { impl<'a, S> Visitor<'a, S> for NoUnusedFragments<'a>
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, defs: &'a Document) { where
S: ScalarValue,
{
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, defs: &'a Document<S>) {
let mut reachable = HashSet::new(); let mut reachable = HashSet::new();
for def in defs { for def in defs {
@ -66,8 +70,8 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
let op_name = op.item.name.as_ref().map(|s| s.item.as_ref()); let op_name = op.item.name.as_ref().map(|s| s.item.as_ref());
self.current_scope = Some(Scope::Operation(op_name)); self.current_scope = Some(Scope::Operation(op_name));
@ -75,8 +79,8 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
self.current_scope = Some(Scope::Fragment(f.item.name.item)); self.current_scope = Some(Scope::Fragment(f.item.name.item));
self.defined_fragments self.defined_fragments
@ -85,8 +89,8 @@ impl<'a> Visitor<'a> for NoUnusedFragments<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.spreads self.spreads
@ -107,10 +111,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn all_fragment_names_are_used() { fn all_fragment_names_are_used() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -137,7 +142,7 @@ mod tests {
#[test] #[test]
fn all_fragment_names_are_used_by_multiple_operations() { fn all_fragment_names_are_used_by_multiple_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -166,7 +171,7 @@ mod tests {
#[test] #[test]
fn contains_unknown_fragments() { fn contains_unknown_fragments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -211,7 +216,7 @@ mod tests {
#[test] #[test]
fn contains_unknown_fragments_with_ref_cycle() { fn contains_unknown_fragments_with_ref_cycle() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -258,7 +263,7 @@ mod tests {
#[test] #[test]
fn contains_unknown_and_undef_fragments() { fn contains_unknown_and_undef_fragments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {

View file

@ -2,6 +2,7 @@ use ast::{Document, Fragment, FragmentSpread, InputValue, Operation, VariableDef
use parser::Spanning; use parser::Spanning;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use validation::{RuleError, ValidatorContext, Visitor}; use validation::{RuleError, ValidatorContext, Visitor};
use value::ScalarValue;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Scope<'a> { pub enum Scope<'a> {
@ -55,8 +56,11 @@ impl<'a> NoUnusedVariables<'a> {
} }
} }
impl<'a> Visitor<'a> for NoUnusedVariables<'a> { impl<'a, S> Visitor<'a, S> for NoUnusedVariables<'a>
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _: &'a Document) { where
S: ScalarValue,
{
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {
for (op_name, def_vars) in &self.defined_variables { for (op_name, def_vars) in &self.defined_variables {
let mut used = HashSet::new(); let mut used = HashSet::new();
let mut visited = HashSet::new(); let mut visited = HashSet::new();
@ -81,8 +85,8 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
let op_name = op.item.name.as_ref().map(|s| s.item); let op_name = op.item.name.as_ref().map(|s| s.item);
self.current_scope = Some(Scope::Operation(op_name)); self.current_scope = Some(Scope::Operation(op_name));
@ -91,16 +95,16 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
self.current_scope = Some(Scope::Fragment(f.item.name.item)); self.current_scope = Some(Scope::Fragment(f.item.name.item));
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.spreads self.spreads
@ -112,8 +116,8 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition), &(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
if let Some(Scope::Operation(ref name)) = self.current_scope { if let Some(Scope::Operation(ref name)) = self.current_scope {
if let Some(vars) = self.defined_variables.get_mut(name) { if let Some(vars) = self.defined_variables.get_mut(name) {
@ -124,8 +128,8 @@ impl<'a> Visitor<'a> for NoUnusedVariables<'a> {
fn enter_argument( fn enter_argument(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
&(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue>), &(_, ref value): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.used_variables self.used_variables
@ -153,10 +157,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn uses_all_variables() { fn uses_all_variables() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query ($a: String, $b: String, $c: String) { query ($a: String, $b: String, $c: String) {
@ -168,7 +173,7 @@ mod tests {
#[test] #[test]
fn uses_all_variables_deeply() { fn uses_all_variables_deeply() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -184,7 +189,7 @@ mod tests {
#[test] #[test]
fn uses_all_variables_deeply_in_inline_fragments() { fn uses_all_variables_deeply_in_inline_fragments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -204,7 +209,7 @@ mod tests {
#[test] #[test]
fn uses_all_variables_in_fragments() { fn uses_all_variables_in_fragments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -229,7 +234,7 @@ mod tests {
#[test] #[test]
fn variable_used_by_fragment_in_multiple_operations() { fn variable_used_by_fragment_in_multiple_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -250,7 +255,7 @@ mod tests {
#[test] #[test]
fn variable_used_by_recursive_fragment() { fn variable_used_by_recursive_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String) { query Foo($a: String) {
@ -267,7 +272,7 @@ mod tests {
#[test] #[test]
fn variable_not_used() { fn variable_not_used() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query ($a: String, $b: String, $c: String) { query ($a: String, $b: String, $c: String) {
@ -283,7 +288,7 @@ mod tests {
#[test] #[test]
fn multiple_variables_not_used_1() { fn multiple_variables_not_used_1() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -305,7 +310,7 @@ mod tests {
#[test] #[test]
fn variable_not_used_in_fragment() { fn variable_not_used_in_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -334,7 +339,7 @@ mod tests {
#[test] #[test]
fn multiple_variables_not_used_2() { fn multiple_variables_not_used_2() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: String, $c: String) { query Foo($a: String, $b: String, $c: String) {
@ -369,7 +374,7 @@ mod tests {
#[test] #[test]
fn variable_not_used_by_unreferenced_fragment() { fn variable_not_used_by_unreferenced_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {
@ -391,7 +396,7 @@ mod tests {
#[test] #[test]
fn variable_not_used_by_fragment_used_by_other_operation() { fn variable_not_used_by_fragment_used_by_other_operation() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($b: String) { query Foo($b: String) {

View file

@ -1,21 +1,28 @@
use std::fmt::Debug;
use ast::{Definition, Document, FragmentSpread, InlineFragment}; use ast::{Definition, Document, FragmentSpread, InlineFragment};
use parser::Spanning; use parser::Spanning;
use schema::meta::MetaType; use schema::meta::MetaType;
use std::collections::HashMap; use std::collections::HashMap;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct PossibleFragmentSpreads<'a> { pub struct PossibleFragmentSpreads<'a, S: Debug + 'a> {
fragment_types: HashMap<&'a str, &'a MetaType<'a>>, fragment_types: HashMap<&'a str, &'a MetaType<'a, S>>,
} }
pub fn factory<'a>() -> PossibleFragmentSpreads<'a> { pub fn factory<'a, S: Debug>() -> PossibleFragmentSpreads<'a, S> {
PossibleFragmentSpreads { PossibleFragmentSpreads {
fragment_types: HashMap::new(), fragment_types: HashMap::new(),
} }
} }
impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> { impl<'a, S> Visitor<'a, S> for PossibleFragmentSpreads<'a, S>
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, defs: &'a Document) { where
S: ScalarValue,
{
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a, S>, defs: &'a Document<S>) {
for def in defs { for def in defs {
if let Definition::Fragment(Spanning { ref item, .. }) = *def { if let Definition::Fragment(Spanning { ref item, .. }) = *def {
if let Some(t) = ctx.schema.concrete_type_by_name(item.type_condition.item) { if let Some(t) = ctx.schema.concrete_type_by_name(item.type_condition.item) {
@ -27,8 +34,8 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
frag: &'a Spanning<InlineFragment>, frag: &'a Spanning<InlineFragment<S>>,
) { ) {
if let (Some(parent_type), Some(frag_type)) = ( if let (Some(parent_type), Some(frag_type)) = (
ctx.parent_type(), ctx.parent_type(),
@ -52,8 +59,8 @@ impl<'a> Visitor<'a> for PossibleFragmentSpreads<'a> {
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let (Some(parent_type), Some(frag_type)) = ( if let (Some(parent_type), Some(frag_type)) = (
ctx.parent_type(), ctx.parent_type(),
@ -95,10 +102,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn of_the_same_object() { fn of_the_same_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectWithinObject on Dog { ...dogFragment } fragment objectWithinObject on Dog { ...dogFragment }
@ -109,7 +117,7 @@ mod tests {
#[test] #[test]
fn of_the_same_object_with_inline_fragment() { fn of_the_same_object_with_inline_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } } fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
@ -119,7 +127,7 @@ mod tests {
#[test] #[test]
fn object_into_an_implemented_interface() { fn object_into_an_implemented_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectWithinInterface on Pet { ...dogFragment } fragment objectWithinInterface on Pet { ...dogFragment }
@ -130,7 +138,7 @@ mod tests {
#[test] #[test]
fn object_into_containing_union() { fn object_into_containing_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment objectWithinUnion on CatOrDog { ...dogFragment } fragment objectWithinUnion on CatOrDog { ...dogFragment }
@ -141,7 +149,7 @@ mod tests {
#[test] #[test]
fn union_into_contained_object() { fn union_into_contained_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment unionWithinObject on Dog { ...catOrDogFragment } fragment unionWithinObject on Dog { ...catOrDogFragment }
@ -152,7 +160,7 @@ mod tests {
#[test] #[test]
fn union_into_overlapping_interface() { fn union_into_overlapping_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment unionWithinInterface on Pet { ...catOrDogFragment } fragment unionWithinInterface on Pet { ...catOrDogFragment }
@ -163,7 +171,7 @@ mod tests {
#[test] #[test]
fn union_into_overlapping_union() { fn union_into_overlapping_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment } fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
@ -174,7 +182,7 @@ mod tests {
#[test] #[test]
fn interface_into_implemented_object() { fn interface_into_implemented_object() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceWithinObject on Dog { ...petFragment } fragment interfaceWithinObject on Dog { ...petFragment }
@ -185,7 +193,7 @@ mod tests {
#[test] #[test]
fn interface_into_overlapping_interface() { fn interface_into_overlapping_interface() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceWithinInterface on Pet { ...beingFragment } fragment interfaceWithinInterface on Pet { ...beingFragment }
@ -196,7 +204,7 @@ mod tests {
#[test] #[test]
fn interface_into_overlapping_interface_in_inline_fragment() { fn interface_into_overlapping_interface_in_inline_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceWithinInterface on Pet { ... on Being { name } } fragment interfaceWithinInterface on Pet { ... on Being { name } }
@ -206,7 +214,7 @@ mod tests {
#[test] #[test]
fn interface_into_overlapping_union() { fn interface_into_overlapping_union() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment interfaceWithinUnion on CatOrDog { ...petFragment } fragment interfaceWithinUnion on CatOrDog { ...petFragment }
@ -217,7 +225,7 @@ mod tests {
#[test] #[test]
fn different_object_into_object() { fn different_object_into_object() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidObjectWithinObject on Cat { ...dogFragment } fragment invalidObjectWithinObject on Cat { ...dogFragment }
@ -232,7 +240,7 @@ mod tests {
#[test] #[test]
fn different_object_into_object_in_inline_fragment() { fn different_object_into_object_in_inline_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidObjectWithinObjectAnon on Cat { fragment invalidObjectWithinObjectAnon on Cat {
@ -248,7 +256,7 @@ mod tests {
#[test] #[test]
fn object_into_not_implementing_interface() { fn object_into_not_implementing_interface() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidObjectWithinInterface on Pet { ...humanFragment } fragment invalidObjectWithinInterface on Pet { ...humanFragment }
@ -263,7 +271,7 @@ mod tests {
#[test] #[test]
fn object_into_not_containing_union() { fn object_into_not_containing_union() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment } fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
@ -278,7 +286,7 @@ mod tests {
#[test] #[test]
fn union_into_not_contained_object() { fn union_into_not_contained_object() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidUnionWithinObject on Human { ...catOrDogFragment } fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
@ -293,7 +301,7 @@ mod tests {
#[test] #[test]
fn union_into_non_overlapping_interface() { fn union_into_non_overlapping_interface() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment } fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
@ -308,7 +316,7 @@ mod tests {
#[test] #[test]
fn union_into_non_overlapping_union() { fn union_into_non_overlapping_union() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment } fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
@ -323,7 +331,7 @@ mod tests {
#[test] #[test]
fn interface_into_non_implementing_object() { fn interface_into_non_implementing_object() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment } fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
@ -338,7 +346,7 @@ mod tests {
#[test] #[test]
fn interface_into_non_overlapping_interface() { fn interface_into_non_overlapping_interface() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidInterfaceWithinInterface on Pet { fragment invalidInterfaceWithinInterface on Pet {
@ -355,7 +363,7 @@ mod tests {
#[test] #[test]
fn interface_into_non_overlapping_interface_in_inline_fragment() { fn interface_into_non_overlapping_interface_in_inline_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidInterfaceWithinInterfaceAnon on Pet { fragment invalidInterfaceWithinInterfaceAnon on Pet {
@ -371,7 +379,7 @@ mod tests {
#[test] #[test]
fn interface_into_non_overlapping_union() { fn interface_into_non_overlapping_union() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment } fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }

View file

@ -3,15 +3,19 @@ use parser::Spanning;
use schema::meta::Field as FieldType; use schema::meta::Field as FieldType;
use schema::model::DirectiveType; use schema::model::DirectiveType;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct ProvidedNonNullArguments {} pub struct ProvidedNonNullArguments;
pub fn factory() -> ProvidedNonNullArguments { pub fn factory() -> ProvidedNonNullArguments {
ProvidedNonNullArguments {} ProvidedNonNullArguments
} }
impl<'a> Visitor<'a> for ProvidedNonNullArguments { impl<'a, S> Visitor<'a, S> for ProvidedNonNullArguments
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { where
S: ScalarValue,
{
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning<Field<S>>) {
let field_name = &field.item.name.item; let field_name = &field.item.name.item;
if let Some(&FieldType { if let Some(&FieldType {
@ -20,13 +24,13 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
}) = ctx.parent_type().and_then(|t| t.field_by_name(field_name)) }) = ctx.parent_type().and_then(|t| t.field_by_name(field_name))
{ {
for meta_arg in meta_args { for meta_arg in meta_args {
if meta_arg.arg_type.is_non_null() if meta_arg.arg_type.is_non_null() && field
&& field .item
.item .arguments
.arguments .as_ref()
.as_ref() .and_then(|args| {
.and_then(|args| args.item.get(&meta_arg.name)) args.item.get(&meta_arg.name)
.is_none() }).is_none()
{ {
ctx.report_error( ctx.report_error(
&field_error_message( &field_error_message(
@ -43,8 +47,8 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
fn enter_directive( fn enter_directive(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
directive: &'a Spanning<Directive>, directive: &'a Spanning<Directive<S>>,
) { ) {
let directive_name = &directive.item.name.item; let directive_name = &directive.item.name.item;
@ -54,13 +58,12 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments {
}) = ctx.schema.directive_by_name(directive_name) }) = ctx.schema.directive_by_name(directive_name)
{ {
for meta_arg in meta_args { for meta_arg in meta_args {
if meta_arg.arg_type.is_non_null() if meta_arg.arg_type.is_non_null() && directive
&& directive .item
.item .arguments
.arguments .as_ref()
.as_ref() .and_then(|args| args.item.get(&meta_arg.name))
.and_then(|args| args.item.get(&meta_arg.name)) .is_none()
.is_none()
{ {
ctx.report_error( ctx.report_error(
&directive_error_message( &directive_error_message(
@ -96,10 +99,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn ignores_unknown_arguments() { fn ignores_unknown_arguments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -113,7 +117,7 @@ mod tests {
#[test] #[test]
fn arg_on_optional_arg() { fn arg_on_optional_arg() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -127,7 +131,7 @@ mod tests {
#[test] #[test]
fn no_arg_on_optional_arg() { fn no_arg_on_optional_arg() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -141,7 +145,7 @@ mod tests {
#[test] #[test]
fn multiple_args() { fn multiple_args() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -155,7 +159,7 @@ mod tests {
#[test] #[test]
fn multiple_args_reverse_order() { fn multiple_args_reverse_order() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -169,7 +173,7 @@ mod tests {
#[test] #[test]
fn no_args_on_multiple_optional() { fn no_args_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -183,7 +187,7 @@ mod tests {
#[test] #[test]
fn one_arg_on_multiple_optional() { fn one_arg_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -197,7 +201,7 @@ mod tests {
#[test] #[test]
fn second_arg_on_multiple_optional() { fn second_arg_on_multiple_optional() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -211,7 +215,7 @@ mod tests {
#[test] #[test]
fn muliple_reqs_on_mixed_list() { fn muliple_reqs_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -225,7 +229,7 @@ mod tests {
#[test] #[test]
fn multiple_reqs_and_one_opt_on_mixed_list() { fn multiple_reqs_and_one_opt_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -239,7 +243,7 @@ mod tests {
#[test] #[test]
fn all_reqs_on_opts_on_mixed_list() { fn all_reqs_on_opts_on_mixed_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -253,7 +257,7 @@ mod tests {
#[test] #[test]
fn missing_one_non_nullable_argument() { fn missing_one_non_nullable_argument() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -271,7 +275,7 @@ mod tests {
#[test] #[test]
fn missing_multiple_non_nullable_arguments() { fn missing_multiple_non_nullable_arguments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -295,7 +299,7 @@ mod tests {
#[test] #[test]
fn incorrect_value_and_missing_argument() { fn incorrect_value_and_missing_argument() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -313,7 +317,7 @@ mod tests {
#[test] #[test]
fn ignores_unknown_directives() { fn ignores_unknown_directives() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -325,7 +329,7 @@ mod tests {
#[test] #[test]
fn with_directives_of_valid_types() { fn with_directives_of_valid_types() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -342,7 +346,7 @@ mod tests {
#[test] #[test]
fn with_directive_with_missing_types() { fn with_directive_with_missing_types() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -1,15 +1,24 @@
use ast::Field; use ast::Field;
use parser::Spanning; use parser::Spanning;
use validation::{RuleError, ValidatorContext, Visitor}; use validation::{RuleError, ValidatorContext, Visitor};
use value::ScalarValue;
pub struct ScalarLeafs {} pub struct ScalarLeafs;
pub fn factory() -> ScalarLeafs { pub fn factory() -> ScalarLeafs {
ScalarLeafs {} ScalarLeafs
} }
impl<'a> Visitor<'a> for ScalarLeafs { impl<'a, S> Visitor<'a, S> for ScalarLeafs
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) { where
S: ScalarValue,
{
fn enter_field(
&mut self,
ctx: &mut ValidatorContext<'a, S>,
field: &'a Spanning<Field<S>>,
) {
let field_name = &field.item.name.item; let field_name = &field.item.name.item;
let error = if let (Some(field_type), Some(field_type_literal)) = let error = if let (Some(field_type), Some(field_type_literal)) =
@ -55,10 +64,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn valid_scalar_selection() { fn valid_scalar_selection() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelection on Dog { fragment scalarSelection on Dog {
@ -70,7 +80,7 @@ mod tests {
#[test] #[test]
fn object_type_missing_selection() { fn object_type_missing_selection() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query directQueryOnObjectWithoutSubFields { query directQueryOnObjectWithoutSubFields {
@ -86,7 +96,7 @@ mod tests {
#[test] #[test]
fn interface_type_missing_selection() { fn interface_type_missing_selection() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -102,7 +112,7 @@ mod tests {
#[test] #[test]
fn valid_scalar_selection_with_args() { fn valid_scalar_selection_with_args() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionWithArgs on Dog { fragment scalarSelectionWithArgs on Dog {
@ -114,7 +124,7 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_on_boolean() { fn scalar_selection_not_allowed_on_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionsNotAllowedOnBoolean on Dog { fragment scalarSelectionsNotAllowedOnBoolean on Dog {
@ -130,7 +140,7 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_on_enum() { fn scalar_selection_not_allowed_on_enum() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionsNotAllowedOnEnum on Cat { fragment scalarSelectionsNotAllowedOnEnum on Cat {
@ -146,7 +156,7 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_with_args() { fn scalar_selection_not_allowed_with_args() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionsNotAllowedWithArgs on Dog { fragment scalarSelectionsNotAllowedWithArgs on Dog {
@ -162,7 +172,7 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_with_directives() { fn scalar_selection_not_allowed_with_directives() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionsNotAllowedWithDirectives on Dog { fragment scalarSelectionsNotAllowedWithDirectives on Dog {
@ -178,7 +188,7 @@ mod tests {
#[test] #[test]
fn scalar_selection_not_allowed_with_directives_and_args() { fn scalar_selection_not_allowed_with_directives_and_args() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog { fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::{Directive, Field, InputValue}; use ast::{Directive, Field, InputValue};
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueArgumentNames<'a> { pub struct UniqueArgumentNames<'a> {
known_names: HashMap<&'a str, SourcePosition>, known_names: HashMap<&'a str, SourcePosition>,
@ -14,19 +15,31 @@ pub fn factory<'a>() -> UniqueArgumentNames<'a> {
} }
} }
impl<'a> Visitor<'a> for UniqueArgumentNames<'a> { impl<'a, S> Visitor<'a, S> for UniqueArgumentNames<'a>
fn enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) { where
S: ScalarValue,
{
fn enter_directive(
&mut self,
_: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Directive<S>>,
) {
self.known_names = HashMap::new(); self.known_names = HashMap::new();
} }
fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) { fn enter_field(
&mut self,
_: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Field<S>>,
) {
self.known_names = HashMap::new(); self.known_names = HashMap::new();
} }
fn enter_argument( fn enter_argument(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue>), &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
match self.known_names.entry(arg_name.item) { match self.known_names.entry(arg_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
@ -52,10 +65,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn no_arguments_on_field() { fn no_arguments_on_field() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -67,11 +81,11 @@ mod tests {
#[test] #[test]
fn no_arguments_on_directive() { fn no_arguments_on_directive() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
field @directive dog @directive
} }
"#, "#,
); );
@ -79,7 +93,7 @@ mod tests {
#[test] #[test]
fn argument_on_field() { fn argument_on_field() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -91,11 +105,11 @@ mod tests {
#[test] #[test]
fn argument_on_directive() { fn argument_on_directive() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
field @directive(arg: "value") dog @directive(arg: "value")
} }
"#, "#,
); );
@ -103,7 +117,7 @@ mod tests {
#[test] #[test]
fn same_argument_on_two_fields() { fn same_argument_on_two_fields() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -116,7 +130,7 @@ mod tests {
#[test] #[test]
fn same_argument_on_field_and_directive() { fn same_argument_on_field_and_directive() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -128,7 +142,7 @@ mod tests {
#[test] #[test]
fn same_argument_on_two_directives() { fn same_argument_on_two_directives() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -140,7 +154,7 @@ mod tests {
#[test] #[test]
fn multiple_field_arguments() { fn multiple_field_arguments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -152,7 +166,7 @@ mod tests {
#[test] #[test]
fn multiple_directive_arguments() { fn multiple_directive_arguments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -164,7 +178,7 @@ mod tests {
#[test] #[test]
fn duplicate_field_arguments() { fn duplicate_field_arguments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -183,7 +197,7 @@ mod tests {
#[test] #[test]
fn many_duplicate_field_arguments() { fn many_duplicate_field_arguments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -211,7 +225,7 @@ mod tests {
#[test] #[test]
fn duplicate_directive_arguments() { fn duplicate_directive_arguments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -230,7 +244,7 @@ mod tests {
#[test] #[test]
fn many_duplicate_directive_arguments() { fn many_duplicate_directive_arguments() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::Fragment; use ast::Fragment;
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueFragmentNames<'a> { pub struct UniqueFragmentNames<'a> {
names: HashMap<&'a str, SourcePosition>, names: HashMap<&'a str, SourcePosition>,
@ -14,11 +15,15 @@ pub fn factory<'a>() -> UniqueFragmentNames<'a> {
} }
} }
impl<'a> Visitor<'a> for UniqueFragmentNames<'a> { impl<'a, S> Visitor<'a, S> for UniqueFragmentNames<'a>
where
S: ScalarValue,
{
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
context: &mut ValidatorContext<'a>, context: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment>, f: &'a Spanning<Fragment<S>>,
) { ) {
match self.names.entry(f.item.name.item) { match self.names.entry(f.item.name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
@ -44,14 +49,17 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn no_fragments() { fn no_fragments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
field dog {
name
}
} }
"#, "#,
); );
@ -59,15 +67,17 @@ mod tests {
#[test] #[test]
fn one_fragment() { fn one_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
...fragA dog {
...fragA
}
} }
fragment fragA on Type { fragment fragA on Dog {
field name
} }
"#, "#,
); );
@ -75,22 +85,24 @@ mod tests {
#[test] #[test]
fn many_fragments() { fn many_fragments() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
...fragA dog {
...fragB ...fragA
...fragC ...fragB
...fragC
}
} }
fragment fragA on Type { fragment fragA on Dog {
fieldA name
} }
fragment fragB on Type { fragment fragB on Dog {
fieldB nickname
} }
fragment fragC on Type { fragment fragC on Dog {
fieldC barkVolume
} }
"#, "#,
); );
@ -98,15 +110,17 @@ mod tests {
#[test] #[test]
fn inline_fragments_always_unique() { fn inline_fragments_always_unique() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
...on Type { dorOrHuman {
fieldA ...on Dog {
} name
...on Type { }
fieldB ...on Dog {
barkVolume
}
} }
} }
"#, "#,
@ -115,14 +129,16 @@ mod tests {
#[test] #[test]
fn fragment_and_operation_named_the_same() { fn fragment_and_operation_named_the_same() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
...Foo dog {
...Foo
}
} }
fragment Foo on Type { fragment Foo on Dog {
field name
} }
"#, "#,
); );
@ -130,24 +146,26 @@ mod tests {
#[test] #[test]
fn fragments_named_the_same() { fn fragments_named_the_same() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
...fragA dog {
...fragA
}
} }
fragment fragA on Type { fragment fragA on Dog {
fieldA name
} }
fragment fragA on Type { fragment fragA on Dog {
fieldB barkVolume
} }
"#, "#,
&[RuleError::new( &[RuleError::new(
&duplicate_message("fragA"), &duplicate_message("fragA"),
&[ &[
SourcePosition::new(65, 4, 19), SourcePosition::new(99, 6, 19),
SourcePosition::new(131, 7, 19), SourcePosition::new(162, 9, 19),
], ],
)], )],
); );
@ -155,21 +173,21 @@ mod tests {
#[test] #[test]
fn fragments_named_the_same_no_reference() { fn fragments_named_the_same_no_reference() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Type { fragment fragA on Dog {
fieldA name
} }
fragment fragA on Type { fragment fragA on Dog {
fieldB barkVolume
} }
"#, "#,
&[RuleError::new( &[RuleError::new(
&duplicate_message("fragA"), &duplicate_message("fragA"),
&[ &[
SourcePosition::new(20, 1, 19), SourcePosition::new(20, 1, 19),
SourcePosition::new(86, 4, 19), SourcePosition::new(83, 4, 19),
], ],
)], )],
); );

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::InputValue; use ast::InputValue;
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueInputFieldNames<'a> { pub struct UniqueInputFieldNames<'a> {
known_name_stack: Vec<HashMap<&'a str, SourcePosition>>, known_name_stack: Vec<HashMap<&'a str, SourcePosition>>,
@ -14,27 +15,30 @@ pub fn factory<'a>() -> UniqueInputFieldNames<'a> {
} }
} }
impl<'a> Visitor<'a> for UniqueInputFieldNames<'a> { impl<'a, S> Visitor<'a, S> for UniqueInputFieldNames<'a>
where
S: ScalarValue,
{
fn enter_object_value( fn enter_object_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, _: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
self.known_name_stack.push(HashMap::new()); self.known_name_stack.push(HashMap::new());
} }
fn exit_object_value( fn exit_object_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, _: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
self.known_name_stack.pop(); self.known_name_stack.pop();
} }
fn enter_object_field( fn enter_object_field(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue>), &(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue<S>>),
) { ) {
if let Some(ref mut known_names) = self.known_name_stack.last_mut() { if let Some(ref mut known_names) = self.known_name_stack.last_mut() {
match known_names.entry(&field_name.item) { match known_names.entry(&field_name.item) {
@ -62,10 +66,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn input_object_with_fields() { fn input_object_with_fields() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -77,7 +82,7 @@ mod tests {
#[test] #[test]
fn same_input_object_within_two_args() { fn same_input_object_within_two_args() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -89,7 +94,7 @@ mod tests {
#[test] #[test]
fn multiple_input_object_fields() { fn multiple_input_object_fields() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -101,7 +106,7 @@ mod tests {
#[test] #[test]
fn allows_for_nested_input_objects_with_similar_fields() { fn allows_for_nested_input_objects_with_similar_fields() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -121,7 +126,7 @@ mod tests {
#[test] #[test]
fn duplicate_input_object_fields() { fn duplicate_input_object_fields() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -140,7 +145,7 @@ mod tests {
#[test] #[test]
fn many_duplicate_input_object_fields() { fn many_duplicate_input_object_fields() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::Operation; use ast::Operation;
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueOperationNames<'a> { pub struct UniqueOperationNames<'a> {
names: HashMap<&'a str, SourcePosition>, names: HashMap<&'a str, SourcePosition>,
@ -14,11 +15,15 @@ pub fn factory<'a>() -> UniqueOperationNames<'a> {
} }
} }
impl<'a> Visitor<'a> for UniqueOperationNames<'a> { impl<'a, S> Visitor<'a, S> for UniqueOperationNames<'a>
where
S: ScalarValue,
{
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
if let Some(ref op_name) = op.item.name { if let Some(ref op_name) = op.item.name {
match self.names.entry(op_name.item) { match self.names.entry(op_name.item) {
@ -46,14 +51,15 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn no_operations() { fn no_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment fragA on Type { fragment fragA on Dog {
field name
} }
"#, "#,
); );
@ -61,7 +67,7 @@ mod tests {
#[test] #[test]
fn one_anon_operation() { fn one_anon_operation() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
{ {
@ -73,7 +79,7 @@ mod tests {
#[test] #[test]
fn one_named_operation() { fn one_named_operation() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -85,15 +91,19 @@ mod tests {
#[test] #[test]
fn multiple_operations() { fn multiple_operations() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
field dog {
name
}
} }
query Bar { query Bar {
field dog {
name
}
} }
"#, "#,
); );
@ -101,7 +111,7 @@ mod tests {
#[test] #[test]
fn multiple_operations_of_different_types() { fn multiple_operations_of_different_types() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
@ -117,14 +127,16 @@ mod tests {
#[test] #[test]
fn fragment_and_operation_named_the_same() { fn fragment_and_operation_named_the_same() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
...Foo dog {
...Foo
}
} }
fragment Foo on Type { fragment Foo on Dog {
field name
} }
"#, "#,
); );
@ -132,21 +144,25 @@ mod tests {
#[test] #[test]
fn multiple_operations_of_same_name() { fn multiple_operations_of_same_name() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
fieldA dog {
name
}
} }
query Foo { query Foo {
fieldB human {
name
}
} }
"#, "#,
&[RuleError::new( &[RuleError::new(
&error_message("Foo"), &error_message("Foo"),
&[ &[
SourcePosition::new(11, 1, 10), SourcePosition::new(11, 1, 10),
SourcePosition::new(64, 4, 10), SourcePosition::new(96, 6, 10),
], ],
)], )],
); );
@ -154,21 +170,23 @@ mod tests {
#[test] #[test]
fn multiple_ops_of_same_name_of_different_types() { fn multiple_ops_of_same_name_of_different_types() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo { query Foo {
fieldA dog {
name
}
} }
mutation Foo { mutation Foo {
fieldB testInput
} }
"#, "#,
&[RuleError::new( &[RuleError::new(
&error_message("Foo"), &error_message("Foo"),
&[ &[
SourcePosition::new(11, 1, 10), SourcePosition::new(11, 1, 10),
SourcePosition::new(64, 4, 10), SourcePosition::new(96, 6, 10),
], ],
)], )],
); );

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::{Operation, VariableDefinition}; use ast::{Operation, VariableDefinition};
use parser::{SourcePosition, Spanning}; use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueVariableNames<'a> { pub struct UniqueVariableNames<'a> {
names: HashMap<&'a str, SourcePosition>, names: HashMap<&'a str, SourcePosition>,
@ -14,19 +15,23 @@ pub fn factory<'a>() -> UniqueVariableNames<'a> {
} }
} }
impl<'a> Visitor<'a> for UniqueVariableNames<'a> { impl<'a, S> Visitor<'a, S> for UniqueVariableNames<'a>
where
S: ScalarValue,
{
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Operation>, _: &'a Spanning<Operation<S>>,
) { ) {
self.names = HashMap::new(); self.names = HashMap::new();
} }
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition), &(ref var_name, _): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
match self.names.entry(var_name.item) { match self.names.entry(var_name.item) {
Entry::Occupied(e) => { Entry::Occupied(e) => {
@ -52,10 +57,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn unique_variable_names() { fn unique_variable_names() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query A($x: Int, $y: String) { __typename } query A($x: Int, $y: String) { __typename }
@ -66,7 +72,7 @@ mod tests {
#[test] #[test]
fn duplicate_variable_names() { fn duplicate_variable_names() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query A($x: Int, $x: Int, $x: String) { __typename } query A($x: Int, $x: Int, $x: String) { __typename }

View file

@ -1,20 +1,25 @@
use ast::VariableDefinition; use ast::VariableDefinition;
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueVariableNames {} pub struct UniqueVariableNames;
pub fn factory() -> UniqueVariableNames { pub fn factory() -> UniqueVariableNames {
UniqueVariableNames {} UniqueVariableNames
} }
impl<'a> Visitor<'a> for UniqueVariableNames { impl<'a, S> Visitor<'a, S> for UniqueVariableNames
where
S: ScalarValue,
{
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
&(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition), &(ref var_name, ref var_def): &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
if let Some(var_type) = ctx.schema if let Some(var_type) = ctx
.schema
.concrete_type_by_name(var_def.var_type.item.innermost_name()) .concrete_type_by_name(var_def.var_type.item.innermost_name())
{ {
if !var_type.is_input() { if !var_type.is_input() {
@ -40,10 +45,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn input_types_are_valid() { fn input_types_are_valid() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) { query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
@ -55,7 +61,7 @@ mod tests {
#[test] #[test]
fn output_types_are_invalid() { fn output_types_are_invalid() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) { query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {

View file

@ -1,9 +1,11 @@
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
use ast::{Document, Fragment, FragmentSpread, Operation, Type, VariableDefinition}; use ast::{Document, Fragment, FragmentSpread, Operation, Type, VariableDefinition};
use parser::Spanning; use parser::Spanning;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Scope<'a> { pub enum Scope<'a> {
@ -11,14 +13,14 @@ pub enum Scope<'a> {
Fragment(&'a str), Fragment(&'a str),
} }
pub struct VariableInAllowedPosition<'a> { pub struct VariableInAllowedPosition<'a, S: Debug + 'a> {
spreads: HashMap<Scope<'a>, HashSet<&'a str>>, spreads: HashMap<Scope<'a>, HashSet<&'a str>>,
variable_usages: HashMap<Scope<'a>, Vec<(Spanning<&'a String>, Type<'a>)>>, variable_usages: HashMap<Scope<'a>, Vec<(Spanning<&'a String>, Type<'a>)>>,
variable_defs: HashMap<Scope<'a>, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a>)>>, variable_defs: HashMap<Scope<'a>, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a, S>)>>,
current_scope: Option<Scope<'a>>, current_scope: Option<Scope<'a>>,
} }
pub fn factory<'a>() -> VariableInAllowedPosition<'a> { pub fn factory<'a, S: Debug>() -> VariableInAllowedPosition<'a, S> {
VariableInAllowedPosition { VariableInAllowedPosition {
spreads: HashMap::new(), spreads: HashMap::new(),
variable_usages: HashMap::new(), variable_usages: HashMap::new(),
@ -27,12 +29,12 @@ pub fn factory<'a>() -> VariableInAllowedPosition<'a> {
} }
} }
impl<'a> VariableInAllowedPosition<'a> { impl<'a, S: Debug> VariableInAllowedPosition<'a, S> {
fn collect_incorrect_usages( fn collect_incorrect_usages(
&self, &self,
from: &Scope<'a>, from: &Scope<'a>,
var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition)>, var_defs: &Vec<&'a (Spanning<&'a str>, VariableDefinition<S>)>,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
visited: &mut HashSet<Scope<'a>>, visited: &mut HashSet<Scope<'a>>,
) { ) {
if visited.contains(from) { if visited.contains(from) {
@ -77,8 +79,11 @@ impl<'a> VariableInAllowedPosition<'a> {
} }
} }
impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> { impl<'a, S> Visitor<'a, S> for VariableInAllowedPosition<'a, S>
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, _: &'a Document) { where
S: ScalarValue,
{
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {
for (op_scope, var_defs) in &self.variable_defs { for (op_scope, var_defs) in &self.variable_defs {
self.collect_incorrect_usages(op_scope, var_defs, ctx, &mut HashSet::new()); self.collect_incorrect_usages(op_scope, var_defs, ctx, &mut HashSet::new());
} }
@ -86,24 +91,24 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<Fragment>, fragment: &'a Spanning<Fragment<S>>,
) { ) {
self.current_scope = Some(Scope::Fragment(fragment.item.name.item)); self.current_scope = Some(Scope::Fragment(fragment.item.name.item));
} }
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
op: &'a Spanning<Operation>, op: &'a Spanning<Operation<S>>,
) { ) {
self.current_scope = Some(Scope::Operation(op.item.name.as_ref().map(|s| s.item))); self.current_scope = Some(Scope::Operation(op.item.name.as_ref().map(|s| s.item)));
} }
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.spreads self.spreads
@ -115,8 +120,8 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
def: &'a (Spanning<&'a str>, VariableDefinition), def: &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
if let Some(ref scope) = self.current_scope { if let Some(ref scope) = self.current_scope {
self.variable_defs self.variable_defs
@ -128,7 +133,7 @@ impl<'a> Visitor<'a> for VariableInAllowedPosition<'a> {
fn enter_variable_value( fn enter_variable_value(
&mut self, &mut self,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
var_name: Spanning<&'a String>, var_name: Spanning<&'a String>,
) { ) {
if let (&Some(ref scope), Some(input_type)) = if let (&Some(ref scope), Some(input_type)) =
@ -158,10 +163,11 @@ mod tests {
use parser::SourcePosition; use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError}; use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test] #[test]
fn boolean_into_boolean() { fn boolean_into_boolean() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($booleanArg: Boolean) query Query($booleanArg: Boolean)
@ -176,7 +182,7 @@ mod tests {
#[test] #[test]
fn boolean_into_boolean_within_fragment() { fn boolean_into_boolean_within_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment booleanArgFrag on ComplicatedArgs { fragment booleanArgFrag on ComplicatedArgs {
@ -191,7 +197,7 @@ mod tests {
"#, "#,
); );
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($booleanArg: Boolean) query Query($booleanArg: Boolean)
@ -209,7 +215,7 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_boolean() { fn non_null_boolean_into_boolean() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($nonNullBooleanArg: Boolean!) query Query($nonNullBooleanArg: Boolean!)
@ -224,7 +230,7 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_boolean_within_fragment() { fn non_null_boolean_into_boolean_within_fragment() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment booleanArgFrag on ComplicatedArgs { fragment booleanArgFrag on ComplicatedArgs {
@ -243,7 +249,7 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int_with_default() { fn int_into_non_null_int_with_default() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($intArg: Int = 1) query Query($intArg: Int = 1)
@ -258,7 +264,7 @@ mod tests {
#[test] #[test]
fn string_list_into_string_list() { fn string_list_into_string_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringListVar: [String]) query Query($stringListVar: [String])
@ -273,7 +279,7 @@ mod tests {
#[test] #[test]
fn non_null_string_list_into_string_list() { fn non_null_string_list_into_string_list() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringListVar: [String!]) query Query($stringListVar: [String!])
@ -288,7 +294,7 @@ mod tests {
#[test] #[test]
fn string_into_string_list_in_item_position() { fn string_into_string_list_in_item_position() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringVar: String) query Query($stringVar: String)
@ -303,7 +309,7 @@ mod tests {
#[test] #[test]
fn non_null_string_into_string_list_in_item_position() { fn non_null_string_into_string_list_in_item_position() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringVar: String!) query Query($stringVar: String!)
@ -318,7 +324,7 @@ mod tests {
#[test] #[test]
fn complex_input_into_complex_input() { fn complex_input_into_complex_input() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($complexVar: ComplexInput) query Query($complexVar: ComplexInput)
@ -333,7 +339,7 @@ mod tests {
#[test] #[test]
fn complex_input_into_complex_input_in_field_position() { fn complex_input_into_complex_input_in_field_position() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($boolVar: Boolean = false) query Query($boolVar: Boolean = false)
@ -348,7 +354,7 @@ mod tests {
#[test] #[test]
fn non_null_boolean_into_non_null_boolean_in_directive() { fn non_null_boolean_into_non_null_boolean_in_directive() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($boolVar: Boolean!) query Query($boolVar: Boolean!)
@ -361,7 +367,7 @@ mod tests {
#[test] #[test]
fn boolean_in_non_null_in_directive_with_default() { fn boolean_in_non_null_in_directive_with_default() {
expect_passes_rule( expect_passes_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($boolVar: Boolean = false) query Query($boolVar: Boolean = false)
@ -374,7 +380,7 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int() { fn int_into_non_null_int() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($intArg: Int) { query Query($intArg: Int) {
@ -395,7 +401,7 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int_within_fragment() { fn int_into_non_null_int_within_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment nonNullIntArgFieldFrag on ComplicatedArgs { fragment nonNullIntArgFieldFrag on ComplicatedArgs {
@ -420,7 +426,7 @@ mod tests {
#[test] #[test]
fn int_into_non_null_int_within_nested_fragment() { fn int_into_non_null_int_within_nested_fragment() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
fragment outerFrag on ComplicatedArgs { fragment outerFrag on ComplicatedArgs {
@ -449,7 +455,7 @@ mod tests {
#[test] #[test]
fn string_over_boolean() { fn string_over_boolean() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringVar: String) { query Query($stringVar: String) {
@ -470,7 +476,7 @@ mod tests {
#[test] #[test]
fn string_into_string_list() { fn string_into_string_list() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringVar: String) { query Query($stringVar: String) {
@ -491,7 +497,7 @@ mod tests {
#[test] #[test]
fn boolean_into_non_null_boolean_in_directive() { fn boolean_into_non_null_boolean_in_directive() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($boolVar: Boolean) { query Query($boolVar: Boolean) {
@ -510,7 +516,7 @@ mod tests {
#[test] #[test]
fn string_into_non_null_boolean_in_directive() { fn string_into_non_null_boolean_in_directive() {
expect_fails_rule( expect_fails_rule::<_, _, DefaultScalarValue>(
factory, factory,
r#" r#"
query Query($stringVar: String) { query Query($stringVar: String) {

View file

@ -4,8 +4,9 @@ use parser::parse_document_source;
use schema::meta::{EnumValue, MetaType}; use schema::meta::{EnumValue, MetaType};
use schema::model::{DirectiveLocation, DirectiveType, RootNode}; use schema::model::{DirectiveLocation, DirectiveType, RootNode};
use types::base::GraphQLType; use types::base::GraphQLType;
use types::scalars::{EmptyMutation, ID}; use types::scalars::ID;
use validation::{visit, MultiVisitor, MultiVisitorNil, RuleError, ValidatorContext, Visitor}; use validation::{visit, MultiVisitorNil, RuleError, ValidatorContext, Visitor};
use value::{ScalarRefValue, ScalarValue};
struct Being; struct Being;
struct Pet; struct Pet;
@ -24,7 +25,15 @@ struct HumanOrAlien;
struct ComplicatedArgs; struct ComplicatedArgs;
struct QueryRoot; pub(crate) struct QueryRoot;
#[derive(Debug, GraphQLInputObject)]
struct TestInput {
id: i32,
name: String,
}
pub(crate) struct MutationRoot;
#[derive(Debug)] #[derive(Debug)]
enum DogCommand { enum DogCommand {
@ -51,7 +60,11 @@ struct ComplexInput {
string_list_field: Option<Vec<Option<String>>>, string_list_field: Option<Vec<Option<String>>>,
} }
impl GraphQLType for Being { impl<S> GraphQLType<S> for Being
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -59,7 +72,10 @@ impl GraphQLType for Being {
Some("Being") Some("Being")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[registry let fields = &[registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
.argument(registry.arg::<Option<bool>>("surname", i))]; .argument(registry.arg::<Option<bool>>("surname", i))];
@ -68,7 +84,11 @@ impl GraphQLType for Being {
} }
} }
impl GraphQLType for Pet { impl<S> GraphQLType<S> for Pet
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -76,7 +96,10 @@ impl GraphQLType for Pet {
Some("Pet") Some("Pet")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[registry let fields = &[registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
.argument(registry.arg::<Option<bool>>("surname", i))]; .argument(registry.arg::<Option<bool>>("surname", i))];
@ -85,7 +108,11 @@ impl GraphQLType for Pet {
} }
} }
impl GraphQLType for Canine { impl<S> GraphQLType<S> for Canine
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -93,7 +120,10 @@ impl GraphQLType for Canine {
Some("Canine") Some("Canine")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[registry let fields = &[registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
.argument(registry.arg::<Option<bool>>("surname", i))]; .argument(registry.arg::<Option<bool>>("surname", i))];
@ -102,7 +132,11 @@ impl GraphQLType for Canine {
} }
} }
impl GraphQLType for DogCommand { impl<S> GraphQLType<S> for DogCommand
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -110,7 +144,10 @@ impl GraphQLType for DogCommand {
Some("DogCommand") Some("DogCommand")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
registry registry
.build_enum_type::<Self>( .build_enum_type::<Self>(
i, i,
@ -119,13 +156,18 @@ impl GraphQLType for DogCommand {
EnumValue::new("HEEL"), EnumValue::new("HEEL"),
EnumValue::new("DOWN"), EnumValue::new("DOWN"),
], ],
) ).into_meta()
.into_meta()
} }
} }
impl FromInputValue for DogCommand { impl<S> FromInputValue<S> for DogCommand
fn from_input_value(v: &InputValue) -> Option<DogCommand> { where
S: ScalarValue,
{
fn from_input_value<'a>(v: &InputValue<S>) -> Option<DogCommand>
where
for<'b> &'b S: ScalarRefValue<'b>,
{
match v.as_enum_value() { match v.as_enum_value() {
Some("SIT") => Some(DogCommand::Sit), Some("SIT") => Some(DogCommand::Sit),
Some("HEEL") => Some(DogCommand::Heel), Some("HEEL") => Some(DogCommand::Heel),
@ -135,7 +177,11 @@ impl FromInputValue for DogCommand {
} }
} }
impl GraphQLType for Dog { impl<S> GraphQLType<S> for Dog
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -143,7 +189,10 @@ impl GraphQLType for Dog {
Some("Dog") Some("Dog")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
@ -169,12 +218,15 @@ impl GraphQLType for Dog {
registry.get_type::<Being>(i), registry.get_type::<Being>(i),
registry.get_type::<Pet>(i), registry.get_type::<Pet>(i),
registry.get_type::<Canine>(i), registry.get_type::<Canine>(i),
]) ]).into_meta()
.into_meta()
} }
} }
impl GraphQLType for FurColor { impl<S> GraphQLType<S> for FurColor
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -182,7 +234,10 @@ impl GraphQLType for FurColor {
Some("FurColor") Some("FurColor")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
registry registry
.build_enum_type::<Self>( .build_enum_type::<Self>(
i, i,
@ -192,13 +247,20 @@ impl GraphQLType for FurColor {
EnumValue::new("TAN"), EnumValue::new("TAN"),
EnumValue::new("SPOTTED"), EnumValue::new("SPOTTED"),
], ],
) ).into_meta()
.into_meta()
} }
} }
impl FromInputValue for FurColor { impl<S> FromInputValue<S> for FurColor
fn from_input_value(v: &InputValue) -> Option<FurColor> { where
S: ScalarValue,
{
fn from_input_value<'a>(v: &InputValue<S>) -> Option<FurColor>
where
// S: 'a,
// &'a S: ScalarRefValue<'a>,
for<'b> &'b S: ScalarRefValue<'b>,
{
match v.as_enum_value() { match v.as_enum_value() {
Some("BROWN") => Some(FurColor::Brown), Some("BROWN") => Some(FurColor::Brown),
Some("BLACK") => Some(FurColor::Black), Some("BLACK") => Some(FurColor::Black),
@ -209,7 +271,11 @@ impl FromInputValue for FurColor {
} }
} }
impl GraphQLType for Cat { impl<S> GraphQLType<S> for Cat
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -217,7 +283,10 @@ impl GraphQLType for Cat {
Some("Cat") Some("Cat")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
@ -235,7 +304,11 @@ impl GraphQLType for Cat {
} }
} }
impl GraphQLType for CatOrDog { impl<S> GraphQLType<S> for CatOrDog
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -243,14 +316,21 @@ impl GraphQLType for CatOrDog {
Some("CatOrDog") Some("CatOrDog")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let types = &[registry.get_type::<Cat>(i), registry.get_type::<Dog>(i)]; let types = &[registry.get_type::<Cat>(i), registry.get_type::<Dog>(i)];
registry.build_union_type::<Self>(i, types).into_meta() registry.build_union_type::<Self>(i, types).into_meta()
} }
} }
impl GraphQLType for Intelligent { impl<S> GraphQLType<S> for Intelligent
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -258,14 +338,21 @@ impl GraphQLType for Intelligent {
Some("Intelligent") Some("Intelligent")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[registry.field::<Option<i32>>("iq", i)]; let fields = &[registry.field::<Option<i32>>("iq", i)];
registry.build_interface_type::<Self>(i, fields).into_meta() registry.build_interface_type::<Self>(i, fields).into_meta()
} }
} }
impl GraphQLType for Human { impl<S> GraphQLType<S> for Human
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -273,7 +360,10 @@ impl GraphQLType for Human {
Some("Human") Some("Human")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
@ -287,12 +377,15 @@ impl GraphQLType for Human {
.interfaces(&[ .interfaces(&[
registry.get_type::<Being>(i), registry.get_type::<Being>(i),
registry.get_type::<Intelligent>(i), registry.get_type::<Intelligent>(i),
]) ]).into_meta()
.into_meta()
} }
} }
impl GraphQLType for Alien { impl<S> GraphQLType<S> for Alien
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -300,7 +393,10 @@ impl GraphQLType for Alien {
Some("Alien") Some("Alien")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<String>>("name", i) .field::<Option<String>>("name", i)
@ -314,12 +410,15 @@ impl GraphQLType for Alien {
.interfaces(&[ .interfaces(&[
registry.get_type::<Being>(i), registry.get_type::<Being>(i),
registry.get_type::<Intelligent>(i), registry.get_type::<Intelligent>(i),
]) ]).into_meta()
.into_meta()
} }
} }
impl GraphQLType for DogOrHuman { impl<S> GraphQLType<S> for DogOrHuman
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -327,14 +426,21 @@ impl GraphQLType for DogOrHuman {
Some("DogOrHuman") Some("DogOrHuman")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let types = &[registry.get_type::<Dog>(i), registry.get_type::<Human>(i)]; let types = &[registry.get_type::<Dog>(i), registry.get_type::<Human>(i)];
registry.build_union_type::<Self>(i, types).into_meta() registry.build_union_type::<Self>(i, types).into_meta()
} }
} }
impl GraphQLType for HumanOrAlien { impl<S> GraphQLType<S> for HumanOrAlien
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -342,14 +448,21 @@ impl GraphQLType for HumanOrAlien {
Some("HumanOrAlien") Some("HumanOrAlien")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let types = &[registry.get_type::<Human>(i), registry.get_type::<Alien>(i)]; let types = &[registry.get_type::<Human>(i), registry.get_type::<Alien>(i)];
registry.build_union_type::<Self>(i, types).into_meta() registry.build_union_type::<Self>(i, types).into_meta()
} }
} }
impl GraphQLType for ComplexInput { impl<S> GraphQLType<S> for ComplexInput
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -357,7 +470,10 @@ impl GraphQLType for ComplexInput {
Some("ComplexInput") Some("ComplexInput")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry.arg::<bool>("requiredField", i), registry.arg::<bool>("requiredField", i),
registry.arg::<Option<i32>>("intField", i), registry.arg::<Option<i32>>("intField", i),
@ -372,8 +488,15 @@ impl GraphQLType for ComplexInput {
} }
} }
impl FromInputValue for ComplexInput { impl<S> FromInputValue<S> for ComplexInput
fn from_input_value(v: &InputValue) -> Option<ComplexInput> { where
S: ScalarValue,
{
fn from_input_value<'a>(v: &InputValue<S>) -> Option<ComplexInput>
where
for<'b> &'b S: ScalarRefValue<'b>, // S: 'a,
// &'a S: ScalarRefValue<'a>
{
let obj = match v.to_object_value() { let obj = match v.to_object_value() {
Some(o) => o, Some(o) => o,
None => return None, None => return None,
@ -392,7 +515,11 @@ impl FromInputValue for ComplexInput {
} }
} }
impl GraphQLType for ComplicatedArgs { impl<S> GraphQLType<S> for ComplicatedArgs
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -400,7 +527,10 @@ impl GraphQLType for ComplicatedArgs {
Some("ComplicatedArgs") Some("ComplicatedArgs")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<String>>("intArgField", i) .field::<Option<String>>("intArgField", i)
@ -449,7 +579,11 @@ impl GraphQLType for ComplicatedArgs {
} }
} }
impl GraphQLType for QueryRoot { impl<S> GraphQLType<S> for QueryRoot
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -457,7 +591,10 @@ impl GraphQLType for QueryRoot {
Some("QueryRoot") Some("QueryRoot")
} }
fn meta<'r>(i: &(), registry: &mut Registry<'r>) -> MetaType<'r> { fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = &[ let fields = &[
registry registry
.field::<Option<Human>>("human", i) .field::<Option<Human>>("human", i)
@ -476,13 +613,47 @@ impl GraphQLType for QueryRoot {
} }
} }
pub fn validate<'a, R, V, F>(r: R, q: &'a str, factory: F) -> Vec<RuleError> impl<S> GraphQLType<S> for MutationRoot
where where
R: GraphQLType<TypeInfo = ()>, S: ScalarValue,
V: Visitor<'a> + 'a, for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = ();
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
Some("MutationRoot")
}
fn meta<'r>(i: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S>
where
S: 'r,
{
let fields = [registry.field::<i32>("testInput", i).argument(
registry.arg_with_default::<TestInput>(
"input",
&TestInput {
id: 423,
name: String::from("foo"),
},
i,
),
)];
registry.build_object_type::<Self>(i, &fields).into_meta()
}
}
pub fn validate<'a, Q, M, V, F, S>(r: Q, m: M, q: &'a str, factory: F) -> Vec<RuleError>
where
for<'b> &'b S: ScalarRefValue<'b>,
S: ScalarValue + 'a,
Q: GraphQLType<S, TypeInfo = ()>,
M: GraphQLType<S, TypeInfo = ()>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
let mut root = RootNode::new(r, EmptyMutation::<()>::new()); let mut root = RootNode::new(r, m);
root.schema.add_directive(DirectiveType::new( root.schema.add_directive(DirectiveType::new(
"onQuery", "onQuery",
@ -515,7 +686,8 @@ where
&[], &[],
)); ));
let doc = parse_document_source(q).expect(&format!("Parse error on input {:#?}", q)); let doc =
parse_document_source(q, &root.schema).expect(&format!("Parse error on input {:#?}", q));
let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc); let mut ctx = ValidatorContext::new(unsafe { ::std::mem::transmute(&root.schema) }, &doc);
let mut mv = MultiVisitorNil.with(factory()); let mut mv = MultiVisitorNil.with(factory());
@ -524,21 +696,26 @@ where
ctx.into_errors() ctx.into_errors()
} }
pub fn expect_passes_rule<'a, V, F>(factory: F, q: &'a str) pub fn expect_passes_rule<'a, V, F, S>(factory: F, q: &'a str)
where where
V: Visitor<'a> + 'a, S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
expect_passes_rule_with_schema(QueryRoot, factory, q); expect_passes_rule_with_schema(QueryRoot, MutationRoot, factory, q);
} }
pub fn expect_passes_rule_with_schema<'a, R, V, F>(r: R, factory: F, q: &'a str) pub fn expect_passes_rule_with_schema<'a, Q, M, V, F, S>(r: Q, m: M, factory: F, q: &'a str)
where where
R: GraphQLType<TypeInfo = ()>, S: ScalarValue + 'a,
V: Visitor<'a> + 'a, for<'b> &'b S: ScalarRefValue<'b>,
Q: GraphQLType<S, TypeInfo = ()>,
M: GraphQLType<S, TypeInfo = ()>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
let errs = validate(r, q, factory); let errs = validate(r, m, q, factory);
if !errs.is_empty() { if !errs.is_empty() {
print_errors(&errs); print_errors(&errs);
@ -546,25 +723,31 @@ where
} }
} }
pub fn expect_fails_rule<'a, V, F>(factory: F, q: &'a str, expected_errors: &[RuleError]) pub fn expect_fails_rule<'a, V, F, S>(factory: F, q: &'a str, expected_errors: &[RuleError])
where where
V: Visitor<'a> + 'a, S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
expect_fails_rule_with_schema(QueryRoot, factory, q, expected_errors); expect_fails_rule_with_schema(QueryRoot, MutationRoot, factory, q, expected_errors);
} }
pub fn expect_fails_rule_with_schema<'a, R, V, F>( pub fn expect_fails_rule_with_schema<'a, Q, M, V, F, S>(
r: R, r: Q,
m: M,
factory: F, factory: F,
q: &'a str, q: &'a str,
expected_errors: &[RuleError], expected_errors: &[RuleError],
) where ) where
R: GraphQLType<TypeInfo = ()>, S: ScalarValue + 'a,
V: Visitor<'a> + 'a, for<'b> &'b S: ScalarRefValue<'b>,
Q: GraphQLType<S, TypeInfo = ()>,
M: GraphQLType<S, TypeInfo = ()>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V, F: Fn() -> V,
{ {
let errs = validate(r, q, factory); let errs = validate(r, m, q, factory);
if errs.is_empty() { if errs.is_empty() {
panic!("Expected rule to fail, but no errors were found"); panic!("Expected rule to fail, but no errors were found");
@ -585,5 +768,6 @@ fn print_errors(errs: &[RuleError]) {
print!("[{:>3},{:>3},{:>3}] ", p.index(), p.line(), p.column()); print!("[{:>3},{:>3},{:>3}] ", p.index(), p.line(), p.column());
} }
println!("{}", err.message()); println!("{}", err.message());
} }
} }

View file

@ -4,156 +4,151 @@ use ast::{
}; };
use parser::Spanning; use parser::Spanning;
use validation::ValidatorContext; use validation::ValidatorContext;
use value::ScalarValue;
#[doc(hidden)] #[doc(hidden)]
pub trait Visitor<'a> { pub trait Visitor<'a, S>
fn enter_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {} where
fn exit_document(&mut self, _: &mut ValidatorContext<'a>, _: &'a Document) {} S: ScalarValue,
{
fn enter_document(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {}
fn exit_document(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Document<S>) {}
fn enter_operation_definition( fn enter_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Operation>, _: &'a Spanning<Operation<S>>,
) { ) {
} }
fn exit_operation_definition( fn exit_operation_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Operation>, _: &'a Spanning<Operation<S>>,
) { ) {
} }
fn enter_fragment_definition( fn enter_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Fragment>, _: &'a Spanning<Fragment<S>>,
) { ) {
} }
fn exit_fragment_definition( fn exit_fragment_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<Fragment>, _: &'a Spanning<Fragment<S>>,
) { ) {
} }
fn enter_variable_definition( fn enter_variable_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, VariableDefinition), _: &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
} }
fn exit_variable_definition( fn exit_variable_definition(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, VariableDefinition), _: &'a (Spanning<&'a str>, VariableDefinition<S>),
) { ) {
} }
fn enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {} fn enter_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Directive<S>>) {}
fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Directive>) {} fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Directive<S>>) {}
fn enter_argument( fn enter_argument(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, Spanning<InputValue>), _: &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
} }
fn exit_argument( fn exit_argument(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<&'a str>, Spanning<InputValue>), _: &'a (Spanning<&'a str>, Spanning<InputValue<S>>),
) { ) {
} }
fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {} fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Vec<Selection<S>>) {}
fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec<Selection>) {} fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Vec<Selection<S>>) {}
fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {} fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {}
fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning<Field>) {} fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning<Field<S>>) {}
fn enter_fragment_spread( fn enter_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<FragmentSpread>, _: &'a Spanning<FragmentSpread<S>>,
) { ) {
} }
fn exit_fragment_spread( fn exit_fragment_spread(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<FragmentSpread>, _: &'a Spanning<FragmentSpread<S>>,
) { ) {
} }
fn enter_inline_fragment( fn enter_inline_fragment(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<InlineFragment>, _: &'a Spanning<InlineFragment<S>>,
) { ) {
} }
fn exit_inline_fragment( fn exit_inline_fragment(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a Spanning<InlineFragment>, _: &'a Spanning<InlineFragment<S>>,
) { ) {
} }
fn enter_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {} fn enter_null_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<()>) {}
fn exit_null_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<()>) {} fn exit_null_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<()>) {}
fn enter_int_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<i32>) {} fn enter_scalar_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a S>) {}
fn exit_int_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<i32>) {} fn exit_scalar_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a S>) {}
fn enter_float_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<f64>) {} fn enter_enum_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a String>) {}
fn exit_float_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<f64>) {} fn exit_enum_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a String>) {}
fn enter_string_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {} fn enter_variable_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a String>) {}
fn exit_string_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {} fn exit_variable_value(&mut self, _: &mut ValidatorContext<'a, S>, _: Spanning<&'a String>) {}
fn enter_boolean_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<bool>) {}
fn exit_boolean_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<bool>) {}
fn enter_enum_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
fn exit_enum_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
fn enter_variable_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
fn exit_variable_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning<&'a String>) {}
fn enter_list_value( fn enter_list_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<Spanning<InputValue>>>, _: Spanning<&'a Vec<Spanning<InputValue<S>>>>,
) { ) {
} }
fn exit_list_value( fn exit_list_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<Spanning<InputValue>>>, _: Spanning<&'a Vec<Spanning<InputValue<S>>>>,
) { ) {
} }
fn enter_object_value( fn enter_object_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, _: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
} }
fn exit_object_value( fn exit_object_value(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>, _: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) { ) {
} }
fn enter_object_field( fn enter_object_field(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<String>, Spanning<InputValue>), _: &'a (Spanning<String>, Spanning<InputValue<S>>),
) { ) {
} }
fn exit_object_field( fn exit_object_field(
&mut self, &mut self,
_: &mut ValidatorContext<'a>, _: &mut ValidatorContext<'a, S>,
_: &'a (Spanning<String>, Spanning<InputValue>), _: &'a (Spanning<String>, Spanning<InputValue<S>>),
) { ) {
} }
} }

View file

@ -6,20 +6,32 @@ use ast::{
}; };
use parser::Spanning; use parser::Spanning;
use schema::meta::Argument; use schema::meta::Argument;
use validation::multi_visitor::MultiVisitorCons;
use validation::{ValidatorContext, Visitor}; use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[doc(hidden)] #[doc(hidden)]
pub fn visit<'a, V: Visitor<'a>>(v: &mut V, ctx: &mut ValidatorContext<'a>, d: &'a Document) { pub fn visit<'a, A, B, S>(
v: &mut MultiVisitorCons<A, B>,
ctx: &mut ValidatorContext<'a, S>,
d: &'a Document<S>,
) where
S: ScalarValue,
MultiVisitorCons<A, B>: Visitor<'a, S>,
{
v.enter_document(ctx, d); v.enter_document(ctx, d);
visit_definitions(v, ctx, d); visit_definitions(v, ctx, d);
v.exit_document(ctx, d); v.exit_document(ctx, d);
} }
fn visit_definitions<'a, V: Visitor<'a>>( fn visit_definitions<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
d: &'a Vec<Definition>, d: &'a Vec<Definition<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
for def in d { for def in d {
let def_type = match *def { let def_type = match *def {
Definition::Fragment(Spanning { Definition::Fragment(Spanning {
@ -47,7 +59,8 @@ fn visit_definitions<'a, V: Visitor<'a>>(
.. ..
}, },
.. ..
}) => ctx.schema }) => ctx
.schema
.concrete_mutation_type() .concrete_mutation_type()
.map(|t| Type::NonNullNamed(Cow::Borrowed(t.name().unwrap()))), .map(|t| Type::NonNullNamed(Cow::Borrowed(t.name().unwrap()))),
}; };
@ -60,33 +73,33 @@ fn visit_definitions<'a, V: Visitor<'a>>(
} }
} }
fn enter_definition<'a, V: Visitor<'a>>( fn enter_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
v: &mut V, where
ctx: &mut ValidatorContext<'a>, S: ScalarValue,
def: &'a Definition, V: Visitor<'a, S>,
) { {
match *def { match *def {
Definition::Operation(ref op) => v.enter_operation_definition(ctx, op), Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f), Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f),
} }
} }
fn exit_definition<'a, V: Visitor<'a>>( fn exit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
v: &mut V, where
ctx: &mut ValidatorContext<'a>, S: ScalarValue,
def: &'a Definition, V: Visitor<'a, S>,
) { {
match *def { match *def {
Definition::Operation(ref op) => v.exit_operation_definition(ctx, op), Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f), Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f),
} }
} }
fn visit_definition<'a, V: Visitor<'a>>( fn visit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
v: &mut V, where
ctx: &mut ValidatorContext<'a>, S: ScalarValue,
def: &'a Definition, V: Visitor<'a, S>,
) { {
match *def { match *def {
Definition::Operation(ref op) => { Definition::Operation(ref op) => {
visit_variable_definitions(v, ctx, &op.item.variable_definitions); visit_variable_definitions(v, ctx, &op.item.variable_definitions);
@ -100,11 +113,14 @@ fn visit_definition<'a, V: Visitor<'a>>(
} }
} }
fn visit_variable_definitions<'a, V: Visitor<'a>>( fn visit_variable_definitions<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
defs: &'a Option<Spanning<VariableDefinitions>>, defs: &'a Option<Spanning<VariableDefinitions<S>>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref defs) = *defs { if let Some(ref defs) = *defs {
for def in defs.item.iter() { for def in defs.item.iter() {
let var_type = def.1.var_type.item.clone(); let var_type = def.1.var_type.item.clone();
@ -122,14 +138,18 @@ fn visit_variable_definitions<'a, V: Visitor<'a>>(
} }
} }
fn visit_directives<'a, V: Visitor<'a>>( fn visit_directives<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
directives: &'a Option<Vec<Spanning<Directive>>>, directives: &'a Option<Vec<Spanning<Directive<S>>>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref directives) = *directives { if let Some(ref directives) = *directives {
for directive in directives { for directive in directives {
let directive_arguments = ctx.schema let directive_arguments = ctx
.schema
.directive_by_name(directive.item.name.item) .directive_by_name(directive.item.name.item)
.map(|d| &d.arguments); .map(|d| &d.arguments);
@ -140,12 +160,15 @@ fn visit_directives<'a, V: Visitor<'a>>(
} }
} }
fn visit_arguments<'a, V: Visitor<'a>>( fn visit_arguments<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
meta_args: &Option<&Vec<Argument<'a>>>, meta_args: &Option<&Vec<Argument<'a, S>>>,
arguments: &'a Option<Spanning<Arguments>>, arguments: &'a Option<Spanning<Arguments<S>>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref arguments) = *arguments { if let Some(ref arguments) = *arguments {
for argument in arguments.item.iter() { for argument in arguments.item.iter() {
let arg_type = meta_args let arg_type = meta_args
@ -163,11 +186,14 @@ fn visit_arguments<'a, V: Visitor<'a>>(
} }
} }
fn visit_selection_set<'a, V: Visitor<'a>>( fn visit_selection_set<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
selection_set: &'a Vec<Selection>, selection_set: &'a Vec<Selection<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
ctx.with_pushed_parent_type(|ctx| { ctx.with_pushed_parent_type(|ctx| {
v.enter_selection_set(ctx, selection_set); v.enter_selection_set(ctx, selection_set);
@ -179,11 +205,14 @@ fn visit_selection_set<'a, V: Visitor<'a>>(
}); });
} }
fn visit_selection<'a, V: Visitor<'a>>( fn visit_selection<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
selection: &'a Selection, selection: &'a Selection<S>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
match *selection { match *selection {
Selection::Field(ref field) => visit_field(v, ctx, field), Selection::Field(ref field) => visit_field(v, ctx, field),
Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread), Selection::FragmentSpread(ref spread) => visit_fragment_spread(v, ctx, spread),
@ -191,12 +220,16 @@ fn visit_selection<'a, V: Visitor<'a>>(
} }
} }
fn visit_field<'a, V: Visitor<'a>>( fn visit_field<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
field: &'a Spanning<Field>, field: &'a Spanning<Field<S>>,
) { ) where
let meta_field = ctx.parent_type() S: ScalarValue,
V: Visitor<'a, S>,
{
let meta_field = ctx
.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item)); .and_then(|t| t.field_by_name(field.item.name.item));
let field_type = meta_field.map(|f| &f.field_type); let field_type = meta_field.map(|f| &f.field_type);
@ -216,11 +249,14 @@ fn visit_field<'a, V: Visitor<'a>>(
}); });
} }
fn visit_fragment_spread<'a, V: Visitor<'a>>( fn visit_fragment_spread<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread>, spread: &'a Spanning<FragmentSpread<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
v.enter_fragment_spread(ctx, spread); v.enter_fragment_spread(ctx, spread);
visit_directives(v, ctx, &spread.item.directives); visit_directives(v, ctx, &spread.item.directives);
@ -228,12 +264,15 @@ fn visit_fragment_spread<'a, V: Visitor<'a>>(
v.exit_fragment_spread(ctx, spread); v.exit_fragment_spread(ctx, spread);
} }
fn visit_inline_fragment<'a, V: Visitor<'a>>( fn visit_inline_fragment<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<InlineFragment>, fragment: &'a Spanning<InlineFragment<S>>,
) { ) where
let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| { S: ScalarValue,
V: Visitor<'a, S>,
{
let mut visit_fn = move |ctx: &mut ValidatorContext<'a, S>| {
v.enter_inline_fragment(ctx, fragment); v.enter_inline_fragment(ctx, fragment);
visit_directives(v, ctx, &fragment.item.directives); visit_directives(v, ctx, &fragment.item.directives);
@ -255,23 +294,26 @@ fn visit_inline_fragment<'a, V: Visitor<'a>>(
} }
} }
fn visit_input_value<'a, V: Visitor<'a>>( fn visit_input_value<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue>, input_value: &'a Spanning<InputValue<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
enter_input_value(v, ctx, input_value); enter_input_value(v, ctx, input_value);
match input_value.item { match input_value.item {
InputValue::Object(ref fields) => for field in fields { InputValue::Object(ref fields) => for field in fields {
let inner_type = ctx.current_input_type_literal() let inner_type = ctx
.current_input_type_literal()
.and_then(|t| match *t { .and_then(|t| match *t {
Type::NonNullNamed(ref name) | Type::Named(ref name) => { Type::NonNullNamed(ref name) | Type::Named(ref name) => {
ctx.schema.concrete_type_by_name(name) ctx.schema.concrete_type_by_name(name)
} }
_ => None, _ => None,
}) }).and_then(|ct| ct.input_field_by_name(&field.0.item))
.and_then(|ct| ct.input_field_by_name(&field.0.item))
.map(|f| &f.arg_type); .map(|f| &f.arg_type);
ctx.with_pushed_input_type(inner_type, |ctx| { ctx.with_pushed_input_type(inner_type, |ctx| {
@ -300,11 +342,14 @@ fn visit_input_value<'a, V: Visitor<'a>>(
exit_input_value(v, ctx, input_value); exit_input_value(v, ctx, input_value);
} }
fn enter_input_value<'a, V: Visitor<'a>>( fn enter_input_value<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue>, input_value: &'a Spanning<InputValue<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
use InputValue::*; use InputValue::*;
let start = &input_value.start; let start = &input_value.start;
@ -312,10 +357,7 @@ fn enter_input_value<'a, V: Visitor<'a>>(
match input_value.item { match input_value.item {
Null => v.enter_null_value(ctx, Spanning::start_end(start, end, ())), Null => v.enter_null_value(ctx, Spanning::start_end(start, end, ())),
Int(ref i) => v.enter_int_value(ctx, Spanning::start_end(start, end, *i)), Scalar(ref s) => v.enter_scalar_value(ctx, Spanning::start_end(start, end, s)),
Float(ref f) => v.enter_float_value(ctx, Spanning::start_end(start, end, *f)),
String(ref s) => v.enter_string_value(ctx, Spanning::start_end(start, end, s)),
Boolean(ref b) => v.enter_boolean_value(ctx, Spanning::start_end(start, end, *b)),
Enum(ref s) => v.enter_enum_value(ctx, Spanning::start_end(start, end, s)), Enum(ref s) => v.enter_enum_value(ctx, Spanning::start_end(start, end, s)),
Variable(ref s) => v.enter_variable_value(ctx, Spanning::start_end(start, end, s)), Variable(ref s) => v.enter_variable_value(ctx, Spanning::start_end(start, end, s)),
List(ref l) => v.enter_list_value(ctx, Spanning::start_end(start, end, l)), List(ref l) => v.enter_list_value(ctx, Spanning::start_end(start, end, l)),
@ -323,11 +365,14 @@ fn enter_input_value<'a, V: Visitor<'a>>(
} }
} }
fn exit_input_value<'a, V: Visitor<'a>>( fn exit_input_value<'a, S, V>(
v: &mut V, v: &mut V,
ctx: &mut ValidatorContext<'a>, ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue>, input_value: &'a Spanning<InputValue<S>>,
) { ) where
S: ScalarValue,
V: Visitor<'a, S>,
{
use InputValue::*; use InputValue::*;
let start = &input_value.start; let start = &input_value.start;
@ -335,10 +380,7 @@ fn exit_input_value<'a, V: Visitor<'a>>(
match input_value.item { match input_value.item {
Null => v.exit_null_value(ctx, Spanning::start_end(start, end, ())), Null => v.exit_null_value(ctx, Spanning::start_end(start, end, ())),
Int(ref i) => v.exit_int_value(ctx, Spanning::start_end(start, end, *i)), Scalar(ref s) => v.exit_scalar_value(ctx, Spanning::start_end(start, end, s)),
Float(ref f) => v.exit_float_value(ctx, Spanning::start_end(start, end, *f)),
String(ref s) => v.exit_string_value(ctx, Spanning::start_end(start, end, s)),
Boolean(ref b) => v.exit_boolean_value(ctx, Spanning::start_end(start, end, *b)),
Enum(ref s) => v.exit_enum_value(ctx, Spanning::start_end(start, end, s)), Enum(ref s) => v.exit_enum_value(ctx, Spanning::start_end(start, end, s)),
Variable(ref s) => v.exit_variable_value(ctx, Spanning::start_end(start, end, s)), Variable(ref s) => v.exit_variable_value(ctx, Spanning::start_end(start, end, s)),
List(ref l) => v.exit_list_value(ctx, Spanning::start_end(start, end, l)), List(ref l) => v.exit_list_value(ctx, Spanning::start_end(start, end, l)),

View file

@ -1,424 +0,0 @@
use ast::{InputValue, ToInputValue};
use parser::Spanning;
use std::iter::FromIterator;
use std::vec::IntoIter;
/// Serializable value returned from query and field execution.
///
/// Used by the execution engine and resolvers to build up the response
/// structure. Similar to the `Json` type found in the serialize crate.
///
/// It is also similar to the `InputValue` type, but can not contain enum
/// values or variables. Also, lists and objects do not contain any location
/// information since they are generated by resolving fields and values rather
/// than parsing a source query.
#[derive(Debug, PartialEq, Clone)]
#[allow(missing_docs)]
pub enum Value {
Null,
Int(i32),
Float(f64),
String(String),
Boolean(bool),
List(Vec<Value>),
Object(Object),
}
/// A Object value
#[derive(Debug, PartialEq, Clone)]
pub struct Object {
key_value_list: Vec<(String, Value)>,
}
impl Object {
/// Create a new Object value with a fixed number of
/// preallocated slots for field-value pairs
pub fn with_capacity(size: usize) -> Self {
Object {
key_value_list: Vec::with_capacity(size),
}
}
/// Add a new field with a value
///
/// If there is already a field with the same name the old value
/// is returned
pub fn add_field<K>(&mut self, k: K, value: Value) -> Option<Value>
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
if let Some(item) = self
.key_value_list
.iter_mut()
.find(|&&mut (ref key, _)| (key as &str) == k)
{
return Some(::std::mem::replace(&mut item.1, value));
}
self.key_value_list.push((k.into(), value));
None
}
/// Check if the object already contains a field with the given name
pub fn contains_field<K>(&self, f: K) -> bool
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.any(|&(ref key, _)| (key as &str) == f)
}
/// Get a iterator over all field value pairs
///
/// This method returns a iterator over `&'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &(String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter(&self) -> FieldIter {
FieldIter {
inner: self.key_value_list.iter(),
}
}
/// Get a iterator over all mutable field value pairs
///
/// This method returns a iterator over `&mut 'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &mut (String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter_mut(&mut self) -> FieldIterMut {
FieldIterMut {
inner: self.key_value_list.iter_mut(),
}
}
/// Get the current number of fields
pub fn field_count(&self) -> usize {
self.key_value_list.len()
}
/// Get the value for a given field
pub fn get_field_value<K>(&self, key: K) -> Option<&Value>
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.find(|&&(ref k, _)| (k as &str) == key)
.map(|&(_, ref value)| value)
}
}
impl IntoIterator for Object {
type Item = (String, Value);
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.key_value_list.into_iter()
}
}
impl From<Object> for Value {
fn from(o: Object) -> Self {
Value::Object(o)
}
}
impl<K> FromIterator<(K, Value)> for Object
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, Value)>,
{
let iter = iter.into_iter();
let mut ret = Self {
key_value_list: Vec::with_capacity(iter.size_hint().0),
};
for (k, v) in iter {
ret.add_field(k, v);
}
ret
}
}
#[doc(hidden)]
pub struct FieldIter<'a> {
inner: ::std::slice::Iter<'a, (String, Value)>,
}
impl<'a> Iterator for FieldIter<'a> {
type Item = &'a (String, Value);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
#[doc(hidden)]
pub struct FieldIterMut<'a> {
inner: ::std::slice::IterMut<'a, (String, Value)>,
}
impl<'a> Iterator for FieldIterMut<'a> {
type Item = &'a mut (String, Value);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl Value {
// CONSTRUCTORS
/// Construct a null value.
pub fn null() -> Value {
Value::Null
}
/// Construct an integer value.
pub fn int(i: i32) -> Value {
Value::Int(i)
}
/// Construct a floating point value.
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())
}
/// Construct a boolean value.
pub fn boolean(b: bool) -> Value {
Value::Boolean(b)
}
/// Construct a list value.
pub fn list(l: Vec<Value>) -> Value {
Value::List(l)
}
/// Construct an object value.
pub fn object(o: Object) -> Value {
Value::Object(o)
}
// DISCRIMINATORS
/// Does this value represent null?
pub fn is_null(&self) -> bool {
match *self {
Value::Null => true,
_ => false,
}
}
/// View the underlying float value, if present.
pub fn as_float_value(&self) -> Option<&f64> {
match *self {
Value::Float(ref f) => Some(f),
_ => None,
}
}
/// View the underlying object value, if present.
pub fn as_object_value(&self) -> Option<&Object> {
match *self {
Value::Object(ref o) => Some(o),
_ => None,
}
}
/// Mutable view into the underlying object value, if present.
pub fn as_mut_object_value(&mut self) -> Option<&mut Object> {
match *self {
Value::Object(ref mut o) => Some(o),
_ => None,
}
}
/// View the underlying list value, if present.
pub fn as_list_value(&self) -> Option<&Vec<Value>> {
match *self {
Value::List(ref l) => Some(l),
_ => None,
}
}
/// View the underlying string value, if present.
pub fn as_string_value(&self) -> Option<&str> {
match *self {
Value::String(ref s) => Some(s),
_ => None,
}
}
}
impl ToInputValue for Value {
fn to_input_value(&self) -> InputValue {
match *self {
Value::Null => InputValue::Null,
Value::Int(i) => InputValue::Int(i),
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_input_value()))
.collect(),
),
Value::Object(ref o) => InputValue::Object(
o.iter()
.map(|&(ref k, ref v)| {
(
Spanning::unlocated(k.clone()),
Spanning::unlocated(v.to_input_value()),
)
})
.collect(),
),
}
}
}
impl<'a> From<&'a str> for Value {
fn from(s: &'a str) -> Value {
Value::string(s)
}
}
impl From<String> for Value {
fn from(s: String) -> Value {
Value::string(s)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Value {
Value::boolean(b)
}
}
impl From<i32> for Value {
fn from(i: i32) -> Value {
Value::int(i)
}
}
impl From<f64> for Value {
fn from(f: f64) -> Value {
Value::float(f)
}
}
impl<T> From<Option<T>> for Value
where
Value: From<T>,
{
fn from(v: Option<T>) -> Value {
match v {
Some(v) => Value::from(v),
None => Value::null(),
}
}
}
/// Construct JSON-like values by using JSON syntax
///
/// This macro can be used to create `Value` instances using a JSON syntax.
/// Value objects are used mostly when creating custom errors from fields.
///
/// Here are some examples; the resulting JSON will look just like what you
/// passed in.
/// ```rust
/// #[macro_use] extern crate juniper;
///
/// # fn main() {
/// graphql_value!(1234);
/// graphql_value!("test");
/// graphql_value!([ 1234, "test", true ]);
/// graphql_value!({ "key": "value", "foo": 1234 });
/// # }
/// ```
#[macro_export]
macro_rules! graphql_value {
([ $($arg:tt),* $(,)* ]) => {
$crate::Value::list(vec![
$( graphql_value!($arg), )*
])
};
({ $($key:tt : $val:tt ),* $(,)* }) => {
$crate::Value::object(vec![
$( ($key, graphql_value!($val)), )*
].into_iter().collect())
};
(None) => ($crate::Value::null());
($e:expr) => ($crate::Value::from($e))
}
#[cfg(test)]
mod tests {
use super::Value;
#[test]
fn value_macro_string() {
assert_eq!(graphql_value!("test"), Value::string("test"));
}
#[test]
fn value_macro_int() {
assert_eq!(graphql_value!(123), Value::int(123));
}
#[test]
fn value_macro_float() {
assert_eq!(graphql_value!(123.5), Value::float(123.5));
}
#[test]
fn value_macro_boolean() {
assert_eq!(graphql_value!(false), Value::boolean(false));
}
#[test]
fn value_macro_option() {
assert_eq!(graphql_value!(Some("test")), Value::string("test"));
assert_eq!(graphql_value!(None), Value::null());
}
#[test]
fn value_macro_list() {
assert_eq!(
graphql_value!([123, "Test", false]),
Value::list(vec![
Value::int(123),
Value::string("Test"),
Value::boolean(false),
])
);
assert_eq!(
graphql_value!([123, [456], 789]),
Value::list(vec![
Value::int(123),
Value::list(vec![Value::int(456)]),
Value::int(789),
])
);
}
#[test]
fn value_macro_object() {
assert_eq!(
graphql_value!({ "key": 123, "next": true }),
Value::object(
vec![("key", Value::int(123)), ("next", Value::boolean(true))]
.into_iter()
.collect(),
)
);
}
}

351
juniper/src/value/mod.rs Normal file
View file

@ -0,0 +1,351 @@
use ast::{InputValue, ToInputValue};
use parser::Spanning;
mod object;
mod scalar;
pub use self::object::Object;
pub use self::scalar::{
DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue,
};
/// Serializable value returned from query and field execution.
///
/// Used by the execution engine and resolvers to build up the response
/// structure. Similar to the `Json` type found in the serialize crate.
///
/// It is also similar to the `InputValue` type, but can not contain enum
/// values or variables. Also, lists and objects do not contain any location
/// information since they are generated by resolving fields and values rather
/// than parsing a source query.
#[derive(Debug, PartialEq, Clone)]
#[allow(missing_docs)]
pub enum Value<S = DefaultScalarValue> {
Null,
Scalar(S),
List(Vec<Value<S>>),
Object(Object<S>),
}
impl<S> Value<S>
where
S: ScalarValue,
{
// CONSTRUCTORS
/// Construct a null value.
pub fn null() -> Self {
Value::Null
}
/// Construct an integer value.
#[deprecated(since = "0.11", note = "Use `Value::scalar` instead")]
pub fn int(i: i32) -> Self {
Self::scalar(i)
}
/// Construct a floating point value.
#[deprecated(since = "0.11", note = "Use `Value::scalar` instead")]
pub fn float(f: f64) -> Self {
Self::scalar(f)
}
/// Construct a string value.
#[deprecated(since = "0.11", note = "Use `Value::scalar` instead")]
pub fn string(s: &str) -> Self {
Self::scalar(s.to_owned())
}
/// Construct a boolean value.
#[deprecated(since = "0.11", note = "Use `Value::scalar` instead")]
pub fn boolean(b: bool) -> Self {
Self::scalar(b)
}
/// Construct a list value.
pub fn list(l: Vec<Self>) -> Self {
Value::List(l)
}
/// Construct an object value.
pub fn object(o: Object<S>) -> Self {
Value::Object(o)
}
/// Construct a scalar value
pub fn scalar<T>(s: T) -> Self
where
T: Into<S>,
{
Value::Scalar(s.into())
}
// DISCRIMINATORS
/// Does this value represent null?
pub fn is_null(&self) -> bool {
match *self {
Value::Null => true,
_ => false,
}
}
/// View the underlying scalar value if present
pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T>
where
&'a S: Into<Option<&'a T>>,
{
match *self {
Value::Scalar(ref s) => s.into(),
_ => None,
}
}
/// View the underlying float value, if present.
#[deprecated(
since = "0.11",
note = "Use `Value::as_scalar_value` instead"
)]
pub fn as_float_value(&self) -> Option<f64>
where
for<'a> &'a S: ScalarRefValue<'a>,
{
self.as_scalar_value::<f64>().map(|v| *v)
}
/// View the underlying object value, if present.
pub fn as_object_value(&self) -> Option<&Object<S>> {
match *self {
Value::Object(ref o) => Some(o),
_ => None,
}
}
/// Mutable view into the underlying object value, if present.
pub fn as_mut_object_value(&mut self) -> Option<&mut Object<S>> {
match *self {
Value::Object(ref mut o) => Some(o),
_ => None,
}
}
/// View the underlying list value, if present.
pub fn as_list_value(&self) -> Option<&Vec<Self>> {
match *self {
Value::List(ref l) => Some(l),
_ => None,
}
}
/// View the underlying scalar value, if present
pub fn as_scalar(&self) -> Option<&S> {
match *self {
Value::Scalar(ref s) => Some(s),
_ => None,
}
}
/// View the underlying string value, if present.
#[deprecated(
since = "0.11",
note = "Use `Value::as_scalar_value` instead"
)]
pub fn as_string_value<'a>(&'a self) -> Option<&'a str>
where
Option<&'a String>: From<&'a S>,
{
self.as_scalar_value::<String>().map(|s| s as &str)
}
}
impl<S: ScalarValue> ToInputValue<S> for Value<S> {
fn to_input_value(&self) -> InputValue<S> {
match *self {
Value::Null => InputValue::Null,
Value::Scalar(ref s) => InputValue::Scalar(s.clone()),
Value::List(ref l) => InputValue::List(
l.iter()
.map(|x| Spanning::unlocated(x.to_input_value()))
.collect(),
),
Value::Object(ref o) => InputValue::Object(
o.iter()
.map(|&(ref k, ref v)| {
(
Spanning::unlocated(k.clone()),
Spanning::unlocated(v.to_input_value()),
)
}).collect(),
),
}
}
}
impl<S, T> From<Option<T>> for Value<S>
where
S: ScalarValue,
Value<S>: From<T>,
{
fn from(v: Option<T>) -> Value<S> {
match v {
Some(v) => v.into(),
None => Value::null(),
}
}
}
impl<'a, S> From<&'a str> for Value<S>
where
S: ScalarValue,
{
fn from(s: &'a str) -> Self {
Value::scalar(s.to_owned())
}
}
impl<S> From<String> for Value<S>
where
S: ScalarValue,
{
fn from(s: String) -> Self {
Value::scalar(s)
}
}
impl<S> From<i32> for Value<S>
where
S: ScalarValue,
{
fn from(i: i32) -> Self {
Value::scalar(i)
}
}
impl<S> From<f64> for Value<S>
where
S: ScalarValue,
{
fn from(f: f64) -> Self {
Value::scalar(f)
}
}
impl<S> From<bool> for Value<S>
where
S: ScalarValue,
{
fn from(b: bool) -> Self {
Value::scalar(b)
}
}
/// Construct JSON-like values by using JSON syntax
///
/// This macro can be used to create `Value` instances using a JSON syntax.
/// Value objects are used mostly when creating custom errors from fields.
///
/// Here are some examples; the resulting JSON will look just like what you
/// passed in.
/// ```rust
/// #[macro_use] extern crate juniper;
/// # use juniper::{Value, DefaultScalarValue};
/// # type V = Value<DefaultScalarValue>;
///
/// # fn main() {
/// # let _: V =
/// graphql_value!(1234);
/// # let _: V =
/// graphql_value!("test");
/// # let _: V =
/// graphql_value!([ 1234, "test", true ]);
/// # let _: V =
/// graphql_value!({ "key": "value", "foo": 1234 });
/// # }
/// ```
#[macro_export]
macro_rules! graphql_value {
([ $($arg:tt),* $(,)* ]) => {
$crate::Value::list(vec![
$( graphql_value!($arg), )*
])
};
({ $($key:tt : $val:tt ),* $(,)* }) => {
$crate::Value::object(vec![
$( ($key, graphql_value!($val)), )*
].into_iter().collect())
};
(None) => ($crate::Value::null());
($e:expr) => ($crate::Value::from($e))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn value_macro_string() {
let s: Value<DefaultScalarValue> = graphql_value!("test");
assert_eq!(s, Value::scalar("test"));
}
#[test]
fn value_macro_int() {
let s: Value<DefaultScalarValue> = graphql_value!(123);
assert_eq!(s, Value::scalar(123));
}
#[test]
fn value_macro_float() {
let s: Value<DefaultScalarValue> = graphql_value!(123.5);
assert_eq!(s, Value::scalar(123.5));
}
#[test]
fn value_macro_boolean() {
let s: Value<DefaultScalarValue> = graphql_value!(false);
assert_eq!(s, Value::scalar(false));
}
#[test]
fn value_macro_option() {
let s: Value<DefaultScalarValue> = graphql_value!(Some("test"));
assert_eq!(s, Value::scalar("test"));
let s: Value<DefaultScalarValue> = graphql_value!(None);
assert_eq!(s, Value::null());
}
#[test]
fn value_macro_list() {
let s: Value<DefaultScalarValue> = graphql_value!([123, "Test", false]);
assert_eq!(
s,
Value::list(vec![
Value::scalar(123),
Value::scalar("Test"),
Value::scalar(false),
])
);
let s: Value<DefaultScalarValue> = graphql_value!([123, [456], 789]);
assert_eq!(
s,
Value::list(vec![
Value::scalar(123),
Value::list(vec![Value::scalar(456)]),
Value::scalar(789),
])
);
}
#[test]
fn value_macro_object() {
let s: Value<DefaultScalarValue> = graphql_value!({ "key": 123, "next": true });
assert_eq!(
s,
Value::object(
vec![("key", Value::scalar(123)), ("next", Value::scalar(true))]
.into_iter()
.collect(),
)
);
}
}

149
juniper/src/value/object.rs Normal file
View file

@ -0,0 +1,149 @@
use std::iter::FromIterator;
use std::vec::IntoIter;
use super::Value;
/// A Object value
#[derive(Debug, Clone, PartialEq)]
pub struct Object<S> {
key_value_list: Vec<(String, Value<S>)>,
}
impl<S> Object<S> {
/// Create a new Object value with a fixed number of
/// preallocated slots for field-value pairs
pub fn with_capacity(size: usize) -> Self {
Object {
key_value_list: Vec::with_capacity(size),
}
}
/// Add a new field with a value
///
/// If there is already a field with the same name the old value
/// is returned
pub fn add_field<K>(&mut self, k: K, value: Value<S>) -> Option<Value<S>>
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
if let Some(item) = self
.key_value_list
.iter_mut()
.find(|&&mut (ref key, _)| (key as &str) == k)
{
return Some(::std::mem::replace(&mut item.1, value));
}
self.key_value_list.push((k.into(), value));
None
}
/// Check if the object already contains a field with the given name
pub fn contains_field<K>(&self, f: K) -> bool
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.any(|&(ref key, _)| (key as &str) == f)
}
/// Get a iterator over all field value pairs
///
/// This method returns a iterator over `&'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &(String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter(&self) -> FieldIter<S> {
FieldIter {
inner: self.key_value_list.iter(),
}
}
/// Get a iterator over all mutable field value pairs
///
/// This method returns a iterator over `&mut 'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &mut (String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter_mut(&mut self) -> FieldIterMut<S> {
FieldIterMut {
inner: self.key_value_list.iter_mut(),
}
}
/// Get the current number of fields
pub fn field_count(&self) -> usize {
self.key_value_list.len()
}
/// Get the value for a given field
pub fn get_field_value<K>(&self, key: K) -> Option<&Value<S>>
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.find(|&&(ref k, _)| (k as &str) == key)
.map(|&(_, ref value)| value)
}
}
impl<S> IntoIterator for Object<S> {
type Item = (String, Value<S>);
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.key_value_list.into_iter()
}
}
impl<S> From<Object<S>> for Value<S> {
fn from(o: Object<S>) -> Self {
Value::Object(o)
}
}
impl<K, S> FromIterator<(K, Value<S>)> for Object<S>
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, Value<S>)>,
{
let iter = iter.into_iter();
let mut ret = Self {
key_value_list: Vec::with_capacity(iter.size_hint().0),
};
for (k, v) in iter {
ret.add_field(k, v);
}
ret
}
}
#[doc(hidden)]
pub struct FieldIter<'a, S: 'a> {
inner: ::std::slice::Iter<'a, (String, Value<S>)>,
}
impl<'a, S> Iterator for FieldIter<'a, S> {
type Item = &'a (String, Value<S>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
#[doc(hidden)]
pub struct FieldIterMut<'a, S: 'a> {
inner: ::std::slice::IterMut<'a, (String, Value<S>)>,
}
impl<'a, S> Iterator for FieldIterMut<'a, S> {
type Item = &'a mut (String, Value<S>);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}

362
juniper/src/value/scalar.rs Normal file
View file

@ -0,0 +1,362 @@
use parser::{ParseError, ScalarToken};
use serde::de;
use serde::ser::Serialize;
use std::fmt::{self, Debug, Display};
/// The result of converting a string into a scalar value
pub type ParseScalarResult<'a, S = DefaultScalarValue> = Result<S, ParseError<'a>>;
/// A trait used to convert a `ScalarToken` into a certain scalar value type
pub trait ParseScalarValue<S = DefaultScalarValue> {
/// See the trait documentation
fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S>;
}
/// A trait marking a type that could be used as internal representation of
/// scalar values in juniper
///
/// The main objective of this abstraction is to allow other libraries to
/// replace the default representation with something that better fits thei
/// needs.
/// There is a custom derive (`#[derive(ScalarValue)]`) available that implements
/// most of the required traits automatically for a enum representing a scalar value.
/// This derives needs a additional annotation of the form
/// `#[juniper(visitor = "VisitorType")]` to specify a type that implements
/// `serde::de::Visitor` and that is used to deserialize the value.
///
/// # Implementing a new scalar value representation
/// The preferred way to define a new scalar value representation is
/// defining a enum containing a variant for each type that needs to be represented
/// at the lowest level.
/// The following example introduces an new variant that is able to store 64 bit integers.
///
/// ```
/// # #[macro_use]
/// # extern crate juniper;
/// # extern crate serde;
/// # use serde::{de, Deserialize, Deserializer};
/// # use juniper::ScalarValue;
/// # use std::fmt;
/// #
/// #[derive(Debug, Clone, PartialEq, ScalarValue)]
/// enum MyScalarValue {
/// Int(i32),
/// Long(i64),
/// Float(f64),
/// String(String),
/// Boolean(bool),
/// }
///
/// impl ScalarValue for MyScalarValue {
/// type Visitor = MyScalarValueVisitor;
///
/// fn as_int(&self) -> Option<i32> {
/// match *self {
/// MyScalarValue::Int(ref i) => Some(*i),
/// _ => None,
/// }
/// }
///
/// fn as_string(&self) -> Option<String> {
/// match *self {
/// MyScalarValue::String(ref s) => Some(s.clone()),
/// _ => None,
/// }
/// }
///
/// fn as_float(&self) -> Option<f64> {
/// match *self {
/// MyScalarValue::Int(ref i) => Some(*i as f64),
/// MyScalarValue::Float(ref f) => Some(*f),
/// _ => None,
/// }
/// }
///
/// fn as_boolean(&self) -> Option<bool> {
/// match *self {
/// MyScalarValue::Boolean(ref b) => Some(*b),
/// _ => None,
/// }
/// }
/// }
///
/// #[derive(Default)]
/// struct MyScalarValueVisitor;
///
/// impl<'de> de::Visitor<'de> for MyScalarValueVisitor {
/// type Value = MyScalarValue;
///
/// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
/// formatter.write_str("a valid input value")
/// }
///
/// fn visit_bool<E>(self, value: bool) -> Result<MyScalarValue, E> {
/// Ok(MyScalarValue::Boolean(value))
/// }
///
/// fn visit_i32<E>(self, value: i32) -> Result<MyScalarValue, E>
/// where
/// E: de::Error,
/// {
/// Ok(MyScalarValue::Int(value))
/// }
///
/// fn visit_i64<E>(self, value: i64) -> Result<MyScalarValue, E>
/// where
/// E: de::Error,
/// {
/// if value <= i32::max_value() as i64 {
/// self.visit_i32(value as i32)
/// } else {
/// Ok(MyScalarValue::Long(value))
/// }
/// }
///
/// fn visit_u32<E>(self, value: u32) -> Result<MyScalarValue, E>
/// where
/// E: de::Error,
/// {
/// if value <= i32::max_value() as u32 {
/// self.visit_i32(value as i32)
/// } else {
/// self.visit_u64(value as u64)
/// }
/// }
///
/// fn visit_u64<E>(self, value: u64) -> Result<MyScalarValue, E>
/// where
/// E: de::Error,
/// {
/// if value <= i64::max_value() as u64 {
/// self.visit_i64(value as i64)
/// } else {
/// // Browser's JSON.stringify serialize all numbers having no
/// // fractional part as integers (no decimal point), so we
/// // must parse large integers as floating point otherwise
/// // we would error on transferring large floating point
/// // numbers.
/// Ok(MyScalarValue::Float(value as f64))
/// }
/// }
///
/// fn visit_f64<E>(self, value: f64) -> Result<MyScalarValue, E> {
/// Ok(MyScalarValue::Float(value))
/// }
///
/// fn visit_str<E>(self, value: &str) -> Result<MyScalarValue, E>
/// where
/// E: de::Error,
/// {
/// self.visit_string(value.into())
/// }
///
/// fn visit_string<E>(self, value: String) -> Result<MyScalarValue, E> {
/// Ok(MyScalarValue::String(value))
/// }
/// }
///
/// # fn main() {}
/// ```
pub trait ScalarValue:
Debug
+ Display
+ PartialEq
+ Clone
+ Serialize
+ From<String>
+ From<bool>
+ From<i32>
+ From<f64>
+ Into<Option<bool>>
+ Into<Option<i32>>
+ Into<Option<f64>>
+ Into<Option<String>>
{
/// Serde visitor used to deserialize this scalar value
type Visitor: for<'de> de::Visitor<'de, Value = Self> + Default;
/// Checks if the current value contains the a value of the current type
///
/// ```
/// # use juniper::{ScalarValue, DefaultScalarValue};
///
/// let value = DefaultScalarValue::Int(42);
///
/// assert_eq!(value.is_type::<i32>(), true);
/// assert_eq!(value.is_type::<f64>(), false);
///
/// ```
fn is_type<'a, T>(&'a self) -> bool
where
T: 'a,
&'a Self: Into<Option<&'a T>>,
{
self.into().is_some()
}
/// Convert the given scalar value into an integer value
///
/// This function is used for implementing `GraphQLType` for `i32` for all
/// scalar values. Implementations should convert all supported integer
/// types with 32 bit or less to an integer if requested.
fn as_int(&self) -> Option<i32>;
/// Convert the given scalar value into a string value
///
/// This function is used for implementing `GraphQLType` for `String` for all
/// scalar values
fn as_string(&self) -> Option<String>;
/// Convert the given scalar value into a float value
///
/// This function is used for implementing `GraphQLType` for `f64` for all
/// scalar values. Implementations should convert all supported integer
/// types with 64 bit or less and all floating point values with 64 bit or
/// less to a float if requested.
fn as_float(&self) -> Option<f64>;
/// Convert the given scalar value into a boolean value
///
/// This function is used for implementing `GraphQLType` for `bool` for all
/// scalar values.
fn as_boolean(&self) -> Option<bool>;
}
/// A marker trait extending the [`ScalarValue`](../trait.ScalarValue.html) trait
///
/// This trait should not be relied on directly by most apps. However, you may
/// need a where clause in the form of `for<'b> &'b S: ScalarRefValue<'b>` to
/// abstract over different scalar value types.
///
/// This is automatically implemented for a type as soon as the type implements
/// `ScalarValue` and the additional conversations.
pub trait ScalarRefValue<'a>:
Debug
+ Into<Option<&'a bool>>
+ Into<Option<&'a i32>>
+ Into<Option<&'a String>>
+ Into<Option<&'a f64>>
{
}
impl<'a, T> ScalarRefValue<'a> for &'a T
where
T: ScalarValue,
&'a T: Into<Option<&'a bool>>
+ Into<Option<&'a i32>>
+ Into<Option<&'a String>>
+ Into<Option<&'a f64>>,
{}
/// The default scalar value representation in juniper
///
/// This types closely follows the graphql specification.
#[derive(Debug, PartialEq, Clone, ScalarValue)]
#[allow(missing_docs)]
pub enum DefaultScalarValue {
Int(i32),
Float(f64),
String(String),
Boolean(bool),
}
impl ScalarValue for DefaultScalarValue {
type Visitor = DefaultScalarValueVisitor;
fn as_int(&self) -> Option<i32> {
match *self {
DefaultScalarValue::Int(ref i) => Some(*i),
_ => None,
}
}
fn as_string(&self) -> Option<String> {
match *self {
DefaultScalarValue::String(ref s) => Some(s.clone()),
_ => None,
}
}
fn as_float(&self) -> Option<f64> {
match *self {
DefaultScalarValue::Int(ref i) => Some(*i as f64),
DefaultScalarValue::Float(ref f) => Some(*f),
_ => None,
}
}
fn as_boolean(&self) -> Option<bool> {
match *self {
DefaultScalarValue::Boolean(ref b) => Some(*b),
_ => None,
}
}
}
impl<'a> From<&'a str> for DefaultScalarValue {
fn from(s: &'a str) -> Self {
DefaultScalarValue::String(s.into())
}
}
#[derive(Default, Clone, Copy, Debug)]
pub struct DefaultScalarValueVisitor;
impl<'de> de::Visitor<'de> for DefaultScalarValueVisitor {
type Value = DefaultScalarValue;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a valid input value")
}
fn visit_bool<E>(self, value: bool) -> Result<DefaultScalarValue, E> {
Ok(DefaultScalarValue::Boolean(value))
}
fn visit_i64<E>(self, value: i64) -> Result<DefaultScalarValue, E>
where
E: de::Error,
{
if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) {
Ok(DefaultScalarValue::Int(value as i32))
} else {
// Browser's JSON.stringify serialize all numbers having no
// fractional part as integers (no decimal point), so we
// must parse large integers as floating point otherwise
// we would error on transferring large floating point
// numbers.
Ok(DefaultScalarValue::Float(value as f64))
}
}
fn visit_u64<E>(self, value: u64) -> Result<DefaultScalarValue, E>
where
E: de::Error,
{
if value <= i32::max_value() as u64 {
self.visit_i64(value as i64)
} else {
// Browser's JSON.stringify serialize all numbers having no
// fractional part as integers (no decimal point), so we
// must parse large integers as floating point otherwise
// we would error on transferring large floating point
// numbers.
Ok(DefaultScalarValue::Float(value as f64))
}
}
fn visit_f64<E>(self, value: f64) -> Result<DefaultScalarValue, E> {
Ok(DefaultScalarValue::Float(value))
}
fn visit_str<E>(self, value: &str) -> Result<DefaultScalarValue, E>
where
E: de::Error,
{
self.visit_string(value.into())
}
fn visit_string<E>(self, value: String) -> Result<DefaultScalarValue, E> {
Ok(DefaultScalarValue::String(value))
}
}

View file

@ -1,7 +1,7 @@
use proc_macro2::{Span, TokenStream}; use proc_macro2::{Span, TokenStream};
use syn; use syn;
use syn::{Data, DeriveInput, Fields, Ident, Meta, NestedMeta, Variant}; use syn::{Data, DeriveInput, Fields, Ident, Variant};
use util::*; use util::*;
@ -9,7 +9,6 @@ use util::*;
struct EnumAttrs { struct EnumAttrs {
name: Option<String>, name: Option<String>,
description: Option<String>, description: Option<String>,
internal: bool,
} }
impl EnumAttrs { impl EnumAttrs {
@ -17,8 +16,6 @@ impl EnumAttrs {
let mut res = EnumAttrs { let mut res = EnumAttrs {
name: None, name: None,
description: None, description: None,
/// Flag to specify whether the calling crate is the "juniper" crate itself.
internal: false,
}; };
// Check doc comments for description. // Check doc comments for description.
@ -27,7 +24,9 @@ impl EnumAttrs {
// Check attributes for name and description. // Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) { if let Some(items) = get_graphql_attr(&input.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -38,19 +37,12 @@ impl EnumAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
match item {
NestedMeta::Meta(Meta::Word(ref ident)) => {
if ident == "_internal" {
res.internal = true;
continue;
}
}
_ => {}
}
panic!(format!( panic!(format!(
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}", "Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
item item
@ -78,7 +70,9 @@ impl EnumVariantAttrs {
// Check attributes for name and description. // Check attributes for name and description.
if let Some(items) = get_graphql_attr(&variant.attrs) { if let Some(items) = get_graphql_attr(&variant.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -89,11 +83,15 @@ impl EnumVariantAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "deprecated", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "deprecated", AttributeValidation::String)
{
res.deprecation = Some(val); res.deprecation = Some(val);
continue; continue;
} }
@ -166,7 +164,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
// Build resolve match clause. // Build resolve match clause.
resolves.extend(quote!{ resolves.extend(quote!{
&#ident::#var_ident => _juniper::Value::String(#name.to_string()), &#ident::#var_ident => _juniper::Value::scalar(String::from(#name)),
}); });
// Build from_input clause. // Build from_input clause.
@ -177,12 +175,15 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
// Build to_input clause. // Build to_input clause.
to_inputs.extend(quote!{ to_inputs.extend(quote!{
&#ident::#var_ident => &#ident::#var_ident =>
_juniper::InputValue::string(#name.to_string()), _juniper::InputValue::scalar(#name.to_string()),
}); });
} }
let body = quote! { let body = quote! {
impl _juniper::GraphQLType for #ident { impl<__S> _juniper::GraphQLType<__S> for #ident
where __S: _juniper::ScalarValue,
for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b>
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -190,8 +191,9 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
Some(#name) Some(#name)
} }
fn meta<'r>(_: &(), registry: &mut _juniper::Registry<'r>) fn meta<'r>(_: &(), registry: &mut _juniper::Registry<'r, __S>)
-> _juniper::meta::MetaType<'r> -> _juniper::meta::MetaType<'r, __S>
where __S: 'r,
{ {
let meta = registry.build_enum_type::<#ident>(&(), &[ let meta = registry.build_enum_type::<#ident>(&(), &[
#(#values)* #(#values)*
@ -203,26 +205,30 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
fn resolve( fn resolve(
&self, &self,
_: &(), _: &(),
_: Option<&[_juniper::Selection]>, _: Option<&[_juniper::Selection<__S>]>,
_: &_juniper::Executor<Self::Context> _: &_juniper::Executor<Self::Context, __S>
) -> _juniper::Value { ) -> _juniper::Value<__S> {
match self { match self {
#(#resolves)* #(#resolves)*
} }
} }
} }
impl _juniper::FromInputValue for #ident { impl<__S: _juniper::ScalarValue> _juniper::FromInputValue<__S> for #ident {
fn from_input_value(v: &_juniper::InputValue) -> Option<#ident> { fn from_input_value(v: &_juniper::InputValue<__S>) -> Option<#ident>
match v.as_enum_value().or_else(|| v.as_string_value()) { where for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b>
{
match v.as_enum_value().or_else(|| {
v.as_scalar_value::<String>().map(|s| s as &str)
}) {
#(#from_inputs)* #(#from_inputs)*
_ => None, _ => None,
} }
} }
} }
impl _juniper::ToInputValue for #ident { impl<__S: _juniper::ScalarValue> _juniper::ToInputValue<__S> for #ident {
fn to_input_value(&self) -> _juniper::InputValue { fn to_input_value(&self) -> _juniper::InputValue<__S> {
match self { match self {
#(#to_inputs)* #(#to_inputs)*
} }
@ -235,35 +241,13 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
Span::call_site(), Span::call_site(),
); );
// This ugly hack makes it possible to use the derive inside juniper itself.
// FIXME: Figure out a better way to do this!
let crate_reference = if attrs.internal {
quote! {
#[doc(hidden)]
mod _juniper {
pub use ::{
InputValue,
Value,
ToInputValue,
FromInputValue,
Executor,
Selection,
Registry,
GraphQLType,
meta
};
}
}
} else {
quote! {
extern crate juniper as _juniper;
}
};
let generated = quote! { let generated = quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)] #[doc(hidden)]
const #dummy_const : () = { const #dummy_const : () = {
#crate_reference mod _juniper {
__juniper_use_everything!();
}
#body #body
}; };
}; };

View file

@ -10,7 +10,7 @@ use util::*;
struct ObjAttrs { struct ObjAttrs {
name: Option<String>, name: Option<String>,
description: Option<String>, description: Option<String>,
internal: bool, scalar: Option<Ident>,
} }
impl ObjAttrs { impl ObjAttrs {
@ -23,7 +23,9 @@ impl ObjAttrs {
// Check attributes for name and description. // Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) { if let Some(items) = get_graphql_attr(&input.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -34,18 +36,17 @@ impl ObjAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
match item { if let Some(AttributeValue::String(scalar)) =
NestedMeta::Meta(Meta::Word(ref ident)) => { keyed_item_value(&item, "scalar", AttributeValidation::String)
if ident == "_internal" { {
res.internal = true; res.scalar = Some(Ident::new(&scalar as &str, Span::call_site()));
continue; continue;
}
}
_ => {}
} }
panic!(format!( panic!(format!(
"Unknown attribute for #[derive(GraphQLInputObject)]: {:?}", "Unknown attribute for #[derive(GraphQLInputObject)]: {:?}",
@ -75,7 +76,9 @@ impl ObjFieldAttrs {
// Check attributes for name and description. // Check attributes for name and description.
if let Some(items) = get_graphql_attr(&variant.attrs) { if let Some(items) = get_graphql_attr(&variant.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -86,14 +89,19 @@ impl ObjFieldAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "default", AttributeValidation::Any) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "default", AttributeValidation::Any)
{
res.default_expr = Some(val); res.default_expr = Some(val);
continue; continue;
} }
match item { match item {
NestedMeta::Meta(Meta::Word(ref ident)) => { NestedMeta::Meta(Meta::Word(ref ident)) => {
if ident == "default" { if ident == "default" {
@ -225,8 +233,8 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
// TODO: investigate the unwraps here, they seem dangerous! // TODO: investigate the unwraps here, they seem dangerous!
match obj.get(#name) { match obj.get(#name) {
#from_input_default #from_input_default
Some(v) => _juniper::FromInputValue::from_input_value(v).unwrap(), Some(ref v) => _juniper::FromInputValue::from_input_value(v).unwrap(),
_ => { None => {
_juniper::FromInputValue::from_input_value(&_juniper::InputValue::null()) _juniper::FromInputValue::from_input_value(&_juniper::InputValue::null())
.unwrap() .unwrap()
}, },
@ -240,8 +248,32 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
}); });
} }
let (_, ty_generics, _) = generics.split_for_impl();
let mut generics = generics.clone();
let scalar = if let Some(scalar) = attrs.scalar {
scalar
} else {
generics.params.push(parse_quote!(__S));
{
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
where_clause
.predicates
.push(parse_quote!(__S: _juniper::ScalarValue));
where_clause
.predicates
.push(parse_quote!(for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b>));
}
Ident::new("__S", Span::call_site())
};
let (impl_generics, _, where_clause) = generics.split_for_impl();
let body = quote! { let body = quote! {
impl #generics _juniper::GraphQLType for #ident #generics { impl#impl_generics _juniper::GraphQLType<#scalar> for #ident #ty_generics
#where_clause
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -251,8 +283,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
fn meta<'r>( fn meta<'r>(
_: &(), _: &(),
registry: &mut _juniper::Registry<'r> registry: &mut _juniper::Registry<'r, #scalar>
) -> _juniper::meta::MetaType<'r> { ) -> _juniper::meta::MetaType<'r, #scalar>
where #scalar: 'r
{
let fields = &[ let fields = &[
#(#meta_fields)* #(#meta_fields)*
]; ];
@ -262,8 +296,13 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
} }
} }
impl #generics _juniper::FromInputValue for #ident #generics { impl#impl_generics _juniper::FromInputValue<#scalar> for #ident #ty_generics
fn from_input_value(value: &_juniper::InputValue) -> Option<#ident #generics> { #where_clause
{
fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option<Self>
where
for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b>
{
if let Some(obj) = value.to_object_value() { if let Some(obj) = value.to_object_value() {
let item = #ident { let item = #ident {
#(#from_inputs)* #(#from_inputs)*
@ -276,8 +315,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
} }
} }
impl #generics _juniper::ToInputValue for #ident #generics { impl#impl_generics _juniper::ToInputValue<#scalar> for #ident #ty_generics
fn to_input_value(&self) -> _juniper::InputValue { #where_clause
{
fn to_input_value(&self) -> _juniper::InputValue<#scalar> {
_juniper::InputValue::object(vec![ _juniper::InputValue::object(vec![
#(#to_inputs)* #(#to_inputs)*
].into_iter().collect()) ].into_iter().collect())
@ -290,32 +331,13 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
Span::call_site(), Span::call_site(),
); );
// This ugly hack makes it possible to use the derive inside juniper itself.
// FIXME: Figure out a better way to do this!
let crate_reference = if attrs.internal {
quote! {
#[doc(hidden)]
mod _juniper {
pub use ::{
InputValue,
FromInputValue,
GraphQLType,
Registry,
meta,
ToInputValue
};
}
}
} else {
quote! {
extern crate juniper as _juniper;
}
};
let generated = quote! { let generated = quote! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)] #[doc(hidden)]
const #dummy_const : () = { const #dummy_const : () = {
#crate_reference mod _juniper {
__juniper_use_everything!();
}
#body #body
}; };
}; };

View file

@ -0,0 +1,127 @@
use proc_macro2::{Span, TokenStream};
use syn::{self, Data, Fields, Ident, Variant};
pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream {
let ident = &ast.ident;
let variants = match ast.data {
Data::Enum(ref enum_data) => &enum_data.variants,
_ => {
panic!("#[derive(ScalarValue)] may only be applied to enums, not to structs");
}
};
let froms = variants
.iter()
.map(|v| derive_from_variant(v, ident))
.collect::<Result<Vec<_>, String>>()
.unwrap_or_else(|s| panic!("{}", s));
let serialize = derive_serialize(variants.iter(), ident);
let display = derive_display(variants.iter(), ident);
let dummy_const = Ident::new(
format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(),
Span::call_site(),
);
quote!{
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)]
const #dummy_const: () = {
mod juniper {
__juniper_use_everything!();
}
extern crate std;
#(#froms)*
#serialize
#display
};
}
}
fn derive_display<'a, I>(variants: I, ident: &Ident) -> TokenStream
where
I: Iterator<Item = &'a Variant>,
{
let arms = variants.map(|v| {
let variant = &v.ident;
quote!(#ident::#variant(ref v) => write!(f, "{}", v),)
});
quote!{
impl std::fmt::Display for #ident {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
#(#arms)*
}
}
}
}
}
fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> TokenStream
where
I: Iterator<Item = &'a Variant>,
{
let arms = variants.map(|v| {
let variant = &v.ident;
quote!(#ident::#variant(ref v) => v.serialize(serializer),)
});
quote!{
impl juniper::serde::Serialize for #ident {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where S: juniper::serde::Serializer
{
match *self {
#(#arms)*
}
}
}
}
}
fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result<TokenStream, String> {
let ty = match variant.fields {
Fields::Unnamed(ref u) if u.unnamed.len() == 1 => &u.unnamed.first().unwrap().value().ty,
_ => {
return Err(String::from(
"Only enums with exactly one unnamed field per variant are supported",
))
}
};
let variant = &variant.ident;
Ok(quote!{
impl std::convert::From<#ty> for #ident {
fn from(t: #ty) -> Self {
#ident::#variant(t)
}
}
impl<'a> std::convert::From<&'a #ident> for std::option::Option<&'a #ty> {
fn from(t: &'a #ident) -> Self {
match *t {
#ident::#variant(ref t) => std::option::Option::Some(t),
_ => std::option::Option::None
}
}
}
impl std::convert::From<#ident> for std::option::Option<#ty> {
fn from(t: #ident) -> Self {
match t {
#ident::#variant(t) => std::option::Option::Some(t),
_ => std::option::Option::None
}
}
}
})
}

View file

@ -1,6 +1,6 @@
use proc_macro2::TokenStream; use proc_macro2::{Span, TokenStream};
use syn; use syn;
use syn::{Data, DeriveInput, Field, Fields}; use syn::{Data, DeriveInput, Field, Fields, Ident};
use util::*; use util::*;
@ -8,6 +8,7 @@ use util::*;
struct ObjAttrs { struct ObjAttrs {
name: Option<String>, name: Option<String>,
description: Option<String>, description: Option<String>,
scalar: Option<Ident>,
} }
impl ObjAttrs { impl ObjAttrs {
@ -20,7 +21,9 @@ impl ObjAttrs {
// Check attributes for name and description. // Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) { if let Some(items) = get_graphql_attr(&input.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -31,10 +34,18 @@ impl ObjAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
if let Some(AttributeValue::String(scalar)) =
keyed_item_value(&item, "scalar", AttributeValidation::String)
{
res.scalar = Some(Ident::new(&scalar as &str, Span::call_site()));
continue;
}
panic!(format!( panic!(format!(
"Unknown object attribute for #[derive(GraphQLObject)]: {:?}", "Unknown object attribute for #[derive(GraphQLObject)]: {:?}",
item item
@ -63,7 +74,9 @@ impl ObjFieldAttrs {
// Check attributes. // Check attributes.
if let Some(items) = get_graphql_attr(&variant.attrs) { if let Some(items) = get_graphql_attr(&variant.attrs) {
for item in items { for item in items {
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "name", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "name", AttributeValidation::String)
{
if is_valid_name(&*val) { if is_valid_name(&*val) {
res.name = Some(val); res.name = Some(val);
continue; continue;
@ -74,11 +87,15 @@ impl ObjFieldAttrs {
); );
} }
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "description", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "description", AttributeValidation::String)
{
res.description = Some(val); res.description = Some(val);
continue; continue;
} }
if let Some(AttributeValue::String(val)) = keyed_item_value(&item, "deprecation", AttributeValidation::String) { if let Some(AttributeValue::String(val)) =
keyed_item_value(&item, "deprecation", AttributeValidation::String)
{
res.deprecation = Some(val); res.deprecation = Some(val);
continue; continue;
} }
@ -170,8 +187,33 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
}); });
} }
let (_, ty_generics, _) = generics.split_for_impl();
let mut generics = generics.clone();
if attrs.scalar.is_none() {
generics.params.push(parse_quote!(__S));
{
let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
where_clause
.predicates
.push(parse_quote!(__S: juniper::ScalarValue));
where_clause
.predicates
.push(parse_quote!(for<'__b> &'__b __S: juniper::ScalarRefValue<'__b>));
}
}
let scalar = attrs
.scalar
.unwrap_or_else(|| Ident::new("__S", Span::call_site()));
let (impl_generics, _, where_clause) = generics.split_for_impl();
let toks = quote! { let toks = quote! {
impl #generics ::juniper::GraphQLType for #ident #generics { impl#impl_generics juniper::GraphQLType<#scalar> for #ident #ty_generics
#where_clause
{
type Context = (); type Context = ();
type TypeInfo = (); type TypeInfo = ();
@ -185,8 +227,10 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
fn meta<'r>( fn meta<'r>(
_: &(), _: &(),
registry: &mut ::juniper::Registry<'r> registry: &mut juniper::Registry<'r, #scalar>
) -> ::juniper::meta::MetaType<'r> { ) -> juniper::meta::MetaType<'r, #scalar>
where #scalar: 'r
{
let fields = &[ let fields = &[
#(#meta_fields)* #(#meta_fields)*
]; ];
@ -199,9 +243,9 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
&self, &self,
_: &(), _: &(),
field_name: &str, field_name: &str,
_: &::juniper::Arguments, _: &juniper::Arguments<#scalar>,
executor: &::juniper::Executor<Self::Context> executor: &juniper::Executor<Self::Context, #scalar>
) -> ::juniper::ExecutionResult ) -> juniper::ExecutionResult<#scalar>
{ {
match field_name { match field_name {
@ -213,5 +257,22 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
} }
}; };
toks let dummy_const = Ident::new(
format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(),
Span::call_site(),
);
quote!{
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)]
const #dummy_const: () = {
mod juniper {
__juniper_use_everything!();
}
extern crate std;
#toks
};
}
} }

View file

@ -10,6 +10,7 @@ extern crate proc_macro;
extern crate proc_macro2; extern crate proc_macro2;
#[macro_use] #[macro_use]
extern crate quote; extern crate quote;
#[macro_use]
extern crate syn; extern crate syn;
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
@ -18,6 +19,7 @@ extern crate regex;
mod derive_enum; mod derive_enum;
mod derive_input_object; mod derive_input_object;
mod derive_object; mod derive_object;
mod derive_juniper_scalar_value;
mod util; mod util;
use proc_macro::TokenStream; use proc_macro::TokenStream;
@ -42,3 +44,10 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
let gen = derive_object::impl_object(&ast); let gen = derive_object::impl_object(&ast);
gen.into() gen.into()
} }
#[proc_macro_derive(ScalarValue)]
pub fn derive_juniper_scalar_value(input: TokenStream) -> TokenStream {
let ast = syn::parse::<syn::DeriveInput>(input).unwrap();
let gen = derive_juniper_scalar_value::impl_scalar_value(&ast);
gen.into()
}

View file

@ -16,7 +16,8 @@ use hyper::header::HeaderValue;
use hyper::rt::Stream; use hyper::rt::Stream;
use hyper::{header, Body, Method, Request, Response, StatusCode}; use hyper::{header, Body, Method, Request, Response, StatusCode};
use juniper::http::GraphQLRequest as JuniperGraphQLRequest; use juniper::http::GraphQLRequest as JuniperGraphQLRequest;
use juniper::{GraphQLType, InputValue, RootNode}; use juniper::serde::Deserialize;
use juniper::{DefaultScalarValue, GraphQLType, InputValue, RootNode, ScalarRefValue, ScalarValue};
use serde_json::error::Error as SerdeError; use serde_json::error::Error as SerdeError;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
@ -25,15 +26,17 @@ use std::sync::Arc;
use tokio::prelude::*; use tokio::prelude::*;
use url::form_urlencoded; use url::form_urlencoded;
pub fn graphql<CtxT, QueryT, MutationT>( pub fn graphql<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT>>, root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>, context: Arc<CtxT>,
request: Request<Body>, request: Request<Body>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error> ) -> impl Future<Item = Response<Body>, Error = hyper::Error>
where where
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static, CtxT: Send + Sync + 'static,
QueryT: GraphQLType<Context = CtxT> + Send + Sync + 'static, QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<Context = CtxT> + Send + Sync + 'static, MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::TypeInfo: Send + Sync, QueryT::TypeInfo: Send + Sync,
MutationT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync,
{ {
@ -63,7 +66,7 @@ where
String::from_utf8(chunk.iter().cloned().collect::<Vec<u8>>()) String::from_utf8(chunk.iter().cloned().collect::<Vec<u8>>())
.map_err(GraphQLRequestError::BodyUtf8) .map_err(GraphQLRequestError::BodyUtf8)
.and_then(|input| { .and_then(|input| {
serde_json::from_str::<GraphQLRequest>(&input) serde_json::from_str::<GraphQLRequest<S>>(&input)
.map_err(GraphQLRequestError::BodyJSONError) .map_err(GraphQLRequestError::BodyJSONError)
}) })
}) })
@ -93,15 +96,17 @@ fn render_error(err: GraphQLRequestError) -> Response<Body> {
resp resp
} }
fn execute_request<CtxT, QueryT, MutationT>( fn execute_request<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT>>, root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>, context: Arc<CtxT>,
request: GraphQLRequest, request: GraphQLRequest<S>,
) -> impl Future<Item = Response<Body>, Error = tokio_threadpool::BlockingError> ) -> impl Future<Item = Response<Body>, Error = tokio_threadpool::BlockingError>
where where
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static, CtxT: Send + Sync + 'static,
QueryT: GraphQLType<Context = CtxT> + Send + Sync + 'static, QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<Context = CtxT> + Send + Sync + 'static, MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::TypeInfo: Send + Sync, QueryT::TypeInfo: Send + Sync,
MutationT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync,
{ {
@ -121,7 +126,10 @@ where
}) })
} }
fn gql_request_from_get(input: &str) -> Result<JuniperGraphQLRequest, GraphQLRequestError> { fn gql_request_from_get<S>(input: &str) -> Result<JuniperGraphQLRequest<S>, GraphQLRequestError>
where
S: ScalarValue,
{
let mut query = None; let mut query = None;
let operation_name = None; let operation_name = None;
let mut variables = None; let mut variables = None;
@ -142,7 +150,7 @@ fn gql_request_from_get(input: &str) -> Result<JuniperGraphQLRequest, GraphQLReq
if variables.is_some() { if variables.is_some() {
return Err(invalid_err("variables")); return Err(invalid_err("variables"));
} }
match serde_json::from_str::<InputValue>(&value) match serde_json::from_str::<InputValue<S>>(&value)
.map_err(GraphQLRequestError::Variables) .map_err(GraphQLRequestError::Variables)
{ {
Ok(parsed_variables) => variables = Some(parsed_variables), Ok(parsed_variables) => variables = Some(parsed_variables),
@ -184,20 +192,29 @@ fn new_html_response(code: StatusCode) -> Response<Body> {
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
enum GraphQLRequest { #[serde(bound = "InputValue<S>: Deserialize<'de>")]
Single(JuniperGraphQLRequest), enum GraphQLRequest<S = DefaultScalarValue>
Batch(Vec<JuniperGraphQLRequest>), where
S: ScalarValue,
{
Single(JuniperGraphQLRequest<S>),
Batch(Vec<JuniperGraphQLRequest<S>>),
} }
impl GraphQLRequest { impl<S> GraphQLRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn execute<'a, CtxT: 'a, QueryT, MutationT>( fn execute<'a, CtxT: 'a, QueryT, MutationT>(
self, self,
root_node: Arc<RootNode<'a, QueryT, MutationT>>, root_node: Arc<RootNode<'a, QueryT, MutationT, S>>,
context: Arc<CtxT>, context: Arc<CtxT>,
) -> impl Future<Item = (bool, hyper::Body), Error = tokio_threadpool::BlockingError> + 'a ) -> impl Future<Item = (bool, hyper::Body), Error = tokio_threadpool::BlockingError> + 'a
where where
QueryT: GraphQLType<Context = CtxT> + 'a, S: 'a,
MutationT: GraphQLType<Context = CtxT> + 'a, QueryT: GraphQLType<S, Context = CtxT> + 'a,
MutationT: GraphQLType<S, Context = CtxT> + 'a,
{ {
match self { match self {
GraphQLRequest::Single(request) => Either::A(future::poll_fn(move || { GraphQLRequest::Single(request) => Either::A(future::poll_fn(move || {

View file

@ -127,31 +127,43 @@ use std::io::Read;
use serde_json::error::Error as SerdeError; use serde_json::error::Error as SerdeError;
use juniper::http; use juniper::http;
use juniper::{GraphQLType, InputValue, RootNode}; use juniper::serde::Deserialize;
use juniper::{DefaultScalarValue, GraphQLType, InputValue, RootNode, ScalarRefValue, ScalarValue};
#[derive(Deserialize)] #[derive(Deserialize)]
#[serde(untagged)] #[serde(untagged)]
enum GraphQLBatchRequest { #[serde(bound = "InputValue<S>: Deserialize<'de>")]
Single(http::GraphQLRequest), enum GraphQLBatchRequest<S = DefaultScalarValue>
Batch(Vec<http::GraphQLRequest>), where
S: ScalarValue,
{
Single(http::GraphQLRequest<S>),
Batch(Vec<http::GraphQLRequest<S>>),
} }
#[derive(Serialize)] #[derive(Serialize)]
#[serde(untagged)] #[serde(untagged)]
enum GraphQLBatchResponse<'a> { enum GraphQLBatchResponse<'a, S = DefaultScalarValue>
Single(http::GraphQLResponse<'a>), where
Batch(Vec<http::GraphQLResponse<'a>>), S: ScalarValue,
{
Single(http::GraphQLResponse<'a, S>),
Batch(Vec<http::GraphQLResponse<'a, S>>),
} }
impl GraphQLBatchRequest { impl<S> GraphQLBatchRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, CtxT, QueryT, MutationT>(
&'a self, &'a self,
root_node: &RootNode<QueryT, MutationT>, root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT, context: &CtxT,
) -> GraphQLBatchResponse<'a> ) -> GraphQLBatchResponse<'a, S>
where where
QueryT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>, MutationT: GraphQLType<S, Context = CtxT>,
{ {
match self { match self {
&GraphQLBatchRequest::Single(ref request) => { &GraphQLBatchRequest::Single(ref request) => {
@ -167,7 +179,10 @@ impl GraphQLBatchRequest {
} }
} }
impl<'a> GraphQLBatchResponse<'a> { impl<'a, S> GraphQLBatchResponse<'a, S>
where
S: ScalarValue,
{
fn is_ok(&self) -> bool { fn is_ok(&self) -> bool {
match self { match self {
&GraphQLBatchResponse::Single(ref response) => response.is_ok(), &GraphQLBatchResponse::Single(ref response) => response.is_ok(),
@ -188,15 +203,17 @@ impl<'a> GraphQLBatchResponse<'a> {
/// this endpoint containing the field `"query"` and optionally `"variables"`. /// this endpoint containing the field `"query"` and optionally `"variables"`.
/// The variables should be a JSON object containing the variable to value /// The variables should be a JSON object containing the variable to value
/// mapping. /// mapping.
pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT> pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT, S = DefaultScalarValue>
where where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static, CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static, CtxT: 'static,
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static, Query: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static, Mutation: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
{ {
context_factory: CtxFactory, context_factory: CtxFactory,
root_node: RootNode<'a, Query, Mutation>, root_node: RootNode<'a, Query, Mutation, S>,
} }
/// Handler that renders `GraphiQL` - a graphical query editor interface /// Handler that renders `GraphiQL` - a graphical query editor interface
@ -220,10 +237,13 @@ fn parse_url_param(params: Option<Vec<String>>) -> IronResult<Option<String>> {
} }
} }
fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputValue>> { fn parse_variable_param<S>(params: Option<Vec<String>>) -> IronResult<Option<InputValue<S>>>
where
S: ScalarValue,
{
if let Some(values) = params { if let Some(values) = params {
Ok( Ok(
serde_json::from_str::<InputValue>(get_single_value(values)?.as_ref()) serde_json::from_str::<InputValue<S>>(get_single_value(values)?.as_ref())
.map(Some) .map(Some)
.map_err(GraphQLIronError::Serde)?, .map_err(GraphQLIronError::Serde)?,
) )
@ -232,12 +252,15 @@ fn parse_variable_param(params: Option<Vec<String>>) -> IronResult<Option<InputV
} }
} }
impl<'a, CtxFactory, Query, Mutation, CtxT> GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT> impl<'a, CtxFactory, Query, Mutation, CtxT, S>
GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT, S>
where where
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static, CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static, CtxT: 'static,
Query: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static, Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static, Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
{ {
/// Build a new GraphQL handler /// Build a new GraphQL handler
/// ///
@ -252,8 +275,9 @@ where
} }
} }
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest> { fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
let url_query_string = req.get_mut::<UrlEncodedQuery>() let url_query_string = req
.get_mut::<UrlEncodedQuery>()
.map_err(GraphQLIronError::Url)?; .map_err(GraphQLIronError::Url)?;
let input_query = parse_url_param(url_query_string.remove("query"))? let input_query = parse_url_param(url_query_string.remove("query"))?
@ -268,17 +292,17 @@ where
))) )))
} }
fn handle_post(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest> { fn handle_post(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
let mut request_payload = String::new(); let mut request_payload = String::new();
itry!(req.body.read_to_string(&mut request_payload)); itry!(req.body.read_to_string(&mut request_payload));
Ok( Ok(
serde_json::from_str::<GraphQLBatchRequest>(request_payload.as_str()) serde_json::from_str::<GraphQLBatchRequest<S>>(request_payload.as_str())
.map_err(GraphQLIronError::Serde)?, .map_err(GraphQLIronError::Serde)?,
) )
} }
fn execute(&self, context: &CtxT, request: GraphQLBatchRequest) -> IronResult<Response> { fn execute(&self, context: &CtxT, request: GraphQLBatchRequest<S>) -> IronResult<Response> {
let response = request.execute(&self.root_node, context); let response = request.execute(&self.root_node, context);
let content_type = "application/json".parse::<Mime>().unwrap(); let content_type = "application/json".parse::<Mime>().unwrap();
let json = serde_json::to_string_pretty(&response).unwrap(); let json = serde_json::to_string_pretty(&response).unwrap();
@ -303,13 +327,15 @@ impl GraphiQLHandler {
} }
} }
impl<'a, CtxFactory, Query, Mutation, CtxT> Handler impl<'a, CtxFactory, Query, Mutation, CtxT, S> Handler
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT> for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT, S>
where where
S: ScalarValue + Sync + Send + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static, CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static, CtxT: 'static,
Query: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static, Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static, Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
'a: 'static, 'a: 'static,
{ {
fn handle(&self, mut req: &mut Request) -> IronResult<Response> { fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
@ -397,7 +423,8 @@ mod tests {
// and newer `hyper` doesn't allow unescaped "{" or "}". // and newer `hyper` doesn't allow unescaped "{" or "}".
fn fixup_url(url: &str) -> String { fn fixup_url(url: &str) -> String {
let url = Url::parse(&format!("http://localhost:3000{}", url)).expect("url to parse"); let url = Url::parse(&format!("http://localhost:3000{}", url)).expect("url to parse");
let path: String = url.path() let path: String = url
.path()
.iter() .iter()
.map(|x| x.to_string()) .map(|x| x.to_string())
.collect::<Vec<String>>() .collect::<Vec<String>>()

View file

@ -1,4 +1,3 @@
[tasks.build-verbose] [tasks.build-verbose]
condition = { channels = ["nightly"] } condition = { channels = ["nightly"] }

View file

@ -59,33 +59,48 @@ use rocket::Request;
use juniper::http; use juniper::http;
use juniper::InputValue; use juniper::InputValue;
use juniper::serde::Deserialize;
use juniper::DefaultScalarValue;
use juniper::FieldError; use juniper::FieldError;
use juniper::GraphQLType; use juniper::GraphQLType;
use juniper::RootNode; use juniper::RootNode;
use juniper::ScalarRefValue;
use juniper::ScalarValue;
#[derive(Debug, Deserialize, PartialEq)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)] #[serde(untagged)]
enum GraphQLBatchRequest { #[serde(bound = "InputValue<S>: Deserialize<'de>")]
Single(http::GraphQLRequest), enum GraphQLBatchRequest<S = DefaultScalarValue>
Batch(Vec<http::GraphQLRequest>), where
S: ScalarValue,
{
Single(http::GraphQLRequest<S>),
Batch(Vec<http::GraphQLRequest<S>>),
} }
#[derive(Serialize)] #[derive(Serialize)]
#[serde(untagged)] #[serde(untagged)]
enum GraphQLBatchResponse<'a> { enum GraphQLBatchResponse<'a, S = DefaultScalarValue>
Single(http::GraphQLResponse<'a>), where
Batch(Vec<http::GraphQLResponse<'a>>), S: ScalarValue,
{
Single(http::GraphQLResponse<'a, S>),
Batch(Vec<http::GraphQLResponse<'a, S>>),
} }
impl GraphQLBatchRequest { impl<S> GraphQLBatchRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
pub fn execute<'a, CtxT, QueryT, MutationT>( pub fn execute<'a, CtxT, QueryT, MutationT>(
&'a self, &'a self,
root_node: &RootNode<QueryT, MutationT>, root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT, context: &CtxT,
) -> GraphQLBatchResponse<'a> ) -> GraphQLBatchResponse<'a, S>
where where
QueryT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>, MutationT: GraphQLType<S, Context = CtxT>,
{ {
match self { match self {
&GraphQLBatchRequest::Single(ref request) => { &GraphQLBatchRequest::Single(ref request) => {
@ -101,7 +116,10 @@ impl GraphQLBatchRequest {
} }
} }
impl<'a> GraphQLBatchResponse<'a> { impl<'a, S> GraphQLBatchResponse<'a, S>
where
S: ScalarValue,
{
fn is_ok(&self) -> bool { fn is_ok(&self) -> bool {
match self { match self {
&GraphQLBatchResponse::Single(ref response) => response.is_ok(), &GraphQLBatchResponse::Single(ref response) => response.is_ok(),
@ -118,7 +136,9 @@ impl<'a> GraphQLBatchResponse<'a> {
/// automatically from both GET and POST routes by implementing the `FromForm` /// automatically from both GET and POST routes by implementing the `FromForm`
/// and `FromData` traits. /// and `FromData` traits.
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct GraphQLRequest(GraphQLBatchRequest); pub struct GraphQLRequest<S = DefaultScalarValue>(GraphQLBatchRequest<S>)
where
S: ScalarValue;
/// Simple wrapper around the result of executing a GraphQL query /// Simple wrapper around the result of executing a GraphQL query
pub struct GraphQLResponse(pub Status, pub String); pub struct GraphQLResponse(pub Status, pub String);
@ -128,16 +148,20 @@ pub fn graphiql_source(graphql_endpoint_url: &str) -> content::Html<String> {
content::Html(juniper::graphiql::graphiql_source(graphql_endpoint_url)) content::Html(juniper::graphiql::graphiql_source(graphql_endpoint_url))
} }
impl GraphQLRequest { impl<S> GraphQLRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// Execute an incoming GraphQL query /// Execute an incoming GraphQL query
pub fn execute<CtxT, QueryT, MutationT>( pub fn execute<CtxT, QueryT, MutationT>(
&self, &self,
root_node: &RootNode<QueryT, MutationT>, root_node: &RootNode<QueryT, MutationT, S>,
context: &CtxT, context: &CtxT,
) -> GraphQLResponse ) -> GraphQLResponse
where where
QueryT: GraphQLType<Context = CtxT>, QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>, MutationT: GraphQLType<S, Context = CtxT>,
{ {
let response = self.0.execute(root_node, context); let response = self.0.execute(root_node, context);
let status = if response.is_ok() { let status = if response.is_ok() {
@ -205,7 +229,10 @@ impl GraphQLResponse {
} }
} }
impl<'f> FromForm<'f> for GraphQLRequest { impl<'f, S> FromForm<'f> for GraphQLRequest<S>
where
S: ScalarValue,
{
type Error = String; type Error = String;
fn from_form(form_items: &mut FormItems<'f>, strict: bool) -> Result<Self, String> { fn from_form(form_items: &mut FormItems<'f>, strict: bool) -> Result<Self, String> {
@ -248,8 +275,10 @@ impl<'f> FromForm<'f> for GraphQLRequest {
Ok(v) => decoded = v, Ok(v) => decoded = v,
Err(e) => return Err(e.description().to_string()), Err(e) => return Err(e.description().to_string()),
} }
variables = Some(serde_json::from_str::<InputValue>(&decoded) variables = Some(
.map_err(|err| err.description().to_owned())?); serde_json::from_str::<InputValue<_>>(&decoded)
.map_err(|err| err.description().to_owned())?,
);
} }
} }
_ => { _ => {
@ -270,7 +299,10 @@ impl<'f> FromForm<'f> for GraphQLRequest {
} }
} }
impl FromData for GraphQLRequest { impl<S> FromData for GraphQLRequest<S>
where
S: ScalarValue,
{
type Error = String; type Error = String;
fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, Self::Error> { fn from_data(request: &Request, data: Data) -> FromDataOutcome<Self, Self::Error> {
@ -311,7 +343,7 @@ mod fromform_tests {
fn check_error(input: &str, error: &str, strict: bool) { fn check_error(input: &str, error: &str, strict: bool) {
let mut items = FormItems::from(input); let mut items = FormItems::from(input);
let result = GraphQLRequest::from_form(&mut items, strict); let result: Result<GraphQLRequest, _> = GraphQLRequest::from_form(&mut items, strict);
assert!(result.is_err()); assert!(result.is_err());
assert_eq!(result.unwrap_err(), error); assert_eq!(result.unwrap_err(), error);
} }
@ -401,7 +433,7 @@ mod fromform_tests {
fn test_url_decode() { fn test_url_decode() {
let form_string = "query=%25foo%20bar+baz%26%3F&operation_name=test"; let form_string = "query=%25foo%20bar+baz%26%3F&operation_name=test";
let mut items = FormItems::from(form_string); let mut items = FormItems::from(form_string);
let result = GraphQLRequest::from_form(&mut items, false); let result: Result<GraphQLRequest, _> = GraphQLRequest::from_form(&mut items, false);
assert!(result.is_ok()); assert!(result.is_ok());
let expected = GraphQLRequest(GraphQLBatchRequest::Single(http::GraphQLRequest::new( let expected = GraphQLRequest(GraphQLBatchRequest::Single(http::GraphQLRequest::new(
"%foo bar baz&?".to_string(), "%foo bar baz&?".to_string(),
@ -477,8 +509,7 @@ mod tests {
.manage(Schema::new( .manage(Schema::new(
Database::new(), Database::new(),
EmptyMutation::<Database>::new(), EmptyMutation::<Database>::new(),
)) )).mount("/", routes![post_graphql_handler, get_graphql_handler])
.mount("/", routes![post_graphql_handler, get_graphql_handler])
} }
fn make_test_response<'r>(request: &LocalRequest<'r>) -> http_tests::TestResponse { fn make_test_response<'r>(request: &LocalRequest<'r>) -> http_tests::TestResponse {

Some files were not shown because too many files have changed in this diff Show more