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_iron",
"juniper_rocket",
"juniper_warp",
"juniper_warp/examples/warp_server/",
]

View file

@ -1,3 +1,16 @@
# [master] yyyy-mm-dd
## 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 = [
"chrono",
"url",
"uuid"
"uuid",
]
[dependencies]

View file

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

View file

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

View file

@ -22,6 +22,7 @@ use schema::model::{RootNode, SchemaType, TypeType};
use types::base::GraphQLType;
use types::name::Name;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
mod look_ahead;
@ -35,9 +36,9 @@ pub use self::look_ahead::{
/// The registry gathers metadata for all types in a schema. It provides
/// convenience methods to convert types implementing the `GraphQLType` trait
/// into `Type` instances and automatically registers them.
pub struct Registry<'r> {
pub struct Registry<'r, S = DefaultScalarValue> {
/// Currently registered types
pub types: FnvHashMap<Name, MetaType<'r>>,
pub types: FnvHashMap<Name, MetaType<'r, S>>,
}
#[derive(Clone)]
@ -50,18 +51,19 @@ pub enum FieldPath<'a> {
///
/// The executor helps drive the query execution in a schema. It keeps track
/// of the current field stack, context, variables, and errors.
pub struct Executor<'a, CtxT>
pub struct Executor<'a, CtxT, S = DefaultScalarValue>
where
CtxT: 'a,
S: 'a,
{
fragments: &'a HashMap<&'a str, &'a Fragment<'a>>,
variables: &'a Variables,
current_selection_set: Option<&'a [Selection<'a>]>,
parent_selection_set: Option<&'a [Selection<'a>]>,
current_type: TypeType<'a>,
schema: &'a SchemaType<'a>,
fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>,
variables: &'a Variables<S>,
current_selection_set: Option<&'a [Selection<'a, S>]>,
parent_selection_set: Option<&'a [Selection<'a, S>]>,
current_type: TypeType<'a, S>,
schema: &'a SchemaType<'a, S>,
context: &'a CtxT,
errors: &'a RwLock<Vec<ExecutionError>>,
errors: &'a RwLock<Vec<ExecutionError<S>>>,
field_path: FieldPath<'a>,
}
@ -70,15 +72,17 @@ where
/// All execution errors contain the source position in the query of the field
/// that failed to resolve. It also contains the field stack.
#[derive(Debug, PartialEq)]
pub struct ExecutionError {
pub struct ExecutionError<S> {
location: SourcePosition,
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
pub fn at_origin(error: FieldError) -> ExecutionError {
pub fn at_origin(error: FieldError<S>) -> ExecutionError<S> {
ExecutionError {
location: SourcePosition::new_origin(),
path: Vec::new(),
@ -87,10 +91,11 @@ impl ExecutionError {
}
}
impl Eq for ExecutionError {}
impl PartialOrd for ExecutionError {
fn partial_cmp(&self, other: &ExecutionError) -> Option<Ordering> {
impl<S> PartialOrd for ExecutionError<S>
where
Self: PartialEq,
{
fn partial_cmp(&self, other: &ExecutionError<S>) -> Option<Ordering> {
(&self.location, &self.path, &self.error.message).partial_cmp(&(
&other.location,
&other.path,
@ -99,8 +104,11 @@ impl PartialOrd for ExecutionError {
}
}
impl Ord for ExecutionError {
fn cmp(&self, other: &ExecutionError) -> Ordering {
impl<S> Ord for ExecutionError<S>
where
Self: Eq,
{
fn cmp(&self, other: &ExecutionError<S>) -> Ordering {
(&self.location, &self.path, &self.error.message).cmp(&(
&other.location,
&other.path,
@ -118,20 +126,24 @@ impl Ord for ExecutionError {
/// which makes error chaining with the `?` operator a breeze:
///
/// ```rust
/// # use juniper::FieldError;
/// fn get_string(data: Vec<u8>) -> Result<String, FieldError> {
/// # use juniper::{FieldError, ScalarValue};
/// fn get_string(data: Vec<u8>) -> Result<String, FieldError>
/// {
/// let s = String::from_utf8(data)?;
/// Ok(s)
/// }
/// ```
#[derive(Debug, PartialEq)]
pub struct FieldError {
pub struct FieldError<S = DefaultScalarValue> {
message: String,
extensions: Value,
extensions: Value<S>,
}
impl<T: Display> From<T> for FieldError {
fn from(e: T) -> FieldError {
impl<T: Display, S> From<T> for FieldError<S>
where
S: ::value::ScalarValue,
{
fn from(e: T) -> FieldError<S> {
FieldError {
message: format!("{}", e),
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
///
/// You can use the `graphql_value!` macro to construct an error:
@ -147,8 +159,10 @@ impl FieldError {
/// ```rust
/// # #[macro_use] extern crate juniper;
/// use juniper::FieldError;
/// # use juniper::DefaultScalarValue;
///
/// # fn sample() {
/// # let _: FieldError<DefaultScalarValue> =
/// FieldError::new(
/// "Could not open connection to the database",
/// graphql_value!({ "internal_error": "Connection refused" })
@ -173,7 +187,7 @@ impl FieldError {
/// ```
///
/// 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 {
message: format!("{}", e),
extensions,
@ -186,82 +200,111 @@ impl FieldError {
}
#[doc(hidden)]
pub fn extensions(&self) -> &Value {
pub fn extensions(&self) -> &Value<S> {
&self.extensions
}
}
/// 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
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
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
/// as return value.
///
/// Any custom error type should implement this trait to convert it to `FieldError`.
pub trait IntoFieldError {
pub trait IntoFieldError<S = DefaultScalarValue> {
#[doc(hidden)]
fn into_field_error(self) -> FieldError;
fn into_field_error(self) -> FieldError<S>;
}
impl IntoFieldError for FieldError {
fn into_field_error(self) -> FieldError {
impl<S> IntoFieldError<S> for FieldError<S> {
fn into_field_error(self) -> FieldError<S> {
self
}
}
#[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)]
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
T: GraphQLType<S>,
S: ScalarValue,
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)))
}
}
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
S: ScalarValue,
T: GraphQLType<S>,
T::Context: FromContext<C>,
for<'b> &'b S: ScalarRefValue<'b>,
{
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
self.map(|v| Some((FromContext::from(ctx), v)))
fn into(self, ctx: &'a C) -> FieldResult<Option<(&'a T::Context, T)>, S> {
self.map(|v: T| Some((<T::Context as FromContext<C>>::from(ctx), v)))
.map_err(|e| e.into_field_error())
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for (&'a T::Context, T) {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for (&'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))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C> for Option<(&'a T::Context, T)> {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, Option<T>)>> {
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C> for 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, Option<T>)>, S> {
Ok(self.map(|(ctx, v)| (ctx, Some(v))))
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, T, C> for FieldResult<(&'a T::Context, T)> {
fn into(self, _: &'a C) -> FieldResult<Option<(&'a T::Context, T)>> {
impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'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, T)>, S> {
self.map(Some)
}
}
impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option<T>, C>
for FieldResult<Option<(&'a T::Context, T)>>
impl<'a, S, T, C> IntoResolvable<'a, S, Option<T>, C>
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))))
}
}
@ -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
pub fn resolve_with_ctx<NewCtxT, T: GraphQLType<Context = NewCtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> ExecutionResult
pub fn resolve_with_ctx<NewCtxT, T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
where
NewCtxT: FromContext<CtxT>,
T: GraphQLType<S, Context = NewCtxT>,
{
self.replaced_context(<NewCtxT as FromContext<CtxT>>::from(self.context))
.resolve(info, value)
}
/// Resolve a single arbitrary value into an `ExecutionResult`
pub fn resolve<T: GraphQLType<Context = CtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> ExecutionResult {
pub fn resolve<T>(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult<S>
where
T: GraphQLType<S, Context = CtxT>,
{
Ok(value.resolve(info, self.current_selection_set, self))
}
/// Resolve a single arbitrary value into a return value
///
/// If the field fails to resolve, `null` will be returned.
pub fn resolve_into_value<T: GraphQLType<Context = CtxT>>(
&self,
info: &T::TypeInfo,
value: &T,
) -> Value {
pub fn resolve_into_value<T>(&self, info: &T::TypeInfo, value: &T) -> Value<S>
where
T: GraphQLType<S, Context = CtxT>,
{
match self.resolve(info, value) {
Ok(v) => v,
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
/// 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 {
fragments: self.fragments,
variables: self.variables,
@ -368,15 +410,16 @@ impl<'a, CtxT> Executor<'a, CtxT> {
field_alias: &'a str,
field_name: &'a str,
location: SourcePosition,
selection_set: Option<&'a [Selection]>,
) -> Executor<CtxT> {
selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT, S> {
Executor {
fragments: self.fragments,
variables: self.variables,
current_selection_set: selection_set,
parent_selection_set: self.current_selection_set,
current_type: self.schema.make_type(
&self.current_type
&self
.current_type
.innermost_concrete()
.field_by_name(field_name)
.expect("Field not found on inner type")
@ -393,8 +436,8 @@ impl<'a, CtxT> Executor<'a, CtxT> {
pub fn type_sub_executor(
&self,
type_name: Option<&'a str>,
selection_set: Option<&'a [Selection]>,
) -> Executor<CtxT> {
selection_set: Option<&'a [Selection<S>]>,
) -> Executor<CtxT, S> {
Executor {
fragments: self.fragments,
variables: self.variables,
@ -420,22 +463,22 @@ impl<'a, CtxT> Executor<'a, CtxT> {
}
/// The currently executing schema
pub fn schema(&self) -> &'a SchemaType {
pub fn schema(&self) -> &'a SchemaType<S> {
self.schema
}
#[doc(hidden)]
pub fn current_type(&self) -> &TypeType<'a> {
pub fn current_type(&self) -> &TypeType<'a, S> {
&self.current_type
}
#[doc(hidden)]
pub fn variables(&self) -> &'a Variables {
pub fn variables(&self) -> &'a Variables<S> {
self.variables
}
#[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)
}
@ -445,13 +488,13 @@ impl<'a, CtxT> Executor<'a, CtxT> {
}
/// 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();
self.push_error_at(error, 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();
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
/// affecting the childs
pub fn look_ahead(&'a self) -> LookAheadSelection<'a> {
pub fn look_ahead(&'a self) -> LookAheadSelection<'a, S> {
self.parent_selection_set
.map(|p| {
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(""),
alias: None,
arguments: Vec::new(),
children: self.current_selection_set
children: self
.current_selection_set
.map(|s| {
s.iter()
.map(|s| ChildSelection {
@ -487,10 +530,8 @@ impl<'a, CtxT> Executor<'a, CtxT> {
self.fragments,
),
applies_for: Applies::All,
})
.collect()
})
.unwrap_or_else(Vec::new),
}).collect()
}).unwrap_or_else(Vec::new),
})
}
}
@ -513,9 +554,9 @@ impl<'a> FieldPath<'a> {
}
}
impl ExecutionError {
impl<S> ExecutionError<S> {
#[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 {
location: location,
path: path.iter().map(|s| (*s).to_owned()).collect(),
@ -524,7 +565,7 @@ impl ExecutionError {
}
/// The error message
pub fn error(&self) -> &FieldError {
pub fn error(&self) -> &FieldError<S> {
&self.error
}
@ -539,16 +580,18 @@ impl ExecutionError {
}
}
pub fn execute_validated_query<'a, QueryT, MutationT, CtxT>(
document: Document,
pub fn execute_validated_query<'a, QueryT, MutationT, CtxT, S>(
document: Document<S>,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>,
variables: &Variables,
root_node: &RootNode<QueryT, MutationT, S>,
variables: &Variables<S>,
context: &CtxT,
) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
S: ScalarValue,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut fragments = vec![];
let mut operation = None;
@ -561,7 +604,7 @@ where
}
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 {
operation = Some(op);
@ -584,8 +627,7 @@ where
def.default_value
.as_ref()
.map(|i| (name.item.to_owned(), i.item.clone()))
})
.collect::<HashMap<String, InputValue>>()
}).collect::<HashMap<String, InputValue<S>>>()
});
let errors = RwLock::new(Vec::new());
@ -642,9 +684,12 @@ where
Ok((value, errors))
}
impl<'r> Registry<'r> {
impl<'r, S> Registry<'r, S>
where
S: ScalarValue + 'r,
{
/// 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 }
}
@ -654,7 +699,8 @@ impl<'r> Registry<'r> {
/// construct its metadata and store it.
pub fn get_type<T>(&mut self, info: &T::TypeInfo) -> Type<'r>
where
T: GraphQLType,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
if let Some(name) = T::name(info) {
let validated_name = name.parse::<Name>().unwrap();
@ -673,9 +719,10 @@ impl<'r> Registry<'r> {
}
/// 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
T: GraphQLType,
T: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Field {
name: name.to_owned(),
@ -687,13 +734,14 @@ impl<'r> Registry<'r> {
}
#[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,
name: &str,
info: &I::TypeInfo,
) -> Field<'r>
) -> Field<'r, S>
where
I: GraphQLType,
I: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
Field {
name: name.to_owned(),
@ -705,9 +753,10 @@ impl<'r> Registry<'r> {
}
/// 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
T: GraphQLType + FromInputValue,
T: GraphQLType<S> + FromInputValue<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
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
/// `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
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())
}
@ -735,22 +790,29 @@ impl<'r> Registry<'r> {
/// Create a scalar meta type
///
/// 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
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()");
ScalarMeta::new::<T>(Cow::Owned(name.to_string()))
}
/// 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);
ListMeta::new(of_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);
NullableMeta::new(of_type)
}
@ -762,10 +824,11 @@ impl<'r> Registry<'r> {
pub fn build_object_type<T>(
&mut self,
info: &T::TypeInfo,
fields: &[Field<'r>],
) -> ObjectMeta<'r>
fields: &[Field<'r, S>],
) -> ObjectMeta<'r, S>
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()");
@ -775,9 +838,14 @@ impl<'r> Registry<'r> {
}
/// 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
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()");
@ -789,10 +857,11 @@ impl<'r> Registry<'r> {
pub fn build_interface_type<T>(
&mut self,
info: &T::TypeInfo,
fields: &[Field<'r>],
) -> InterfaceMeta<'r>
fields: &[Field<'r, S>],
) -> InterfaceMeta<'r, S>
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()");
@ -804,7 +873,8 @@ impl<'r> Registry<'r> {
/// Create a union meta type builder
pub fn build_union_type<T>(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r>
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()");
@ -815,10 +885,11 @@ impl<'r> Registry<'r> {
pub fn build_input_object_type<T>(
&mut self,
info: &T::TypeInfo,
args: &[Argument<'r>],
) -> InputObjectMeta<'r>
args: &[Argument<'r, S>],
) -> InputObjectMeta<'r, S>
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()");

View file

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

View file

@ -4,11 +4,10 @@ use parser::SourcePosition;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use validation::RuleError;
use value::{Value, Object};
use value::{DefaultScalarValue, Object, Value};
use GraphQLError::ValidationError;
#[derive(GraphQLEnum, Debug)]
#[graphql(_internal)]
enum Color {
Red,
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
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -45,7 +44,7 @@ where
fn run_query<F>(query: &str, f: F)
where
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
run_variable_query(query, Variables::new(), f);
}
@ -53,14 +52,20 @@ where
#[test]
fn accepts_enum_literal() {
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]
fn serializes_as_output() {
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() {
run_variable_query(
"query q($color: Color!) { toString(color: $color) }",
vec![("color".to_owned(), InputValue::string("RED"))]
vec![("color".to_owned(), InputValue::scalar("RED"))]
.into_iter()
.collect(),
|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 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()
.collect();
@ -120,7 +128,7 @@ fn does_not_accept_incorrect_type_in_variables() {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
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()
.collect();

View file

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

View file

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

View file

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

View file

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

View file

@ -1,10 +1,10 @@
use ast::{FromInputValue, InputValue};
use ast::InputValue;
use executor::Variables;
use parser::SourcePosition;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use validation::RuleError;
use value::{Object, Value};
use value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, Value};
use GraphQLError::ValidationError;
#[derive(Debug)]
@ -14,22 +14,26 @@ struct TestType;
graphql_scalar!(TestComplexScalar {
resolve(&self) -> Value {
Value::string("SerializedValue")
Value::scalar(String::from("SerializedValue"))
}
from_input_value(v: &InputValue) -> Option<TestComplexScalar> {
if let Some(s) = v.as_string_value() {
if s == "SerializedValue" {
if let Some(s) = v.as_scalar_value::<String>() {
if *s == "SerializedValue" {
return Some(TestComplexScalar);
}
}
None
}
from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> {
<String as ParseScalarValue<_>>::from_str(value)
}
});
#[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
#[graphql(scalar = "DefaultScalarValue")]
struct TestInputObject {
a: Option<String>,
b: Option<Vec<Option<String>>>,
@ -38,67 +42,24 @@ struct TestInputObject {
}
#[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
#[graphql(scalar = "DefaultScalarValue")]
struct TestNestedInputObject {
na: TestInputObject,
nb: String,
}
#[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct ExampleInputObject {
a: Option<String>,
b: i32,
}
#[derive(GraphQLInputObject, Debug)]
#[graphql(_internal)]
struct InputWithDefaults {
#[graphql(default = "123")]
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| {
field field_with_object_input(input: Option<TestInputObject>) -> String {
format!("{:?}", input)
@ -144,10 +105,6 @@ graphql_object!(TestType: () |&self| {
format!("a: {:?}", arg.a)
}
field input_with_custom(input: CustomInput) -> String {
format!("{:?}", input)
}
field integer_input(value: i32) -> String {
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
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -167,7 +124,7 @@ where
assert_eq!(errs, []);
println!("Result: {:#?}", result);
println!("Result: {:?}", result);
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)
where
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
run_variable_query(query, Variables::new(), f);
}
@ -185,10 +142,10 @@ where
fn inline_complex_input() {
run_query(
r#"{ fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"}) }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_query(
r#"{ fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"}) }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_query(
r#"{ fieldWithObjectInput(input: {c: "baz", d: "SerializedValue"}) }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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]
fn variable_complex_input() {
run_variable_query(
@ -278,18 +182,18 @@ fn variable_complex_input() {
"input".to_owned(),
InputValue::object(
vec![
("a", InputValue::string("foo")),
("b", InputValue::list(vec![InputValue::string("bar")])),
("c", InputValue::string("baz")),
("a", InputValue::scalar("foo")),
("b", InputValue::list(vec![InputValue::scalar("bar")])),
("c", InputValue::scalar("baz")),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect(),
|result| {
.collect(),
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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(),
InputValue::object(
vec![
("a", InputValue::string("foo")),
("b", InputValue::string("bar")),
("c", InputValue::string("baz")),
("a", InputValue::scalar("foo")),
("b", InputValue::scalar("bar")),
("c", InputValue::scalar("baz")),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect(),
|result| {
.collect(),
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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(),
InputValue::object(
vec![
("c", InputValue::string("baz")),
("d", InputValue::string("SerializedValue")),
("c", InputValue::scalar("baz")),
("d", InputValue::scalar("SerializedValue")),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect(),
|result| {
.collect(),
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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(),
InputValue::object(
vec![
("a", InputValue::string("foo")),
("b", InputValue::string("bar")),
("a", InputValue::scalar("foo")),
("b", InputValue::scalar("bar")),
("c", InputValue::null()),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect();
.collect();
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 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()
.collect();
@ -398,13 +302,13 @@ fn variable_error_on_omit_non_null() {
"input".to_owned(),
InputValue::object(
vec![
("a", InputValue::string("foo")),
("b", InputValue::string("bar")),
("a", InputValue::scalar("foo")),
("b", InputValue::scalar("bar")),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect();
.collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -428,12 +332,12 @@ fn variable_multiple_errors_with_nesting() {
InputValue::object(
vec![(
"na",
InputValue::object(vec![("a", InputValue::string("foo"))].into_iter().collect()),
InputValue::object(vec![("a", InputValue::scalar("foo"))].into_iter().collect()),
)].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect();
.collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -458,15 +362,15 @@ fn variable_error_on_additional_field() {
"input".to_owned(),
InputValue::object(
vec![
("a", InputValue::string("foo")),
("b", InputValue::string("bar")),
("c", InputValue::string("baz")),
("extra", InputValue::string("dog")),
("a", InputValue::scalar("foo")),
("b", InputValue::scalar("bar")),
("c", InputValue::scalar("baz")),
("extra", InputValue::scalar("dog")),
].into_iter()
.collect(),
.collect(),
),
)].into_iter()
.collect();
.collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -481,22 +385,25 @@ fn variable_error_on_additional_field() {
#[test]
fn allow_nullable_inputs_to_be_omitted() {
run_query(r#"{ fieldWithNullableStringInput }"#, |result| {
assert_eq!(
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))
);
});
run_query(
r#"{ fieldWithNullableStringInput }"#,
|result: &Object<DefaultScalarValue>| {
assert_eq!(
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::scalar(r#"None"#))
);
},
);
}
#[test]
fn allow_nullable_inputs_to_be_omitted_in_variable() {
run_query(
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_query(
r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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())]
.into_iter()
.collect(),
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_variable_query(
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
vec![("value".to_owned(), InputValue::string("a"))]
vec![("value".to_owned(), InputValue::scalar("a"))]
.into_iter()
.collect(),
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_query(
r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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() {
run_variable_query(
r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#,
vec![("value".to_owned(), InputValue::string("a"))]
vec![("value".to_owned(), InputValue::scalar("a"))]
.into_iter()
.collect(),
|result| {
assert_eq!(
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() {
run_query(
r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| {
|result: &Object<DefaultScalarValue>| {
assert_eq!(
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())]
.into_iter()
.collect(),
|result| {
assert_eq!(result.get_field_value("list"), Some(&Value::string(r#"None"#)));
|result: &Object<DefaultScalarValue>| {
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) }"#,
vec![(
"input".to_owned(),
InputValue::list(vec![InputValue::string("A")]),
InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter()
.collect(),
.collect(),
|result| {
assert_eq!(
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![(
"input".to_owned(),
InputValue::list(vec![
InputValue::string("A"),
InputValue::scalar("A"),
InputValue::null(),
InputValue::string("B"),
InputValue::scalar("B"),
]),
)].into_iter()
.collect(),
.collect(),
|result| {
assert_eq!(
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) }"#,
vec![(
"input".to_owned(),
InputValue::list(vec![InputValue::string("A")]),
InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter()
.collect(),
.collect(),
|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![(
"input".to_owned(),
InputValue::list(vec![
InputValue::string("A"),
InputValue::scalar("A"),
InputValue::null(),
InputValue::string("B"),
InputValue::scalar("B"),
]),
)].into_iter()
.collect(),
.collect(),
|result| {
assert_eq!(
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()
.collect(),
|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) }"#,
vec![(
"input".to_owned(),
InputValue::list(vec![InputValue::string("A")]),
InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter()
.collect(),
.collect(),
|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![(
"input".to_owned(),
InputValue::list(vec![
InputValue::string("A"),
InputValue::scalar("A"),
InputValue::null(),
InputValue::string("B"),
InputValue::scalar("B"),
]),
)].into_iter()
.collect();
.collect();
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![(
"input".to_owned(),
InputValue::list(vec![
InputValue::string("A"),
InputValue::scalar("A"),
InputValue::null(),
InputValue::string("B"),
InputValue::scalar("B"),
]),
)].into_iter()
.collect();
.collect();
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) }"#,
vec![(
"input".to_owned(),
InputValue::list(vec![InputValue::string("A")]),
InputValue::list(vec![InputValue::scalar("A")]),
)].into_iter()
.collect(),
.collect(),
|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 vars = vec![(
"value".to_owned(),
InputValue::list(vec![InputValue::string("A"), InputValue::string("B")]),
InputValue::list(vec![InputValue::scalar("A"), InputValue::scalar("B")]),
)].into_iter()
.collect();
.collect();
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 vars = vec![(
"value".to_owned(),
InputValue::list(vec![InputValue::string("A"), InputValue::string("B")]),
InputValue::list(vec![InputValue::scalar("A"), InputValue::scalar("B")]),
)].into_iter()
.collect();
.collect();
let error = ::execute(query, None, &schema, &vars, &()).unwrap_err();
@ -896,7 +818,7 @@ fn default_argument_when_not_provided() {
run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| {
assert_eq!(
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| {
assert_eq!(
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| {
assert_eq!(
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| {
assert_eq!(
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| {
assert_eq!(
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() {
run_variable_query(
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()
.collect(),
|result| {
assert_eq!(
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| {
assert_eq!(
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| {
assert_eq!(
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| {
assert_eq!(
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))
Some(&Value::scalar(r#"a: 1"#))
);
});
run_variable_query(
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()
.collect(),
|result| {
assert_eq!(
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| {
assert_eq!(
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))
Some(&Value::scalar(r#"a: 1"#))
);
},
);
run_variable_query(
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()
.collect(),
|result| {
assert_eq!(
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() {
run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(1))]
vec![("var".to_owned(), InputValue::scalar(1))]
.into_iter()
.collect(),
|result| {
assert_eq!(
result.get_field_value("integerInput"),
Some(&Value::string(r#"value: 1"#))
Some(&Value::scalar(r#"value: 1"#))
);
},
);
run_variable_query(
r#"query q($var: Int!) { integerInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(-1))]
vec![("var".to_owned(), InputValue::scalar(-1))]
.into_iter()
.collect(),
|result| {
assert_eq!(
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 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()
.collect();
@ -1165,7 +1087,7 @@ mod integers {
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
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()
.collect();
@ -1188,13 +1110,13 @@ mod floats {
fn float_values_should_work() {
run_variable_query(
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()
.collect(),
|result| {
assert_eq!(
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() {
run_variable_query(
r#"query q($var: Float!) { floatInput(value: $var) }"#,
vec![("var".to_owned(), InputValue::int(-1))]
vec![("var".to_owned(), InputValue::scalar(-1))]
.into_iter()
.collect(),
|result| {
assert_eq!(
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 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()
.collect();

View file

@ -2,11 +2,12 @@
pub mod graphiql;
use serde::ser;
use serde::ser::SerializeMap;
use serde::de::Deserialize;
use serde::ser::{self, Serialize, SerializeMap};
use ast::InputValue;
use executor::ExecutionError;
use value::{DefaultScalarValue, ScalarRefValue, ScalarValue};
use {FieldError, GraphQLError, GraphQLType, RootNode, Value, Variables};
/// 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",
/// "operationName", and "variables" manually.
#[derive(Deserialize, Clone, Serialize, PartialEq, Debug)]
pub struct GraphQLRequest {
pub struct GraphQLRequest<S = DefaultScalarValue>
where
S: ScalarValue,
{
query: String,
#[serde(rename = "operationName")]
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> {
self.operation_name.as_ref().map(|oper_name| &**oper_name)
}
fn variables(&self) -> Variables {
fn variables(&self) -> Variables<S> {
self.variables
.as_ref()
.and_then(|iv| {
@ -38,16 +46,15 @@ impl GraphQLRequest {
.map(|(k, v)| (k.to_owned(), v.clone()))
.collect()
})
})
.unwrap_or_default()
}).unwrap_or_default()
}
/// Construct a new GraphQL request from parts
pub fn new(
query: String,
operation_name: Option<String>,
variables: Option<InputValue>,
) -> GraphQLRequest {
variables: Option<InputValue<S>>,
) -> Self {
GraphQLRequest {
query: query,
operation_name: operation_name,
@ -61,12 +68,14 @@ impl GraphQLRequest {
/// top level of this crate.
pub fn execute<'a, CtxT, QueryT, MutationT>(
&'a self,
root_node: &RootNode<QueryT, MutationT>,
root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT,
) -> GraphQLResponse<'a>
) -> GraphQLResponse<'a, S>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
S: ScalarValue,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{
GraphQLResponse(::execute(
&self.query,
@ -83,11 +92,16 @@ impl GraphQLRequest {
/// 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
/// 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
pub fn error(error: FieldError) -> Self {
pub fn error(error: FieldError<S>) -> Self {
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>
where
S: ser::Serializer,

View file

@ -15,35 +15,53 @@
*/
use chrono::prelude::*;
use parser::{ParseError, ScalarToken, Token};
use value::{ParseScalarResult, ParseScalarValue};
use Value;
#[doc(hidden)]
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"
resolve(&self) -> Value {
Value::string(self.to_rfc3339())
Value::scalar(self.to_rfc3339())
}
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())
}
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"
resolve(&self) -> Value {
Value::string(self.to_rfc3339())
Value::scalar(self.to_rfc3339())
}
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()))
}
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:
@ -51,40 +69,53 @@ graphql_scalar!(DateTime<Utc> as "DateTimeUtc" {
// inherent lack of precision required for the time zone resolution.
// For serialization and deserialization uses, it is best to use
// `NaiveDate` instead."
graphql_scalar!(NaiveDate {
graphql_scalar!(NaiveDate where Scalar = <S>{
description: "NaiveDate"
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> {
v.as_string_value()
v.as_scalar_value::<String>()
.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
/// datetimes. Values will be truncated to microsecond resolution.
graphql_scalar!(NaiveDateTime {
graphql_scalar!(NaiveDateTime where Scalar = <S> {
description: "NaiveDateTime"
resolve(&self) -> Value {
Value::float(self.timestamp() as f64)
Value::scalar(self.timestamp() as f64)
}
from_input_value(v: &InputValue) -> Option<NaiveDateTime> {
v.as_float_value()
.and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0))
v.as_scalar_value::<f64>()
.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)]
mod test {
use chrono::prelude::*;
use value::DefaultScalarValue;
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 expected = DateTime::parse_from_rfc3339(raw).unwrap();
@ -108,7 +139,7 @@ mod test {
}
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 expected = DateTime::parse_from_rfc3339(raw)
@ -135,7 +166,8 @@ mod test {
#[test]
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 m = 12;
let d = 19;
@ -153,7 +185,7 @@ mod test {
#[test]
fn naivedatetime_from_input_value() {
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 expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap();
@ -171,11 +203,11 @@ mod integration_test {
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value};
#[test]
fn test_serialization() {
struct Root {}
struct Root;
graphql_object!(Root: () |&self| {
field exampleNaiveDate() -> NaiveDate {
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) =
::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed");
@ -211,18 +243,18 @@ mod integration_test {
result,
Value::object(
vec![
("exampleNaiveDate", Value::string("2015-03-14")),
("exampleNaiveDateTime", Value::float(1467969011.0)),
("exampleNaiveDate", Value::scalar("2015-03-14")),
("exampleNaiveDateTime", Value::scalar(1467969011.0)),
(
"exampleDateTimeFixedOffset",
Value::string("1996-12-19T16:39:57-08:00"),
Value::scalar("1996-12-19T16:39:57-08:00"),
),
(
"exampleDateTimeUtc",
Value::string("1970-01-01T00:01:01+00:00"),
Value::scalar("1970-01-01T00:01:01+00:00"),
),
].into_iter()
.collect()
.collect()
)
);
}

View file

@ -8,14 +8,17 @@ use ast::InputValue;
use executor::ExecutionError;
use parser::{ParseError, SourcePosition, Spanning};
use validation::RuleError;
use {GraphQLError, Object, Value};
use {GraphQLError, Object, ScalarValue, Value};
#[derive(Serialize)]
struct SerializeHelper {
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>
where
S: ser::Serializer,
@ -51,92 +54,192 @@ impl<'a> ser::Serialize for GraphQLError<'a> {
GraphQLError::ValidationError(ref errs) => errs.serialize(serializer),
GraphQLError::NoOperationProvided => [SerializeHelper {
message: "Must provide an operation",
}].serialize(serializer),
}]
.serialize(serializer),
GraphQLError::MultipleOperationsProvided => [SerializeHelper {
message: "Must provide operation name \
if query contains multiple operations",
}].serialize(serializer),
}]
.serialize(serializer),
GraphQLError::UnknownOperationName => [SerializeHelper {
message: "Unknown operation",
}].serialize(serializer),
}]
.serialize(serializer),
}
}
}
impl<'de> de::Deserialize<'de> for InputValue {
fn deserialize<D>(deserializer: D) -> Result<InputValue, D::Error>
impl<'de, S> de::Deserialize<'de> for InputValue<S>
where
S: ScalarValue,
{
fn deserialize<D>(deserializer: D) -> Result<InputValue<S>, D::Error>
where
D: de::Deserializer<'de>,
{
struct InputValueVisitor;
struct InputValueVisitor<S: ScalarValue>(S::Visitor);
impl<'de> de::Visitor<'de> for InputValueVisitor {
type Value = InputValue;
impl<S: ScalarValue> Default for InputValueVisitor<S> {
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 {
formatter.write_str("a valid input value")
}
fn visit_bool<E>(self, value: bool) -> Result<InputValue, E> {
Ok(InputValue::boolean(value))
}
fn visit_i64<E>(self, value: i64) -> Result<InputValue, E>
fn visit_bool<E>(self, value: bool) -> Result<InputValue<S>, E>
where
E: de::Error,
{
if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) {
Ok(InputValue::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(InputValue::float(value as f64))
self.0.visit_bool(value).map(InputValue::Scalar)
}
fn visit_i8<E>(self, value: i8) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_i8(value).map(InputValue::Scalar)
}
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
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(InputValue::float(value as f64))
self.0.visit_u8(value).map(InputValue::Scalar)
}
fn visit_u16<E>(self, value: u16) -> Result<InputValue<S>, E>
where
E: de::Error,
{
self.0.visit_u16(value).map(InputValue::Scalar)
}
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> {
Ok(InputValue::float(value))
}
fn visit_str<E>(self, value: &str) -> Result<InputValue, E>
fn visit_f32<E>(self, value: f32) -> Result<InputValue<S>, E>
where
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> {
Ok(InputValue::string(value))
fn visit_f64<E>(self, value: f64) -> Result<InputValue<S>, E>
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())
}
fn visit_unit<E>(self) -> Result<InputValue, E> {
fn visit_unit<E>(self) -> Result<InputValue<S>, E>
where
E: de::Error,
{
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
V: de::SeqAccess<'de>,
{
@ -149,40 +252,45 @@ impl<'de> de::Deserialize<'de> for InputValue {
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
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()? {
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>
where
S: ser::Serializer,
{
match *self {
InputValue::Null | InputValue::Variable(_) => serializer.serialize_unit(),
InputValue::Int(v) => serializer.serialize_i64(i64::from(v)),
InputValue::Float(v) => serializer.serialize_f64(v),
InputValue::String(ref v) | InputValue::Enum(ref v) => serializer.serialize_str(v),
InputValue::Boolean(v) => serializer.serialize_bool(v),
InputValue::List(ref v) => v.iter()
InputValue::Scalar(ref s) => s.serialize(serializer),
InputValue::Enum(ref v) => serializer.serialize_str(v),
InputValue::List(ref v) => v
.iter()
.map(|x| x.item.clone())
.collect::<Vec<_>>()
.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()))
.collect::<IndexMap<_, _>>()
.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>
where
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>
where
S: ser::Serializer,
{
match *self {
Value::Null => serializer.serialize_unit(),
Value::Int(v) => serializer.serialize_i64(i64::from(v)),
Value::Float(v) => serializer.serialize_f64(v),
Value::String(ref v) => serializer.serialize_str(v),
Value::Boolean(v) => serializer.serialize_bool(v),
Value::Scalar(ref s) => s.serialize(serializer),
Value::List(ref v) => v.serialize(serializer),
Value::Object(ref v) => v.serialize(serializer),
}
@ -289,27 +400,27 @@ mod tests {
use ast::InputValue;
use serde_json::from_str;
use serde_json::to_string;
use value::{DefaultScalarValue, Object};
use {FieldError, Value};
use ::value::Object;
#[test]
fn int() {
assert_eq!(
from_str::<InputValue>("1235").unwrap(),
InputValue::int(1235)
from_str::<InputValue<DefaultScalarValue>>("1235").unwrap(),
InputValue::scalar(1235)
);
}
#[test]
fn float() {
assert_eq!(
from_str::<InputValue>("2.0").unwrap(),
InputValue::float(2.0)
from_str::<InputValue<DefaultScalarValue>>("2.0").unwrap(),
InputValue::scalar(2.0)
);
// large value without a decimal part is also float
assert_eq!(
from_str::<InputValue>("123567890123").unwrap(),
InputValue::float(123567890123.0)
from_str::<InputValue<DefaultScalarValue>>("123567890123").unwrap(),
InputValue::scalar(123567890123.0)
);
}
@ -323,8 +434,8 @@ mod tests {
#[test]
fn error_extensions() {
let mut obj = Object::with_capacity(1);
obj.add_field("foo".to_string(), Value::String("bar".to_string()));
let mut obj: Object<DefaultScalarValue> = Object::with_capacity(1);
obj.add_field("foo".to_string(), Value::scalar("bar"));
assert_eq!(
to_string(&ExecutionError::at_origin(FieldError::new(
"foo error",

View file

@ -1,18 +1,23 @@
use url::Url;
use value::{ParseScalarResult, ParseScalarValue};
use Value;
graphql_scalar!(Url {
graphql_scalar!(Url where Scalar = <S>{
description: "Url"
resolve(&self) -> Value {
Value::string(self.as_str())
Value::scalar(self.as_str().to_owned())
}
from_input_value(v: &InputValue) -> Option<Url> {
v.as_string_value()
v.as_scalar_value::<String>()
.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)]
@ -22,7 +27,7 @@ mod test {
#[test]
fn url_from_input_value() {
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 url = Url::parse(raw).unwrap();

View file

@ -1,28 +1,39 @@
use uuid::Uuid;
use parser::{ParseError, ScalarToken, Token};
use value::ParseScalarResult;
use Value;
graphql_scalar!(Uuid {
graphql_scalar!(Uuid where Scalar = <S> {
description: "Uuid"
resolve(&self) -> Value {
Value::string(self.to_string())
Value::scalar(self.to_string())
}
from_input_value(v: &InputValue) -> Option<Uuid> {
v.as_string_value()
v.as_scalar_value::<String>()
.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)]
mod test {
use uuid::Uuid;
use value::DefaultScalarValue;
#[test]
fn uuid_from_input_value() {
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 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)]
extern crate serde;
#[doc(hidden)]
#[macro_use]
pub extern crate serde;
#[macro_use]
extern crate serde_derive;
@ -111,13 +113,28 @@ extern crate url;
extern crate uuid;
// 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)]
#[macro_use]
extern crate juniper_codegen;
#[doc(hidden)]
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]
mod value;
#[macro_use]
@ -125,7 +142,7 @@ mod macros;
mod ast;
mod executor;
pub mod parser;
mod schema;
pub(crate) mod schema;
mod types;
mod util;
mod validation;
@ -159,13 +176,15 @@ pub use executor::{
Context, ExecutionError, ExecutionResult, Executor, FieldError, FieldResult, FromContext,
IntoFieldError, IntoResolvable, Registry, Variables,
};
pub use schema::meta;
pub use schema::model::RootNode;
pub use types::base::{Arguments, GraphQLType, TypeKind};
pub use types::scalars::{EmptyMutation, ID};
pub use validation::RuleError;
pub use value::{Value, Object};
pub use schema::meta;
pub use value::{
DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue,
Value,
};
/// An error that prevented query execution
#[derive(Debug, PartialEq)]
@ -179,19 +198,20 @@ pub enum GraphQLError<'a> {
}
/// 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,
operation_name: Option<&str>,
root_node: &RootNode<QueryT, MutationT>,
variables: &Variables,
root_node: &'a RootNode<QueryT, MutationT, S>,
variables: &Variables<S>,
context: &CtxT,
) -> Result<(Value, Vec<ExecutionError>), GraphQLError<'a>>
) -> Result<(Value<S>, Vec<ExecutionError<S>>), GraphQLError<'a>>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
S: ScalarValue,
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);

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_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 | {
$( $items:tt )*
}
@generate,
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 {
type Context = $ctxt;
type TypeInfo = ();
__juniper_impl_trait!(
impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
type Context = $ctx;
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;
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);
fn name(_ : &Self::TypeInfo) -> Option<&str> {
Some($($outname)*)
}
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 {
graphql_interface!(
@ concrete_type_name,
($outname, context, $ctxt),
$($items)*);
}
#[allow(unused_variables)]
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(stringify!($arg_name)))
.expect(__graphql__concat!(
"Argument ",
__graphql__stringify!($arg_name),
" missing - validation must have failed"
));
)*
$(
let $executor = &executor;
)*
$body
})();
fn resolve_into_type(
&$mainself,
_: &(),
type_name: &str,
_: Option<&[$crate::Selection]>,
executor: &$crate::Executor<Self::Context>,
)
-> $crate::ExecutionResult
{
graphql_interface!(
@ resolve_into_type,
($outname, type_name, executor, $ctxt),
$($items)*);
return $crate::IntoResolvable::into(result, executor.context())
.and_then(|res| {
match res {
Some((ctx, r)) => {
executor.replaced_context(ctx)
.resolve_with_ctx(&(), &r)
}
None => Ok($crate::Value::null())
}
});
}
)*
__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 | {
$( $items:tt )*
}
@parse,
meta = {$($meta:tt)*},
rest = $($rest:tt)*
) => {
graphql_interface!(
($($lifetime),*) $name : $ctxt as $outname | &$mainself | { $( $items )* });
__juniper_parse_field_list!(
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 | {
$( $items:tt )*
}
$($rest: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)]
#[macro_export]
macro_rules! __graphql__vec {
($($t:tt)*) => ( vec!($($t)*) );
macro_rules! __graphql__concat {
($($t:tt)*) => ( concat!($($t)*) );
}
#[doc(hidden)]
#[macro_export]
macro_rules! __graphql__compile_error {
($t:expr) => ( compile_error!($t) );
}
#[macro_use]
mod common;
#[macro_use]
mod object;
#[macro_use]
@ -25,10 +33,6 @@ mod interface;
#[macro_use]
mod scalar;
#[macro_use]
mod args;
#[macro_use]
mod field;
#[macro_use]
mod union;
#[cfg(test)]

View file

@ -120,8 +120,8 @@ even have to be backed by a trait!
## Emitting errors
`FieldResult<T>` is a type alias for `Result<T, FieldError>`, where
`FieldResult` is a tuple that contains an error message and optionally a
`FieldResult<T, S = DefaultScalarValue>` is a type alias for `Result<T, FieldError<S>>`, where
`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
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
@ -149,17 +149,52 @@ graphql_object!(User: () |&self| {
# 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
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
the GraphQL schema. It takes one of the following two forms:
type, which lifetime parameters or generics to define, which name to use in
the GraphQL schema and which scalar value type is used. It takes the
following form:
```text
ExposedType: ContextType as "ExposedName" |&self| { items... }
<Generics> ExposedType: ContextType as "ExposedName" |&self| { items... }
<Generics> ExposedType: ContextType as "ExposedName" where Scalar = <S> |&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
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_rules! graphql_object {
( @as_item, $i:item) => { $i };
( @as_expr, $e:expr) => { $e };
// field deprecated <reason> <name>(...) -> <type> as <description> { ... }
(
@gather_object_meta,
$reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr,
field deprecated
$reason:tt
$name:ident
$args:tt -> $t:ty
as $desc:tt
$body:block
$( $rest:tt )*
@generate,
meta = {
lifetimes = [$($lifetimes:tt,)*],
name = $name: ty,
ctx = $ctx: ty,
main_self = $main_self: ident,
outname = {$($outname: tt)*},
scalar = {$($scalar: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!(
@apply_args,
$reg,
$reg.field_convert::<$t, _, Self::Context>(
&$crate::to_camel_case(__graphql__stringify!($name)), $info)
.description($desc)
.deprecated($reason),
$info,
$args));
__juniper_impl_trait!(
impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
type Context = $ctx;
type TypeInfo = ();
graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*);
};
// 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);
fn name(_ : &Self::TypeInfo) -> Option<&str> {
Some($($outname)*)
}
if let Some(interfaces) = interfaces {
mt = mt.interfaces(&interfaces);
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 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 {
$outname.to_owned()
}
#[allow(unused_variables)]
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)]
#[allow(unused_mut)]
fn resolve_field(
&$mainself,
info: &(),
field: &str,
args: &$crate::Arguments,
executor: &$crate::Executor<Self::Context>
)
-> $crate::ExecutionResult
{
__graphql__build_field_matches!(
($outname, $mainself, field, args, executor),
(),
$($items)*);
return $crate::IntoResolvable::into(result, executor.context())
.and_then(|res| {
match res {
Some((ctx, r)) => {
executor.replaced_context(ctx)
.resolve_with_ctx(&(), &r)
}
None => Ok($crate::Value::null())
}
});
}
)*
__graphql__panic!("Field {} not found on type {}", field, $($outname)*);
}
}
});
);
};
(
<$( $lifetime:tt ),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | {
$( $items:tt )*
}
@parse_interfaces,
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!(
( $($lifetime),* ); $name; $ctxt; $outname; $mainself; $( $items )*);
__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 = {
interfaces = [$($interface,)*],
},
},
items = [$({$($items)*},)*],
rest = $($rest)*
);
};
(
$name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | {
$( $items:tt )*
}
@parse_interfaces,
success_callback = $success_callback: ident,
additional_parser = {$($additional:tt)*},
meta = { $($meta:tt)* },
items = [$({$($items: tt)*},)*],
rest = interfaces: $($rest:tt)*
) => {
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 | {
$( $items:tt )*
}
$($rest:tt)*
) => {
graphql_object!(
( ); $name; $ctxt; (__graphql__stringify!($name)); $mainself; $( $items )*);
};
__juniper_parse_object_header!(
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
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
# #[macro_use] extern crate juniper;
# use juniper::{Value, FieldResult};
# use juniper::{Value, FieldResult, ParseScalarValue, ParseScalarResult};
struct UserID(String);
graphql_scalar!(UserID {
@ -26,6 +31,10 @@ graphql_scalar!(UserID {
from_input_value(v: &InputValue) -> Option<UserID> {
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() { }
@ -38,119 +47,297 @@ usable as arguments and default values.
*/
#[macro_export(local_inner_macros)]
macro_rules! graphql_scalar {
( @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.
( @as_expr $e:expr) => { $e };
(
@generate,
( $name:ty, $outname:expr, $descr:tt ),
(
( $resolve_selfvar:ident, $resolve_retval:ty, $resolve_body:block ),
( $fiv_arg:ident, $fiv_result:ty, $fiv_body:block )
)
meta = {
name = $name:ty,
outname = {$($outname:tt)+},
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 {
type Context = ();
type TypeInfo = ();
__juniper_impl_trait!(
impl <$($scalar)+> GraphQLType for $name {
type Context = ();
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
Some(graphql_scalar!( @as_expr, $outname ))
}
fn name(_: &Self::TypeInfo) -> Option<&str> {
Some(graphql_scalar!(@as_expr $($outname)+))
}
fn meta<'r>(
info: &(),
registry: &mut $crate::Registry<'r>
) -> $crate::meta::MetaType<'r> {
graphql_scalar!(
@maybe_apply, $descr, description,
registry.build_scalar_type::<Self>(info))
.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 meta = registry.build_scalar_type::<Self>(info);
$(
let meta = meta.description($descr);
)*
meta.into_meta()
}
fn resolve(
&$resolve_selfvar,
_: &(),
_: Option<&[$crate::Selection]>,
_: &$crate::Executor<Self::Context>) -> $resolve_retval {
$resolve_body
}
}
fn resolve(
&$resolve_self_var,
_: &(),
_: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)+)>]>,
_: &$crate::Executor<
Self::Context,
__juniper_insert_generic!($($scalar)+)
>) -> $crate::Value<__juniper_insert_generic!($($scalar)+)> {
$resolve_body
}
});
impl $crate::ToInputValue for $name {
fn to_input_value(&$resolve_selfvar) -> $crate::InputValue {
$crate::ToInputValue::to_input_value(&$resolve_body)
__juniper_impl_trait!(
impl<$($scalar)+> ToInputValue for $name {
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 {
fn from_input_value($fiv_arg: &$crate::InputValue) -> $fiv_result {
$fiv_body
__juniper_impl_trait!(
impl<$($scalar)+> FromInputValue for $name {
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
(
@parse,
$meta:tt,
$acc:tt,
@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)+},
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 { ... }
(
@parse,
$meta:tt,
( $_ignored:tt, $fiv:tt ),
resolve(&$selfvar:ident) -> $retval:ty $body:block $($rest:tt)*
@parse_functions,
meta = {$($meta:tt)*},
$(resolve = {$($resolve_body: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) -> ... { ... }
(
@parse,
$meta:tt,
( $resolve:tt, $_ignored:tt ),
from_input_value($arg:ident: &InputValue) -> $result:ty $body:block $($rest:tt)*
@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_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>
(
@parse,
( $name:ty, $outname:expr, $_ignored:tt ),
$acc:tt,
description: $descr:tt $($rest:tt)*
@parse_functions,
meta = {
name = $name:ty,
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" { ... }
( $name:ty as $outname:tt { $( $items:tt )* }) => {
graphql_scalar!( @parse, ( $name, $outname, None ), ( None, None ), $($items)* );
(
@parse,
meta = {
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
// RustName { ... }
( $name:ty { $( $items:tt )* }) => {
graphql_scalar!( @parse, ( $name, __graphql__stringify!($name), None ), ( None, None ), $($items)* );
(@$($stuff:tt)*) => {
__graphql__compile_error!("Invalid syntax for `graphql_scalar!`");
};
($($rest:tt)*) => {
__juniper_parse_object_header!(
callback = graphql_scalar,
rest = $($rest)*
);
}
}

View file

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

View file

@ -2,9 +2,10 @@ use ast::InputValue;
use executor::FieldResult;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::{Object, Value};
use value::{DefaultScalarValue, Object, Value};
struct Interface;
#[derive(Debug)]
struct Root;
/*
@ -57,7 +58,7 @@ graphql_interface!(Interface: () |&self| {
fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
where
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -72,7 +73,7 @@ where
}
"#;
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()
.collect();
@ -103,10 +104,10 @@ where
.expect("Field not an object")
.get_field_value("name")
.expect("name field missing from field")
.as_string_value()
.expect("name is not a string") == field_name
})
.next()
.as_scalar_value::<String>()
.expect("name is not a string")
== field_name
}).next()
.expect("Field not found")
.as_object_value()
.expect("Field is not an object");
@ -121,12 +122,12 @@ fn introspect_object_field_simple() {
run_field_info_query("Root", "simple", |field| {
assert_eq!(
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("isDeprecated"),
Some(&Value::boolean(false))
Some(&Value::scalar(false))
);
assert_eq!(
field.get_field_value("deprecationReason"),
@ -140,12 +141,12 @@ fn introspect_interface_field_simple() {
run_field_info_query("Interface", "simple", |field| {
assert_eq!(
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("isDeprecated"),
Some(&Value::boolean(false))
Some(&Value::scalar(false))
);
assert_eq!(
field.get_field_value("deprecationReason"),
@ -159,39 +160,60 @@ fn introspect_object_field_description() {
run_field_info_query("Root", "description", |field| {
assert_eq!(
field.get_field_value("name"),
Some(&Value::string("description"))
Some(&Value::scalar("description"))
);
assert_eq!(
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]
fn introspect_interface_field_description() {
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!(
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]
fn introspect_object_field_deprecated() {
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("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
);
assert_eq!(
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]
fn introspect_interface_field_deprecated() {
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("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(
field.get_field_value("isDeprecated"),
Some(&Value::scalar(true))
);
assert_eq!(
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]
fn introspect_object_field_deprecated_descr() {
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!(
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!(
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]
fn introspect_interface_field_deprecated_descr() {
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!(
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!(
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 schema::model::RootNode;
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)
where
F: Fn(&Object, &Vec<Value>) -> (),
F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -143,7 +143,7 @@ where
}
"#;
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()
.collect();
@ -175,13 +175,13 @@ fn introspect_custom_name() {
run_type_info_query("ACustomNamedInterface", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("ACustomNamedInterface"))
Some(&Value::scalar("ACustomNamedInterface"))
);
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -192,12 +192,12 @@ fn introspect_custom_name() {
#[test]
fn introspect_with_lifetime() {
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!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -208,12 +208,12 @@ fn introspect_with_lifetime() {
#[test]
fn introspect_with_generics() {
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!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -224,15 +224,15 @@ fn introspect_with_generics() {
#[test]
fn introspect_description_first() {
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!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -243,15 +243,15 @@ fn introspect_description_first() {
#[test]
fn introspect_fields_first() {
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!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -262,15 +262,15 @@ fn introspect_fields_first() {
#[test]
fn introspect_interfaces_first() {
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!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -283,16 +283,16 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
Some(&Value::scalar("CommasWithTrailing"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -303,15 +303,15 @@ fn introspect_commas_with_trailing() {
#[test]
fn introspect_commas_on_meta() {
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!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))
@ -324,16 +324,16 @@ fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma"))
Some(&Value::scalar("ResolversWithTrailingComma"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
fields.contains(&Value::object(
vec![("name", Value::string("simple"))]
vec![("name", Value::scalar("simple"))]
.into_iter()
.collect(),
))

View file

@ -4,7 +4,7 @@ use ast::InputValue;
use executor::{Context, FieldResult};
use schema::model::RootNode;
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)
where
F: Fn(&Object, &Vec<Value>) -> (),
F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -169,7 +169,7 @@ where
}
"#;
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()
.collect();
@ -202,7 +202,7 @@ fn introspect_custom_name() {
run_type_info_query("ACustomNamedType", |object, fields| {
assert_eq!(
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!(
@ -222,7 +222,7 @@ fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |object, fields| {
assert_eq!(
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!(
@ -242,7 +242,7 @@ fn introspect_with_generics() {
run_type_info_query("WithGenerics", |object, fields| {
assert_eq!(
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!(
@ -262,20 +262,20 @@ fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("DescriptionFirst"))
Some(&Value::scalar("DescriptionFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
("kind", Value::string("INTERFACE")),
("name", Value::scalar("Interface")),
("kind", Value::scalar("INTERFACE")),
].into_iter()
.collect(),
.collect(),
)]))
);
@ -291,20 +291,20 @@ fn introspect_fields_first() {
run_type_info_query("FieldsFirst", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("FieldsFirst"))
Some(&Value::scalar("FieldsFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
("kind", Value::string("INTERFACE")),
("name", Value::scalar("Interface")),
("kind", Value::scalar("INTERFACE")),
].into_iter()
.collect(),
.collect(),
)]))
);
@ -320,20 +320,20 @@ fn introspect_interfaces_first() {
run_type_info_query("InterfacesFirst", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("InterfacesFirst"))
Some(&Value::scalar("InterfacesFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
("kind", Value::string("INTERFACE")),
("name", Value::scalar("Interface")),
("kind", Value::scalar("INTERFACE")),
].into_iter()
.collect(),
.collect(),
)]))
);
@ -349,20 +349,20 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
Some(&Value::scalar("CommasWithTrailing"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
("kind", Value::string("INTERFACE")),
("name", Value::scalar("Interface")),
("kind", Value::scalar("INTERFACE")),
].into_iter()
.collect(),
.collect(),
)]))
);
@ -378,20 +378,20 @@ fn introspect_commas_on_meta() {
run_type_info_query("CommasOnMeta", |object, fields| {
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("CommasOnMeta"))
Some(&Value::scalar("CommasOnMeta"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
("kind", Value::string("INTERFACE")),
("name", Value::scalar("Interface")),
("kind", Value::scalar("INTERFACE")),
].into_iter()
.collect(),
.collect(),
)]))
);

View file

@ -1,7 +1,7 @@
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::{Value, Object};
use value::{Value, Object, DefaultScalarValue, ParseScalarValue, ParseScalarResult};
struct DefaultName(i32);
struct OtherOrder(i32);
@ -19,45 +19,62 @@ Syntax to validate:
*/
graphql_scalar!(DefaultName {
graphql_scalar!(DefaultName where Scalar = <S> {
resolve(&self) -> Value {
Value::int(self.0)
Value::scalar(self.0)
}
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 {
from_input_value(v: &InputValue) -> Option<OtherOrder> {
v.as_int_value().map(|i| OtherOrder(i))
resolve(&self) -> Value {
Value::scalar(self.0)
}
resolve(&self) -> Value {
Value::int(self.0)
from_input_value(v: &InputValue) -> Option<OtherOrder> {
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 {
Value::int(self.0)
Value::scalar(self.0)
}
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"
resolve(&self) -> Value {
Value::int(self.0)
Value::scalar(self.0)
}
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)
where
F: Fn(&Object) -> (),
F: Fn(&Object<DefaultScalarValue>) -> (),
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -95,13 +112,18 @@ where
#[test]
fn path_in_resolve_return_type() {
struct ResolvePath(i32);
graphql_scalar!(ResolvePath {
resolve(&self) -> self::Value {
Value::int(self.0)
Value::scalar(self.0)
}
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| {
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()));
});
}
@ -135,7 +157,7 @@ fn other_order_introspection() {
"#;
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()));
});
}
@ -152,7 +174,7 @@ fn named_introspection() {
"#;
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()));
});
}
@ -171,11 +193,11 @@ fn scalar_description_introspection() {
run_type_info_query(doc, |type_info| {
assert_eq!(
type_info.get_field_value("name"),
Some(&Value::string("ScalarDescription"))
Some(&Value::scalar("ScalarDescription"))
);
assert_eq!(
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 schema::model::RootNode;
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)
where
F: Fn(&Object, &Vec<Value>) -> (),
F: Fn(&Object<DefaultScalarValue>, &Vec<Value<DefaultScalarValue>>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -124,7 +124,7 @@ where
}
"#;
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()
.collect();
@ -154,12 +154,12 @@ where
#[test]
fn introspect_custom_name() {
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!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -170,12 +170,12 @@ fn introspect_custom_name() {
#[test]
fn introspect_with_lifetime() {
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!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -186,12 +186,12 @@ fn introspect_with_lifetime() {
#[test]
fn introspect_with_generics() {
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!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -202,15 +202,15 @@ fn introspect_with_generics() {
#[test]
fn introspect_description_first() {
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!(
union.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -221,15 +221,15 @@ fn introspect_description_first() {
#[test]
fn introspect_resolvers_first() {
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!(
union.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -242,16 +242,16 @@ fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |union, possible_types| {
assert_eq!(
union.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
Some(&Value::scalar("CommasWithTrailing"))
);
assert_eq!(
union.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))
@ -264,16 +264,16 @@ fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |union, possible_types| {
assert_eq!(
union.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma"))
Some(&Value::scalar("ResolversWithTrailingComma"))
);
assert_eq!(
union.get_field_value("description"),
Some(&Value::string("A description"))
Some(&Value::scalar("A description"))
);
assert!(
possible_types.contains(&Value::object(
vec![("name", Value::string("Concrete"))]
vec![("name", Value::scalar("Concrete"))]
.into_iter()
.collect(),
))

View file

@ -19,164 +19,119 @@ resolvers.
*/
#[macro_export(local_inner_macros)]
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 | {
$( $items:tt )*
}
@generate,
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 {
type Context = $ctxt;
type TypeInfo = ();
__juniper_impl_trait!(
impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name {
type Context = $ctx;
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
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);
fn name(_ : &Self::TypeInfo) -> Option<&str> {
Some($($outname)*)
}
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 {
graphql_union!(
@ concrete_type_name,
($outname, context, $ctxt),
$($items)*);
}
#[allow(unused_variables)]
fn concrete_type_name(&$main_self, context: &Self::Context, _info: &Self::TypeInfo) -> String {
$(let $resolver_ctx = &context;)*
fn resolve_into_type(
&$mainself,
_: &(),
type_name: &str,
_: Option<&[$crate::Selection]>,
executor: &$crate::Executor<Self::Context>,
)
-> $crate::ExecutionResult
{
graphql_union!(
@ resolve_into_type,
($outname, type_name, executor, $ctxt),
$($items)*);
$(
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 | {
$( $items:tt )*
}
) => {
graphql_union!(
($($lifetime),*) $name : $ctxt as $outname | &$mainself | { $( $items )* });
};
(
$name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | {
$( $items:tt )*
}
@parse,
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!`");
};
(
$name:ty : $ctxt:ty | &$mainself:ident | {
$( $items:tt )*
}
) => {
graphql_union!(() $name : $ctxt as (__graphql__stringify!($name)) | &$mainself | { $( $items )* });
($($rest: tt)*) => {
__juniper_parse_object_header!(
callback = graphql_union,
rest = $($rest)*
);
};
}

View file

@ -10,19 +10,34 @@ use parser::{
Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token,
UnlocatedParseResult,
};
use schema::meta::{Argument, Field as MetaField};
use schema::model::SchemaType;
use value::ScalarValue;
#[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 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();
loop {
defs.push(parse_definition(parser)?);
defs.push(parse_definition(parser, schema)?);
if parser.peek().item == Token::EndOfFile {
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 {
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => {
Ok(Definition::Operation(parse_operation_definition(parser)?))
}
Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(parser)?)),
Token::CurlyOpen | Token::Name("query") | Token::Name("mutation") => Ok(
Definition::Operation(parse_operation_definition(parser, schema)?),
),
Token::Name("fragment") => Ok(Definition::Fragment(parse_fragment_definition(
parser, schema,
)?)),
_ => 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 {
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(
&selection_set.start,
@ -58,13 +89,20 @@ fn parse_operation_definition<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Op
} else {
let start_pos = parser.peek().start.clone();
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 {
Token::Name(_) => Some(parser.expect_name()?),
_ => None,
};
let variable_definitions = parse_variable_definitions(parser)?;
let directives = parse_directives(parser)?;
let selection_set = parse_selection_set(parser)?;
let variable_definitions = parse_variable_definitions(parser, schema)?;
let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Spanning::start_end(
&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 {
start: start_pos, ..
} = 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"))?;
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(
&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>,
) -> 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 {
Ok(Some(parse_selection_set(parser)?))
Ok(Some(parse_selection_set(parser, schema, fields)?))
} else {
Ok(None)
}
}
fn parse_selection_set<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, Vec<Selection<'a>>> {
parser.unlocated_delimited_nonempty_list(&Token::CurlyOpen, parse_selection, &Token::CurlyClose)
fn parse_selection_set<'a, 'b, S>(
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 {
Token::Ellipsis => parse_fragment(parser),
_ => parse_field(parser).map(Selection::Field),
Token::Ellipsis => parse_fragment(parser, schema, fields),
_ => 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 {
start: ref start_pos,
..
@ -141,8 +221,13 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
Token::Name("on") => {
parser.next()?;
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(
&start_pos.clone(),
@ -155,7 +240,7 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
)))
}
Token::CurlyOpen => {
let selection_set = parse_selection_set(parser)?;
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Selection::InlineFragment(Spanning::start_end(
&start_pos.clone(),
@ -169,7 +254,7 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
}
Token::Name(_) => {
let frag_name = parser.expect_name()?;
let directives = parse_directives(parser)?;
let directives = parse_directives(parser, schema)?;
Ok(Selection::FragmentSpread(Spanning::start_end(
&start_pos.clone(),
@ -184,8 +269,8 @@ fn parse_fragment<'a>(parser: &mut Parser<'a>) -> UnlocatedParseResult<'a, Selec
)))
}
Token::At => {
let directives = parse_directives(parser)?;
let selection_set = parse_selection_set(parser)?;
let directives = parse_directives(parser, schema)?;
let selection_set = parse_selection_set(parser, schema, fields)?;
Ok(Selection::InlineFragment(Spanning::start_end(
&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 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()
};
let arguments = parse_arguments(parser)?;
let directives = parse_directives(parser)?;
let selection_set = parse_optional_selection_set(parser)?;
let field = fields.and_then(|f| f.iter().find(|f| f.name == name.item));
let args = field
.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(
&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 {
Ok(None)
} else {
Ok(Some(
parser
.delimited_nonempty_list(&Token::ParenOpen, parse_argument, &Token::ParenClose)?
.map(|args| Arguments {
.delimited_nonempty_list(
&Token::ParenOpen,
|p| parse_argument(p, schema, arguments),
&Token::ParenClose,
)?.map(|args| Arguments {
items: args.into_iter().map(|s| s.item).collect(),
}),
))
}
}
fn parse_argument<'a>(
fn parse_argument<'a, 'b, S>(
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 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)?;
let value = parse_value_literal(parser, false)?;
let value = parse_value_literal(parser, false, schema, tpe)?;
Ok(Spanning::start_end(
&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>,
) -> OptionParseResult<'a, VariableDefinitions<'a>> {
schema: &'b SchemaType<'b, S>,
) -> OptionParseResult<'a, VariableDefinitions<'a, S>>
where
S: ScalarValue,
{
if parser.peek().item != Token::ParenOpen {
Ok(None)
} else {
@ -279,28 +406,32 @@ fn parse_variable_definitions<'a>(
parser
.delimited_nonempty_list(
&Token::ParenOpen,
parse_variable_definition,
|p| parse_variable_definition(p, schema),
&Token::ParenClose,
)?
.map(|defs| VariableDefinitions {
)?.map(|defs| VariableDefinitions {
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>,
) -> 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 {
start: start_pos, ..
} = parser.expect(&Token::Dollar)?;
let var_name = parser.expect_name()?;
parser.expect(&Token::Colon)?;
let var_type = parse_type(parser)?;
let tpe = schema.lookup_type(&var_type.item);
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 {
None
};
@ -321,27 +452,44 @@ fn parse_variable_definition<'a>(
))
}
fn parse_directives<'a>(
fn parse_directives<'a, 'b, S>(
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 {
Ok(None)
} else {
let mut items = Vec::new();
while parser.peek().item == Token::At {
items.push(parse_directive(parser)?);
items.push(parse_directive(parser, schema)?);
}
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 {
start: start_pos, ..
} = parser.expect(&Token::At)?;
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(
&start_pos,

View file

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

View file

@ -11,6 +11,6 @@ mod tests;
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::utils::{SourcePosition, Spanning};

View file

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

View file

@ -3,13 +3,25 @@ use ast::{
};
use parser::document::parse_document_source;
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 {
parse_document_source(s).expect(&format!("Parse error on input {:#?}", s))
fn parse_document<S>(s: &str) -> Document<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>> {
match parse_document_source(s) {
fn parse_document_error<'a, S>(s: &'a str) -> Spanning<ParseError<'a>>
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),
Err(err) => err,
}
@ -18,7 +30,7 @@ fn parse_document_error<'a>(s: &'a str) -> Spanning<ParseError<'a>> {
#[test]
fn simple_ast() {
assert_eq!(
parse_document(
parse_document::<DefaultScalarValue>(
r#"
{
node(id: 4) {
@ -59,7 +71,7 @@ fn simple_ast() {
Spanning::start_end(
&SourcePosition::new(40, 2, 25),
&SourcePosition::new(41, 2, 26),
InputValue::int(4),
InputValue::scalar(4),
),
)],
},
@ -107,7 +119,7 @@ fn simple_ast() {
#[test]
fn errors() {
assert_eq!(
parse_document_error("{"),
parse_document_error::<DefaultScalarValue>("{"),
Spanning::zero_width(
&SourcePosition::new(1, 0, 1),
ParseError::UnexpectedEndOfFile
@ -115,7 +127,7 @@ fn errors() {
);
assert_eq!(
parse_document_error("{ ...MissingOn }\nfragment MissingOn Type"),
parse_document_error::<DefaultScalarValue>("{ ...MissingOn }\nfragment MissingOn Type"),
Spanning::start_end(
&SourcePosition::new(36, 1, 19),
&SourcePosition::new(40, 1, 23),
@ -124,7 +136,7 @@ fn errors() {
);
assert_eq!(
parse_document_error("{ ...on }"),
parse_document_error::<DefaultScalarValue>("{ ...on }"),
Spanning::start_end(
&SourcePosition::new(8, 0, 8),
&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>>> {
let mut tokens = Vec::new();
@ -148,7 +148,7 @@ fn strings() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(8, 0, 8),
Token::String("simple".to_owned())
Token::Scalar(ScalarToken::String("simple"))
)
);
@ -157,7 +157,7 @@ fn strings() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&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(
&SourcePosition::new(0, 0, 0),
&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(
&SourcePosition::new(0, 0, 0),
&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(
&SourcePosition::new(0, 0, 0),
&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(
&SourcePosition::new(0, 0, 0),
&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,
start: SourcePosition,
end: SourcePosition,
expected: f64,
expected: &str,
) {
let parsed = tokenize_single(source);
assert_eq!(parsed.start, start);
assert_eq!(parsed.end, end);
match parsed.item {
Token::Float(actual) => {
let relative_error = ((expected - actual) / actual).abs();
Token::Scalar(ScalarToken::Float(actual)) => {
assert!(
relative_error.abs() < 0.001,
expected == actual,
"[expected] {} != {} [actual]",
expected,
actual
@ -352,7 +351,7 @@ fn numbers() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1),
Token::Int(4)
Token::Scalar(ScalarToken::Int("4"))
)
);
@ -360,14 +359,14 @@ fn numbers() {
"4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
4.123,
"4.123",
);
assert_float_token_eq(
"4.0",
SourcePosition::new(0, 0, 0),
SourcePosition::new(3, 0, 3),
4.0,
"4.0",
);
assert_eq!(
@ -375,7 +374,7 @@ fn numbers() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2),
Token::Int(-4)
Token::Scalar(ScalarToken::Int("-4"))
)
);
@ -384,7 +383,7 @@ fn numbers() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1),
Token::Int(9)
Token::Scalar(ScalarToken::Int("9"))
)
);
@ -393,7 +392,7 @@ fn numbers() {
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(1, 0, 1),
Token::Int(0)
Token::Scalar(ScalarToken::Int("0"))
)
);
@ -401,77 +400,77 @@ fn numbers() {
"-4.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
-4.123,
"-4.123",
);
assert_float_token_eq(
"0.123",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
0.123,
"0.123",
);
assert_float_token_eq(
"123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4,
"123e4",
);
assert_float_token_eq(
"123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(5, 0, 5),
123e4,
"123E4",
);
assert_float_token_eq(
"123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e-4,
"123e-4",
);
assert_float_token_eq(
"123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(6, 0, 6),
123e4,
"123e+4",
);
assert_float_token_eq(
"-1.123e4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4,
"-1.123e4",
);
assert_float_token_eq(
"-1.123E4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(8, 0, 8),
-1.123e4,
"-1.123E4",
);
assert_float_token_eq(
"-1.123e-4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e-4,
"-1.123e-4",
);
assert_float_token_eq(
"-1.123e+4",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e4,
"-1.123e+4",
);
assert_float_token_eq(
"-1.123e45",
SourcePosition::new(0, 0, 0),
SourcePosition::new(9, 0, 9),
-1.123e45,
"-1.123e45",
);
}
@ -653,19 +652,19 @@ fn punctuation_error() {
fn display() {
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!(
format!("{}", Token::String("some string".to_owned())),
format!("{}", Token::Scalar(ScalarToken::String("some string"))),
"\"some string\""
);
assert_eq!(
format!(
"{}",
Token::String("string with \\ escape and \" quote".to_owned())
Token::Scalar(ScalarToken::String("string with \\ escape and \" quote"))
),
"\"string with \\\\ escape and \\\" quote\""
);

View file

@ -1,60 +1,121 @@
use indexmap::IndexMap;
use ast::InputValue;
use ast::{FromInputValue, InputValue, Type};
use parser::value::parse_value_literal;
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 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]
fn input_value_literals() {
assert_eq!(
parse_value("123"),
parse_value::<DefaultScalarValue>("123", &scalar_meta::<i32>("Int")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(3, 0, 3),
InputValue::int(123)
InputValue::scalar(123)
)
);
assert_eq!(
parse_value("123.45"),
parse_value::<DefaultScalarValue>("123.45", &scalar_meta::<f64>("Float")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(6, 0, 6),
InputValue::float(123.45)
InputValue::scalar(123.45)
)
);
assert_eq!(
parse_value("true"),
parse_value::<DefaultScalarValue>("true", &scalar_meta::<bool>("Bool")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(4, 0, 4),
InputValue::boolean(true)
InputValue::scalar(true)
)
);
assert_eq!(
parse_value("false"),
parse_value::<DefaultScalarValue>("false", &scalar_meta::<bool>("Bool")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(5, 0, 5),
InputValue::boolean(false)
InputValue::scalar(false)
)
);
assert_eq!(
parse_value(r#""test""#),
parse_value::<DefaultScalarValue>(r#""test""#, &scalar_meta::<String>("String")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&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!(
parse_value("enum_value"),
parse_value::<DefaultScalarValue>("enum_value", &MetaType::Enum(e)),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(10, 0, 10),
@ -62,7 +123,7 @@ fn input_value_literals() {
)
);
assert_eq!(
parse_value("$variable"),
parse_value::<DefaultScalarValue>("$variable", &scalar_meta::<i32>("Int")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(9, 0, 9),
@ -70,7 +131,7 @@ fn input_value_literals() {
)
);
assert_eq!(
parse_value("[]"),
parse_value::<DefaultScalarValue>("[]", &scalar_meta::<i32>("Int")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2),
@ -78,7 +139,7 @@ fn input_value_literals() {
)
);
assert_eq!(
parse_value("[1, [2, 3]]"),
parse_value::<DefaultScalarValue>("[1, [2, 3]]", &scalar_meta::<i32>("Int")),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(11, 0, 11),
@ -86,7 +147,7 @@ fn input_value_literals() {
Spanning::start_end(
&SourcePosition::new(1, 0, 1),
&SourcePosition::new(2, 0, 2),
InputValue::int(1),
InputValue::scalar(1),
),
Spanning::start_end(
&SourcePosition::new(4, 0, 4),
@ -95,28 +156,35 @@ fn input_value_literals() {
Spanning::start_end(
&SourcePosition::new(5, 0, 5),
&SourcePosition::new(6, 0, 6),
InputValue::int(2),
InputValue::scalar(2),
),
Spanning::start_end(
&SourcePosition::new(8, 0, 8),
&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!(
parse_value("{}"),
parse_value::<DefaultScalarValue>("{}", meta),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(2, 0, 2),
InputValue::object(IndexMap::<String, InputValue>::new())
InputValue::object(IndexMap::<String, InputValue<DefaultScalarValue>>::new())
)
);
assert_eq!(
parse_value(r#"{key: 123, other: {foo: "bar"}}"#),
parse_value::<DefaultScalarValue>(
r#"{key: 123, other: {foo: "bar"}}"#,
meta
),
Spanning::start_end(
&SourcePosition::new(0, 0, 0),
&SourcePosition::new(31, 0, 31),
@ -130,7 +198,7 @@ fn input_value_literals() {
Spanning::start_end(
&SourcePosition::new(6, 0, 6),
&SourcePosition::new(9, 0, 9),
InputValue::int(123),
InputValue::scalar(123),
),
),
(
@ -151,7 +219,7 @@ fn input_value_literals() {
Spanning::start_end(
&SourcePosition::new(24, 0, 24),
&SourcePosition::new(29, 0, 29),
InputValue::string("bar"),
InputValue::scalar("bar"),
),
)]),
),

View file

@ -1,5 +1,4 @@
use std::fmt;
use std::hash::{Hash, Hasher};
/// A reference to a line and column in an input source file
#[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
/// character pointed by the `start` field and ending just before the `end`
/// marker.
#[derive(Debug)]
pub struct Spanning<T: fmt::Debug> {
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
pub struct Spanning<T> {
/// The wrapped item
pub item: T,
@ -28,7 +27,7 @@ pub struct Spanning<T: fmt::Debug> {
pub end: SourcePosition,
}
impl<T: fmt::Debug> Spanning<T> {
impl<T> Spanning<T> {
#[doc(hidden)]
pub fn zero_width(pos: &SourcePosition, item: T) -> Spanning<T> {
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 {
#[doc(hidden)]
pub fn new(index: usize, line: usize, col: usize) -> SourcePosition {

View file

@ -1,99 +1,176 @@
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>,
is_const: bool,
) -> ParseResult<'a, InputValue> {
match *parser.peek() {
Spanning {
item: Token::BracketOpen,
..
} => parse_list_literal(parser, is_const),
Spanning {
item: Token::CurlyOpen,
..
} => parse_object_literal(parser, is_const),
Spanning {
item: Token::Dollar,
..
} if !is_const =>
schema: &'b SchemaType<'b, S>,
tpe: Option<&MetaType<'b, S>>,
) -> ParseResult<'a, InputValue<S>>
where
S: ScalarValue,
{
match (parser.peek(), tpe) {
(
&Spanning {
item: Token::BracketOpen,
..
},
_,
) => 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)
}
Spanning {
item: Token::Int(i),
..
} => Ok(parser.next()?.map(|_| InputValue::int(i))),
Spanning {
item: Token::Float(f),
..
} => Ok(parser.next()?.map(|_| InputValue::float(f))),
Spanning {
item: Token::String(_),
..
} => Ok(parser.next()?.map(|t| {
if let Token::String(s) = t {
InputValue::string(s)
(
&Spanning {
item: Token::Scalar(_),
..
},
Some(&MetaType::Scalar(ref s)),
) => {
if let Spanning {
item: Token::Scalar(scalar),
start,
end,
} = parser.next()?
{
(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 {
panic!("Internal parser error");
unreachable!()
}
})),
Spanning {
item: Token::Name("true"),
..
} => Ok(parser.next()?.map(|_| InputValue::boolean(true))),
Spanning {
item: Token::Name("false"),
..
} => Ok(parser.next()?.map(|_| InputValue::boolean(false))),
Spanning {
item: Token::Name("null"),
..
} => Ok(parser.next()?.map(|_| InputValue::null())),
Spanning {
item: Token::Name(name),
..
} => Ok(parser
}
(
&Spanning {
item: Token::Scalar(_),
..
},
_,
) => {
if let Spanning {
item: Token::Scalar(token),
start,
end,
} = parser.next()?
{
parse_scalar_literal_by_infered_type(token, &start, &end, schema)
} else {
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()?
.map(|_| InputValue::enum_value(name.to_owned()))),
_ => Err(parser.next()?.map(ParseError::UnexpectedToken)),
}
}
fn parse_list_literal<'a>(parser: &mut Parser<'a>, is_const: bool) -> ParseResult<'a, InputValue> {
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
.delimited_list(
&Token::BracketOpen,
|p| parse_value_literal(p, is_const),
|p| parse_value_literal(p, is_const, schema, tpe),
&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>,
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
.delimited_list(
&Token::CurlyOpen,
|p| parse_object_field(p, is_const),
|p| parse_object_field(p, is_const, schema, object_tpe),
&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>,
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 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)?;
let value = parse_value_literal(parser, is_const)?;
let value = parse_value_literal(parser, is_const, schema, tpe)?;
Ok(Spanning::start_end(
&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 {
start: start_pos, ..
} = parser.expect(&Token::Dollar)?;
@ -118,3 +198,43 @@ fn parse_variable_literal<'a>(parser: &mut Parser<'a>) -> ParseResult<'a, InputV
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 ast::{FromInputValue, InputValue, Type};
use parser::{ParseError, ScalarToken};
use schema::model::SchemaType;
use types::base::TypeKind;
use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue};
/// Scalar type metadata
pub struct ScalarMeta<'a> {
pub struct ScalarMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result<S, ParseError<'b>>,
}
/// List type metadata
@ -32,38 +35,37 @@ pub struct NullableMeta<'a> {
/// Object type metadata
#[derive(Debug)]
pub struct ObjectMeta<'a> {
pub struct ObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub fields: Vec<Field<'a>>,
pub fields: Vec<Field<'a, S>>,
#[doc(hidden)]
pub interface_names: Vec<String>,
}
/// Enum type metadata
pub struct EnumMeta<'a> {
pub struct EnumMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub values: Vec<EnumValue>,
#[doc(hidden)]
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
}
/// Interface type metadata
#[derive(Debug)]
pub struct InterfaceMeta<'a> {
pub struct InterfaceMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub fields: Vec<Field<'a>>,
pub fields: Vec<Field<'a, S>>,
}
/// Union type metadata
@ -78,15 +80,14 @@ pub struct UnionMeta<'a> {
}
/// Input object metadata
pub struct InputObjectMeta<'a> {
pub struct InputObjectMeta<'a, S> {
#[doc(hidden)]
pub name: Cow<'a, str>,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub input_fields: Vec<Argument<'a>>,
#[doc(hidden)]
pub try_parse_fn: Box<Fn(&InputValue) -> bool + Send + Sync>,
pub input_fields: Vec<Argument<'a, S>>,
pub(crate) try_parse_fn: for<'b> fn(&'b InputValue<S>) -> bool,
}
/// A placeholder for not-yet-registered types
@ -101,36 +102,36 @@ pub struct PlaceholderMeta<'a> {
/// Generic type metadata
#[derive(Debug)]
pub enum MetaType<'a> {
pub enum MetaType<'a, S = DefaultScalarValue> {
#[doc(hidden)]
Scalar(ScalarMeta<'a>),
Scalar(ScalarMeta<'a, S>),
#[doc(hidden)]
List(ListMeta<'a>),
#[doc(hidden)]
Nullable(NullableMeta<'a>),
#[doc(hidden)]
Object(ObjectMeta<'a>),
Object(ObjectMeta<'a, S>),
#[doc(hidden)]
Enum(EnumMeta<'a>),
Enum(EnumMeta<'a, S>),
#[doc(hidden)]
Interface(InterfaceMeta<'a>),
Interface(InterfaceMeta<'a, S>),
#[doc(hidden)]
Union(UnionMeta<'a>),
#[doc(hidden)]
InputObject(InputObjectMeta<'a>),
InputObject(InputObjectMeta<'a, S>),
#[doc(hidden)]
Placeholder(PlaceholderMeta<'a>),
}
/// Metadata for a field
#[derive(Debug, Clone)]
pub struct Field<'a> {
pub struct Field<'a, S> {
#[doc(hidden)]
pub name: String,
#[doc(hidden)]
pub description: Option<String>,
#[doc(hidden)]
pub arguments: Option<Vec<Argument<'a>>>,
pub arguments: Option<Vec<Argument<'a, S>>>,
#[doc(hidden)]
pub field_type: Type<'a>,
#[doc(hidden)]
@ -139,7 +140,7 @@ pub struct Field<'a> {
/// Metadata for an argument to a field
#[derive(Debug, Clone)]
pub struct Argument<'a> {
pub struct Argument<'a, S> {
#[doc(hidden)]
pub name: String,
#[doc(hidden)]
@ -147,7 +148,7 @@ pub struct Argument<'a> {
#[doc(hidden)]
pub arg_type: Type<'a>,
#[doc(hidden)]
pub default_value: Option<InputValue>,
pub default_value: Option<InputValue<S>>,
}
/// Metadata for a single value in an enum
@ -168,7 +169,7 @@ pub struct EnumValue {
pub deprecation_reason: Option<String>,
}
impl<'a> MetaType<'a> {
impl<'a, S> MetaType<'a, S> {
/// Access the name of the type, if applicable
///
/// 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
///
/// 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 {
MetaType::Object(ObjectMeta { 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
///
/// 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 {
MetaType::InputObject(InputObjectMeta {
ref input_fields, ..
@ -283,7 +284,7 @@ impl<'a> MetaType<'a> {
/// `true` if it can be parsed as the provided type.
///
/// 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 {
MetaType::Scalar(ScalarMeta {
ref try_parse_fn, ..
@ -293,7 +294,7 @@ impl<'a> MetaType<'a> {
})
| MetaType::InputObject(InputObjectMeta {
ref try_parse_fn, ..
}) => Some(try_parse_fn),
}) => Some(*try_parse_fn),
_ => None,
}
}
@ -337,30 +338,54 @@ impl<'a> MetaType<'a> {
_ => 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
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 {
name: name,
description: None,
try_parse_fn: Box::new(|v: &InputValue| {
<T as FromInputValue>::from_input_value(v).is_some()
}),
try_parse_fn: try_parse_fn::<S, T>,
parse_fn: <T as ParseScalarValue<S>>::from_str,
}
}
/// Set the description for the given scalar type
///
/// 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
}
/// 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)
}
}
@ -372,7 +397,7 @@ impl<'a> ListMeta<'a> {
}
/// 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)
}
}
@ -384,14 +409,17 @@ impl<'a> NullableMeta<'a> {
}
/// 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)
}
}
impl<'a> ObjectMeta<'a> {
impl<'a, S> ObjectMeta<'a, S>
where
S: ScalarValue,
{
/// 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 {
name: name,
description: None,
@ -403,7 +431,7 @@ impl<'a> ObjectMeta<'a> {
/// Set the description for the object
///
/// 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
}
@ -412,7 +440,7 @@ impl<'a> ObjectMeta<'a> {
///
/// If a list of interfaces already was provided prior to calling this method, they will be
/// overwritten.
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a> {
pub fn interfaces(mut self, interfaces: &[Type<'a>]) -> ObjectMeta<'a, S> {
self.interface_names = interfaces
.iter()
.map(|t| t.innermost_name().to_owned())
@ -421,41 +449,49 @@ impl<'a> ObjectMeta<'a> {
}
/// 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)
}
}
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
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 {
name: name,
description: None,
values: values.to_vec(),
try_parse_fn: Box::new(|v: &InputValue| {
<T as FromInputValue>::from_input_value(v).is_some()
}),
try_parse_fn: try_parse_fn::<S, T>,
}
}
/// Set the description of the type
///
/// 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
}
/// 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)
}
}
impl<'a> InterfaceMeta<'a> {
impl<'a, S> InterfaceMeta<'a, S>
where
S: ScalarValue,
{
/// 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 {
name: name,
description: None,
@ -466,13 +502,13 @@ impl<'a> InterfaceMeta<'a> {
/// Set the description of the type
///
/// 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
}
/// 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)
}
}
@ -499,46 +535,47 @@ impl<'a> UnionMeta<'a> {
}
/// 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)
}
}
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
pub fn new<T: FromInputValue>(
name: Cow<'a, str>,
input_fields: &[Argument<'a>],
) -> InputObjectMeta<'a> {
pub fn new<T: FromInputValue<S>>(name: Cow<'a, str>, input_fields: &[Argument<'a, S>]) -> Self
where
for<'b> &'b S: ScalarRefValue<'b>,
{
InputObjectMeta {
name: name,
description: None,
input_fields: input_fields.to_vec(),
try_parse_fn: Box::new(|v: &InputValue| {
<T as FromInputValue>::from_input_value(v).is_some()
}),
try_parse_fn: try_parse_fn::<S, T>,
}
}
/// Set the description of the type
///
/// 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
}
/// 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)
}
}
impl<'a> Field<'a> {
impl<'a, S> Field<'a, S> {
/// Set the description of the field
///
/// 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
}
@ -546,7 +583,7 @@ impl<'a> Field<'a> {
/// Add an argument to the field
///
/// 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 {
None => {
self.arguments = Some(vec![argument]);
@ -562,15 +599,15 @@ impl<'a> Field<'a> {
/// Set the deprecation reason
///
/// 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
}
}
impl<'a> Argument<'a> {
impl<'a, S> Argument<'a, S> {
#[doc(hidden)]
pub fn new(name: &str, arg_type: Type<'a>) -> Argument<'a> {
pub fn new(name: &str, arg_type: Type<'a>) -> Self {
Argument {
name: name.to_owned(),
description: None,
@ -582,7 +619,7 @@ impl<'a> Argument<'a> {
/// Set the description of the argument
///
/// 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
}
@ -590,7 +627,7 @@ impl<'a> Argument<'a> {
/// Set the default value of the argument
///
/// 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
}
@ -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 {
fmt.debug_struct("ScalarMeta")
.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 {
fmt.debug_struct("EnumMeta")
.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 {
fmt.debug_struct("InputObjectMeta")
.field("name", &self.name)
@ -651,3 +688,11 @@ impl<'a> fmt::Debug for InputObjectMeta<'a> {
.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 types::base::GraphQLType;
use types::name::Name;
use value::{DefaultScalarValue, ScalarRefValue, ScalarValue};
/// Root query node of a schema
///
/// This brings the mutation and query types together, and provides the
/// 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)]
pub query_type: QueryT,
#[doc(hidden)]
@ -22,35 +28,37 @@ pub struct RootNode<'a, QueryT: GraphQLType, MutationT: GraphQLType> {
#[doc(hidden)]
pub mutation_info: MutationT::TypeInfo,
#[doc(hidden)]
pub schema: SchemaType<'a>,
pub schema: SchemaType<'a, S>,
}
/// Metadata for a schema
pub struct SchemaType<'a> {
types: FnvHashMap<Name, MetaType<'a>>,
#[derive(Debug)]
pub struct SchemaType<'a, S> {
pub(crate) types: FnvHashMap<Name, MetaType<'a, S>>,
query_type_name: 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)]
pub enum TypeType<'a> {
Concrete(&'a MetaType<'a>),
NonNull(Box<TypeType<'a>>),
List(Box<TypeType<'a>>),
pub enum TypeType<'a, S: 'a> {
Concrete(&'a MetaType<'a, S>),
NonNull(Box<TypeType<'a, S>>),
List(Box<TypeType<'a, S>>),
}
pub struct DirectiveType<'a> {
#[derive(Debug)]
pub struct DirectiveType<'a, S> {
pub name: String,
pub description: Option<String>,
pub locations: Vec<DirectiveLocation>,
pub arguments: Vec<Argument<'a>>,
pub arguments: Vec<Argument<'a, S>>,
}
#[derive(GraphQLEnum, Clone, PartialEq, Eq, Debug)]
#[graphql(name = "__DirectiveLocation", _internal)]
#[derive(Clone, PartialEq, Eq, Debug, GraphQLEnum)]
#[graphql(name = "__DirectiveLocation")]
pub enum DirectiveLocation {
Query,
Mutation,
@ -63,24 +71,31 @@ pub enum DirectiveLocation {
InlineFragment,
}
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
impl<'a, QueryT, MutationT, S> RootNode<'a, QueryT, MutationT, S>
where
QueryT: GraphQLType<TypeInfo = ()>,
MutationT: GraphQLType<TypeInfo = ()>,
S: ScalarValue + 'a,
QueryT: GraphQLType<S, TypeInfo = ()>,
MutationT: GraphQLType<S, TypeInfo = ()>,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// Construct a new root node from query and mutation nodes
///
/// If the schema should not support mutations, use the
/// `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, (), ())
}
}
impl<'a, QueryT, MutationT> RootNode<'a, QueryT, MutationT>
impl<'a, S, QueryT, MutationT> RootNode<'a, QueryT, MutationT, S>
where
QueryT: GraphQLType,
MutationT: GraphQLType,
QueryT: GraphQLType<S>,
MutationT: GraphQLType<S>,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// Construct a new root node from query and mutation nodes,
/// while also providing type info objects for the query and
@ -90,7 +105,10 @@ where
mutation_obj: MutationT,
query_info: QueryT::TypeInfo,
mutation_info: MutationT::TypeInfo,
) -> RootNode<'a, QueryT, MutationT> {
) -> Self
where
for<'b> &'b S: ScalarRefValue<'b>,
{
RootNode {
query_type: query_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>(
query_info: &QueryT::TypeInfo,
mutation_info: &MutationT::TypeInfo,
) -> SchemaType<'a>
) -> Self
where
QueryT: GraphQLType,
MutationT: GraphQLType,
S: ScalarValue + 'a,
QueryT: GraphQLType<S>,
MutationT: GraphQLType<S>,
for<'b> &'b S: ScalarRefValue<'b>,
{
let mut directives = FnvHashMap::default();
let query_type_name: String;
@ -119,12 +139,14 @@ impl<'a> SchemaType<'a> {
.get_type::<QueryT>(query_info)
.innermost_name()
.to_owned();
mutation_type_name = registry
.get_type::<MutationT>(mutation_info)
.innermost_name()
.to_owned();
registry.get_type::<SchemaType>(&());
registry.get_type::<SchemaType<S>>(&());
directives.insert("skip".to_owned(), DirectiveType::new_skip(&mut registry));
directives.insert(
"include".to_owned(),
@ -132,9 +154,9 @@ impl<'a> SchemaType<'a> {
);
let mut meta_fields = vec![
registry.field::<SchemaType>("__schema", &()),
registry.field::<SchemaType<S>>("__schema", &()),
registry
.field::<TypeType>("__type", &())
.field::<TypeType<S>>("__type", &())
.argument(registry.arg::<String>("name", &())),
];
@ -153,7 +175,6 @@ impl<'a> SchemaType<'a> {
panic!("Type {:?} is still a placeholder type", of_type);
}
}
SchemaType {
types: registry.types,
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);
}
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))
}
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)
}
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(
self.types
.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
.get(&self.query_type_name)
.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 {
Some(
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.concrete_type_by_name(name)
.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()
}
pub fn concrete_type_list(&self) -> Vec<&MetaType> {
pub fn concrete_type_list(&self) -> Vec<&MetaType<S>> {
self.types.values().collect()
}
pub fn make_type(&self, t: &Type) -> TypeType {
pub fn make_type(&self, t: &Type) -> TypeType<S> {
match *t {
Type::NonNullNamed(ref n) => TypeType::NonNull(Box::new(
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()
}
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)
}
pub fn type_overlap(&self, t1: &MetaType, t2: &MetaType) -> bool {
if (t1 as *const MetaType) == (t2 as *const MetaType) {
pub fn type_overlap(&self, t1: &MetaType<S>, t2: &MetaType<S>) -> bool {
if (t1 as *const MetaType<S>) == (t2 as *const MetaType<S>) {
return true;
}
match (t1.is_abstract(), t2.is_abstract()) {
(true, true) => self.possible_types(t1)
(true, true) => self
.possible_types(t1)
.iter()
.any(|t| self.is_possible_type(t2, t)),
(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 {
MetaType::Union(UnionMeta {
ref of_type_names, ..
@ -262,7 +293,8 @@ impl<'a> SchemaType<'a> {
.iter()
.flat_map(|t| self.concrete_type_by_name(t))
.collect(),
MetaType::Interface(InterfaceMeta { ref name, .. }) => self.concrete_type_list()
MetaType::Interface(InterfaceMeta { ref name, .. }) => self
.concrete_type_list()
.into_iter()
.filter(|t| match **t {
MetaType::Object(ObjectMeta {
@ -270,16 +302,19 @@ impl<'a> SchemaType<'a> {
..
}) => interface_names.iter().any(|iname| iname == name),
_ => false,
})
.collect(),
}).collect(),
_ => panic!("Can't retrieve possible types from non-abstract meta type"),
}
}
pub fn is_possible_type(&self, abstract_type: &MetaType, possible_type: &MetaType) -> bool {
pub fn is_possible_type(
&self,
abstract_type: &MetaType<S>,
possible_type: &MetaType<S>,
) -> bool {
self.possible_types(abstract_type)
.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 {
@ -318,9 +353,9 @@ impl<'a> SchemaType<'a> {
}
}
impl<'a> TypeType<'a> {
impl<'a, S> TypeType<'a, S> {
#[inline]
pub fn to_concrete(&self) -> Option<&'a MetaType> {
pub fn to_concrete(&self) -> Option<&'a MetaType<S>> {
match *self {
TypeType::Concrete(t) => Some(t),
_ => None,
@ -328,7 +363,7 @@ impl<'a> TypeType<'a> {
}
#[inline]
pub fn innermost_concrete(&self) -> &'a MetaType {
pub fn innermost_concrete(&self) -> &'a MetaType<S> {
match *self {
TypeType::Concrete(t) => t,
TypeType::NonNull(ref n) | TypeType::List(ref n) => n.innermost_concrete(),
@ -336,7 +371,7 @@ impl<'a> TypeType<'a> {
}
#[inline]
pub fn list_contents(&self) -> Option<&TypeType<'a>> {
pub fn list_contents(&self) -> Option<&TypeType<'a, S>> {
match *self {
TypeType::List(ref n) => Some(n),
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(
name: &str,
locations: &[DirectiveLocation],
arguments: &[Argument<'a>],
) -> DirectiveType<'a> {
arguments: &[Argument<'a, S>],
) -> DirectiveType<'a, S> {
DirectiveType {
name: name.to_owned(),
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(
"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(
"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
}
@ -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 {
match *self {
TypeType::Concrete(t) => f.write_str(t.name().unwrap()),

View file

@ -1,7 +1,7 @@
use ast::Selection;
use executor::{ExecutionResult, Executor, Registry};
use types::base::{Arguments, GraphQLType, TypeKind};
use value::Value;
use ast::Selection;
use value::{ScalarRefValue, ScalarValue, Value};
use schema::meta::{
Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, MetaType, ObjectMeta,
@ -9,10 +9,12 @@ use schema::meta::{
};
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
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
S: ScalarValue,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
for<'b> &'b S: ScalarRefValue<'b>,
{
type Context = CtxT;
type TypeInfo = QueryT::TypeInfo;
@ -21,7 +23,11 @@ where
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)
}
@ -29,9 +35,9 @@ where
&self,
info: &QueryT::TypeInfo,
field: &str,
args: &Arguments,
executor: &Executor<CtxT>,
) -> ExecutionResult {
args: &Arguments<S>,
executor: &Executor<CtxT, S>,
) -> ExecutionResult<S> {
match field {
"__schema" => executor
.replaced_context(&self.schema)
@ -49,11 +55,11 @@ where
fn resolve(
&self,
info: &Self::TypeInfo,
selection_set: Option<&[Selection]>,
executor: &Executor<Self::Context>,
) -> Value {
use value::Object;
selection_set: Option<&[Selection<S>]>,
executor: &Executor<Self::Context, S>,
) -> Value<S> {
use types::base::resolve_selection_set_into;
use value::Object;
if let Some(selection_set) = selection_set {
let mut result = Object::with_capacity(selection_set.len());
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| {
field types() -> Vec<TypeType> {
graphql_object!(<'a> SchemaType<'a, S>: SchemaType<'a, S> as "__Schema"
where Scalar = <S: 'a> |&self|
{
field types() -> Vec<TypeType<S>> {
self.type_list()
.into_iter()
.filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false))
.collect()
}
field query_type() -> TypeType {
field query_type() -> TypeType<S> {
self.query_type()
}
field mutation_type() -> Option<TypeType> {
field mutation_type() -> Option<TypeType<S>> {
self.mutation_type()
}
// Included for compatibility with the introspection query in GraphQL.js
field subscription_type() -> Option<TypeType> {
field subscription_type() -> Option<TypeType<S>> {
None
}
field directives() -> Vec<&DirectiveType> {
field directives() -> Vec<&DirectiveType<S>> {
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> {
match *self {
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 {
TypeType::Concrete(&MetaType::Interface(InterfaceMeta { 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 {
TypeType::Concrete(_) => None,
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 {
TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref 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 {
TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => {
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();
match *self {
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 {
&self.name
}
@ -207,11 +219,11 @@ graphql_object!(<'a> Field<'a>: SchemaType<'a> as "__Field" |&self| {
&self.description
}
field args() -> Vec<&Argument> {
field args() -> Vec<&Argument<S>> {
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)
}
@ -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 {
&self.name
}
@ -233,7 +247,7 @@ graphql_object!(<'a> Argument<'a>: SchemaType<'a> as "__InputValue" |&self| {
&self.description
}
field type(&executor) -> TypeType {
field type(&executor) -> TypeType<S> {
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 {
&self.name
}
@ -260,8 +274,10 @@ graphql_object!(EnumValue: () as "__EnumValue" |&self| {
}
});
graphql_object!(<'a> DirectiveType<'a>: SchemaType<'a> as "__Directive" |&self| {
field name() -> &String {
graphql_object!(<'a> DirectiveType<'a, S>: SchemaType<'a, S> as "__Directive"
where Scalar = <S: 'a> |&self|
{
field name() -> &String {
&self.name
}
@ -273,7 +289,7 @@ graphql_object!(<'a> DirectiveType<'a>: SchemaType<'a> as "__Directive" |&self|
&self.locations
}
field args() -> &Vec<Argument> {
field args() -> &Vec<Argument<S>> {
&self.arguments
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,14 +1,15 @@
use std::convert::From;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Deref;
use std::{char, u32};
use ast::{FromInputValue, InputValue, Selection, ToInputValue};
use value::Value;
use schema::meta::MetaType;
use executor::{Executor, Registry};
use parser::{LexerError, ParseError, ScalarToken, Token};
use schema::meta::MetaType;
use types::base::GraphQLType;
use value::{ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue, Value};
/// 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 {
Value::string(&self.0)
Value::scalar(self.0.clone())
}
from_input_value(v: &InputValue) -> Option<ID> {
match *v {
InputValue::String(ref s) => Some(ID(s.to_owned())),
InputValue::Int(i) => Some(ID(format!("{}", i))),
InputValue::Scalar(ref s) => {
s.as_string().or_else(|| s.as_int().map(|i| i.to_string()))
.map(ID)
}
_ => 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 {
Value::string(self)
Value::scalar(self.clone())
}
from_input_value(v: &InputValue) -> Option<String> {
match *v {
InputValue::String(ref s) => Some(s.clone()),
InputValue::Scalar(ref s) => s.as_string(),
_ => 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 TypeInfo = ();
@ -65,62 +167,106 @@ impl<'a> GraphQLType for &'a str {
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()
}
fn resolve(&self, _: &(), _: Option<&[Selection]>, _: &Executor<Self::Context>) -> Value {
Value::string(self)
fn resolve(
&self,
_: &(),
_: Option<&[Selection<S>]>,
_: &Executor<Self::Context, S>,
) -> Value<S> {
Value::scalar(String::from(*self))
}
}
impl<'a> ToInputValue for &'a str {
fn to_input_value(&self) -> InputValue {
InputValue::string(self)
impl<'a, S> ToInputValue<S> for &'a str
where
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 {
Value::boolean(*self)
Value::scalar(*self)
}
from_input_value(v: &InputValue) -> Option<bool> {
match *v {
InputValue::Boolean(b) => Some(b),
InputValue::Scalar(ref b) => b.as_boolean(),
_ => 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 {
Value::int(*self)
Value::scalar(*self)
}
from_input_value(v: &InputValue) -> Option<i32> {
match *v {
InputValue::Int(i) => Some(i),
InputValue::Scalar(ref i) => i.as_int(),
_ => 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 {
Value::float(*self)
Value::scalar(*self)
}
from_input_value(v: &InputValue) -> Option<f64> {
match *v {
InputValue::Int(i) => Some(f64::from(i)),
InputValue::Float(f) => Some(f),
InputValue::Scalar(ref s) => s.as_float(),
_ => 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 TypeInfo = ();
@ -128,21 +274,38 @@ impl GraphQLType for () {
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()
}
}
impl FromInputValue for () {
fn from_input_value(_: &InputValue) -> Option<()> {
impl<S> ParseScalarValue<S> for ()
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
}
}
/// 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
/// generated for the schema.
#[derive(Debug)]
pub struct EmptyMutation<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 TypeInfo = ();
@ -164,7 +331,11 @@ impl<T> GraphQLType for EmptyMutation<T> {
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()
}
}
@ -172,6 +343,8 @@ impl<T> GraphQLType for EmptyMutation<T> {
#[cfg(test)]
mod tests {
use super::ID;
use parser::ScalarToken;
use value::{DefaultScalarValue, ParseScalarValue};
#[test]
fn test_id_from_string() {
@ -185,4 +358,26 @@ mod tests {
let id = ID(String::from("foo"));
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::model::{SchemaType, TypeType};
use std::collections::HashSet;
use value::ScalarValue;
pub fn is_valid_literal_value(
schema: &SchemaType,
arg_type: &TypeType,
arg_value: &InputValue,
) -> bool {
pub fn is_valid_literal_value<S>(
schema: &SchemaType<S>,
arg_type: &TypeType<S>,
arg_value: &InputValue<S>,
) -> bool
where
S: ScalarValue,
{
match *arg_type {
TypeType::NonNull(ref inner) => if arg_value.is_null() {
false
@ -23,7 +27,7 @@ pub fn is_valid_literal_value(
TypeType::Concrete(t) => {
// Even though InputValue::String can be parsed into an enum, they
// are not valid as enum *literals* in a GraphQL query.
if let (&InputValue::String(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
if let (&InputValue::Scalar(_), Some(&MetaType::Enum(EnumMeta { .. }))) =
(arg_value, arg_type.to_concrete())
{
return false;
@ -31,10 +35,7 @@ pub fn is_valid_literal_value(
match *arg_value {
InputValue::Null | InputValue::Variable(_) => true,
ref v @ InputValue::Int(_)
| ref v @ InputValue::Float(_)
| ref v @ InputValue::String(_)
| ref v @ InputValue::Boolean(_)
ref v @ InputValue::Scalar(_)
| ref v @ InputValue::Enum(_) => if let Some(parse_fn) = t.input_value_parse_fn() {
parse_fn(v)
} else {

View file

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

View file

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

View file

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

View file

@ -4,250 +4,260 @@ use ast::{
};
use parser::Spanning;
use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[doc(hidden)]
pub trait MultiVisitor<'a> {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, f: F);
pub struct MultiVisitorNil;
fn with<V: Visitor<'a>>(self, visitor: V) -> MultiVisitorCons<V, Self>
where
Self: Sized,
{
impl MultiVisitorNil {
pub fn with<V>(self, visitor: V) -> MultiVisitorCons<V, 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)]
pub struct MultiVisitorCons<A, B>(A, B);
impl<'a, A: Visitor<'a>, B: MultiVisitor<'a>> MultiVisitor<'a> for MultiVisitorCons<A, B> {
fn visit_all<F: FnMut(&mut Visitor<'a>) -> ()>(&mut self, mut f: F) {
f(&mut self.0);
self.1.visit_all(f);
impl<A, B> MultiVisitorCons<A, B> {
pub fn with<V>(self, visitor: V) -> MultiVisitorCons<V, Self> {
MultiVisitorCons(visitor, self)
}
}
impl<'a, M> Visitor<'a> for M
where
M: MultiVisitor<'a>,
{
fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
self.visit_all(|v| v.enter_document(ctx, doc));
}
impl<'a, S> Visitor<'a, S> for MultiVisitorNil where S: ScalarValue {}
fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) {
self.visit_all(|v| v.exit_document(ctx, doc));
impl<'a, A, B, S> Visitor<'a, S> for MultiVisitorCons<A, B>
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
op: &'a Spanning<Operation>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<Fragment>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<Fragment>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
def: &'a (Spanning<&'a str>, VariableDefinition),
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
def: &'a (Spanning<&'a str>, VariableDefinition),
ctx: &mut ValidatorContext<'a, S>,
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>) {
self.visit_all(|v| v.enter_directive(ctx, d));
fn enter_directive(
&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>) {
self.visit_all(|v| v.exit_directive(ctx, d));
fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a, S>, d: &'a Spanning<Directive<S>>) {
self.0.exit_directive(ctx, d);
self.1.exit_directive(ctx, d);
}
fn enter_argument(
&mut self,
ctx: &mut ValidatorContext<'a>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
arg: &'a (Spanning<&'a str>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
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>) {
self.visit_all(|v| v.enter_selection_set(ctx, s));
fn enter_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec<Selection<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>) {
self.visit_all(|v| v.exit_selection_set(ctx, s));
fn exit_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec<Selection<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>) {
self.visit_all(|v| v.enter_field(ctx, f));
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning<Field<S>>) {
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>) {
self.visit_all(|v| v.exit_field(ctx, f));
fn exit_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning<Field<S>>) {
self.0.exit_field(ctx, f);
self.1.exit_field(ctx, f);
}
fn enter_fragment_spread(
&mut self,
ctx: &mut ValidatorContext<'a>,
s: &'a Spanning<FragmentSpread>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
s: &'a Spanning<FragmentSpread>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<InlineFragment>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a Spanning<InlineFragment>,
ctx: &mut ValidatorContext<'a, S>,
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<()>) {
self.visit_all(|v| v.enter_null_value(ctx, n.clone()));
fn enter_null_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<()>) {
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<()>) {
self.visit_all(|v| v.exit_null_value(ctx, n.clone()));
fn exit_null_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<()>) {
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>) {
self.visit_all(|v| v.enter_int_value(ctx, i.clone()));
fn enter_scalar_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<&'a S>) {
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>) {
self.visit_all(|v| v.exit_int_value(ctx, i.clone()));
fn exit_scalar_value(&mut self, ctx: &mut ValidatorContext<'a, S>, n: Spanning<&'a S>) {
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>) {
self.visit_all(|v| v.enter_float_value(ctx, f.clone()));
fn enter_enum_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
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>) {
self.visit_all(|v| v.exit_float_value(ctx, f.clone()));
fn exit_enum_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
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>) {
self.visit_all(|v| v.enter_string_value(ctx, s.clone()));
fn enter_variable_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
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>) {
self.visit_all(|v| v.exit_string_value(ctx, s.clone()));
}
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 exit_variable_value(&mut self, ctx: &mut ValidatorContext<'a, S>, s: Spanning<&'a String>) {
self.0.exit_variable_value(ctx, s);
self.1.exit_variable_value(ctx, s);
}
fn enter_list_value(
&mut self,
ctx: &mut ValidatorContext<'a>,
l: Spanning<&'a Vec<Spanning<InputValue>>>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
l: Spanning<&'a Vec<Spanning<InputValue>>>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
o: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a (Spanning<String>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
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(
&mut self,
ctx: &mut ValidatorContext<'a>,
f: &'a (Spanning<String>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
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 parser::Spanning;
use schema::meta::Argument;
use std::fmt::Debug;
use types::utilities::is_valid_literal_value;
use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct ArgumentsOfCorrectType<'a> {
current_args: Option<&'a Vec<Argument<'a>>>,
pub struct ArgumentsOfCorrectType<'a, S: Debug + '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 }
}
impl<'a> Visitor<'a> for ArgumentsOfCorrectType<'a> {
impl<'a, S> Visitor<'a, S> for ArgumentsOfCorrectType<'a, S>
where
S: ScalarValue,
{
fn enter_directive(
&mut self,
ctx: &mut ValidatorContext<'a>,
directive: &'a Spanning<Directive>,
ctx: &mut ValidatorContext<'a, S>,
directive: &'a Spanning<Directive<S>>,
) {
self.current_args = ctx.schema
self.current_args = ctx
.schema
.directive_by_name(directive.item.name.item)
.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;
}
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning<Field>) {
self.current_args = ctx.parent_type()
fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning<Field<S>>) {
self.current_args = ctx
.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item))
.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;
}
fn enter_argument(
&mut self,
ctx: &mut ValidatorContext<'a>,
&(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
&(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))
{
let meta_type = ctx.schema.make_type(&argument_meta.arg_type);
@ -70,10 +78,11 @@ mod tests {
use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test]
fn good_null_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -87,7 +96,7 @@ mod tests {
#[test]
fn null_into_int() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -105,7 +114,7 @@ mod tests {
#[test]
fn good_int_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -119,7 +128,7 @@ mod tests {
#[test]
fn good_boolean_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -133,7 +142,7 @@ mod tests {
#[test]
fn good_string_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -147,7 +156,7 @@ mod tests {
#[test]
fn good_float_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -161,7 +170,7 @@ mod tests {
#[test]
fn int_into_float() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -175,7 +184,7 @@ mod tests {
#[test]
fn int_into_id() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -189,7 +198,7 @@ mod tests {
#[test]
fn string_into_id() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -203,7 +212,7 @@ mod tests {
#[test]
fn good_enum_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -217,7 +226,7 @@ mod tests {
#[test]
fn int_into_string() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -235,7 +244,7 @@ mod tests {
#[test]
fn float_into_string() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -253,7 +262,7 @@ mod tests {
#[test]
fn boolean_into_string() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -271,7 +280,7 @@ mod tests {
#[test]
fn unquoted_string_into_string() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -289,7 +298,7 @@ mod tests {
#[test]
fn string_into_int() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -307,7 +316,7 @@ mod tests {
#[test]
fn unquoted_string_into_int() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -325,7 +334,7 @@ mod tests {
#[test]
fn simple_float_into_int() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -343,7 +352,7 @@ mod tests {
#[test]
fn float_into_int() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -361,7 +370,7 @@ mod tests {
#[test]
fn string_into_float() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -379,7 +388,7 @@ mod tests {
#[test]
fn boolean_into_float() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -397,7 +406,7 @@ mod tests {
#[test]
fn unquoted_into_float() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -415,7 +424,7 @@ mod tests {
#[test]
fn int_into_boolean() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -433,7 +442,7 @@ mod tests {
#[test]
fn float_into_boolean() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -451,7 +460,7 @@ mod tests {
#[test]
fn string_into_boolean() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -469,7 +478,7 @@ mod tests {
#[test]
fn unquoted_into_boolean() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -487,7 +496,7 @@ mod tests {
#[test]
fn float_into_id() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -505,7 +514,7 @@ mod tests {
#[test]
fn boolean_into_id() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -523,7 +532,7 @@ mod tests {
#[test]
fn unquoted_into_id() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -541,7 +550,7 @@ mod tests {
#[test]
fn int_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -559,7 +568,7 @@ mod tests {
#[test]
fn float_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -577,7 +586,7 @@ mod tests {
#[test]
fn string_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -595,7 +604,7 @@ mod tests {
#[test]
fn boolean_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -613,7 +622,7 @@ mod tests {
#[test]
fn unknown_enum_value_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -631,7 +640,7 @@ mod tests {
#[test]
fn different_case_enum_value_into_enum() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -649,7 +658,7 @@ mod tests {
#[test]
fn good_list_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -663,7 +672,7 @@ mod tests {
#[test]
fn empty_list_value() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -677,7 +686,7 @@ mod tests {
#[test]
fn single_value_into_list() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -691,7 +700,7 @@ mod tests {
#[test]
fn incorrect_item_type() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -709,7 +718,7 @@ mod tests {
#[test]
fn single_value_of_incorrect_type() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -727,7 +736,7 @@ mod tests {
#[test]
fn arg_on_optional_arg() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -741,7 +750,7 @@ mod tests {
#[test]
fn no_arg_on_optional_arg() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -755,7 +764,7 @@ mod tests {
#[test]
fn multiple_args() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -769,7 +778,7 @@ mod tests {
#[test]
fn multiple_args_reverse_order() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -783,7 +792,7 @@ mod tests {
#[test]
fn no_args_on_multiple_optional() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -797,7 +806,7 @@ mod tests {
#[test]
fn one_arg_on_multiple_optional() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -811,7 +820,7 @@ mod tests {
#[test]
fn second_arg_on_multiple_optional() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -825,7 +834,7 @@ mod tests {
#[test]
fn multiple_reqs_on_mixed_list() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -839,7 +848,7 @@ mod tests {
#[test]
fn multiple_reqs_and_one_opt_on_mixed_list() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -853,7 +862,7 @@ mod tests {
#[test]
fn all_reqs_and_opts_on_mixed_list() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -867,7 +876,7 @@ mod tests {
#[test]
fn incorrect_value_type() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -891,7 +900,7 @@ mod tests {
#[test]
fn incorrect_value_and_missing_argument() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -909,7 +918,7 @@ mod tests {
#[test]
fn optional_arg_despite_required_field_in_type() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -923,7 +932,7 @@ mod tests {
#[test]
fn partial_object_only_required() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -937,7 +946,7 @@ mod tests {
#[test]
fn partial_object_required_field_can_be_falsy() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -951,7 +960,7 @@ mod tests {
#[test]
fn partial_object_including_required() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -965,7 +974,7 @@ mod tests {
#[test]
fn full_object() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -985,7 +994,7 @@ mod tests {
#[test]
fn full_object_with_fields_in_different_order() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -1005,7 +1014,7 @@ mod tests {
#[test]
fn partial_object_missing_required() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -1023,7 +1032,7 @@ mod tests {
#[test]
fn partial_object_invalid_field_type() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -1044,7 +1053,7 @@ mod tests {
#[test]
fn partial_object_unknown_field_arg() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -1065,7 +1074,7 @@ mod tests {
#[test]
fn directive_with_valid_types() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -1082,7 +1091,7 @@ mod tests {
#[test]
fn directive_with_incorrect_types() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,7 @@ use std::collections::hash_map::{Entry, HashMap};
use ast::Fragment;
use parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueFragmentNames<'a> {
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(
&mut self,
context: &mut ValidatorContext<'a>,
f: &'a Spanning<Fragment>,
context: &mut ValidatorContext<'a, S>,
f: &'a Spanning<Fragment<S>>,
) {
match self.names.entry(f.item.name.item) {
Entry::Occupied(e) => {
@ -44,14 +49,17 @@ mod tests {
use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test]
fn no_fragments() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
field
dog {
name
}
}
"#,
);
@ -59,15 +67,17 @@ mod tests {
#[test]
fn one_fragment() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
...fragA
dog {
...fragA
}
}
fragment fragA on Type {
field
fragment fragA on Dog {
name
}
"#,
);
@ -75,22 +85,24 @@ mod tests {
#[test]
fn many_fragments() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
...fragA
...fragB
...fragC
dog {
...fragA
...fragB
...fragC
}
}
fragment fragA on Type {
fieldA
fragment fragA on Dog {
name
}
fragment fragB on Type {
fieldB
fragment fragB on Dog {
nickname
}
fragment fragC on Type {
fieldC
fragment fragC on Dog {
barkVolume
}
"#,
);
@ -98,15 +110,17 @@ mod tests {
#[test]
fn inline_fragments_always_unique() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
...on Type {
fieldA
}
...on Type {
fieldB
dorOrHuman {
...on Dog {
name
}
...on Dog {
barkVolume
}
}
}
"#,
@ -115,14 +129,16 @@ mod tests {
#[test]
fn fragment_and_operation_named_the_same() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
query Foo {
...Foo
dog {
...Foo
}
}
fragment Foo on Type {
field
fragment Foo on Dog {
name
}
"#,
);
@ -130,24 +146,26 @@ mod tests {
#[test]
fn fragments_named_the_same() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
...fragA
dog {
...fragA
}
}
fragment fragA on Type {
fieldA
fragment fragA on Dog {
name
}
fragment fragA on Type {
fieldB
fragment fragA on Dog {
barkVolume
}
"#,
&[RuleError::new(
&duplicate_message("fragA"),
&[
SourcePosition::new(65, 4, 19),
SourcePosition::new(131, 7, 19),
SourcePosition::new(99, 6, 19),
SourcePosition::new(162, 9, 19),
],
)],
);
@ -155,21 +173,21 @@ mod tests {
#[test]
fn fragments_named_the_same_no_reference() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
fragment fragA on Type {
fieldA
fragment fragA on Dog {
name
}
fragment fragA on Type {
fieldB
fragment fragA on Dog {
barkVolume
}
"#,
&[RuleError::new(
&duplicate_message("fragA"),
&[
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 parser::{SourcePosition, Spanning};
use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
pub struct UniqueInputFieldNames<'a> {
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(
&mut self,
_: &mut ValidatorContext<'a>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
_: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) {
self.known_name_stack.push(HashMap::new());
}
fn exit_object_value(
&mut self,
_: &mut ValidatorContext<'a>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue>)>>,
_: &mut ValidatorContext<'a, S>,
_: Spanning<&'a Vec<(Spanning<String>, Spanning<InputValue<S>>)>>,
) {
self.known_name_stack.pop();
}
fn enter_object_field(
&mut self,
ctx: &mut ValidatorContext<'a>,
&(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue>),
ctx: &mut ValidatorContext<'a, S>,
&(ref field_name, _): &'a (Spanning<String>, Spanning<InputValue<S>>),
) {
if let Some(ref mut known_names) = self.known_name_stack.last_mut() {
match known_names.entry(&field_name.item) {
@ -62,10 +66,11 @@ mod tests {
use parser::SourcePosition;
use validation::{expect_fails_rule, expect_passes_rule, RuleError};
use value::DefaultScalarValue;
#[test]
fn input_object_with_fields() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -77,7 +82,7 @@ mod tests {
#[test]
fn same_input_object_within_two_args() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -89,7 +94,7 @@ mod tests {
#[test]
fn multiple_input_object_fields() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -101,7 +106,7 @@ mod tests {
#[test]
fn allows_for_nested_input_objects_with_similar_fields() {
expect_passes_rule(
expect_passes_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -121,7 +126,7 @@ mod tests {
#[test]
fn duplicate_input_object_fields() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{
@ -140,7 +145,7 @@ mod tests {
#[test]
fn many_duplicate_input_object_fields() {
expect_fails_rule(
expect_fails_rule::<_, _, DefaultScalarValue>(
factory,
r#"
{

View file

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

View file

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

View file

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

View file

@ -4,8 +4,9 @@ use parser::parse_document_source;
use schema::meta::{EnumValue, MetaType};
use schema::model::{DirectiveLocation, DirectiveType, RootNode};
use types::base::GraphQLType;
use types::scalars::{EmptyMutation, ID};
use validation::{visit, MultiVisitor, MultiVisitorNil, RuleError, ValidatorContext, Visitor};
use types::scalars::ID;
use validation::{visit, MultiVisitorNil, RuleError, ValidatorContext, Visitor};
use value::{ScalarRefValue, ScalarValue};
struct Being;
struct Pet;
@ -24,7 +25,15 @@ struct HumanOrAlien;
struct ComplicatedArgs;
struct QueryRoot;
pub(crate) struct QueryRoot;
#[derive(Debug, GraphQLInputObject)]
struct TestInput {
id: i32,
name: String,
}
pub(crate) struct MutationRoot;
#[derive(Debug)]
enum DogCommand {
@ -51,7 +60,11 @@ struct ComplexInput {
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 TypeInfo = ();
@ -59,7 +72,10 @@ impl GraphQLType for 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
.field::<Option<String>>("name", 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 TypeInfo = ();
@ -76,7 +96,10 @@ impl GraphQLType for 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
.field::<Option<String>>("name", 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 TypeInfo = ();
@ -93,7 +120,10 @@ impl GraphQLType for 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
.field::<Option<String>>("name", 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 TypeInfo = ();
@ -110,7 +144,10 @@ impl GraphQLType for 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
.build_enum_type::<Self>(
i,
@ -119,13 +156,18 @@ impl GraphQLType for DogCommand {
EnumValue::new("HEEL"),
EnumValue::new("DOWN"),
],
)
.into_meta()
).into_meta()
}
}
impl FromInputValue for DogCommand {
fn from_input_value(v: &InputValue) -> Option<DogCommand> {
impl<S> FromInputValue<S> for 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() {
Some("SIT") => Some(DogCommand::Sit),
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 TypeInfo = ();
@ -143,7 +189,10 @@ impl GraphQLType for 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 = &[
registry
.field::<Option<String>>("name", i)
@ -169,12 +218,15 @@ impl GraphQLType for Dog {
registry.get_type::<Being>(i),
registry.get_type::<Pet>(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 TypeInfo = ();
@ -182,7 +234,10 @@ impl GraphQLType for 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
.build_enum_type::<Self>(
i,
@ -192,13 +247,20 @@ impl GraphQLType for FurColor {
EnumValue::new("TAN"),
EnumValue::new("SPOTTED"),
],
)
.into_meta()
).into_meta()
}
}
impl FromInputValue for FurColor {
fn from_input_value(v: &InputValue) -> Option<FurColor> {
impl<S> FromInputValue<S> for 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() {
Some("BROWN") => Some(FurColor::Brown),
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 TypeInfo = ();
@ -217,7 +283,10 @@ impl GraphQLType for 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 = &[
registry
.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 TypeInfo = ();
@ -243,14 +316,21 @@ impl GraphQLType for 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)];
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 TypeInfo = ();
@ -258,14 +338,21 @@ impl GraphQLType for 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)];
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 TypeInfo = ();
@ -273,7 +360,10 @@ impl GraphQLType for 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 = &[
registry
.field::<Option<String>>("name", i)
@ -287,12 +377,15 @@ impl GraphQLType for Human {
.interfaces(&[
registry.get_type::<Being>(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 TypeInfo = ();
@ -300,7 +393,10 @@ impl GraphQLType for 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 = &[
registry
.field::<Option<String>>("name", i)
@ -314,12 +410,15 @@ impl GraphQLType for Alien {
.interfaces(&[
registry.get_type::<Being>(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 TypeInfo = ();
@ -327,14 +426,21 @@ impl GraphQLType for 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)];
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 TypeInfo = ();
@ -342,14 +448,21 @@ impl GraphQLType for 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)];
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 TypeInfo = ();
@ -357,7 +470,10 @@ impl GraphQLType for 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 = &[
registry.arg::<bool>("requiredField", i),
registry.arg::<Option<i32>>("intField", i),
@ -372,8 +488,15 @@ impl GraphQLType for ComplexInput {
}
}
impl FromInputValue for ComplexInput {
fn from_input_value(v: &InputValue) -> Option<ComplexInput> {
impl<S> FromInputValue<S> for 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() {
Some(o) => o,
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 TypeInfo = ();
@ -400,7 +527,10 @@ impl GraphQLType for 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 = &[
registry
.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 TypeInfo = ();
@ -457,7 +591,10 @@ impl GraphQLType for 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 = &[
registry
.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
R: GraphQLType<TypeInfo = ()>,
V: Visitor<'a> + 'a,
S: ScalarValue,
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,
{
let mut root = RootNode::new(r, EmptyMutation::<()>::new());
let mut root = RootNode::new(r, m);
root.schema.add_directive(DirectiveType::new(
"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 mv = MultiVisitorNil.with(factory());
@ -524,21 +696,26 @@ where
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
V: Visitor<'a> + 'a,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
V: Visitor<'a, S> + 'a,
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
R: GraphQLType<TypeInfo = ()>,
V: Visitor<'a> + 'a,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
Q: GraphQLType<S, TypeInfo = ()>,
M: GraphQLType<S, TypeInfo = ()>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V,
{
let errs = validate(r, q, factory);
let errs = validate(r, m, q, factory);
if !errs.is_empty() {
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
V: Visitor<'a> + 'a,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
V: Visitor<'a, S> + 'a,
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>(
r: R,
pub fn expect_fails_rule_with_schema<'a, Q, M, V, F, S>(
r: Q,
m: M,
factory: F,
q: &'a str,
expected_errors: &[RuleError],
) where
R: GraphQLType<TypeInfo = ()>,
V: Visitor<'a> + 'a,
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
Q: GraphQLType<S, TypeInfo = ()>,
M: GraphQLType<S, TypeInfo = ()>,
V: Visitor<'a, S> + 'a,
F: Fn() -> V,
{
let errs = validate(r, q, factory);
let errs = validate(r, m, q, factory);
if errs.is_empty() {
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());
}
println!("{}", err.message());
}
}

View file

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

View file

@ -6,20 +6,32 @@ use ast::{
};
use parser::Spanning;
use schema::meta::Argument;
use validation::multi_visitor::MultiVisitorCons;
use validation::{ValidatorContext, Visitor};
use value::ScalarValue;
#[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);
visit_definitions(v, ctx, d);
v.exit_document(ctx, d);
}
fn visit_definitions<'a, V: Visitor<'a>>(
fn visit_definitions<'a, S, V>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
d: &'a Vec<Definition>,
) {
ctx: &mut ValidatorContext<'a, S>,
d: &'a Vec<Definition<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
for def in d {
let def_type = match *def {
Definition::Fragment(Spanning {
@ -47,7 +59,8 @@ fn visit_definitions<'a, V: Visitor<'a>>(
..
},
..
}) => ctx.schema
}) => ctx
.schema
.concrete_mutation_type()
.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>>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
def: &'a Definition,
) {
fn enter_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
where
S: ScalarValue,
V: Visitor<'a, S>,
{
match *def {
Definition::Operation(ref op) => v.enter_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.enter_fragment_definition(ctx, f),
}
}
fn exit_definition<'a, V: Visitor<'a>>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
def: &'a Definition,
) {
fn exit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
where
S: ScalarValue,
V: Visitor<'a, S>,
{
match *def {
Definition::Operation(ref op) => v.exit_operation_definition(ctx, op),
Definition::Fragment(ref f) => v.exit_fragment_definition(ctx, f),
}
}
fn visit_definition<'a, V: Visitor<'a>>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
def: &'a Definition,
) {
fn visit_definition<'a, S, V>(v: &mut V, ctx: &mut ValidatorContext<'a, S>, def: &'a Definition<S>)
where
S: ScalarValue,
V: Visitor<'a, S>,
{
match *def {
Definition::Operation(ref op) => {
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,
ctx: &mut ValidatorContext<'a>,
defs: &'a Option<Spanning<VariableDefinitions>>,
) {
ctx: &mut ValidatorContext<'a, S>,
defs: &'a Option<Spanning<VariableDefinitions<S>>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref defs) = *defs {
for def in defs.item.iter() {
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,
ctx: &mut ValidatorContext<'a>,
directives: &'a Option<Vec<Spanning<Directive>>>,
) {
ctx: &mut ValidatorContext<'a, S>,
directives: &'a Option<Vec<Spanning<Directive<S>>>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref directives) = *directives {
for directive in directives {
let directive_arguments = ctx.schema
let directive_arguments = ctx
.schema
.directive_by_name(directive.item.name.item)
.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,
ctx: &mut ValidatorContext<'a>,
meta_args: &Option<&Vec<Argument<'a>>>,
arguments: &'a Option<Spanning<Arguments>>,
) {
ctx: &mut ValidatorContext<'a, S>,
meta_args: &Option<&Vec<Argument<'a, S>>>,
arguments: &'a Option<Spanning<Arguments<S>>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
if let Some(ref arguments) = *arguments {
for argument in arguments.item.iter() {
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,
ctx: &mut ValidatorContext<'a>,
selection_set: &'a Vec<Selection>,
) {
ctx: &mut ValidatorContext<'a, S>,
selection_set: &'a Vec<Selection<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
ctx.with_pushed_parent_type(|ctx| {
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,
ctx: &mut ValidatorContext<'a>,
selection: &'a Selection,
) {
ctx: &mut ValidatorContext<'a, S>,
selection: &'a Selection<S>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
match *selection {
Selection::Field(ref field) => visit_field(v, ctx, field),
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,
ctx: &mut ValidatorContext<'a>,
field: &'a Spanning<Field>,
) {
let meta_field = ctx.parent_type()
ctx: &mut ValidatorContext<'a, S>,
field: &'a Spanning<Field<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
let meta_field = ctx
.parent_type()
.and_then(|t| t.field_by_name(field.item.name.item));
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,
ctx: &mut ValidatorContext<'a>,
spread: &'a Spanning<FragmentSpread>,
) {
ctx: &mut ValidatorContext<'a, S>,
spread: &'a Spanning<FragmentSpread<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
v.enter_fragment_spread(ctx, spread);
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);
}
fn visit_inline_fragment<'a, V: Visitor<'a>>(
fn visit_inline_fragment<'a, S, V>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
fragment: &'a Spanning<InlineFragment>,
) {
let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| {
ctx: &mut ValidatorContext<'a, S>,
fragment: &'a Spanning<InlineFragment<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
let mut visit_fn = move |ctx: &mut ValidatorContext<'a, S>| {
v.enter_inline_fragment(ctx, fragment);
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,
ctx: &mut ValidatorContext<'a>,
input_value: &'a Spanning<InputValue>,
) {
ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
enter_input_value(v, ctx, input_value);
match input_value.item {
InputValue::Object(ref fields) => for field in fields {
let inner_type = ctx.current_input_type_literal()
let inner_type = ctx
.current_input_type_literal()
.and_then(|t| match *t {
Type::NonNullNamed(ref name) | Type::Named(ref name) => {
ctx.schema.concrete_type_by_name(name)
}
_ => 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);
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);
}
fn enter_input_value<'a, V: Visitor<'a>>(
fn enter_input_value<'a, S, V>(
v: &mut V,
ctx: &mut ValidatorContext<'a>,
input_value: &'a Spanning<InputValue>,
) {
ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
use InputValue::*;
let start = &input_value.start;
@ -312,10 +357,7 @@ fn enter_input_value<'a, V: Visitor<'a>>(
match input_value.item {
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)),
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)),
Scalar(ref s) => v.enter_scalar_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)),
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,
ctx: &mut ValidatorContext<'a>,
input_value: &'a Spanning<InputValue>,
) {
ctx: &mut ValidatorContext<'a, S>,
input_value: &'a Spanning<InputValue<S>>,
) where
S: ScalarValue,
V: Visitor<'a, S>,
{
use InputValue::*;
let start = &input_value.start;
@ -335,10 +380,7 @@ fn exit_input_value<'a, V: Visitor<'a>>(
match input_value.item {
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)),
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)),
Scalar(ref s) => v.exit_scalar_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)),
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 syn;
use syn::{Data, DeriveInput, Fields, Ident, Meta, NestedMeta, Variant};
use syn::{Data, DeriveInput, Fields, Ident, Variant};
use util::*;
@ -9,7 +9,6 @@ use util::*;
struct EnumAttrs {
name: Option<String>,
description: Option<String>,
internal: bool,
}
impl EnumAttrs {
@ -17,8 +16,6 @@ impl EnumAttrs {
let mut res = EnumAttrs {
name: None,
description: None,
/// Flag to specify whether the calling crate is the "juniper" crate itself.
internal: false,
};
// Check doc comments for description.
@ -27,7 +24,9 @@ impl EnumAttrs {
// Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) {
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) {
res.name = Some(val);
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);
continue;
}
match item {
NestedMeta::Meta(Meta::Word(ref ident)) => {
if ident == "_internal" {
res.internal = true;
continue;
}
}
_ => {}
}
panic!(format!(
"Unknown attribute for #[derive(GraphQLEnum)]: {:?}",
item
@ -78,7 +70,9 @@ impl EnumVariantAttrs {
// Check attributes for name and description.
if let Some(items) = get_graphql_attr(&variant.attrs) {
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) {
res.name = Some(val);
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);
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);
continue;
}
@ -166,7 +164,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
// Build resolve match clause.
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.
@ -177,12 +175,15 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
// Build to_input clause.
to_inputs.extend(quote!{
&#ident::#var_ident =>
_juniper::InputValue::string(#name.to_string()),
_juniper::InputValue::scalar(#name.to_string()),
});
}
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 TypeInfo = ();
@ -190,8 +191,9 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
Some(#name)
}
fn meta<'r>(_: &(), registry: &mut _juniper::Registry<'r>)
-> _juniper::meta::MetaType<'r>
fn meta<'r>(_: &(), registry: &mut _juniper::Registry<'r, __S>)
-> _juniper::meta::MetaType<'r, __S>
where __S: 'r,
{
let meta = registry.build_enum_type::<#ident>(&(), &[
#(#values)*
@ -203,26 +205,30 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
fn resolve(
&self,
_: &(),
_: Option<&[_juniper::Selection]>,
_: &_juniper::Executor<Self::Context>
) -> _juniper::Value {
_: Option<&[_juniper::Selection<__S>]>,
_: &_juniper::Executor<Self::Context, __S>
) -> _juniper::Value<__S> {
match self {
#(#resolves)*
}
}
}
impl _juniper::FromInputValue for #ident {
fn from_input_value(v: &_juniper::InputValue) -> Option<#ident> {
match v.as_enum_value().or_else(|| v.as_string_value()) {
impl<__S: _juniper::ScalarValue> _juniper::FromInputValue<__S> for #ident {
fn from_input_value(v: &_juniper::InputValue<__S>) -> Option<#ident>
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)*
_ => None,
}
}
}
impl _juniper::ToInputValue for #ident {
fn to_input_value(&self) -> _juniper::InputValue {
impl<__S: _juniper::ScalarValue> _juniper::ToInputValue<__S> for #ident {
fn to_input_value(&self) -> _juniper::InputValue<__S> {
match self {
#(#to_inputs)*
}
@ -235,35 +241,13 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream {
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! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)]
const #dummy_const : () = {
#crate_reference
mod _juniper {
__juniper_use_everything!();
}
#body
};
};

View file

@ -10,7 +10,7 @@ use util::*;
struct ObjAttrs {
name: Option<String>,
description: Option<String>,
internal: bool,
scalar: Option<Ident>,
}
impl ObjAttrs {
@ -23,7 +23,9 @@ impl ObjAttrs {
// Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) {
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) {
res.name = Some(val);
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);
continue;
}
match item {
NestedMeta::Meta(Meta::Word(ref ident)) => {
if ident == "_internal" {
res.internal = true;
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!(
"Unknown attribute for #[derive(GraphQLInputObject)]: {:?}",
@ -75,7 +76,9 @@ impl ObjFieldAttrs {
// Check attributes for name and description.
if let Some(items) = get_graphql_attr(&variant.attrs) {
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) {
res.name = Some(val);
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);
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);
continue;
}
match item {
NestedMeta::Meta(Meta::Word(ref ident)) => {
if ident == "default" {
@ -225,8 +233,8 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
// TODO: investigate the unwraps here, they seem dangerous!
match obj.get(#name) {
#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())
.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! {
impl #generics _juniper::GraphQLType for #ident #generics {
impl#impl_generics _juniper::GraphQLType<#scalar> for #ident #ty_generics
#where_clause
{
type Context = ();
type TypeInfo = ();
@ -251,8 +283,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
fn meta<'r>(
_: &(),
registry: &mut _juniper::Registry<'r>
) -> _juniper::meta::MetaType<'r> {
registry: &mut _juniper::Registry<'r, #scalar>
) -> _juniper::meta::MetaType<'r, #scalar>
where #scalar: 'r
{
let fields = &[
#(#meta_fields)*
];
@ -262,8 +296,13 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
}
}
impl #generics _juniper::FromInputValue for #ident #generics {
fn from_input_value(value: &_juniper::InputValue) -> Option<#ident #generics> {
impl#impl_generics _juniper::FromInputValue<#scalar> for #ident #ty_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() {
let item = #ident {
#(#from_inputs)*
@ -276,8 +315,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
}
}
impl #generics _juniper::ToInputValue for #ident #generics {
fn to_input_value(&self) -> _juniper::InputValue {
impl#impl_generics _juniper::ToInputValue<#scalar> for #ident #ty_generics
#where_clause
{
fn to_input_value(&self) -> _juniper::InputValue<#scalar> {
_juniper::InputValue::object(vec![
#(#to_inputs)*
].into_iter().collect())
@ -290,32 +331,13 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream {
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! {
#[allow(non_upper_case_globals, unused_attributes, unused_qualifications)]
#[doc(hidden)]
const #dummy_const : () = {
#crate_reference
mod _juniper {
__juniper_use_everything!();
}
#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::{Data, DeriveInput, Field, Fields};
use syn::{Data, DeriveInput, Field, Fields, Ident};
use util::*;
@ -8,6 +8,7 @@ use util::*;
struct ObjAttrs {
name: Option<String>,
description: Option<String>,
scalar: Option<Ident>,
}
impl ObjAttrs {
@ -20,7 +21,9 @@ impl ObjAttrs {
// Check attributes for name and description.
if let Some(items) = get_graphql_attr(&input.attrs) {
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) {
res.name = Some(val);
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);
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!(
"Unknown object attribute for #[derive(GraphQLObject)]: {:?}",
item
@ -63,7 +74,9 @@ impl ObjFieldAttrs {
// Check attributes.
if let Some(items) = get_graphql_attr(&variant.attrs) {
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) {
res.name = Some(val);
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);
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);
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! {
impl #generics ::juniper::GraphQLType for #ident #generics {
impl#impl_generics juniper::GraphQLType<#scalar> for #ident #ty_generics
#where_clause
{
type Context = ();
type TypeInfo = ();
@ -185,8 +227,10 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
fn meta<'r>(
_: &(),
registry: &mut ::juniper::Registry<'r>
) -> ::juniper::meta::MetaType<'r> {
registry: &mut juniper::Registry<'r, #scalar>
) -> juniper::meta::MetaType<'r, #scalar>
where #scalar: 'r
{
let fields = &[
#(#meta_fields)*
];
@ -199,9 +243,9 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream {
&self,
_: &(),
field_name: &str,
_: &::juniper::Arguments,
executor: &::juniper::Executor<Self::Context>
) -> ::juniper::ExecutionResult
_: &juniper::Arguments<#scalar>,
executor: &juniper::Executor<Self::Context, #scalar>
) -> juniper::ExecutionResult<#scalar>
{
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;
#[macro_use]
extern crate quote;
#[macro_use]
extern crate syn;
#[macro_use]
extern crate lazy_static;
@ -18,6 +19,7 @@ extern crate regex;
mod derive_enum;
mod derive_input_object;
mod derive_object;
mod derive_juniper_scalar_value;
mod util;
use proc_macro::TokenStream;
@ -42,3 +44,10 @@ pub fn derive_object(input: TokenStream) -> TokenStream {
let gen = derive_object::impl_object(&ast);
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::{header, Body, Method, Request, Response, StatusCode};
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 std::error::Error;
use std::fmt;
@ -25,15 +26,17 @@ use std::sync::Arc;
use tokio::prelude::*;
use url::form_urlencoded;
pub fn graphql<CtxT, QueryT, MutationT>(
root_node: Arc<RootNode<'static, QueryT, MutationT>>,
pub fn graphql<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>,
request: Request<Body>,
) -> impl Future<Item = Response<Body>, Error = hyper::Error>
where
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static,
QueryT: GraphQLType<Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<Context = CtxT> + Send + Sync + 'static,
QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::TypeInfo: Send + Sync,
MutationT::TypeInfo: Send + Sync,
{
@ -63,7 +66,7 @@ where
String::from_utf8(chunk.iter().cloned().collect::<Vec<u8>>())
.map_err(GraphQLRequestError::BodyUtf8)
.and_then(|input| {
serde_json::from_str::<GraphQLRequest>(&input)
serde_json::from_str::<GraphQLRequest<S>>(&input)
.map_err(GraphQLRequestError::BodyJSONError)
})
})
@ -93,15 +96,17 @@ fn render_error(err: GraphQLRequestError) -> Response<Body> {
resp
}
fn execute_request<CtxT, QueryT, MutationT>(
root_node: Arc<RootNode<'static, QueryT, MutationT>>,
fn execute_request<CtxT, QueryT, MutationT, S>(
root_node: Arc<RootNode<'static, QueryT, MutationT, S>>,
context: Arc<CtxT>,
request: GraphQLRequest,
request: GraphQLRequest<S>,
) -> impl Future<Item = Response<Body>, Error = tokio_threadpool::BlockingError>
where
S: ScalarValue + Send + Sync + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxT: Send + Sync + 'static,
QueryT: GraphQLType<Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<Context = CtxT> + Send + Sync + 'static,
QueryT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
MutationT: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
QueryT::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 operation_name = None;
let mut variables = None;
@ -142,7 +150,7 @@ fn gql_request_from_get(input: &str) -> Result<JuniperGraphQLRequest, GraphQLReq
if variables.is_some() {
return Err(invalid_err("variables"));
}
match serde_json::from_str::<InputValue>(&value)
match serde_json::from_str::<InputValue<S>>(&value)
.map_err(GraphQLRequestError::Variables)
{
Ok(parsed_variables) => variables = Some(parsed_variables),
@ -184,20 +192,29 @@ fn new_html_response(code: StatusCode) -> Response<Body> {
#[derive(Deserialize)]
#[serde(untagged)]
enum GraphQLRequest {
Single(JuniperGraphQLRequest),
Batch(Vec<JuniperGraphQLRequest>),
#[serde(bound = "InputValue<S>: Deserialize<'de>")]
enum GraphQLRequest<S = DefaultScalarValue>
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>(
self,
root_node: Arc<RootNode<'a, QueryT, MutationT>>,
root_node: Arc<RootNode<'a, QueryT, MutationT, S>>,
context: Arc<CtxT>,
) -> impl Future<Item = (bool, hyper::Body), Error = tokio_threadpool::BlockingError> + 'a
where
QueryT: GraphQLType<Context = CtxT> + 'a,
MutationT: GraphQLType<Context = CtxT> + 'a,
S: 'a,
QueryT: GraphQLType<S, Context = CtxT> + 'a,
MutationT: GraphQLType<S, Context = CtxT> + 'a,
{
match self {
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 juniper::http;
use juniper::{GraphQLType, InputValue, RootNode};
use juniper::serde::Deserialize;
use juniper::{DefaultScalarValue, GraphQLType, InputValue, RootNode, ScalarRefValue, ScalarValue};
#[derive(Deserialize)]
#[serde(untagged)]
enum GraphQLBatchRequest {
Single(http::GraphQLRequest),
Batch(Vec<http::GraphQLRequest>),
#[serde(bound = "InputValue<S>: Deserialize<'de>")]
enum GraphQLBatchRequest<S = DefaultScalarValue>
where
S: ScalarValue,
{
Single(http::GraphQLRequest<S>),
Batch(Vec<http::GraphQLRequest<S>>),
}
#[derive(Serialize)]
#[serde(untagged)]
enum GraphQLBatchResponse<'a> {
Single(http::GraphQLResponse<'a>),
Batch(Vec<http::GraphQLResponse<'a>>),
enum GraphQLBatchResponse<'a, S = DefaultScalarValue>
where
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>(
&'a self,
root_node: &RootNode<QueryT, MutationT>,
root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT,
) -> GraphQLBatchResponse<'a>
) -> GraphQLBatchResponse<'a, S>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
{
match self {
&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 {
match self {
&GraphQLBatchResponse::Single(ref response) => response.is_ok(),
@ -188,15 +203,17 @@ impl<'a> GraphQLBatchResponse<'a> {
/// this endpoint containing the field `"query"` and optionally `"variables"`.
/// The variables should be a JSON object containing the variable to value
/// mapping.
pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
pub struct GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT, S = DefaultScalarValue>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static,
Query: GraphQLType<Context = CtxT> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT> + Send + Sync + 'static,
Query: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
Mutation: GraphQLType<S, Context = CtxT> + Send + Sync + 'static,
{
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
@ -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 {
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_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
S: ScalarValue + 'a,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static,
Query: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
{
/// Build a new GraphQL handler
///
@ -252,8 +275,9 @@ where
}
}
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest> {
let url_query_string = req.get_mut::<UrlEncodedQuery>()
fn handle_get(&self, req: &mut Request) -> IronResult<GraphQLBatchRequest<S>> {
let url_query_string = req
.get_mut::<UrlEncodedQuery>()
.map_err(GraphQLIronError::Url)?;
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();
itry!(req.body.read_to_string(&mut request_payload));
Ok(
serde_json::from_str::<GraphQLBatchRequest>(request_payload.as_str())
serde_json::from_str::<GraphQLBatchRequest<S>>(request_payload.as_str())
.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 content_type = "application/json".parse::<Mime>().unwrap();
let json = serde_json::to_string_pretty(&response).unwrap();
@ -303,13 +327,15 @@ impl GraphiQLHandler {
}
}
impl<'a, CtxFactory, Query, Mutation, CtxT> Handler
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT>
impl<'a, CtxFactory, Query, Mutation, CtxT, S> Handler
for GraphQLHandler<'a, CtxFactory, Query, Mutation, CtxT, S>
where
S: ScalarValue + Sync + Send + 'static,
for<'b> &'b S: ScalarRefValue<'b>,
CtxFactory: Fn(&mut Request) -> IronResult<CtxT> + Send + Sync + 'static,
CtxT: 'static,
Query: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Query: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
Mutation: GraphQLType<S, Context = CtxT, TypeInfo = ()> + Send + Sync + 'static,
'a: 'static,
{
fn handle(&self, mut req: &mut Request) -> IronResult<Response> {
@ -397,7 +423,8 @@ mod tests {
// and newer `hyper` doesn't allow unescaped "{" or "}".
fn fixup_url(url: &str) -> String {
let url = Url::parse(&format!("http://localhost:3000{}", url)).expect("url to parse");
let path: String = url.path()
let path: String = url
.path()
.iter()
.map(|x| x.to_string())
.collect::<Vec<String>>()

View file

@ -1,4 +1,3 @@
[tasks.build-verbose]
condition = { channels = ["nightly"] }
@ -15,4 +14,4 @@ condition = { channels = ["nightly"], env = { "TARGET" = "x86_64-pc-windows-msvc
condition = { channels = ["nightly"] }
[tasks.ci-coverage-flow.windows]
disabled = true
disabled = true

View file

@ -59,33 +59,48 @@ use rocket::Request;
use juniper::http;
use juniper::InputValue;
use juniper::serde::Deserialize;
use juniper::DefaultScalarValue;
use juniper::FieldError;
use juniper::GraphQLType;
use juniper::RootNode;
use juniper::ScalarRefValue;
use juniper::ScalarValue;
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum GraphQLBatchRequest {
Single(http::GraphQLRequest),
Batch(Vec<http::GraphQLRequest>),
#[serde(bound = "InputValue<S>: Deserialize<'de>")]
enum GraphQLBatchRequest<S = DefaultScalarValue>
where
S: ScalarValue,
{
Single(http::GraphQLRequest<S>),
Batch(Vec<http::GraphQLRequest<S>>),
}
#[derive(Serialize)]
#[serde(untagged)]
enum GraphQLBatchResponse<'a> {
Single(http::GraphQLResponse<'a>),
Batch(Vec<http::GraphQLResponse<'a>>),
enum GraphQLBatchResponse<'a, S = DefaultScalarValue>
where
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>(
&'a self,
root_node: &RootNode<QueryT, MutationT>,
root_node: &'a RootNode<QueryT, MutationT, S>,
context: &CtxT,
) -> GraphQLBatchResponse<'a>
) -> GraphQLBatchResponse<'a, S>
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
{
match self {
&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 {
match self {
&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`
/// and `FromData` traits.
#[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
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))
}
impl GraphQLRequest {
impl<S> GraphQLRequest<S>
where
S: ScalarValue,
for<'b> &'b S: ScalarRefValue<'b>,
{
/// Execute an incoming GraphQL query
pub fn execute<CtxT, QueryT, MutationT>(
&self,
root_node: &RootNode<QueryT, MutationT>,
root_node: &RootNode<QueryT, MutationT, S>,
context: &CtxT,
) -> GraphQLResponse
where
QueryT: GraphQLType<Context = CtxT>,
MutationT: GraphQLType<Context = CtxT>,
QueryT: GraphQLType<S, Context = CtxT>,
MutationT: GraphQLType<S, Context = CtxT>,
{
let response = self.0.execute(root_node, context);
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;
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,
Err(e) => return Err(e.description().to_string()),
}
variables = Some(serde_json::from_str::<InputValue>(&decoded)
.map_err(|err| err.description().to_owned())?);
variables = Some(
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;
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) {
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_eq!(result.unwrap_err(), error);
}
@ -401,7 +433,7 @@ mod fromform_tests {
fn test_url_decode() {
let form_string = "query=%25foo%20bar+baz%26%3F&operation_name=test";
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());
let expected = GraphQLRequest(GraphQLBatchRequest::Single(http::GraphQLRequest::new(
"%foo bar baz&?".to_string(),
@ -477,8 +509,7 @@ mod tests {
.manage(Schema::new(
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 {

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