From 7cb35aaa17ea3c272c4860616a66cfa2e7d78003 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Mon, 27 Aug 2018 23:38:13 +0200 Subject: [PATCH 01/20] Introduce a abstraction for scalar values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Before this possible scalar values where hard coded to be representable in one of the following types: `i32`, `f64`, `String` or `bool`. This restricts what types of custom scalar values could be defined. (For example it was not possible to define a scalar value that represents a `i64` without mapping it to a string (that would be not efficient)) One solution to fix that example above would simply be to change the internal representation to allow it represent a `i64`, but this would only fix the problem for one type (till someone want's to support `i128` for example…). Also this would make juniper not following the graphql standard closely. This commit tries to explore another approach, by making the exact "internal" representation of scalar values swappable (in such a way that a down stream crate could provide it's 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 following their needs. To that we need to change several things in the current implementation * Add some traits that abstract the behaviour 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 knowlege from the existing schema to the parser. * Fix some macros and derives to follow the new behaviour This also contains a unrelated change about the way `juniper_codegen` resolves items from juniper. In detail the `_internal` flag is removed the the resolution is replaced by an macro. This is a drive by change because I was annoyed of adding new items to that list. --- juniper/src/ast.rs | 246 ++++--- juniper/src/executor/look_ahead.rs | 139 ++-- juniper/src/executor/mod.rs | 322 +++++---- juniper/src/executor_tests/custom_scalar.rs | 386 +++++++++++ juniper/src/executor_tests/directives.rs | 8 +- juniper/src/executor_tests/enums.rs | 33 +- juniper/src/executor_tests/executor.rs | 151 +++-- .../src/executor_tests/interfaces_unions.rs | 20 +- .../src/executor_tests/introspection/enums.rs | 12 +- .../introspection/input_object.rs | 103 +-- .../src/executor_tests/introspection/mod.rs | 62 +- juniper/src/executor_tests/mod.rs | 1 + juniper/src/executor_tests/variables.rs | 295 ++++---- juniper/src/http/mod.rs | 54 +- juniper/src/integrations/chrono.rs | 50 +- juniper/src/integrations/serde.rs | 108 +-- juniper/src/integrations/url.rs | 12 +- juniper/src/integrations/uuid.rs | 14 +- juniper/src/lib.rs | 49 +- juniper/src/macros/interface.rs | 27 +- juniper/src/macros/mod.rs | 7 + juniper/src/macros/object.rs | 23 +- juniper/src/macros/scalar.rs | 389 +++++++++-- juniper/src/macros/tests/args.rs | 5 +- juniper/src/macros/tests/field.rs | 80 ++- juniper/src/macros/tests/interface.rs | 4 +- juniper/src/macros/tests/object.rs | 8 +- juniper/src/macros/tests/scalar.rs | 36 +- juniper/src/macros/tests/union.rs | 4 +- juniper/src/macros/union.rs | 19 +- juniper/src/parser/document.rs | 275 ++++++-- juniper/src/parser/lexer.rs | 307 +++------ juniper/src/parser/parser.rs | 4 +- juniper/src/parser/tests/lexer.rs | 61 +- juniper/src/parser/tests/value.rs | 89 ++- juniper/src/parser/utils.rs | 46 +- juniper/src/parser/value.rs | 209 ++++-- juniper/src/schema/meta.rs | 181 +++-- juniper/src/schema/model.rs | 170 +++-- juniper/src/schema/schema.rs | 640 ++++++++++++------ juniper/src/tests/introspection_tests.rs | 41 +- juniper/src/tests/model.rs | 1 - juniper/src/tests/query_tests.rs | 104 +-- juniper/src/tests/type_info_tests.rs | 27 +- juniper/src/types/base.rs | 111 +-- juniper/src/types/containers.rs | 107 ++- juniper/src/types/pointers.rs | 128 ++-- juniper/src/types/scalars.rs | 213 +++++- juniper/src/types/utilities.rs | 21 +- juniper/src/validation/context.rs | 25 +- juniper/src/validation/input_value.rs | 92 ++- juniper/src/validation/mod.rs | 4 +- juniper/src/validation/multi_visitor.rs | 264 ++++---- .../rules/arguments_of_correct_type.rs | 161 ++--- .../rules/default_values_of_correct_type.rs | 29 +- .../rules/fields_on_correct_type.rs | 56 +- .../rules/fragments_on_composite_types.rs | 37 +- .../validation/rules/known_argument_names.rs | 56 +- .../src/validation/rules/known_directives.rs | 60 +- .../validation/rules/known_fragment_names.rs | 20 +- .../src/validation/rules/known_type_names.rs | 34 +- .../rules/lone_anonymous_operation.rs | 28 +- juniper/src/validation/rules/mod.rs | 12 +- .../validation/rules/no_fragment_cycles.rs | 52 +- .../rules/no_undefined_variables.rs | 83 +-- .../validation/rules/no_unused_fragments.rs | 31 +- .../validation/rules/no_unused_variables.rs | 53 +- .../rules/overlapping_fields_can_be_merged.rs | 359 ++++++---- .../rules/possible_fragment_spreads.rs | 70 +- .../rules/provided_non_null_arguments.rs | 56 +- juniper/src/validation/rules/scalar_leafs.rs | 36 +- .../validation/rules/unique_argument_names.rs | 50 +- .../validation/rules/unique_fragment_names.rs | 104 +-- .../rules/unique_input_field_names.rs | 31 +- .../rules/unique_operation_names.rs | 62 +- .../validation/rules/unique_variable_names.rs | 20 +- .../rules/variables_are_input_types.rs | 22 +- .../rules/variables_in_allowed_position.rs | 82 +-- juniper/src/validation/test_harness.rs | 335 +++++++-- juniper/src/validation/traits.rs | 119 ++-- juniper/src/validation/visitor.rs | 202 +++--- juniper/src/value.rs | 424 ------------ juniper/src/value/mod.rs | 338 +++++++++ juniper/src/value/object.rs | 149 ++++ juniper/src/value/scalar.rs | 253 +++++++ juniper_codegen/src/derive_enum.rs | 54 +- juniper_codegen/src/derive_input_object.rs | 115 ++-- 87 files changed, 5987 insertions(+), 3393 deletions(-) create mode 100644 juniper/src/executor_tests/custom_scalar.rs delete mode 100644 juniper/src/value.rs create mode 100644 juniper/src/value/mod.rs create mode 100644 juniper/src/value/object.rs create mode 100644 juniper/src/value/scalar.rs diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 8d10d3b25..d11e05ef9 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -8,6 +8,7 @@ use indexmap::IndexMap; use executor::Variables; use parser::Spanning; +use value::{ScalarRefValue, ScalarValue}; /// 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 { Null, - Int(i32), - Float(f64), - String(String), - Boolean(bool), + Scalar(S), Enum(String), Variable(String), - List(Vec>), - Object(Vec<(Spanning, Spanning)>), + List(Vec>>), + Object(Vec<(Spanning, Spanning>)>), } #[derive(Clone, PartialEq, Debug)] -pub struct VariableDefinition<'a> { +pub struct VariableDefinition<'a, S> { pub var_type: Spanning>, - pub default_value: Option>, + pub default_value: Option>>, } #[derive(Clone, PartialEq, Debug)] -pub struct Arguments<'a> { - pub items: Vec<(Spanning<&'a str>, Spanning)>, +pub struct Arguments<'a, S> { + pub items: Vec<(Spanning<&'a str>, Spanning>)>, } #[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>, pub name: Spanning<&'a str>, - pub arguments: Option>>, - pub directives: Option>>>, - pub selection_set: Option>>, + pub arguments: Option>>, + pub directives: Option>>>, + pub selection_set: Option>>, } #[derive(Clone, PartialEq, Debug)] -pub struct FragmentSpread<'a> { +pub struct FragmentSpread<'a, S> { pub name: Spanning<&'a str>, - pub directives: Option>>>, + pub directives: Option>>>, } #[derive(Clone, PartialEq, Debug)] -pub struct InlineFragment<'a> { +pub struct InlineFragment<'a, S> { pub type_condition: Option>, - pub directives: Option>>>, - pub selection_set: Vec>, + pub directives: Option>>>, + pub selection_set: Vec>, } /// 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>), - FragmentSpread(Spanning>), - InlineFragment(Spanning>), +pub enum Selection<'a, S> { + Field(Spanning>), + FragmentSpread(Spanning>), + InlineFragment(Spanning>), } #[derive(Clone, PartialEq, Debug)] -pub struct Directive<'a> { +pub struct Directive<'a, S> { pub name: Spanning<&'a str>, - pub arguments: Option>>, + pub arguments: Option>>, } #[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>, - pub variable_definitions: Option>>, - pub directives: Option>>>, - pub selection_set: Vec>, + pub variable_definitions: Option>>, + pub directives: Option>>>, + pub selection_set: Vec>, } #[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>>>, - pub selection_set: Vec>, + pub directives: Option>>>, + pub selection_set: Vec>, } #[derive(Clone, PartialEq, Debug)] -pub enum Definition<'a> { - Operation(Spanning>), - Fragment(Spanning>), +pub enum Definition<'a, S> { + Operation(Spanning>), + Fragment(Spanning>), } -pub type Document<'a> = Vec>; +pub type Document<'a, S> = Vec>; /// Parse an unstructured input value into a Rust data type. /// @@ -153,15 +151,17 @@ pub type Document<'a> = Vec>; /// 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: Sized { /// Performs the conversion. - fn from_input_value(v: &InputValue) -> Option; + fn from_input_value(v: &InputValue) -> Option + where + for<'b> &'b S: ScalarRefValue<'b>; } /// Losslessly clones a Rust data type into an InputValue. -pub trait ToInputValue: Sized { +pub trait ToInputValue: Sized { /// Performs the conversion. - fn to_input_value(&self) -> InputValue; + fn to_input_value(&self) -> InputValue; } impl<'a> Type<'a> { @@ -205,39 +205,49 @@ impl<'a> fmt::Display for Type<'a> { } } -impl InputValue { +impl InputValue +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) + pub fn int(i: i32) -> Self { + Self::scalar(i) } /// Construct a floating point value. - pub fn float(f: f64) -> InputValue { - InputValue::Float(f) + pub fn float(f: f64) -> Self { + Self::scalar(f) } /// Construct a boolean value. - pub fn boolean(b: bool) -> InputValue { - InputValue::Boolean(b) + pub fn boolean(b: bool) -> Self { + Self::scalar(b) } /// Construct a string value. - pub fn string>(s: T) -> InputValue { - InputValue::String(s.as_ref().to_owned()) + pub fn string>(s: T) -> Self { + InputValue::scalar(s.as_ref()) + } + + pub fn scalar(v: T) -> Self + where + T: Into, + { + InputValue::Scalar(v.into()) } /// Construct an enum value. - pub fn enum_value>(s: T) -> InputValue { + pub fn enum_value>(s: T) -> Self { InputValue::Enum(s.as_ref().to_owned()) } /// Construct a variable value. - pub fn variable>(v: T) -> InputValue { + pub fn variable>(v: T) -> Self { InputValue::Variable(v.as_ref().to_owned()) } @@ -246,12 +256,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 { + pub fn list(l: Vec) -> Self { InputValue::List(l.into_iter().map(Spanning::unlocated).collect()) } /// Construct a located list. - pub fn parsed_list(l: Vec>) -> InputValue { + pub fn parsed_list(l: Vec>) -> Self { InputValue::List(l) } @@ -259,7 +269,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(o: IndexMap) -> InputValue + pub fn object(o: IndexMap) -> Self where K: AsRef + Eq + Hash, { @@ -270,18 +280,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, Spanning)>) -> InputValue { + pub fn parsed_object(o: Vec<(Spanning, Spanning)>) -> 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) -> Self { match self { InputValue::Variable(v) => vars.get(&v).map_or_else(InputValue::null, Clone::clone), InputValue::List(l) => InputValue::List( @@ -301,9 +310,10 @@ impl InputValue { /// Shorthand form of invoking `FromInputValue::from()`. pub fn convert(&self) -> Option where - T: FromInputValue, + T: FromInputValue, + for<'b> &'b S: ScalarRefValue<'b>, { - ::from_input_value(self) + >::from_input_value(self) } /// Does the value represent null? @@ -331,34 +341,48 @@ impl InputValue { } /// View the underlying int value, if present. - pub fn as_int_value(&self) -> Option { - match *self { - InputValue::Int(i) => Some(i), - _ => None, - } + pub fn as_int_value<'a>(&'a self) -> Option + where + &'a S: Into>, + { + self.as_scalar_value() } /// View the underlying float value, if present. - pub fn as_float_value(&self) -> Option { - match *self { - InputValue::Float(f) => Some(f), - _ => None, - } + pub fn as_float_value<'a>(&'a self) -> Option + where + &'a S: Into>, + { + self.as_scalar_value() } /// View the underlying string value, if present. - pub fn as_string_value(&self) -> Option<&str> { + pub fn as_string_value<'a>(&'a self) -> Option<&'a str> + where + &'a S: Into>, + { + self.as_scalar_value() + } + + pub fn as_scalar(&self) -> Option<&S> { match *self { - InputValue::String(ref s) => Some(s), + InputValue::Scalar(ref s) => Some(s), _ => None, } } + pub fn as_scalar_value<'a, T>(&'a self) -> Option + where + &'a S: Into>, + { + 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> { + pub fn to_object_value<'a>(&'a self) -> Option> { match *self { InputValue::Object(ref o) => Some( o.iter() @@ -373,7 +397,7 @@ impl InputValue { /// /// This constructs a new vector that contain references to the values /// in `self`. - pub fn to_list_value(&self) -> Option> { + pub fn to_list_value(&self) -> Option> { match *self { InputValue::List(ref l) => Some(l.iter().map(|s| &s.item).collect()), _ => None, @@ -384,10 +408,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 +421,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 +444,14 @@ impl InputValue { } } -impl fmt::Display for InputValue { +impl fmt::Display for InputValue +where + S: ScalarValue, +{ 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) => write!(f, "{}", s), InputValue::Enum(ref v) => write!(f, "{}", v), InputValue::Variable(ref v) => write!(f, "${}", v), InputValue::List(ref v) => { @@ -460,16 +483,16 @@ impl fmt::Display for InputValue { } } -impl<'a> Arguments<'a> { - pub fn into_iter(self) -> vec::IntoIter<(Spanning<&'a str>, Spanning)> { +impl<'a, S> Arguments<'a, S> { + pub fn into_iter(self) -> vec::IntoIter<(Spanning<&'a str>, Spanning>)> { self.items.into_iter() } - pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, Spanning)> { + pub fn iter(&self) -> slice::Iter<(Spanning<&'a str>, Spanning>)> { self.items.iter() } - pub fn iter_mut(&mut self) -> slice::IterMut<(Spanning<&'a str>, Spanning)> { + pub fn iter_mut(&mut self) -> slice::IterMut<(Spanning<&'a str>, Spanning>)> { self.items.iter_mut() } @@ -477,7 +500,7 @@ impl<'a> Arguments<'a> { self.items.len() } - pub fn get(&self, key: &str) -> Option<&Spanning> { + pub fn get(&self, key: &str) -> Option<&Spanning>> { self.items .iter() .filter(|&&(ref k, _)| k.item == key) @@ -486,8 +509,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)> { self.items.iter() } } @@ -496,32 +519,35 @@ impl<'a> VariableDefinitions<'a> { mod tests { use super::InputValue; use parser::Spanning; + use value::{DefaultScalarValue, Value}; + + type TestValue = InputValue; #[test] fn test_input_value_fmt() { - let value = InputValue::null(); + let value: TestValue = InputValue::null(); assert_eq!(format!("{}", value), "null"); - let value = InputValue::int(123); + let value: TestValue = InputValue::int(123); assert_eq!(format!("{}", value), "123"); - let value = InputValue::float(12.3); + let value: TestValue = InputValue::float(12.3); assert_eq!(format!("{}", value), "12.3"); - let value = InputValue::string("FOO".to_owned()); + let value: TestValue = InputValue::string("FOO".to_owned()); assert_eq!(format!("{}", value), "\"FOO\""); - let value = InputValue::boolean(true); + let value: TestValue = InputValue::boolean(true); assert_eq!(format!("{}", value), "true"); - let value = InputValue::enum_value("BAR".to_owned()); + let value: TestValue = InputValue::enum_value("BAR".to_owned()); assert_eq!(format!("{}", value), "BAR"); - let value = InputValue::variable("baz".to_owned()); + let value: TestValue = 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 value: TestValue = InputValue::list(list); assert_eq!(format!("{}", value), "[1, 2]"); let object = vec![ @@ -534,7 +560,7 @@ mod tests { Spanning::unlocated(InputValue::int(2)), ), ]; - let value = InputValue::parsed_object(object); + let value: TestValue = InputValue::parsed_object(object); assert_eq!(format!("{}", value), "{foo: 1, bar: 2}"); } } diff --git a/juniper/src/executor/look_ahead.rs b/juniper/src/executor/look_ahead.rs index 0c9454557..4e199bbff 100644 --- a/juniper/src/executor/look_ahead.rs +++ b/juniper/src/executor/look_ahead.rs @@ -1,5 +1,6 @@ use ast::{Directive, Fragment, InputValue, Selection}; use parser::Spanning; +use value::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>), - Object(Vec<(&'a str, LookAheadValue<'a>)>), + List(Vec>), + 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, vars: &'a Variables) -> 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), - vars: &'a Variables, + &(ref name, ref value): &'a (Spanning<&'a str>, Spanning>), + vars: &'a Variables, ) -> Self { LookAheadArgument { name: name.item, @@ -80,86 +80,96 @@ 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>, - pub(super) children: Vec>, + pub(super) arguments: Vec>, + pub(super) children: Vec>, } -impl<'a> LookAheadSelection<'a> { - fn should_include(directives: Option<&Vec>>, vars: &Variables) -> bool { +impl<'a, S> LookAheadSelection<'a, S> +where + S: ScalarValue, + &'a S: Into>, +{ + fn should_include<'b, 'c>( + directives: Option<&'b Vec>>>, + vars: &'c Variables, + ) -> 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.into().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 + b.into().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, + 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> { - let empty: &[Selection] = &[]; + vars: &'a Variables, + fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>, + ) -> Option> { + let empty: &[Selection] = &[]; match *s { Selection::Field(ref field) => { let field = &field.item; @@ -178,8 +188,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 +265,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 +276,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 +286,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>, - children: Vec>, + arguments: Vec>, + children: Vec>, } /// A set of common methods for `ConcreteLookAheadSelection` and `LookAheadSelection` -pub trait LookAheadMethods { +pub trait LookAheadMethods { /// Get the name of the field represented by the current selection fn field_name(&self) -> &str; @@ -298,15 +307,15 @@ pub trait LookAheadMethods { } /// Get the top level arguments for the current selection - fn arguments(&self) -> &[LookAheadArgument]; + fn arguments(&self) -> &[LookAheadArgument]; /// 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> { self.arguments().iter().find(|a| a.name == name) } } -impl<'a> LookAheadMethods for ConcreteLookAheadSelection<'a> { +impl<'a, S> LookAheadMethods for ConcreteLookAheadSelection<'a, S> { fn field_name(&self) -> &str { self.alias.unwrap_or(self.name) } @@ -315,12 +324,12 @@ impl<'a> LookAheadMethods for ConcreteLookAheadSelection<'a> { self.children.iter().find(|c| c.name == name) } - fn arguments(&self) -> &[LookAheadArgument] { + fn arguments(&self) -> &[LookAheadArgument] { &self.arguments } } -impl<'a> LookAheadMethods for LookAheadSelection<'a> { +impl<'a, S> LookAheadMethods for LookAheadSelection<'a, S> { fn field_name(&self) -> &str { self.alias.unwrap_or(self.name) } @@ -332,7 +341,7 @@ impl<'a> LookAheadMethods for LookAheadSelection<'a> { .map(|s| &s.inner) } - fn arguments(&self) -> &[LookAheadArgument] { + fn arguments(&self) -> &[LookAheadArgument] { &self.arguments } } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index f6d62779a..789fab56c 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashMap; -use std::fmt::Display; +use std::fmt::{Debug, Display}; use std::sync::RwLock; use fnv::FnvHashMap; @@ -22,6 +22,7 @@ use schema::model::{RootNode, SchemaType, TypeType}; use types::base::GraphQLType; use types::name::Name; +use value::{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: Debug> { /// Currently registered types - pub types: FnvHashMap>, + pub types: FnvHashMap>, } #[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, S, CtxT> where CtxT: 'a, + S: Debug + '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, + 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>, + errors: &'a RwLock>>, 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 { location: SourcePosition, path: Vec, - error: FieldError, + error: FieldError, } -impl ExecutionError { +impl Eq for ExecutionError where Self: PartialEq {} + +impl ExecutionError { /// 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) -> ExecutionError { 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 { +impl PartialOrd for ExecutionError +where + Self: PartialEq, +{ + fn partial_cmp(&self, other: &ExecutionError) -> Option { (&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 Ord for ExecutionError +where + Self: Eq, +{ + fn cmp(&self, other: &ExecutionError) -> Ordering { (&self.location, &self.path, &self.error.message).cmp(&( &other.location, &other.path, @@ -118,20 +126,25 @@ impl Ord for ExecutionError { /// which makes error chaining with the `?` operator a breeze: /// /// ```rust -/// # use juniper::FieldError; -/// fn get_string(data: Vec) -> Result { +/// # use juniper::{FieldError, ScalarValue}; +/// fn get_string(data: Vec) -> Result> +/// where S: ScalarValue +/// { /// let s = String::from_utf8(data)?; /// Ok(s) /// } /// ``` #[derive(Debug, PartialEq)] -pub struct FieldError { +pub struct FieldError { message: String, - extensions: Value, + extensions: Value, } -impl From for FieldError { - fn from(e: T) -> FieldError { +impl From for FieldError +where + S: ::value::ScalarValue, +{ + fn from(e: T) -> FieldError { FieldError { message: format!("{}", e), extensions: Value::null(), @@ -139,7 +152,7 @@ impl From for FieldError { } } -impl FieldError { +impl FieldError { /// Construct a new error with additional data /// /// You can use the `graphql_value!` macro to construct an error: @@ -147,8 +160,10 @@ impl FieldError { /// ```rust /// # #[macro_use] extern crate juniper; /// use juniper::FieldError; + /// # use juniper::DefaultScalarValue; /// /// # fn sample() { + /// # let _: FieldError = /// FieldError::new( /// "Could not open connection to the database", /// graphql_value!({ "internal_error": "Connection refused" }) @@ -173,7 +188,7 @@ impl FieldError { /// ``` /// /// If the argument is `Value::null()`, no extra data will be included. - pub fn new(e: T, extensions: Value) -> FieldError { + pub fn new(e: T, extensions: Value) -> FieldError { FieldError { message: format!("{}", e), extensions, @@ -186,82 +201,111 @@ impl FieldError { } #[doc(hidden)] - pub fn extensions(&self) -> &Value { + pub fn extensions(&self) -> &Value { &self.extensions } } /// The result of resolving the value of a field of type `T` -pub type FieldResult = Result; +pub type FieldResult = Result>; /// The result of resolving an unspecified field -pub type ExecutionResult = Result; +pub type ExecutionResult = Result, FieldError>; /// The map of variables used for substitution during query execution -pub type Variables = HashMap; +pub type Variables = HashMap>; /// 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 { #[doc(hidden)] - fn into_field_error(self) -> FieldError; + fn into_field_error(self) -> FieldError; } -impl IntoFieldError for FieldError { - fn into_field_error(self) -> FieldError { +impl IntoFieldError for FieldError { + fn into_field_error(self) -> FieldError { self } } #[doc(hidden)] -pub trait IntoResolvable<'a, T: GraphQLType, C>: Sized { +pub trait IntoResolvable<'a, S, T: GraphQLType, C>: Sized +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ #[doc(hidden)] - fn into(self, ctx: &'a C) -> FieldResult>; + fn into(self, ctx: &'a C) -> FieldResult, 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: ScalarValue, T::Context: FromContext, + for<'b> &'b S: ScalarRefValue<'b>, { - fn into(self, ctx: &'a C) -> FieldResult> { + fn into(self, ctx: &'a C) -> FieldResult, S> { Ok(Some((FromContext::from(ctx), self))) } } -impl<'a, T: GraphQLType, C, E: IntoFieldError> IntoResolvable<'a, T, C> for Result +impl<'a, S, T, C, E: IntoFieldError> IntoResolvable<'a, S, T, C> for Result where + S: ScalarValue, + T: GraphQLType, T::Context: FromContext, + for<'b> &'b S: ScalarRefValue<'b>, { - fn into(self, ctx: &'a C) -> FieldResult> { + fn into(self, ctx: &'a C) -> FieldResult, S> { self.map(|v| Some((FromContext::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> { +impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for (&'a T::Context, T) +where + S: ScalarValue, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, +{ + fn into(self, _: &'a C) -> FieldResult, S> { Ok(Some(self)) } } -impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option, C> for Option<(&'a T::Context, T)> { - fn into(self, _: &'a C) -> FieldResult)>> { +impl<'a, S, T, C> IntoResolvable<'a, S, Option, C> for Option<(&'a T::Context, T)> +where + S: ScalarValue, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, +{ + fn into(self, _: &'a C) -> FieldResult)>, 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> { +impl<'a, S, T, C> IntoResolvable<'a, S, T, C> for FieldResult<(&'a T::Context, T), S> +where + S: ScalarValue, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, +{ + fn into(self, _: &'a C) -> FieldResult, S> { self.map(Some) } } -impl<'a, T: GraphQLType, C> IntoResolvable<'a, Option, C> - for FieldResult> +impl<'a, S, T, C> IntoResolvable<'a, S, Option, C> + for FieldResult, S> +where + S: ScalarValue, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { - fn into(self, _: &'a C) -> FieldResult)>> { + fn into(self, _: &'a C) -> FieldResult)>, S> { self.map(|o| o.map(|(ctx, v)| (ctx, Some(v)))) } } @@ -304,37 +348,48 @@ where } } -impl<'a, CtxT> Executor<'a, CtxT> { +impl<'a, CtxT, S> Executor<'a, S, CtxT> +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>( + pub fn resolve_with_ctx>( &self, info: &T::TypeInfo, value: &T, - ) -> ExecutionResult + ) -> ExecutionResult where NewCtxT: FromContext, + for<'b> &'b S: ScalarRefValue<'b>, { self.replaced_context(>::from(self.context)) .resolve(info, value) } /// Resolve a single arbitrary value into an `ExecutionResult` - pub fn resolve>( + pub fn resolve>( &self, info: &T::TypeInfo, value: &T, - ) -> ExecutionResult { + ) -> ExecutionResult + where + for<'b> &'b S: ScalarRefValue<'b>, + { 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>( + pub fn resolve_into_value>( &self, info: &T::TypeInfo, value: &T, - ) -> Value { + ) -> Value + where + for<'b> &'b S: ScalarRefValue<'b>, + { match self.resolve(info, value) { Ok(v) => v, Err(e) => { @@ -348,7 +403,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, S, NewCtxT> { Executor { fragments: self.fragments, variables: self.variables, @@ -368,15 +423,16 @@ impl<'a, CtxT> Executor<'a, CtxT> { field_alias: &'a str, field_name: &'a str, location: SourcePosition, - selection_set: Option<&'a [Selection]>, - ) -> Executor { + selection_set: Option<&'a [Selection]>, + ) -> Executor { 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 +449,8 @@ impl<'a, CtxT> Executor<'a, CtxT> { pub fn type_sub_executor( &self, type_name: Option<&'a str>, - selection_set: Option<&'a [Selection]>, - ) -> Executor { + selection_set: Option<&'a [Selection]>, + ) -> Executor { Executor { fragments: self.fragments, variables: self.variables, @@ -420,22 +476,22 @@ impl<'a, CtxT> Executor<'a, CtxT> { } /// The currently executing schema - pub fn schema(&self) -> &'a SchemaType { + pub fn schema(&self) -> &'a SchemaType { 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 { 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> { self.fragments.get(name).map(|f| *f) } @@ -445,13 +501,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) { 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, location: SourcePosition) { let mut path = Vec::new(); self.field_path.construct_path(&mut path); @@ -468,16 +524,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 +543,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 +567,9 @@ impl<'a> FieldPath<'a> { } } -impl ExecutionError { +impl ExecutionError { #[doc(hidden)] - pub fn new(location: SourcePosition, path: &[&str], error: FieldError) -> ExecutionError { + pub fn new(location: SourcePosition, path: &[&str], error: FieldError) -> ExecutionError { ExecutionError { location: location, path: path.iter().map(|s| (*s).to_owned()).collect(), @@ -524,7 +578,7 @@ impl ExecutionError { } /// The error message - pub fn error(&self) -> &FieldError { + pub fn error(&self) -> &FieldError { &self.error } @@ -539,16 +593,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, operation_name: Option<&str>, - root_node: &RootNode, - variables: &Variables, + root_node: &RootNode, + variables: &Variables, context: &CtxT, -) -> Result<(Value, Vec), GraphQLError<'a>> +) -> Result<(Value, Vec>), GraphQLError<'a>> where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let mut fragments = vec![]; let mut operation = None; @@ -584,8 +640,7 @@ where def.default_value .as_ref() .map(|i| (name.item.to_owned(), i.item.clone())) - }) - .collect::>() + }).collect::>>() }); let errors = RwLock::new(Vec::new()); @@ -642,9 +697,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>) -> Registry<'r> { + pub fn new(types: FnvHashMap>) -> Registry<'r, S> { Registry { types: types } } @@ -654,7 +712,8 @@ impl<'r> Registry<'r> { /// construct its metadata and store it. pub fn get_type(&mut self, info: &T::TypeInfo) -> Type<'r> where - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { if let Some(name) = T::name(info) { let validated_name = name.parse::().unwrap(); @@ -673,9 +732,10 @@ impl<'r> Registry<'r> { } /// Create a field with the provided name - pub fn field(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r> + pub fn field(&mut self, name: &str, info: &T::TypeInfo) -> Field<'r, S> where - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { Field { name: name.to_owned(), @@ -687,13 +747,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, + for<'b> &'b S: ScalarRefValue<'b>, { Field { name: name.to_owned(), @@ -705,9 +766,10 @@ impl<'r> Registry<'r> { } /// Create an argument with the provided name - pub fn arg(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r> + pub fn arg(&mut self, name: &str, info: &T::TypeInfo) -> Argument<'r, S> where - T: GraphQLType + FromInputValue, + T: GraphQLType + FromInputValue, + for<'b> &'b S: ScalarRefValue<'b>, { Argument::new(name, self.get_type::(info)) } @@ -716,9 +778,15 @@ impl<'r> Registry<'r> { /// /// When called with type `T`, the actual argument will be given the type /// `Option`. - pub fn arg_with_default(&mut self, name: &str, value: &T, info: &T::TypeInfo) -> Argument<'r> + pub fn arg_with_default( + &mut self, + name: &str, + value: &T, + info: &T::TypeInfo, + ) -> Argument<'r, S> where - T: GraphQLType + ToInputValue + FromInputValue, + T: GraphQLType + ToInputValue + FromInputValue, + for<'b> &'b S: ScalarRefValue<'b>, { Argument::new(name, self.get_type::>(info)).default_value(value.to_input_value()) } @@ -735,22 +803,29 @@ impl<'r> Registry<'r> { /// Create a scalar meta type /// /// This expects the type to implement `FromInputValue`. - pub fn build_scalar_type(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r> + pub fn build_scalar_type(&mut self, info: &T::TypeInfo) -> ScalarMeta<'r, S> where - T: FromInputValue + GraphQLType, + T: FromInputValue + GraphQLType + ParseScalarValue + 'r, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Scalar types must be named. Implement name()"); ScalarMeta::new::(Cow::Owned(name.to_string())) } /// Create a list meta type - pub fn build_list_type(&mut self, info: &T::TypeInfo) -> ListMeta<'r> { + pub fn build_list_type>(&mut self, info: &T::TypeInfo) -> ListMeta<'r> + where + for<'b> &'b S: ScalarRefValue<'b>, + { let of_type = self.get_type::(info); ListMeta::new(of_type) } /// Create a nullable meta type - pub fn build_nullable_type(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> { + pub fn build_nullable_type>(&mut self, info: &T::TypeInfo) -> NullableMeta<'r> + where + for<'b> &'b S: ScalarRefValue<'b>, + { let of_type = self.get_type::(info); NullableMeta::new(of_type) } @@ -762,10 +837,11 @@ impl<'r> Registry<'r> { pub fn build_object_type( &mut self, info: &T::TypeInfo, - fields: &[Field<'r>], - ) -> ObjectMeta<'r> + fields: &[Field<'r, S>], + ) -> ObjectMeta<'r, S> where - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Object types must be named. Implement name()"); @@ -775,9 +851,14 @@ impl<'r> Registry<'r> { } /// Create an enum meta type - pub fn build_enum_type(&mut self, info: &T::TypeInfo, values: &[EnumValue]) -> EnumMeta<'r> + pub fn build_enum_type( + &mut self, + info: &T::TypeInfo, + values: &[EnumValue], + ) -> EnumMeta<'r, S> where - T: FromInputValue + GraphQLType, + T: FromInputValue + GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Enum types must be named. Implement name()"); @@ -789,10 +870,11 @@ impl<'r> Registry<'r> { pub fn build_interface_type( &mut self, info: &T::TypeInfo, - fields: &[Field<'r>], - ) -> InterfaceMeta<'r> + fields: &[Field<'r, S>], + ) -> InterfaceMeta<'r, S> where - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Interface types must be named. Implement name()"); @@ -804,7 +886,8 @@ impl<'r> Registry<'r> { /// Create a union meta type builder pub fn build_union_type(&mut self, info: &T::TypeInfo, types: &[Type<'r>]) -> UnionMeta<'r> where - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Union types must be named. Implement name()"); @@ -815,10 +898,11 @@ impl<'r> Registry<'r> { pub fn build_input_object_type( &mut self, info: &T::TypeInfo, - args: &[Argument<'r>], - ) -> InputObjectMeta<'r> + args: &[Argument<'r, S>], + ) -> InputObjectMeta<'r, S> where - T: FromInputValue + GraphQLType, + T: FromInputValue + GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let name = T::name(info).expect("Input object types must be named. Implement name()"); diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs new file mode 100644 index 000000000..b0cb65aae --- /dev/null +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -0,0 +1,386 @@ +use ast::{FromInputValue, InputValue, Selection, ToInputValue}; +use executor::{ExecutionResult, Executor, Registry, Variables}; +use parser::ParseError; +use parser::Token; +use schema::meta::{MetaType, ScalarMeta}; +use schema::model::RootNode; +use serde::de::{self, Deserialize, Deserializer}; +use serde::ser::{Serialize, Serializer}; +use std::fmt::{self, Display}; +use types::base::{Arguments, GraphQLType}; +use types::scalars::EmptyMutation; +use value::{Object, ScalarRefValue, ScalarValue, Value}; + +#[derive(Debug, Clone, PartialEq)] +enum MyScalarValue { + Int(i32), + Long(i64), + Float(f64), + String(String), + Boolean(bool), +} + +impl ScalarValue for MyScalarValue {} + +impl Display for MyScalarValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + MyScalarValue::Int(i) => write!(f, "{}", i), + MyScalarValue::Long(i) => write!(f, "{}", i), + MyScalarValue::Float(n) => write!(f, "{}", n), + MyScalarValue::String(ref s) => write!(f, "\"{}\"", s), + MyScalarValue::Boolean(b) => write!(f, "{}", b), + } + } +} + +impl<'a> From<&'a str> for MyScalarValue { + fn from(s: &'a str) -> Self { + MyScalarValue::String(s.into()) + } +} + +impl From for MyScalarValue { + fn from(s: String) -> Self { + (&s as &str).into() + } +} + +impl From for MyScalarValue { + fn from(b: bool) -> Self { + MyScalarValue::Boolean(b) + } +} + +impl From for MyScalarValue { + fn from(i: i32) -> Self { + MyScalarValue::Int(i) + } +} + +impl From for MyScalarValue { + fn from(i: i64) -> Self { + MyScalarValue::Long(i) + } +} + +impl From for MyScalarValue { + fn from(f: f64) -> Self { + MyScalarValue::Float(f) + } +} + +impl From for Option { + fn from(s: MyScalarValue) -> Self { + match s { + MyScalarValue::Boolean(b) => Some(b), + _ => None, + } + } +} + +impl From for Option { + fn from(s: MyScalarValue) -> Self { + match s { + MyScalarValue::Int(i) => Some(i), + _ => None, + } + } +} + +impl From for Option { + fn from(s: MyScalarValue) -> Self { + match s { + MyScalarValue::Float(s) => Some(s), + MyScalarValue::Int(i) => Some(i as f64), + _ => None, + } + } +} + +impl From for Option { + fn from(s: MyScalarValue) -> Self { + match s { + MyScalarValue::String(s) => Some(s.clone()), + _ => None, + } + } +} + +impl<'a> From<&'a MyScalarValue> for Option { + fn from(s: &'a MyScalarValue) -> Self { + match *s { + MyScalarValue::Boolean(b) => Some(b), + _ => None, + } + } +} + +impl<'a> From<&'a MyScalarValue> for Option { + fn from(s: &'a MyScalarValue) -> Self { + match *s { + MyScalarValue::Float(b) => Some(b), + MyScalarValue::Int(i) => Some(i as f64), + _ => None, + } + } +} + +impl<'a> From<&'a MyScalarValue> for Option { + fn from(s: &'a MyScalarValue) -> Self { + match *s { + MyScalarValue::Int(b) => Some(b), + _ => None, + } + } +} + +impl<'a> From<&'a MyScalarValue> for Option { + fn from(s: &'a MyScalarValue) -> Self { + match *s { + MyScalarValue::Long(l) => Some(l), + _ => None, + } + } +} + +impl<'a> From<&'a MyScalarValue> for Option<&'a str> { + fn from(s: &'a MyScalarValue) -> Self { + match *s { + MyScalarValue::String(ref s) => Some(s), + _ => None, + } + } +} + +impl Serialize for MyScalarValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + MyScalarValue::Int(v) => serializer.serialize_i32(v), + MyScalarValue::Long(v) => serializer.serialize_i64(v), + MyScalarValue::Float(v) => serializer.serialize_f64(v), + MyScalarValue::String(ref v) => serializer.serialize_str(v), + MyScalarValue::Boolean(v) => serializer.serialize_bool(v), + } + } +} + +impl<'de> Deserialize<'de> for MyScalarValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + 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(self, value: bool) -> Result { + Ok(MyScalarValue::Boolean(value)) + } + + fn visit_i32(self, value: i32) -> Result + where + E: de::Error, + { + Ok(MyScalarValue::Int(value)) + } + + fn visit_i64(self, value: i64) -> Result + where + E: de::Error, + { + Ok(MyScalarValue::Long(value)) + } + + fn visit_u32(self, value: u32) -> Result + 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(self, value: u64) -> Result + 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(self, value: f64) -> Result { + Ok(MyScalarValue::Float(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.into()) + } + + fn visit_string(self, value: String) -> Result { + Ok(MyScalarValue::String(value)) + } + } + + deserializer.deserialize_any(MyScalarValueVisitor) + } +} + +graphql_scalar!(i64 as "Long" where Scalar = MyScalarValue { + resolve(&self) -> Value { + Value::scalar(*self) + } + + from_input_value(v: &InputValue) -> Option { + match *v { + InputValue::Scalar(ref i) => <_ as Into>>::into(i), + _ => None, + } + } + + from_str(value: &str) -> Result { + value + .parse() + .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) + .map(|s: i64| s.into()) + } +}); + +struct TestType; + +impl GraphQLType for TestType { + type Context = (); + type TypeInfo = (); + + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("TestType") + } + + fn meta<'r>( + info: &Self::TypeInfo, + registry: &mut Registry<'r, MyScalarValue>, + ) -> MetaType<'r, MyScalarValue> + where + MyScalarValue: 'r, + for<'b> &'b MyScalarValue: ScalarRefValue<'b>, + { + let long_field = registry.field::("longField", info); + + let long_arg = registry.arg::("longArg", info); + + let long_field_with_arg = registry + .field::("longWithArg", info) + .argument(long_arg); + + registry + .build_object_type::(info, &[long_field, long_field_with_arg]) + .into_meta() + } + + fn resolve_field( + &self, + info: &Self::TypeInfo, + field_name: &str, + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field_name { + "longField" => Ok(Value::Scalar(MyScalarValue::Long( + (::std::i32::MAX as i64) + 1, + ))), + "longWithArg" => Ok(Value::Scalar(MyScalarValue::Long( + args.get::("longArg").unwrap(), + ))), + _ => unreachable!(), + } + } +} + +fn run_variable_query(query: &str, vars: Variables, f: F) +where + F: Fn(&Object) -> (), +{ + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + + let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed"); + + assert_eq!(errs, []); + + println!("Result: {:?}", result); + + let obj = result.as_object_value().expect("Result is not an object"); + + f(obj); +} + +fn run_query(query: &str, f: F) +where + F: Fn(&Object) -> (), +{ + run_variable_query(query, Variables::new(), f); +} + +#[test] +fn querying_long() { + run_query("{ longField }", |result| { + assert_eq!( + result.get_field_value("longField"), + Some(&Value::scalar((::std::i32::MAX as i64) + 1)) + ); + }); +} + +#[test] +fn querying_long_arg() { + run_query( + &format!( + "{{ longWithArg(longArg: {}) }}", + (::std::i32::MAX as i64) + 3 + ), + |result| { + assert_eq!( + result.get_field_value("longWithArg"), + Some(&Value::scalar((::std::i32::MAX as i64) + 3)) + ); + }, + ); +} + +#[test] +fn querying_long_variable() { + run_variable_query( + "query q($test: Long!){ longWithArg(longArg: $test) }", + vec![( + "test".to_owned(), + InputValue::Scalar(MyScalarValue::Long((::std::i32::MAX as i64) + 42)), + )].into_iter() + .collect(), + |result| { + assert_eq!( + result.get_field_value("longWithArg"), + Some(&Value::scalar((::std::i32::MAX as i64) + 42)) + ); + }, + ); +} diff --git a/juniper/src/executor_tests/directives.rs b/juniper/src/executor_tests/directives.rs index 03fa0b0a0..00189c858 100644 --- a/juniper/src/executor_tests/directives.rs +++ b/juniper/src/executor_tests/directives.rs @@ -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(query: &str, vars: Variables, f: F) +fn run_variable_query(query: &str, vars: Variables, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); @@ -34,7 +34,7 @@ where fn run_query(query: &str, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { run_variable_query(query, Variables::new(), f); } diff --git a/juniper/src/executor_tests/enums.rs b/juniper/src/executor_tests/enums.rs index eacc075cf..09223ce72 100644 --- a/juniper/src/executor_tests/enums.rs +++ b/juniper/src/executor_tests/enums.rs @@ -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(query: &str, vars: Variables, f: F) +fn run_variable_query(query: &str, vars: Variables, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); @@ -45,7 +44,7 @@ where fn run_query(query: &str, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { run_variable_query(query, Variables::new(), f); } @@ -53,20 +52,27 @@ 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::string("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::string("RED")) + ); }); } #[test] fn does_not_accept_string_literals() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ toString(color: "RED") }"#; let vars = vec![].into_iter().collect(); @@ -90,14 +96,18 @@ fn accepts_strings_in_variables() { .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::string("Color::Red")) + ); }, ); } #[test] fn does_not_accept_incorrect_enum_name_in_variables() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![("color".to_owned(), InputValue::string("BLURPLE"))] @@ -117,7 +127,8 @@ fn does_not_accept_incorrect_enum_name_in_variables() { #[test] fn does_not_accept_incorrect_type_in_variables() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![("color".to_owned(), InputValue::int(123))] diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index 12dc41c1f..d295c744d 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -2,7 +2,7 @@ mod field_execution { use ast::InputValue; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{DefaultScalarValue, Value}; struct DataType; struct DeepDataType; @@ -34,7 +34,8 @@ mod field_execution { #[test] fn test() { - let schema = RootNode::new(DataType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(DataType, EmptyMutation::<()>::new()); let doc = r" query Example($size: Int) { a, @@ -104,7 +105,7 @@ mod field_execution { ("a", Value::string("Apple")), ("b", Value::string("Banana")), ].into_iter() - .collect(), + .collect(), ), Value::null(), Value::object( @@ -112,16 +113,16 @@ mod field_execution { ("a", Value::string("Apple")), ("b", Value::string("Banana")), ].into_iter() - .collect(), + .collect(), ), ]), ), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect() + .collect() ) ); } @@ -130,7 +131,7 @@ mod field_execution { mod merge_parallel_fragments { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{DefaultScalarValue, Value}; struct Type; @@ -143,7 +144,8 @@ mod merge_parallel_fragments { #[test] fn test() { - let schema = RootNode::new(Type, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Type, EmptyMutation::<()>::new()); let doc = r" { a, ...FragOne, ...FragTwo } fragment FragOne on Type { @@ -181,17 +183,17 @@ mod merge_parallel_fragments { ("b", Value::string("Banana")), ("c", Value::string("Cherry")), ].into_iter() - .collect(), + .collect(), ), ), ("c", Value::string("Cherry")), ].into_iter() - .collect(), + .collect(), ), ), ("c", Value::string("Cherry")), ].into_iter() - .collect() + .collect() ) ); } @@ -200,7 +202,7 @@ mod merge_parallel_fragments { mod merge_parallel_inline_fragments { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{Value, DefaultScalarValue}; struct Type; struct Other; @@ -223,12 +225,13 @@ mod merge_parallel_inline_fragments { #[test] fn test() { - let schema = RootNode::new(Type, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Type, EmptyMutation::<()>::new()); let doc = r" { a, ...FragOne } fragment FragOne on Type { b - deep: deep { + deep: deep { b deeper: other { deepest: deep { @@ -279,10 +282,10 @@ mod merge_parallel_inline_fragments { ("b", Value::string("Banana")), ("c", Value::string("Cherry")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), + .collect(), ), Value::object( vec![( @@ -292,21 +295,21 @@ mod merge_parallel_inline_fragments { ("b", Value::string("Banana")), ("c", Value::string("Cherry")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), + .collect(), ), ]), ), ("c", Value::string("Cherry")), ].into_iter() - .collect(), + .collect(), ), ), ("c", Value::string("Cherry")), ].into_iter() - .collect() + .collect() ) ); } @@ -316,7 +319,7 @@ mod threads_context_correctly { use executor::Context; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{Value, DefaultScalarValue}; struct Schema; @@ -332,7 +335,8 @@ mod threads_context_correctly { #[test] fn test() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -369,7 +373,7 @@ mod dynamic_context_switching { use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{Value, DefaultScalarValue}; struct Schema; @@ -391,14 +395,14 @@ mod dynamic_context_switching { executor.context().items.get(&key).map(|c| (c, ItemRef)) } - field item_res(&executor, key: i32) -> FieldResult<(&InnerContext, ItemRef)> { + field item_res(&executor, key: i32) -> FieldResult<(&InnerContext, ItemRef), __S> { let res = executor.context().items.get(&key) .ok_or(format!("Could not find key {}", key)) .map(|c| (c, ItemRef))?; Ok(res) } - field item_res_opt(&executor, key: i32) -> FieldResult> { + field item_res_opt(&executor, key: i32) -> FieldResult, __S> { if key > 100 { Err(format!("Key too large: {}", key))?; } @@ -419,7 +423,8 @@ mod dynamic_context_switching { #[test] fn test_opt() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ first: itemOpt(key: 0) { value }, missing: itemOpt(key: 2) { value } }"; let vars = vec![].into_iter().collect(); @@ -439,7 +444,7 @@ mod dynamic_context_switching { }, ), ].into_iter() - .collect(), + .collect(), }; let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed"); @@ -462,14 +467,15 @@ mod dynamic_context_switching { ), ("missing", Value::null()), ].into_iter() - .collect() + .collect() ) ); } #[test] fn test_res_success() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { first: itemRes(key: 0) { value } @@ -493,7 +499,7 @@ mod dynamic_context_switching { }, ), ].into_iter() - .collect(), + .collect(), }; let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed"); @@ -513,14 +519,15 @@ mod dynamic_context_switching { .collect(), ), )].into_iter() - .collect() + .collect() ) ); } #[test] fn test_res_fail() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { missing: itemRes(key: 2) { value } @@ -544,7 +551,7 @@ mod dynamic_context_switching { }, ), ].into_iter() - .collect(), + .collect(), }; let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed"); @@ -565,7 +572,8 @@ mod dynamic_context_switching { #[test] fn test_res_opt() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { first: itemResOpt(key: 0) { value } @@ -591,7 +599,7 @@ mod dynamic_context_switching { }, ), ].into_iter() - .collect(), + .collect(), }; let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed"); @@ -622,14 +630,15 @@ mod dynamic_context_switching { ("missing", Value::null()), ("tooLarge", Value::null()), ].into_iter() - .collect() + .collect() ) ); } #[test] fn test_always() { - let schema = RootNode::new(Schema, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ first: itemAlways(key: 0) { value } }"; let vars = vec![].into_iter().collect(); @@ -649,7 +658,7 @@ mod dynamic_context_switching { }, ), ].into_iter() - .collect(), + .collect(), }; let (result, errs) = ::execute(doc, None, &schema, &vars, &ctx).expect("Execution failed"); @@ -669,7 +678,7 @@ mod dynamic_context_switching { .collect(), ), )].into_iter() - .collect() + .collect() ) ); } @@ -680,7 +689,7 @@ mod propagates_errors_to_nullable_fields { use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{DefaultScalarValue, ScalarValue, Value}; struct Schema; struct Inner; @@ -689,15 +698,18 @@ mod propagates_errors_to_nullable_fields { NotFound, } - impl IntoFieldError for CustomError { - fn into_field_error(self) -> FieldError { + impl IntoFieldError for CustomError + where + S: ScalarValue, + { + fn into_field_error(self) -> FieldError { match self { - CustomError::NotFound => FieldError::new( - "Not Found", - graphql_value!({ + CustomError::NotFound => { + let v: Value = graphql_value!({ "type": "NOT_FOUND" - }), - ), + }); + FieldError::new("Not Found", v) + } } } } @@ -711,14 +723,15 @@ mod propagates_errors_to_nullable_fields { graphql_object!(Inner: () |&self| { field nullable_field() -> Option { Some(Inner) } field non_nullable_field() -> Inner { Inner } - field nullable_error_field() -> FieldResult> { Err("Error for nullableErrorField")? } - field non_nullable_error_field() -> FieldResult<&str> { Err("Error for nonNullableErrorField")? } + field nullable_error_field() -> FieldResult, __S> { Err("Error for nullableErrorField")? } + field non_nullable_error_field() -> FieldResult<&str, __S> { Err("Error for nonNullableErrorField")? } field custom_error_field() -> Result<&str, CustomError> { Err(CustomError::NotFound) } }); #[test] fn nullable_first_level() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -744,7 +757,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_nullable_first_level() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -767,7 +781,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn custom_error_first_level() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { customErrorField } }"; let vars = vec![].into_iter().collect(); @@ -790,7 +805,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn nullable_nested_level() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nullableField { nonNullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -816,7 +832,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_nullable_nested_level() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableField { nonNullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -839,7 +856,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn nullable_innermost() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableField { nullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -865,7 +883,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_null_list() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inners { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -888,7 +907,8 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_null_list_of_nullable() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ nullableInners { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -938,7 +958,7 @@ mod propagates_errors_to_nullable_fields { mod named_operations { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{Value, DefaultScalarValue}; use GraphQLError; struct Schema; @@ -949,7 +969,8 @@ mod named_operations { #[test] fn uses_inline_operation_if_no_name_provided() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -966,7 +987,8 @@ mod named_operations { #[test] fn uses_only_named_operation() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { a }"; let vars = vec![].into_iter().collect(); @@ -983,7 +1005,8 @@ mod named_operations { #[test] fn uses_named_operation_if_name_provided() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -1001,7 +1024,8 @@ mod named_operations { #[test] fn error_if_multiple_operations_provided_but_no_name() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -1013,7 +1037,8 @@ mod named_operations { #[test] fn error_if_unknown_operation_name_provided() { - let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs index 55b849c90..460803bc8 100644 --- a/juniper/src/executor_tests/interfaces_unions.rs +++ b/juniper/src/executor_tests/interfaces_unions.rs @@ -1,7 +1,7 @@ mod interface { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{DefaultScalarValue, Value}; trait Pet { fn name(&self) -> &str; @@ -77,7 +77,7 @@ mod interface { #[test] fn test() { - let schema = RootNode::new( + let schema: RootNode = RootNode::new( Schema { pets: vec![ Box::new(Dog { @@ -124,18 +124,18 @@ mod interface { ("name", Value::string("Odie")), ("woofs", Value::boolean(true)), ].into_iter() - .collect(), + .collect(), ), Value::object( vec![ ("name", Value::string("Garfield")), ("meows", Value::boolean(false)), ].into_iter() - .collect(), + .collect(), ), ]), )].into_iter() - .collect() + .collect() ) ); } @@ -144,7 +144,7 @@ mod interface { mod union { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{Value, DefaultScalarValue}; trait Pet { fn as_dog(&self) -> Option<&Dog> { @@ -206,7 +206,7 @@ mod union { #[test] fn test() { - let schema = RootNode::new( + let schema: RootNode = RootNode::new( Schema { pets: vec![ Box::new(Dog { @@ -256,7 +256,7 @@ mod union { ("name", Value::string("Odie")), ("woofs", Value::boolean(true)), ].into_iter() - .collect(), + .collect(), ), Value::object( vec![ @@ -264,11 +264,11 @@ mod union { ("name", Value::string("Garfield")), ("meows", Value::boolean(false)), ].into_iter() - .collect(), + .collect(), ), ]), )].into_iter() - .collect() + .collect() ) ); } diff --git a/juniper/src/executor_tests/introspection/enums.rs b/juniper/src/executor_tests/introspection/enums.rs index 05f7a5b65..e2c398135 100644 --- a/juniper/src/executor_tests/introspection/enums.rs +++ b/juniper/src/executor_tests/introspection/enums.rs @@ -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(doc: &str, f: F) where - F: Fn((&Object, &Vec)) -> (), + F: Fn((&Object, &Vec>)) -> (), { let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index cef3d7482..f29dbc55f 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -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(doc: &str, f: F) where - F: Fn(&Object, &Vec) -> (), + F: Fn(&Object, &Vec>) -> (), { let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); @@ -154,8 +147,14 @@ 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::string("DefaultName")) + ); + assert_eq!( + type_info.get_field_value("description"), + Some(&Value::null()) + ); assert_eq!(fields.len(), 2); @@ -175,12 +174,12 @@ fn default_name_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); @@ -200,12 +199,12 @@ fn default_name_introspection() { .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 = InputValue::object( vec![ ("fieldOne", InputValue::string("number one")), ("fieldTwo", InputValue::string("number two")), ].into_iter() - .collect(), + .collect(), ); let dv: Option = FromInputValue::from_input_value(&iv); @@ -257,7 +256,10 @@ fn no_trailing_comma_introspection() { type_info.get_field_value("name"), Some(&Value::string("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); @@ -277,12 +279,12 @@ fn no_trailing_comma_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); @@ -302,12 +304,12 @@ fn no_trailing_comma_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -335,8 +337,14 @@ 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::string("Derive")) + ); + assert_eq!( + type_info.get_field_value("description"), + Some(&Value::null()) + ); assert_eq!(fields.len(), 1); @@ -356,12 +364,12 @@ fn derive_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -406,7 +414,10 @@ fn named_introspection() { type_info.get_field_value("name"), Some(&Value::string("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); @@ -426,12 +437,12 @@ fn named_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -459,7 +470,10 @@ 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::string("Description")) + ); assert_eq!( type_info.get_field_value("description"), Some(&Value::string("Description for the input object")) @@ -483,12 +497,12 @@ fn description_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -520,7 +534,10 @@ fn field_description_introspection() { type_info.get_field_value("name"), Some(&Value::string("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); @@ -540,12 +557,12 @@ fn field_description_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); @@ -565,12 +582,12 @@ fn field_description_introspection() { .collect(), ), )].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -611,7 +628,7 @@ fn field_with_defaults_introspection() { ), ("defaultValue", Value::string("123")), ].into_iter() - .collect(), + .collect(), )) ); @@ -625,7 +642,7 @@ fn field_with_defaults_introspection() { ), ("defaultValue", Value::string("456")), ].into_iter() - .collect(), + .collect(), )) ); }); diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index e7f22ce53..ef7ba5a42 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -8,10 +8,11 @@ use self::input_object::{NamedPublic, NamedPublicWithDescription}; use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::Value; +use parser::ParseError; +use value::{DefaultScalarValue, Value, ParseScalarValue}; #[derive(GraphQLEnum)] -#[graphql(name = "SampleEnum", _internal)] +#[graphql(name = "SampleEnum")] enum Sample { One, Two, @@ -23,7 +24,7 @@ struct Interface {} struct Root {} -graphql_scalar!(Scalar as "SampleScalar" { +graphql_scalar!(Scalar as "SampleScalar" where Scalar = { resolve(&self) -> Value { Value::int(self.0) } @@ -31,6 +32,10 @@ graphql_scalar!(Scalar as "SampleScalar" { from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| Scalar(i)) } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); graphql_interface!(Interface: () as "SampleInterface" |&self| { @@ -71,7 +76,8 @@ fn test_execution() { second: sampleScalar(first: 10 second: 20) } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -88,7 +94,7 @@ fn test_execution() { ("first", Value::int(123)), ("second", Value::int(30)), ].into_iter() - .collect() + .collect() ) ); } @@ -114,7 +120,8 @@ fn enum_introspection() { } } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -173,7 +180,7 @@ fn enum_introspection() { ("isDeprecated", Value::boolean(false)), ("deprecationReason", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); @@ -185,7 +192,7 @@ fn enum_introspection() { ("isDeprecated", Value::boolean(false)), ("deprecationReason", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); } @@ -225,7 +232,8 @@ fn interface_introspection() { } } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -310,17 +318,17 @@ fn interface_introspection() { ("name", Value::string("SampleEnum")), ("kind", Value::string("ENUM")), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect(), + .collect(), ), ), ("isDeprecated", Value::boolean(false)), ("deprecationReason", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); } @@ -371,7 +379,8 @@ fn object_introspection() { } } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -451,17 +460,17 @@ fn object_introspection() { ("name", Value::string("SampleEnum")), ("kind", Value::string("ENUM")), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect(), + .collect(), ), ), ("isDeprecated", Value::boolean(false)), ("deprecationReason", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); @@ -494,16 +503,16 @@ fn object_introspection() { ("kind", Value::string("SCALAR")), ("ofType", Value::null()), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::null()), ].into_iter() - .collect(), + .collect(), ), Value::object( vec![ @@ -517,12 +526,12 @@ fn object_introspection() { ("kind", Value::string("SCALAR")), ("ofType", Value::null()), ].into_iter() - .collect(), + .collect(), ), ), ("defaultValue", Value::string("123")), ].into_iter() - .collect(), + .collect(), ), ]), ), @@ -539,17 +548,17 @@ fn object_introspection() { ("name", Value::string("SampleScalar")), ("kind", Value::string("SCALAR")), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect(), + .collect(), ), ), ("isDeprecated", Value::boolean(false)), ("deprecationReason", Value::null()), ].into_iter() - .collect(), + .collect(), )) ); } @@ -571,7 +580,8 @@ fn scalar_introspection() { } } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -600,7 +610,7 @@ fn scalar_introspection() { ("inputFields", Value::null()), ("ofType", Value::null()), ].into_iter() - .collect() + .collect() ) ); } diff --git a/juniper/src/executor_tests/mod.rs b/juniper/src/executor_tests/mod.rs index 010976183..53d32e175 100644 --- a/juniper/src/executor_tests/mod.rs +++ b/juniper/src/executor_tests/mod.rs @@ -4,3 +4,4 @@ mod executor; mod interfaces_unions; mod introspection; mod variables; +mod custom_scalar; diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 8af05fd6d..1d5ef860a 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -1,10 +1,11 @@ -use ast::{FromInputValue, InputValue}; +use ast::InputValue; use executor::Variables; +use parser::ParseError; use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; use validation::RuleError; -use value::{Object, Value}; +use value::{DefaultScalarValue, Object, ParseScalarValue, Value}; use GraphQLError::ValidationError; #[derive(Debug)] @@ -12,7 +13,7 @@ struct TestComplexScalar; struct TestType; -graphql_scalar!(TestComplexScalar { +graphql_scalar!(TestComplexScalar where Scalar = { resolve(&self) -> Value { Value::string("SerializedValue") } @@ -26,10 +27,13 @@ graphql_scalar!(TestComplexScalar { None } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); #[derive(GraphQLInputObject, Debug)] -#[graphql(_internal)] struct TestInputObject { a: Option, b: Option>>, @@ -38,67 +42,23 @@ struct TestInputObject { } #[derive(GraphQLInputObject, Debug)] -#[graphql(_internal)] struct TestNestedInputObject { na: TestInputObject, nb: String, } #[derive(GraphQLInputObject, Debug)] -#[graphql(_internal)] struct ExampleInputObject { a: Option, 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 { - 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::("variant", &()), - registry.arg::("value", &()), - ]; - registry - .build_input_object_type::(&(), fields) - .into_meta() - } -} - graphql_object!(TestType: () |&self| { field field_with_object_input(input: Option) -> String { format!("{:?}", input) @@ -144,10 +104,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 +113,9 @@ graphql_object!(TestType: () |&self| { } }); -fn run_variable_query(query: &str, vars: Variables, f: F) +fn run_variable_query(query: &str, vars: Variables, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); @@ -167,7 +123,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 +132,7 @@ where fn run_query(query: &str, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { run_variable_query(query, Variables::new(), f); } @@ -185,7 +141,7 @@ where fn inline_complex_input() { run_query( r#"{ fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"}) }"#, - |result| { + |result: &Object| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); @@ -197,7 +153,7 @@ 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| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); @@ -209,7 +165,7 @@ 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| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#))); @@ -217,59 +173,6 @@ fn inline_runs_from_input_value_on_scalar() { ); } -#[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( @@ -282,11 +185,11 @@ fn variable_complex_input() { ("b", InputValue::list(vec![InputValue::string("bar")])), ("c", InputValue::string("baz")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), - |result| { + .collect(), + |result: &Object| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); @@ -306,11 +209,11 @@ fn variable_parse_single_value_to_list() { ("b", InputValue::string("bar")), ("c", InputValue::string("baz")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), - |result| { + .collect(), + |result: &Object| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#))); @@ -329,11 +232,11 @@ fn variable_runs_from_input_value_on_scalar() { ("c", InputValue::string("baz")), ("d", InputValue::string("SerializedValue")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), - |result| { + .collect(), + |result: &Object| { assert_eq!( result.get_field_value("fieldWithObjectInput"), Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#))); @@ -343,7 +246,8 @@ fn variable_runs_from_input_value_on_scalar() { #[test] fn variable_error_on_nested_non_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -354,10 +258,10 @@ fn variable_error_on_nested_non_null() { ("b", InputValue::string("bar")), ("c", InputValue::null()), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -372,7 +276,8 @@ fn variable_error_on_nested_non_null() { #[test] fn variable_error_on_incorrect_type() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + 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"))] @@ -391,7 +296,8 @@ fn variable_error_on_incorrect_type() { #[test] fn variable_error_on_omit_non_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -401,10 +307,10 @@ fn variable_error_on_omit_non_null() { ("a", InputValue::string("foo")), ("b", InputValue::string("bar")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -419,7 +325,8 @@ fn variable_error_on_omit_non_null() { #[test] fn variable_multiple_errors_with_nesting() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestNestedInputObject) { fieldWithNestedObjectInput(input: $input) }"#; @@ -430,10 +337,10 @@ fn variable_multiple_errors_with_nesting() { "na", InputValue::object(vec![("a", InputValue::string("foo"))].into_iter().collect()), )].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -451,7 +358,8 @@ fn variable_multiple_errors_with_nesting() { #[test] fn variable_error_on_additional_field() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -463,10 +371,10 @@ fn variable_error_on_additional_field() { ("c", InputValue::string("baz")), ("extra", InputValue::string("dog")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -481,19 +389,22 @@ 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| { + assert_eq!( + result.get_field_value("fieldWithNullableStringInput"), + Some(&Value::string(r#"None"#)) + ); + }, + ); } #[test] fn allow_nullable_inputs_to_be_omitted_in_variable() { run_query( r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#, - |result| { + |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), Some(&Value::string(r#"None"#)) @@ -506,7 +417,7 @@ 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| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), Some(&Value::string(r#"None"#)) @@ -522,7 +433,7 @@ fn allow_nullable_inputs_to_be_set_to_null_in_variable() { vec![("value".to_owned(), InputValue::null())] .into_iter() .collect(), - |result| { + |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), Some(&Value::string(r#"None"#)) @@ -538,7 +449,7 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() { vec![("value".to_owned(), InputValue::string("a"))] .into_iter() .collect(), - |result| { + |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), Some(&Value::string(r#"Some("a")"#)) @@ -551,7 +462,7 @@ 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| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), Some(&Value::string(r#"Some("a")"#)) @@ -562,7 +473,8 @@ fn allow_nullable_inputs_to_be_set_to_value_directly() { #[test] fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![].into_iter().collect(); @@ -580,7 +492,8 @@ fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { #[test] fn does_not_allow_non_nullable_input_to_be_set_to_null_in_variable() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![("value".to_owned(), InputValue::null())] @@ -618,7 +531,7 @@ 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| { assert_eq!( result.get_field_value("fieldWithNonNullableStringInput"), Some(&Value::string(r#""a""#)) @@ -634,8 +547,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| { + assert_eq!( + result.get_field_value("list"), + Some(&Value::string(r#"None"#)) + ); }, ); } @@ -648,7 +564,7 @@ fn allow_lists_to_contain_values() { "input".to_owned(), InputValue::list(vec![InputValue::string("A")]), )].into_iter() - .collect(), + .collect(), |result| { assert_eq!( result.get_field_value("list"), @@ -670,7 +586,7 @@ fn allow_lists_to_contain_null() { InputValue::string("B"), ]), )].into_iter() - .collect(), + .collect(), |result| { assert_eq!( result.get_field_value("list"), @@ -682,7 +598,8 @@ fn allow_lists_to_contain_null() { #[test] fn does_not_allow_non_null_lists_to_be_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String]!) { nnList(input: $input) }"#; let vars = vec![("input".to_owned(), InputValue::null())] @@ -708,9 +625,12 @@ fn allow_non_null_lists_to_contain_values() { "input".to_owned(), InputValue::list(vec![InputValue::string("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::string(r#"[Some("A")]"#)) + ); }, ); } @@ -726,7 +646,7 @@ fn allow_non_null_lists_to_contain_null() { InputValue::string("B"), ]), )].into_iter() - .collect(), + .collect(), |result| { assert_eq!( result.get_field_value("nnList"), @@ -744,7 +664,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::string(r#"None"#)) + ); }, ); } @@ -757,16 +680,20 @@ fn allow_lists_of_non_null_to_contain_values() { "input".to_owned(), InputValue::list(vec![InputValue::string("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::string(r#"Some(["A"])"#)) + ); }, ); } #[test] fn does_not_allow_lists_of_non_null_to_contain_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]) { listNn(input: $input) }"#; let vars = vec![( @@ -777,7 +704,7 @@ fn does_not_allow_lists_of_non_null_to_contain_null() { InputValue::string("B"), ]), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -791,7 +718,8 @@ fn does_not_allow_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![( @@ -802,7 +730,7 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { InputValue::string("B"), ]), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -816,7 +744,8 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_be_null() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![("value".to_owned(), InputValue::null())] @@ -842,23 +771,27 @@ fn allow_non_null_lists_of_non_null_to_contain_values() { "input".to_owned(), InputValue::list(vec![InputValue::string("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::string(r#"["A"]"#)) + ); }, ); } #[test] fn does_not_allow_invalid_types_to_be_used_as_values() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); 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")]), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -872,14 +805,15 @@ fn does_not_allow_invalid_types_to_be_used_as_values() { #[test] fn does_not_allow_unknown_types_to_be_used_as_values() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); 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")]), )].into_iter() - .collect(); + .collect(); let error = ::execute(query, None, &schema, &vars, &()).unwrap_err(); @@ -989,7 +923,8 @@ fn nullable_input_object_arguments_successful_with_variables() { #[test] fn does_not_allow_missing_required_field() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ exampleInput(arg: {a: "abc"}) }"#; let vars = vec![].into_iter().collect(); @@ -1007,7 +942,8 @@ fn does_not_allow_missing_required_field() { #[test] fn does_not_allow_null_in_required_field() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ exampleInput(arg: {a: "abc", b: null}) }"#; let vars = vec![].into_iter().collect(); @@ -1025,7 +961,8 @@ fn does_not_allow_null_in_required_field() { #[test] fn does_not_allow_missing_variable_for_required_field() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let vars = vec![].into_iter().collect(); @@ -1043,7 +980,8 @@ fn does_not_allow_missing_variable_for_required_field() { #[test] fn does_not_allow_null_variable_for_required_field() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let vars = vec![("var".to_owned(), InputValue::null())] @@ -1142,7 +1080,8 @@ mod integers { #[test] fn does_not_coerce_from_float() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + 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))] @@ -1162,7 +1101,8 @@ mod integers { #[test] fn does_not_coerce_from_string() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let vars = vec![("var".to_owned(), InputValue::string("10"))] @@ -1218,7 +1158,8 @@ mod floats { #[test] fn does_not_coerce_from_string() { - let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Float!) { floatInput(value: $var) }"#; let vars = vec![("var".to_owned(), InputValue::string("10"))] diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index 9a1146983..a70f8999d 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -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::{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 +where + S: ScalarValue, +{ query: String, #[serde(rename = "operationName")] operation_name: Option, - variables: Option, + #[serde(bound(deserialize = "InputValue: Deserialize<'de> + Serialize"))] + variables: Option>, } -impl GraphQLRequest { +impl GraphQLRequest +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 { 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, - variables: Option, - ) -> GraphQLRequest { + variables: Option>, + ) -> 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, + root_node: &'a RootNode, context: &CtxT, - ) -> GraphQLResponse<'a> + ) -> GraphQLResponse<'a, S> where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { GraphQLResponse(::execute( &self.query, @@ -83,11 +92,14 @@ 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), GraphQLError<'a>>); +pub struct GraphQLResponse<'a, S>(Result<(Value, Vec>), 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) -> Self { GraphQLResponse(Ok((Value::null(), vec![ExecutionError::at_origin(error)]))) } @@ -100,7 +112,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: Serialize, + ExecutionError: Serialize, + GraphQLError<'a>: Serialize, +{ fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index f6fce0dae..07dd24452 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -15,58 +15,73 @@ */ use chrono::prelude::*; +use parser::ParseError; +use value::{ParseScalarValue, ScalarValue}; use Value; #[doc(hidden)] pub static RFC3339_FORMAT: &'static str = "%Y-%m-%dT%H:%M:%S%.f%:z"; -graphql_scalar!(DateTime as "DateTimeFixedOffset" { +graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = { description: "DateTime" resolve(&self) -> Value { - Value::string(self.to_rfc3339()) + Value::string(&self.to_rfc3339()) } from_input_value(v: &InputValue) -> Option> { v.as_string_value() .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) } + + from_str(value: &str) -> Result { + Ok(S::from(value)) + } }); -graphql_scalar!(DateTime as "DateTimeUtc" { +graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { description: "DateTime" resolve(&self) -> Value { - Value::string(self.to_rfc3339()) + Value::string(&self.to_rfc3339()) } from_input_value(v: &InputValue) -> Option> { v.as_string_value() .and_then(|s| (s.parse::>().ok())) } + + from_str(value: &str) -> Result { + Ok(S::from(value)) + } }); + // Don't use `Date` as the docs say: // "[Date] should be considered ambiguous at best, due to the " // 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 = { description: "NaiveDate" resolve(&self) -> Value { - Value::string(self.format("%Y-%m-%d").to_string()) + Value::string(&self.format("%Y-%m-%d").to_string()) } from_input_value(v: &InputValue) -> Option { v.as_string_value() .and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) } + + from_str(value: &str) -> Result { + Ok(S::from(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 = { description: "NaiveDateTime" resolve(&self) -> Value { @@ -77,14 +92,19 @@ graphql_scalar!(NaiveDateTime { v.as_float_value() .and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0)) } + + from_str(value: &str) -> Result { + >::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 = ::InputValue::string(raw.to_string()); let parsed: DateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = DateTime::parse_from_rfc3339(raw).unwrap(); @@ -108,7 +128,7 @@ mod test { } fn datetime_utc_test(raw: &'static str) { - let input = ::InputValue::String(raw.to_string()); + let input: ::InputValue = ::InputValue::string(raw.to_string()); let parsed: DateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = DateTime::parse_from_rfc3339(raw) @@ -135,7 +155,8 @@ mod test { #[test] fn naivedate_from_input_value() { - let input = ::InputValue::String("1996-12-19".to_string()); + let input: ::InputValue = + ::InputValue::string("1996-12-19".to_string()); let y = 1996; let m = 12; let d = 19; @@ -153,7 +174,7 @@ mod test { #[test] fn naivedatetime_from_input_value() { let raw = 1_000_000_000_f64; - let input = ::InputValue::Float(raw); + let input: ::InputValue = ::InputValue::float(raw); let parsed: NaiveDateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap(); @@ -171,7 +192,7 @@ mod integration_test { use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::Value; + use value::{DefaultScalarValue, Value}; #[test] fn test_serialization() { @@ -200,7 +221,8 @@ mod integration_test { } "#; - let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema: RootNode = + RootNode::new(Root {}, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -222,7 +244,7 @@ mod integration_test { Value::string("1970-01-01T00:01:01+00:00"), ), ].into_iter() - .collect() + .collect() ) ); } diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index 7d11b86b7..1c3144a2e 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -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 ser::Serialize for ExecutionError +where + T: ScalarValue, +{ fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -51,37 +54,52 @@ 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(deserializer: D) -> Result +impl<'de, S> de::Deserialize<'de> for InputValue +where + S: de::Deserialize<'de> + ScalarValue, +{ + fn deserialize(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { - struct InputValueVisitor; + struct InputValueVisitor(::std::marker::PhantomData); + + impl Default for InputValueVisitor { + fn default() -> Self { + InputValueVisitor(Default::default()) + } + } - impl<'de> de::Visitor<'de> for InputValueVisitor { - type Value = InputValue; + impl<'de, S> de::Visitor<'de> for InputValueVisitor + where + S: ScalarValue + de::Deserialize<'de>, + { + type Value = InputValue; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a valid input value") } - fn visit_bool(self, value: bool) -> Result { + fn visit_bool(self, value: bool) -> Result, E> { Ok(InputValue::boolean(value)) } - fn visit_i64(self, value: i64) -> Result + fn visit_i64(self, value: i64) -> Result, E> where E: de::Error, { @@ -97,7 +115,7 @@ impl<'de> de::Deserialize<'de> for InputValue { } } - fn visit_u64(self, value: u64) -> Result + fn visit_u64(self, value: u64) -> Result, E> where E: de::Error, { @@ -113,30 +131,30 @@ impl<'de> de::Deserialize<'de> for InputValue { } } - fn visit_f64(self, value: f64) -> Result { + fn visit_f64(self, value: f64) -> Result, E> { Ok(InputValue::float(value)) } - fn visit_str(self, value: &str) -> Result + fn visit_str(self, value: &str) -> Result, E> where E: de::Error, { self.visit_string(value.into()) } - fn visit_string(self, value: String) -> Result { + fn visit_string(self, value: String) -> Result, E> { Ok(InputValue::string(value)) } - fn visit_none(self) -> Result { + fn visit_none(self) -> Result, E> { Ok(InputValue::null()) } - fn visit_unit(self) -> Result { + fn visit_unit(self) -> Result, E> { Ok(InputValue::null()) } - fn visit_seq(self, mut visitor: V) -> Result + fn visit_seq(self, mut visitor: V) -> Result, V::Error> where V: de::SeqAccess<'de>, { @@ -149,40 +167,45 @@ impl<'de> de::Deserialize<'de> for InputValue { Ok(InputValue::list(values)) } - fn visit_map(self, mut visitor: V) -> Result + fn visit_map(self, mut visitor: V) -> Result, V::Error> where V: de::MapAccess<'de>, { - let mut values: IndexMap = IndexMap::new(); + let mut object = IndexMap::>::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 ser::Serialize for InputValue +where + T: ScalarValue, +{ fn serialize(&self, serializer: S) -> Result 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::>() .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::>() .serialize(serializer), @@ -250,7 +273,10 @@ impl<'a> ser::Serialize for Spanning> { } } -impl ser::Serialize for Object { +impl ser::Serialize for Object +where + T: ser::Serialize, +{ fn serialize(&self, serializer: S) -> Result where S: ser::Serializer, @@ -266,17 +292,17 @@ impl ser::Serialize for Object { } } -impl ser::Serialize for Value { +impl ser::Serialize for Value +where + T: ser::Serialize, +{ fn serialize(&self, serializer: S) -> Result 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), } @@ -290,12 +316,12 @@ mod tests { use serde_json::from_str; use serde_json::to_string; use {FieldError, Value}; - use ::value::Object; + use value::{DefaultScalarValue, Object}; #[test] fn int() { assert_eq!( - from_str::("1235").unwrap(), + from_str::>("1235").unwrap(), InputValue::int(1235) ); } @@ -303,12 +329,12 @@ mod tests { #[test] fn float() { assert_eq!( - from_str::("2.0").unwrap(), + from_str::>("2.0").unwrap(), InputValue::float(2.0) ); // large value without a decimal part is also float assert_eq!( - from_str::("123567890123").unwrap(), + from_str::>("123567890123").unwrap(), InputValue::float(123567890123.0) ); } diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 75bdd5716..dc82118c1 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -1,8 +1,10 @@ use url::Url; +use parser::ParseError; +use value::{ParseScalarValue, ScalarValue}; use Value; -graphql_scalar!(Url { +graphql_scalar!(Url where Scalar = { description: "Url" resolve(&self) -> Value { @@ -13,16 +15,22 @@ graphql_scalar!(Url { v.as_string_value() .and_then(|s| Url::parse(s).ok()) } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); + #[cfg(test)] mod test { use url::Url; + use value::DefaultScalarValue; #[test] fn url_from_input_value() { let raw = "https://example.net/"; - let input = ::InputValue::String(raw.to_string()); + let input: ::InputValue = ::InputValue::string(raw.to_string()); let parsed: Url = ::FromInputValue::from_input_value(&input).unwrap(); let url = Url::parse(raw).unwrap(); diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index 91886007d..290c14fe4 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -1,28 +1,36 @@ use uuid::Uuid; +use parser::ParseError; +use value::{ParseScalarValue, ScalarValue}; use Value; -graphql_scalar!(Uuid { +graphql_scalar!(Uuid where Scalar = { description: "Uuid" resolve(&self) -> Value { - Value::string(self.to_string()) + Value::string(&self.to_string()) } from_input_value(v: &InputValue) -> Option { v.as_string_value() .and_then(|s| Uuid::parse_str(s).ok()) } + + from_str(value: &str) -> Result { + Ok(S::from(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 = ::InputValue::string(raw.to_string()); let parsed: Uuid = ::FromInputValue::from_input_value(&input).unwrap(); let id = Uuid::parse_str(raw).unwrap(); diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 18d05f638..a4269a5be 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -125,7 +125,7 @@ mod macros; mod ast; mod executor; pub mod parser; -mod schema; +pub(crate) mod schema; mod types; mod util; mod validation; @@ -133,7 +133,7 @@ mod validation; // https://github.com/rust-lang/cargo/issues/1520 pub mod http; pub mod integrations; -// TODO: remove this alias export in 0.10. (breaking change) +// // TODO: remove this alias export in 0.10. (breaking change) pub use http::graphiql; #[cfg(all(test, not(feature = "expose-test-schema")))] @@ -144,7 +144,7 @@ pub mod tests; #[cfg(test)] mod executor_tests; -// Needs to be public because macros use it. +// // Needs to be public because macros use it. pub use util::to_camel_case; use executor::execute_validated_query; @@ -159,13 +159,12 @@ 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, ScalarRefValue, ScalarValue, Value, ParseScalarValue}; /// An error that prevented query execution #[derive(Debug, PartialEq)] @@ -179,36 +178,38 @@ 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, - variables: &Variables, + root_node: &'a RootNode, + variables: &Variables, context: &CtxT, -) -> Result<(Value, Vec), GraphQLError<'a>> +) -> Result<(Value, Vec>), GraphQLError<'a>> where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + QueryT: GraphQLType, + MutationT: GraphQLType, { let document = parse_document_source(document_source)?; - { - let errors = validate_input_values(variables, &document, &root_node.schema); + { + let errors = validate_input_values(variables, &document, &root_node.schema); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); + if !errors.is_empty() { + return Err(GraphQLError::ValidationError(errors)); + } } - } - { - let mut ctx = ValidatorContext::new(&root_node.schema, &document); - visit_all_rules(&mut ctx, &document); + { + let mut ctx = ValidatorContext::new(&root_node.schema, &document); + visit_all_rules(&mut ctx, &document); - let errors = ctx.into_errors(); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); + let errors = ctx.into_errors(); + if !errors.is_empty() { + return Err(GraphQLError::ValidationError(errors)); + } } - } execute_validated_query(document, operation_name, root_node, variables, context) } diff --git a/juniper/src/macros/interface.rs b/juniper/src/macros/interface.rs index 129088d1f..3b0bca8ef 100644 --- a/juniper/src/macros/interface.rs +++ b/juniper/src/macros/interface.rs @@ -202,7 +202,7 @@ macro_rules! graphql_interface { $( if ($resolver as Option<$srctype>).is_some() { - return (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned(); + return (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap().to_owned(); } )* @@ -219,7 +219,7 @@ macro_rules! graphql_interface { let $ctxtvar = &$execarg.context(); $( - if $typenamearg == (<$srctype as $crate::GraphQLType>::name(&())).unwrap() { + if $typenamearg == (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap() { return $execarg.resolve(&(), &$resolver); } )* @@ -238,7 +238,10 @@ macro_rules! graphql_interface { $( $items:tt )* } ) => { - graphql_interface!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + graphql_interface!(@as_item, impl<$($lifetime, )* __S> $crate::GraphQLType<__S> for $name + where __S: $crate::ScalarValue, + for<'__b> &'__b __S: $crate::ScalarRefValue<'__b> + { type Context = $ctxt; type TypeInfo = (); @@ -250,8 +253,10 @@ macro_rules! graphql_interface { #[allow(unused_mut)] fn meta<'r>( info: &(), - registry: &mut $crate::Registry<'r> - ) -> $crate::meta::MetaType<'r> { + registry: &mut $crate::Registry<'r, __S> + ) -> $crate::meta::MetaType<'r, __S> + where __S: 'r + { let mut fields = Vec::new(); let mut description = None; graphql_interface!( @@ -272,9 +277,9 @@ macro_rules! graphql_interface { &$mainself, info: &(), field: &str, - args: &$crate::Arguments, - mut executor: &$crate::Executor - ) -> $crate::ExecutionResult { + args: &$crate::Arguments<__S>, + mut executor: &$crate::Executor<__S, Self::Context> + ) -> $crate::ExecutionResult<__S> { __graphql__build_field_matches!( ($outname, $mainself, field, args, executor), (), @@ -292,10 +297,10 @@ macro_rules! graphql_interface { &$mainself, _: &(), type_name: &str, - _: Option<&[$crate::Selection]>, - executor: &$crate::Executor, + _: Option<&[$crate::Selection<__S>]>, + executor: &$crate::Executor<__S, Self::Context>, ) - -> $crate::ExecutionResult + -> $crate::ExecutionResult<__S> { graphql_interface!( @ resolve_into_type, diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 5347fbac6..f9e4b19d5 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -31,5 +31,12 @@ mod field; #[macro_use] mod union; +#[macro_export] +macro_rules! __juniper_use_everything { + () => { + pub use $crate::*; + }; +} + #[cfg(test)] mod tests; diff --git a/juniper/src/macros/object.rs b/juniper/src/macros/object.rs index 96c38f7e2..f77096944 100644 --- a/juniper/src/macros/object.rs +++ b/juniper/src/macros/object.rs @@ -120,7 +120,7 @@ even have to be backed by a trait! ## Emitting errors -`FieldResult` is a type alias for `Result`, where +`FieldResult` is a type alias for `Result>`, where `FieldResult` 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 @@ -137,11 +137,11 @@ automatically via the `?` operator, or you can construct them yourself using struct User { id: String } graphql_object!(User: () |&self| { - field id() -> FieldResult<&String> { + field id() -> FieldResult<&String, __S> { Ok(&self.id) } - field name() -> FieldResult<&String> { + field name() -> FieldResult<&String, __S> { Err("Does not have a name".to_owned())? } }); @@ -372,7 +372,10 @@ macro_rules! graphql_object { ( $($lifetime:tt)* ); $name:ty; $ctxt:ty; $outname:expr; $mainself:ident; $($items:tt)* ) => { - graphql_object!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + graphql_object!(@as_item, impl<$($lifetime,)* __S> $crate::GraphQLType<__S> for $name + where __S: $crate::ScalarValue, + for<'__b> &'__b __S: $crate::ScalarRefValue<'__b>, + { type Context = $ctxt; type TypeInfo = (); @@ -384,8 +387,10 @@ macro_rules! graphql_object { #[allow(unused_mut)] fn meta<'r>( info: &(), - registry: &mut $crate::Registry<'r> - ) -> $crate::meta::MetaType<'r> { + registry: &mut $crate::Registry<'r, __S> + ) -> $crate::meta::MetaType<'r, __S> + where __S: 'r + { let mut fields = Vec::new(); let mut description = None; let mut interfaces: Option> = None; @@ -416,10 +421,10 @@ macro_rules! graphql_object { &$mainself, info: &(), field: &str, - args: &$crate::Arguments, - executor: &$crate::Executor + args: &$crate::Arguments<__S>, + executor: &$crate::Executor<__S, Self::Context> ) - -> $crate::ExecutionResult + -> $crate::ExecutionResult<__S> { __graphql__build_field_matches!( ($outname, $mainself, field, args, executor), diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index 13db7bbb8..d1878056a 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -38,63 +38,136 @@ usable as arguments and default values. */ #[macro_export(local_inner_macros)] macro_rules! graphql_scalar { - ( @as_expr, $e:expr) => { $e }; + ( @as_expr $e:expr) => { $e }; + ( + @insert_generic + <$generic:tt> + ) => { + $generic + }; + ( + @insert_generic + $scalar: ty + ) => { + $scalar + }; + + ( + @impl_trait + impl< <$generic:tt> > $impl_trait:tt for $name:ty { + $($body:tt)+ + } + ) => { + impl<$generic> $crate::$impl_trait<$generic> for $name + where $generic: $crate::ScalarValue, + for<'__b> &'__b $generic: $crate::ScalarRefValue<'__b> + { + $($body)+ + } + }; + + ( + @impl_trait + impl<$scalar:ty> $impl_trait:tt for $name:ty { + $($body:tt)+ + } + ) => { + impl $crate::$impl_trait<$scalar> for $name { + $($body)+ + } + }; + + - // Calls $val.$func($arg) if $arg is not None - ( @maybe_apply, None, $func:ident, $val:expr ) => { $val }; - ( @maybe_apply, $arg:tt, $func:ident, $val:expr ) => { $val.$func($arg) }; // Each of the @parse match arms accumulates data up to a call to @generate // - // ( $name, $outname, $descr ): the name of the Rust type and the name of the - // GraphQL scalar (as a string), and the description of the scalar (as a - // string or none). - // - // ( $resolve_selfvar, $resolve_body ): the "self" argument and body for the - // resolve() method on GraphQLType and the to_input_value() method on ToInputValue. - // - // ( $fiv_arg, $fiv_result, $fiv_body ): the method argument, result type, - // and body for the from() method on FromInputValue. ( @generate, - ( $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, + }, + 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, + }, + ) => { - impl $crate::GraphQLType for $name { - type Context = (); - type TypeInfo = (); + graphql_scalar!( + @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: &Self::TypeInfo, + registry: &mut $crate::Registry<'r, graphql_scalar!(@insert_generic $($scalar)+)> + ) -> $crate::meta::MetaType<'r, graphql_scalar!(@insert_generic $($scalar)+)> + where for<'__b> &'__b graphql_scalar!(@insert_generic $($scalar)+): $crate::ScalarRefValue<'__b>, + graphql_scalar!(@insert_generic $($scalar)+): 'r + { + let meta = registry.build_scalar_type::(info); + $( + let meta = meta.description($descr); + )* + meta.into_meta() + } - fn meta<'r>( - info: &(), - registry: &mut $crate::Registry<'r> - ) -> $crate::meta::MetaType<'r> { - graphql_scalar!( - @maybe_apply, $descr, description, - registry.build_scalar_type::(info)) - .into_meta() + fn resolve( + &$resolve_self_var, + _: &(), + _: Option<&[$crate::Selection]>, + _: &$crate::Executor) -> $crate::Value { + $resolve_body + } + }); + + graphql_scalar!( + @impl_trait + impl<$($scalar)+> ToInputValue for $name { + fn to_input_value(&$resolve_self_var) -> $crate::InputValue { + let v = $resolve_body; + $crate::ToInputValue::to_input_value(&v) + } } + ); - fn resolve( - &$resolve_selfvar, - _: &(), - _: Option<&[$crate::Selection]>, - _: &$crate::Executor) -> $resolve_retval { - $resolve_body + graphql_scalar!( + @impl_trait + impl<$($scalar)+> FromInputValue for $name { + fn from_input_value( + $from_input_value_arg: &$crate::InputValue + ) -> $from_input_value_result { + $from_input_value_body + } } - } + ); - impl $crate::ToInputValue for $name { - fn to_input_value(&$resolve_selfvar) -> $crate::InputValue { - $crate::ToInputValue::to_input_value(&$resolve_body) + graphql_scalar!( + @impl_trait + impl<$($scalar)+> ParseScalarValue for $name { + fn from_str($from_str_arg: &str) -> $from_str_result { + $from_str_body + } } - } + ); impl $crate::FromInputValue for $name { fn from_input_value($fiv_arg: &$crate::InputValue) -> $fiv_result { @@ -106,51 +179,247 @@ macro_rules! graphql_scalar { // No more items to parse ( @parse, - $meta:tt, - $acc:tt, + 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, + 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 = + ) => { + compile_error!("Missing resolve function"); + }; + + ( + @parse, + meta = { + name = $name:ty, + outname = {$($outname:tt)+}, + scalar = {$($scalar:tt)+}, + $(description = $descr:tt,)* + }, + resolve = {$($resolve_body:tt)+}, + $(from_str = {$($from_str_body:tt)+})*, + rest = + ) => { + compile_error!("Missing from_input_value function"); + }; + + ( + @parse, + 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 = + ) =>{ + 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)* + 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) -> Value $body:block $($rest:tt)* ) => { - graphql_scalar!( @parse, $meta, ( ($selfvar, $retval, $body), $fiv ), $($rest)* ); + graphql_scalar!( + @parse, + meta = {$($meta)*}, + resolve = { + self_var = $selfvar, + body = $body, + }, + $(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)* + 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, + 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 + ( + @parse, + meta = { $($meta:tt)* }, + $(resolve = {$($resolve_body:tt)+},)* + $(from_input_value = {$($from_input_value_body:tt)+},)* + $(from_str = {$($from_str_body:tt)+},)* + rest = from_str($value_arg:ident: &str) -> $result:ty $body:block $($rest:tt)* + ) => { + graphql_scalar!( + @parse, + meta = { $($meta)* }, + $(resolve = {$($resolve_body)+},)* + $(from_input_value = {$($from_input_value_body)+},)* + from_str = { + value_arg = $value_arg, + result = $result, + body = $body, + }, + rest = $($rest)* + ); }; // description: ( @parse, - ( $name:ty, $outname:expr, $_ignored:tt ), - $acc:tt, - description: $descr:tt $($rest:tt)* + 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, + 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 "GrahpQLName" where scalar + ($name:ty as $outname:tt where Scalar = <$generic:tt> { $($items:tt)* }) => { + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {$outname}, + scalar = {<$generic>}, + }, + rest = $($items)* + ); }; + ($name:ty as $outname:tt where Scalar = $scalar: ty { $($items:tt)* }) => { + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {$outname}, + scalar = {$scalar}, + }, + rest = $($items)* + ); + }; // Entry point: // RustName as "GraphQLName" { ... } ( $name:ty as $outname:tt { $( $items:tt )* }) => { - graphql_scalar!( @parse, ( $name, $outname, None ), ( None, None ), $($items)* ); + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {$outname}, + scalar = {<__S>}, + }, + rest = $($items)* + ); }; + + ($name:ty where Scalar = <$generic:tt> { $($items:tt)* }) => { + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {stringify!($name)}, + scalar = {<$generic>}, + }, + rest = $($items)* + ); + }; + + ($name:ty where Scalar = $scalar: ty { $($items:tt)* }) => { + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {stringify!($name)}, + scalar = {$scalar}, + }, + rest = $($items)* + ) + }; // Entry point // RustName { ... } ( $name:ty { $( $items:tt )* }) => { - graphql_scalar!( @parse, ( $name, __graphql__stringify!($name), None ), ( None, None ), $($items)* ); + graphql_scalar!( + @parse, + meta = { + name = $name, + outname = {stringify!($name)}, + scalar = {<__S>}, + }, + rest = $($items)* + ); }; } diff --git a/juniper/src/macros/tests/args.rs b/juniper/src/macros/tests/args.rs index 2b2fac79c..3c74ea751 100644 --- a/juniper/src/macros/tests/args.rs +++ b/juniper/src/macros/tests/args.rs @@ -1,7 +1,7 @@ use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::Value; +use value::{Value, DefaultScalarValue}; 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(field_name: &str, f: F) where - F: Fn(&Vec) -> (), + F: Fn(&Vec>) -> (), { let doc = r#" { diff --git a/juniper/src/macros/tests/field.rs b/juniper/src/macros/tests/field.rs index 2e7ee1b42..e3e82e9fe 100644 --- a/juniper/src/macros/tests/field.rs +++ b/juniper/src/macros/tests/field.rs @@ -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; /* @@ -30,11 +31,11 @@ graphql_object!(Root: () |&self| { field deprecated "Deprecation reason" deprecated_descr() -> i32 as "Field description" { 0 } - field with_field_result() -> FieldResult { Ok(0) } + field with_field_result() -> FieldResult { Ok(0) } field with_return() -> i32 { return 0; } - field with_return_field_result() -> FieldResult { return Ok(0); } + field with_return_field_result() -> FieldResult { return Ok(0); } interfaces: [Interface] }); @@ -57,7 +58,7 @@ graphql_interface!(Interface: () |&self| { fn run_field_info_query(type_name: &str, field_name: &str, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { let doc = r#" query ($typeName: String!) { @@ -104,9 +105,9 @@ where .get_field_value("name") .expect("name field missing from field") .as_string_value() - .expect("name is not a string") == field_name - }) - .next() + .expect("name is not a string") + == field_name + }).next() .expect("Field not found") .as_object_value() .expect("Field is not an object"); @@ -165,30 +166,51 @@ fn introspect_object_field_description() { field.get_field_value("description"), Some(&Value::string("Field description")) ); - assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(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::string("description")) + ); assert_eq!( field.get_field_value("description"), Some(&Value::string("Field description")) ); - assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(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::string("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::boolean(true)) + ); assert_eq!( field.get_field_value("deprecationReason"), Some(&Value::string("Deprecation reason")) @@ -199,9 +221,15 @@ 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::string("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::boolean(true)) + ); assert_eq!( field.get_field_value("deprecationReason"), Some(&Value::string("Deprecation reason")) @@ -212,12 +240,18 @@ 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::string("deprecatedDescr")) + ); assert_eq!( field.get_field_value("description"), Some(&Value::string("Field description")) ); - assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true))); + assert_eq!( + field.get_field_value("isDeprecated"), + Some(&Value::boolean(true)) + ); assert_eq!( field.get_field_value("deprecationReason"), Some(&Value::string("Deprecation reason")) @@ -228,12 +262,18 @@ 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::string("deprecatedDescr")) + ); assert_eq!( field.get_field_value("description"), Some(&Value::string("Field description")) ); - assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true))); + assert_eq!( + field.get_field_value("isDeprecated"), + Some(&Value::boolean(true)) + ); assert_eq!( field.get_field_value("deprecationReason"), Some(&Value::string("Deprecation reason")) diff --git a/juniper/src/macros/tests/interface.rs b/juniper/src/macros/tests/interface.rs index 126201e9d..a45474268 100644 --- a/juniper/src/macros/tests/interface.rs +++ b/juniper/src/macros/tests/interface.rs @@ -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(type_name: &str, f: F) where - F: Fn(&Object, &Vec) -> (), + F: Fn(&Object, &Vec>) -> (), { let doc = r#" query ($typeName: String!) { diff --git a/juniper/src/macros/tests/object.rs b/juniper/src/macros/tests/object.rs index 21e460f42..fe046f663 100644 --- a/juniper/src/macros/tests/object.rs +++ b/juniper/src/macros/tests/object.rs @@ -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::{Object, Value, DefaultScalarValue}; /* @@ -116,11 +116,11 @@ graphql_object!(CtxSwitcher: InnerContext |&self| { Some((executor.context(), InnerType)) } - field ctx_switch_res(&executor) -> FieldResult<(&InnerContext, InnerType)> { + field ctx_switch_res(&executor) -> FieldResult<(&InnerContext, InnerType), __S> { Ok((executor.context(), InnerType)) } - field ctx_switch_res_opt(&executor) -> FieldResult> { + field ctx_switch_res_opt(&executor) -> FieldResult, __S> { Ok(Some((executor.context(), InnerType))) } }); @@ -143,7 +143,7 @@ graphql_object!(<'a> Root: InnerContext as "Root" |&self| { fn run_type_info_query(type_name: &str, f: F) where - F: Fn(&Object, &Vec) -> (), + F: Fn(&Object, &Vec>) -> (), { let doc = r#" query ($typeName: String!) { diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index 8cd5dc3d9..36e23935e 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -1,7 +1,8 @@ use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::{Value, Object}; +use value::{Value, Object, DefaultScalarValue, ParseScalarValue}; +use parser::ParseError; struct DefaultName(i32); struct OtherOrder(i32); @@ -19,7 +20,7 @@ Syntax to validate: */ -graphql_scalar!(DefaultName { +graphql_scalar!(DefaultName where Scalar = { resolve(&self) -> Value { Value::int(self.0) } @@ -27,19 +28,26 @@ graphql_scalar!(DefaultName { from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| DefaultName(i)) } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); +graphql_scalar!(OtherOrder where Scalar = { + resolve(&self) -> Value { + Value::int(self.0) + } -graphql_scalar!(OtherOrder { - from_input_value(v: &InputValue) -> Option { + from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| OtherOrder(i)) } - resolve(&self) -> Value { - Value::int(self.0) + + from_str(value: &str) -> Result { + >::from_str(value) } }); - -graphql_scalar!(Named as "ANamedScalar" { +graphql_scalar!(Named as "ANamedScalar" where Scalar = { resolve(&self) -> Value { Value::int(self.0) } @@ -47,9 +55,13 @@ graphql_scalar!(Named as "ANamedScalar" { from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| Named(i)) } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); -graphql_scalar!(ScalarDescription { +graphql_scalar!(ScalarDescription where Scalar = { description: "A sample scalar, represented as an integer" resolve(&self) -> Value { @@ -59,6 +71,10 @@ graphql_scalar!(ScalarDescription { from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| ScalarDescription(i)) } + + from_str(value: &str) -> Result { + >::from_str(value) + } }); graphql_object!(Root: () |&self| { @@ -70,7 +86,7 @@ graphql_object!(Root: () |&self| { fn run_type_info_query(doc: &str, f: F) where - F: Fn(&Object) -> (), + F: Fn(&Object) -> (), { let schema = RootNode::new(Root {}, EmptyMutation::<()>::new()); diff --git a/juniper/src/macros/tests/union.rs b/juniper/src/macros/tests/union.rs index dd622be17..aecccf0d5 100644 --- a/juniper/src/macros/tests/union.rs +++ b/juniper/src/macros/tests/union.rs @@ -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(type_name: &str, f: F) where - F: Fn(&Object, &Vec) -> (), + F: Fn(&Object, &Vec>) -> (), { let doc = r#" query ($typeName: String!) { diff --git a/juniper/src/macros/union.rs b/juniper/src/macros/union.rs index 451125b5b..130ffa4fa 100644 --- a/juniper/src/macros/union.rs +++ b/juniper/src/macros/union.rs @@ -64,7 +64,7 @@ macro_rules! graphql_union { $( if let Some(_) = $resolver as Option<$srctype> { - return (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned(); + return (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap().to_owned(); } )* @@ -82,7 +82,7 @@ macro_rules! graphql_union { let $ctxtvar = &$execarg.context(); $( - if $typenamearg == (<$srctype as $crate::GraphQLType>::name(&())).unwrap().to_owned() { + if $typenamearg == (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap().to_owned() { return $execarg.resolve(&(), &$resolver); } )* @@ -108,7 +108,10 @@ macro_rules! graphql_union { $( $items:tt )* } ) => { - graphql_union!(@as_item, impl<$($lifetime)*> $crate::GraphQLType for $name { + graphql_union!(@as_item, impl<$($lifetime, )* __S> $crate::GraphQLType<__S> for $name + where __S: $crate::value::ScalarValue, + for<'__b> &'__b __S: $crate::value::ScalarRefValue<'__b> + { type Context = $ctxt; type TypeInfo = (); @@ -118,7 +121,9 @@ macro_rules! graphql_union { #[allow(unused_assignments)] #[allow(unused_mut)] - fn meta<'r>(_: &(), registry: &mut $crate::Registry<'r>) -> $crate::meta::MetaType<'r> { + fn meta<'r>(_: &(), registry: &mut $crate::Registry<'r, __S>) -> $crate::meta::MetaType<'r, __S> + where __S: 'r + { let mut types; let mut description = None; graphql_union!(@ gather_meta, (registry, types, description), $($items)*); @@ -142,10 +147,10 @@ macro_rules! graphql_union { &$mainself, _: &(), type_name: &str, - _: Option<&[$crate::Selection]>, - executor: &$crate::Executor, + _: Option<&[$crate::Selection<__S>]>, + executor: &$crate::Executor<__S, Self::Context>, ) - -> $crate::ExecutionResult + -> $crate::ExecutionResult<__S> { graphql_union!( @ resolve_into_type, diff --git a/juniper/src/parser/document.rs b/juniper/src/parser/document.rs index b54b0234e..16cedaa1a 100644 --- a/juniper/src/parser/document.rs +++ b/juniper/src/parser/document.rs @@ -10,19 +10,34 @@ use parser::{ Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token, UnlocatedParseResult, }; +use schema::meta::{Argument, Field as MetaField, MetaType}; +use schema::model::SchemaType; +use value::ScalarValue; #[doc(hidden)] -pub fn parse_document_source(s: &str) -> UnlocatedParseResult { +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,37 @@ 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) + .unwrap_or_else(Vec::new); + let selection_set = parse_selection_set(parser, schema, &fields)?; Ok(Spanning::start_end( &selection_set.start, @@ -58,13 +91,19 @@ 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)).unwrap_or_else(Vec::new); + 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 +119,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 +140,21 @@ 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)) + .ok_or_else(|| { + println!( + "{:?}", + schema.types.values().map(|v| v.name()).collect::>() + ); + println!("{}", type_cond.item); + unimplemented!() + })?; + + let directives = parse_directives(parser, schema)?; + let selection_set = parse_selection_set(parser, schema, &fields)?; Ok(Spanning::start_end( &start_pos, @@ -110,28 +168,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>> { + schema: &'b SchemaType<'b, S>, + fields: &[&MetaField<'b, S>], +) -> OptionParseResult<'a, Vec>> +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>> { - 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: &[&MetaField<'b, S>], +) -> ParseResult<'a, Vec>> +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: &[&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: &[&MetaField<'b, S>], +) -> UnlocatedParseResult<'a, Selection<'a, S>> +where + S: ScalarValue, +{ let Spanning { start: ref start_pos, .. @@ -141,8 +229,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)) + .ok_or_else(|| unimplemented!())?; + 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 +248,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 +262,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 +277,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 +294,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: &[&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 +310,26 @@ 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.iter().find(|f| f.name == name.item).ok_or_else(|| { + println!("Field: {}", name.item); + println!( + "Fields: {:?}", + fields.iter().map(|f| &f.name).collect::>() + ); + unimplemented!() + })?; + + let arguments = parse_arguments( + parser, + schema, + &field.arguments.as_ref().map(|a| a as &[_]).unwrap_or(&[]), + )?; + let fields = schema + .lookup_type(&field.field_type) + .and_then(|m| m.fields(schema)) + .unwrap_or_else(Vec::new); + 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 +350,50 @@ 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: &[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)> { + schema: &'b SchemaType<'b, S>, + arguments: &[Argument<'b, S>], +) -> ParseResult<'a, (Spanning<&'a str>, Spanning>)> +where + S: ScalarValue, +{ let name = parser.expect_name()?; + let arg = arguments + .iter() + .find(|a| a.name == name.item) + .ok_or_else(|| unimplemented!())?; + + let tpe = schema + .lookup_type(&arg.arg_type) + .ok_or_else(|| unimplemented!())?; + 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 +410,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 +424,34 @@ 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) + .ok_or_else(|| unimplemented!())?; 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 +472,41 @@ fn parse_variable_definition<'a>( )) } -fn parse_directives<'a>( +fn parse_directives<'a, 'b, S>( parser: &mut Parser<'a>, -) -> OptionParseResult<'a, Vec>>> { + schema: &'b SchemaType<'b, S>, +) -> OptionParseResult<'a, Vec>>> +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) + .ok_or_else(|| unimplemented!())?; + + let arguments = parse_arguments(parser, schema, &directive.arguments)?; Ok(Spanning::start_end( &start_pos, diff --git a/juniper/src/parser/lexer.rs b/juniper/src/parser/lexer.rs index 6be679947..6c7142443 100644 --- a/juniper/src/parser/lexer.rs +++ b/juniper/src/parser/lexer.rs @@ -21,9 +21,7 @@ pub struct Lexer<'a> { #[allow(missing_docs)] pub enum Token<'a> { Name(&'a str), - Int(i32), - Float(f64), - String(String), + Scalar(&'a str), ExclamationMark, Dollar, ParenOpen, @@ -180,7 +178,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, @@ -201,106 +199,36 @@ impl<'a> Lexer<'a> { Ok(Spanning::start_end( &start_pos, &self.position, - Token::Name(&self.source[start_idx..end_idx + 1]), + Token::Name(&self.source[start_idx..=end_idx]), )) } 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 == '"'); - - let mut acc = String::new(); + if start_ch != '"' { + return Err(Spanning::zero_width( + &self.position, + LexerError::UnterminatedString, + )); + } - 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(); + let mut escaped = false; - match self.peek_char() { - Some((_, '"')) => { - self.next_char(); - acc.push('"'); - } - Some((_, '\\')) => { - self.next_char(); - acc.push('\\'); - } - Some((_, '/')) => { - self.next_char(); - acc.push('/'); - } - Some((_, 'b')) => { - self.next_char(); - acc.push('\u{0008}'); - } - Some((_, 'f')) => { - self.next_char(); - acc.push('\u{000c}'); - } - Some((_, 'n')) => { - self.next_char(); - acc.push('\n'); - } - Some((_, 'r')) => { - self.next_char(); - acc.push('\r'); - } - Some((_, 't')) => { - self.next_char(); - acc.push('\t'); - } - Some((_, 'u')) => { - let start_pos = self.position.clone(); - self.next_char(); - acc.push(self.scan_escaped_unicode(&start_pos)?); - } - Some((_, ch)) => { - let mut s = String::from("\\"); - s.push(ch); - - return Err(Spanning::zero_width( - &self.position, - LexerError::UnknownEscapeSequence(s), - )); - } - None => { - return Err(Spanning::zero_width( - &self.position, - LexerError::UnterminatedString, - )); - } - } - if let Some((_, ch)) = self.peek_char() { - if ch == 'n' {} - } else { - return Err(Spanning::zero_width( + while let Some((idx, ch)) = self.next_char() { + match ch { + '\\' => escaped = true, + '"' if !escaped => { + return Ok(Spanning::start_end( + &start_pos, &self.position, - LexerError::UnterminatedString, + Token::Scalar(&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); + _ => escaped = false, } } @@ -335,7 +263,7 @@ impl<'a> Lexer<'a> { len += 1; } - let escape = &self.source[start_idx..end_idx + 1]; + let escape = &self.source[start_idx..=end_idx]; if len != 4 { return Err(Spanning::zero_width( @@ -360,123 +288,104 @@ impl<'a> Lexer<'a> { } 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 start_pos = self.position; + let (start_idx, _) = self.peek_char().ok_or(Spanning::zero_width( + &self.position, + LexerError::UnexpectedEndOfFile, + ))?; - let mut is_negative = false; + let mut last_idx = start_idx; + let mut last_char = '1'; - if let Some((_, ch)) = self.peek_char() { - if ch == '-' { - self.next_char(); - is_negative = true; - } else if ch == '+' { - self.next_char(); + 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'), + )); } - } - 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) + self.next_char(); + last_char = ch; + } else if last_char == '-' { + return Err(Spanning::zero_width( + &self.position, + LexerError::UnexpectedCharacter(ch), + )); } else { - 0f64 - } - }) - .map(|m| if int_part < 0 { -m } else { m }); - - let exp = exp_part.map(|e| f64::from(e)).map(|e| 10f64.powf(e)); - - Ok(Spanning::start_end( - &start_pos, - &self.position, - 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) + break idx; } - }, - )) - } - - fn scan_integer_part(&mut self) -> Result> { - 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; } }; - 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() { + 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 }) - } - } - - fn scan_digits(&mut self) -> Result> { - 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), - )); + 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; + } + }; } - - 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' { 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; + } + }; } } - - i32::from_str_radix(&self.source[start_idx..end_idx + 1], 10) - .map_err(|_| Spanning::zero_width(&start_pos, LexerError::InvalidNumber)) + let number = &self.source[start_idx..end_idx]; + let end_pos = &self.position; + Ok(Spanning::start_end( + &start_pos, + end_pos, + Token::Scalar(number), + )) } } @@ -529,11 +438,7 @@ 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) => { - write!(f, "\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")) - } + Token::Scalar(s) => write!(f, "{}", s), Token::ExclamationMark => write!(f, "!"), Token::Dollar => write!(f, "$"), Token::ParenOpen => write!(f, "("), diff --git a/juniper/src/parser/parser.rs b/juniper/src/parser/parser.rs index a6105c7e4..01f9bd8db 100644 --- a/juniper/src/parser/parser.rs +++ b/juniper/src/parser/parser.rs @@ -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)), diff --git a/juniper/src/parser/tests/lexer.rs b/juniper/src/parser/tests/lexer.rs index 59cf12f2d..ca1bf2429 100644 --- a/juniper/src/parser/tests/lexer.rs +++ b/juniper/src/parser/tests/lexer.rs @@ -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("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(" 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("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("escaped \n\r\u{0008}\t\u{000c}") ) ); @@ -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("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("unicode \u{1234}\u{5678}\u{90ab}\u{cdef}") ) ); } @@ -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(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("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("-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("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("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("123")), "123"); - assert_eq!(format!("{}", Token::Float(4.5)), "4.5"); + assert_eq!(format!("{}", Token::Scalar("4.5")), "4.5"); assert_eq!( - format!("{}", Token::String("some string".to_owned())), + format!("{}", Token::Scalar("some string")), "\"some string\"" ); assert_eq!( format!( "{}", - Token::String("string with \\ escape and \" quote".to_owned()) + Token::Scalar("string with \\ escape and \" quote") ), "\"string with \\\\ escape and \\\" quote\"" ); diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index cc77729fa..dc0101be9 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -1,20 +1,58 @@ 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 { +use schema::meta::{Field, MetaType, ObjectMeta, ScalarMeta}; +use schema::model::SchemaType; +use types::scalars::EmptyMutation; + +struct Query; + +graphql_object!(Query: () |&self| { + field int_field() -> i32 { + 42 + } + + field float_field() -> f64 { + 3.14 + } + + field string_field() -> String { + "".into() + } + + field bool_field() -> bool { + true + } +}); + +fn scalar_meta(name: &'static str) -> MetaType +where + T: FromInputValue + ParseScalarValue + 'static, +{ + MetaType::Scalar(ScalarMeta::new::(name.into())) +} + +fn parse_value(s: &str, meta: MetaType) -> Spanning> +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::>(&(), &()); - parse_value_literal(&mut parser, false).expect(&format!("Parse error on input {:#?}", s)) + parse_value_literal(&mut parser, false, &schema, &meta) + .expect(&format!("Parse error on input {:#?}", s)) } #[test] fn input_value_literals() { assert_eq!( - parse_value("123"), + parse_value::("123", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(3, 0, 3), @@ -22,7 +60,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("123.45"), + parse_value::("123.45", scalar_meta::("Float")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(6, 0, 6), @@ -30,7 +68,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("true"), + parse_value::("true", scalar_meta::("Bool")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(4, 0, 4), @@ -38,7 +76,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("false"), + parse_value::("false", scalar_meta::("Bool")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(5, 0, 5), @@ -46,7 +84,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value(r#""test""#), + parse_value::(r#""test""#, scalar_meta::("String")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(6, 0, 6), @@ -54,7 +92,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("enum_value"), + parse_value::("enum_value", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(10, 0, 10), @@ -62,7 +100,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("$variable"), + parse_value::("$variable", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(9, 0, 9), @@ -70,7 +108,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("[]"), + parse_value::("[]", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(2, 0, 2), @@ -78,7 +116,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("[1, [2, 3]]"), + parse_value::("[1, [2, 3]]", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(11, 0, 11), @@ -108,15 +146,36 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value("{}"), + parse_value::("{}", scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(2, 0, 2), - InputValue::object(IndexMap::::new()) + InputValue::object(IndexMap::>::new()) ) ); + let fields = [ + Field { + name: "key".into(), + description: None, + arguments: None, + field_type: Type::NonNullNamed("Int".into()), + deprecation_reason: None, + }, + Field { + name: "other".into(), + description: None, + arguments: None, + field_type: Type::NonNullNamed("Bar".into()), + deprecation_reason: None, + }, + ]; + let meta = ObjectMeta::new("foo".into(), &fields); + assert_eq!( - parse_value(r#"{key: 123, other: {foo: "bar"}}"#), + parse_value::( + r#"{key: 123, other: {foo: "bar"}}"#, + MetaType::Object(meta) + ), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(31, 0, 31), diff --git a/juniper/src/parser/utils.rs b/juniper/src/parser/utils.rs index 6f141f256..c80178004 100644 --- a/juniper/src/parser/utils.rs +++ b/juniper/src/parser/utils.rs @@ -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 { +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)] +pub struct Spanning { /// The wrapped item pub item: T, @@ -28,7 +27,7 @@ pub struct Spanning { pub end: SourcePosition, } -impl Spanning { +impl Spanning { #[doc(hidden)] pub fn zero_width(pos: &SourcePosition, item: T) -> Spanning { Spanning { @@ -94,45 +93,6 @@ impl Spanning { } } -impl Clone for Spanning -where - T: Clone + fmt::Debug, -{ - fn clone(&self) -> Self { - Spanning { - start: self.start.clone(), - end: self.end.clone(), - item: self.item.clone(), - } - } -} - -impl PartialEq for Spanning -where - T: PartialEq + fmt::Debug, -{ - fn eq(&self, other: &Self) -> bool { - self.start == other.start && self.end == other.end && self.item == other.item - } -} - -impl Eq for Spanning -where - T: Eq + fmt::Debug, -{ -} - -impl Hash for Spanning -where - T: Hash + fmt::Debug, -{ - fn hash(&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 { diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs index 016df991c..f4d4f9fae 100644 --- a/juniper/src/parser/value.rs +++ b/juniper/src/parser/value.rs @@ -1,99 +1,183 @@ -use ast::InputValue; +use ast::{InputValue, Type}; +use parser::lexer::LexerError; use parser::{ParseError, ParseResult, Parser, Spanning, Token}; +use schema::meta::{MetaType, ObjectMeta}; +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: &MetaType<'b, S>, +) -> ParseResult<'a, InputValue> +where + S: ScalarValue, +{ + match (parser.peek(), tpe) { + ( + &Spanning { + item: Token::BracketOpen, + .. + }, + _, + ) => parse_list_literal(parser, is_const, schema, tpe), + ( + &Spanning { + item: Token::CurlyOpen, + .. + }, + MetaType::Object(ref o), + ) => parse_object_literal(parser, is_const, schema, 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(_), + .. + }, + MetaType::Scalar(ref s), + ) => { + if let Spanning { + item: Token::Scalar(scalar), + start, + end, + } = parser.next()? + { + println!("{:?}", scalar); + (s.parse_fn)(scalar) + .map(|s| Spanning::start_end(&start, &end, InputValue::Scalar(s))) + .map_err(|e| Spanning::start_end(&start, &end, e)) } 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(_), + .. + }, + MetaType::Enum(_), + ) => { + if let Spanning { + item: Token::Scalar(scalar), + start, + end, + } = parser.next()? + { + if let Some(MetaType::Scalar(s)) = schema.concrete_type_by_name("String") { + (s.parse_fn)(scalar) + .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") + } + } else { + unreachable!() + } + } + ( + &Spanning { + item: Token::Name("true"), + .. + }, + MetaType::Scalar(ref _s), + ) => Ok(parser.next()?.map(|_| InputValue::boolean(true))), + ( + &Spanning { + item: Token::Name("false"), + .. + }, + &MetaType::Scalar(ref _s), + ) => Ok(parser.next()?.map(|_| InputValue::boolean(false))), + ( + &Spanning { + item: Token::Name("null"), + .. + }, + _, + ) => Ok(parser.next()?.map(|_| InputValue::null())), + ( + &Spanning { + item: Token::Name(name), + .. + }, + MetaType::Enum(_), + ) => 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: &MetaType<'b, S>, +) -> ParseResult<'a, InputValue> +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: &ObjectMeta<'b, S>, +) -> ParseResult<'a, InputValue> +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, Spanning)> { + schema: &'b SchemaType<'b, S>, + object_meta: &ObjectMeta<'b, S>, +) -> ParseResult<'a, (Spanning, Spanning>)> +where + S: ScalarValue, +{ let key = parser.expect_name()?; + let field = object_meta + .fields + .iter() + .find(|f| f.name == key.item) + .ok_or_else(|| unimplemented!())?; + + let tpe = schema + .lookup_type(&field.field_type) + .ok_or_else(|| unimplemented!())?; + 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 +186,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> +where + S: ScalarValue, +{ let Spanning { start: start_pos, .. } = parser.expect(&Token::Dollar)?; diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index ab6203467..8061ae710 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -4,16 +4,20 @@ use std::borrow::Cow; use std::fmt; use ast::{FromInputValue, InputValue, Type}; +use parser::ParseError; +use schema::model::SchemaType; use types::base::TypeKind; +use value::{ParseScalarValue, ScalarRefValue, ScalarValue}; /// Scalar type metadata -pub struct ScalarMeta<'a> { +pub struct ScalarMeta<'a, S: fmt::Debug> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] pub description: Option, #[doc(hidden)] - pub try_parse_fn: Box bool + Send + Sync>, + pub try_parse_fn: Box) -> bool + Send + Sync>, + pub(crate) parse_fn: fn(&str) -> Result, } /// List type metadata @@ -32,19 +36,19 @@ pub struct NullableMeta<'a> { /// Object type metadata #[derive(Debug)] -pub struct ObjectMeta<'a> { +pub struct ObjectMeta<'a, S: fmt::Debug> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] pub description: Option, #[doc(hidden)] - pub fields: Vec>, + pub fields: Vec>, #[doc(hidden)] pub interface_names: Vec, } /// Enum type metadata -pub struct EnumMeta<'a> { +pub struct EnumMeta<'a, S: fmt::Debug> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -52,18 +56,18 @@ pub struct EnumMeta<'a> { #[doc(hidden)] pub values: Vec, #[doc(hidden)] - pub try_parse_fn: Box bool + Send + Sync>, + pub try_parse_fn: Box) -> bool + Send + Sync>, } /// Interface type metadata #[derive(Debug)] -pub struct InterfaceMeta<'a> { +pub struct InterfaceMeta<'a, S: fmt::Debug> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] pub description: Option, #[doc(hidden)] - pub fields: Vec>, + pub fields: Vec>, } /// Union type metadata @@ -78,15 +82,15 @@ pub struct UnionMeta<'a> { } /// Input object metadata -pub struct InputObjectMeta<'a> { +pub struct InputObjectMeta<'a, S: fmt::Debug> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] pub description: Option, #[doc(hidden)] - pub input_fields: Vec>, + pub input_fields: Vec>, #[doc(hidden)] - pub try_parse_fn: Box bool + Send + Sync>, + pub try_parse_fn: Box) -> bool + Send + Sync>, } /// A placeholder for not-yet-registered types @@ -101,36 +105,36 @@ pub struct PlaceholderMeta<'a> { /// Generic type metadata #[derive(Debug)] -pub enum MetaType<'a> { +pub enum MetaType<'a, S: fmt::Debug> { #[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: fmt::Debug> { #[doc(hidden)] pub name: String, #[doc(hidden)] pub description: Option, #[doc(hidden)] - pub arguments: Option>>, + pub arguments: Option>>, #[doc(hidden)] pub field_type: Type<'a>, #[doc(hidden)] @@ -139,7 +143,7 @@ pub struct Field<'a> { /// Metadata for an argument to a field #[derive(Debug, Clone)] -pub struct Argument<'a> { +pub struct Argument<'a, S: fmt::Debug> { #[doc(hidden)] pub name: String, #[doc(hidden)] @@ -147,7 +151,7 @@ pub struct Argument<'a> { #[doc(hidden)] pub arg_type: Type<'a>, #[doc(hidden)] - pub default_value: Option, + pub default_value: Option>, } /// Metadata for a single value in an enum @@ -168,7 +172,7 @@ pub struct EnumValue { pub deprecation_reason: Option, } -impl<'a> MetaType<'a> { +impl<'a, S: fmt::Debug> MetaType<'a, S> { /// Access the name of the type, if applicable /// /// Lists, non-null wrappers, and placeholders don't have names. @@ -232,7 +236,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> { match *self { MetaType::Object(ObjectMeta { ref fields, .. }) | MetaType::Interface(InterfaceMeta { ref fields, .. }) => { @@ -245,7 +249,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> { match *self { MetaType::InputObject(InputObjectMeta { ref input_fields, .. @@ -283,7 +287,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 bool + Send + Sync>> { + pub fn input_value_parse_fn(&self) -> Option<&Box) -> bool + Send + Sync>> { match *self { MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. @@ -337,30 +341,54 @@ impl<'a> MetaType<'a> { _ => false, } } + + pub(crate) fn fields<'b>(&self, schema: &'b SchemaType) -> Option>> { + match schema.lookup_type(&self.as_type()) { + Some(MetaType::Interface(ref i)) => Some(i.fields.iter().collect()), + Some(MetaType::Object(ref o)) => Some(o.fields.iter().collect()), + Some(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)) + .flatten() + .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(name: Cow<'a, str>) -> ScalarMeta<'a> { + pub fn new(name: Cow<'a, str>) -> Self + where + T: FromInputValue + ParseScalarValue + 'a, + for<'b> &'b S: ScalarRefValue<'b>, + { ScalarMeta { name: name, description: None, - try_parse_fn: Box::new(|v: &InputValue| { - ::from_input_value(v).is_some() + try_parse_fn: Box::new(|v: &InputValue| { + >::from_input_value(v).is_some() }), + parse_fn: >::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 +400,7 @@ impl<'a> ListMeta<'a> { } /// Wrap the list in a generic meta type - pub fn into_meta(self) -> MetaType<'a> { + pub fn into_meta(self) -> MetaType<'a, S> { MetaType::List(self) } } @@ -384,14 +412,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(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 +434,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 +443,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,20 +452,26 @@ 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(name: Cow<'a, str>, values: &[EnumValue]) -> EnumMeta<'a> { + pub fn new>(name: Cow<'a, str>, values: &[EnumValue]) -> Self + where + for<'b> &'b S: ScalarRefValue<'b>, + { EnumMeta { name: name, description: None, values: values.to_vec(), - try_parse_fn: Box::new(|v: &InputValue| { - ::from_input_value(v).is_some() + try_parse_fn: Box::new(|v: &InputValue| { + >::from_input_value(v).is_some() }), } } @@ -442,20 +479,23 @@ impl<'a> EnumMeta<'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) -> 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 +506,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,23 +539,26 @@ impl<'a> UnionMeta<'a> { } /// 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::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( - name: Cow<'a, str>, - input_fields: &[Argument<'a>], - ) -> InputObjectMeta<'a> { + pub fn new>(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| { - ::from_input_value(v).is_some() + try_parse_fn: Box::new(|v: &InputValue| { + >::from_input_value(v).is_some() }), } } @@ -523,22 +566,22 @@ impl<'a> InputObjectMeta<'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) -> 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: fmt::Debug> 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 +589,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 +605,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: fmt::Debug> 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 +625,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 +633,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) -> Self { self.default_value = Some(default_value); self } @@ -623,7 +666,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 +675,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 +685,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) diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs index 7822a0934..96e53bc12 100644 --- a/juniper/src/schema/model.rs +++ b/juniper/src/schema/model.rs @@ -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::{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, S, QueryT: GraphQLType, MutationT: GraphQLType> +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>, +#[derive(Debug)] +pub struct SchemaType<'a, S: fmt::Debug> { + pub(crate) types: FnvHashMap>, query_type_name: String, mutation_type_name: Option, - directives: FnvHashMap>, + directives: FnvHashMap>, } -impl<'a> Context for SchemaType<'a> {} +impl<'a, S: fmt::Debug> Context for SchemaType<'a, S> {} #[derive(Clone)] -pub enum TypeType<'a> { - Concrete(&'a MetaType<'a>), - NonNull(Box>), - List(Box>), +pub enum TypeType<'a, S: fmt::Debug + 'a> { + Concrete(&'a MetaType<'a, S>), + NonNull(Box>), + List(Box>), } -pub struct DirectiveType<'a> { +#[derive(Debug)] +pub struct DirectiveType<'a, S: fmt::Debug> { pub name: String, pub description: Option, pub locations: Vec, - pub arguments: Vec>, + pub arguments: Vec>, } -#[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, S, QueryT, MutationT> where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue + 'a, + QueryT: GraphQLType, + MutationT: GraphQLType, + 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) -> RootNode<'a, S, QueryT, MutationT> + 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, S, QueryT, MutationT> where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLType, + MutationT: GraphQLType, + 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> { + ) -> RootNode<'a, S, QueryT, MutationT> + 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: fmt::Debug> SchemaType<'a, S> { pub fn new( query_info: &QueryT::TypeInfo, mutation_info: &MutationT::TypeInfo, - ) -> SchemaType<'a> + ) -> Self where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue + 'a, + QueryT: GraphQLType, + MutationT: GraphQLType, + 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::(query_info) .innermost_name() .to_owned(); + mutation_type_name = registry .get_type::(mutation_info) .innermost_name() .to_owned(); - registry.get_type::(&()); + registry.get_type::>(&()); + 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::("__schema", &()), + registry.field::>("__schema", &()), registry - .field::("__type", &()) + .field::>("__type", &()) .argument(registry.arg::("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 { + pub fn type_by_name(&self, name: &str) -> Option> { 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> { self.types.get(name) } - pub fn query_type(&self) -> TypeType { + pub(crate) fn lookup_type(&self, tpe: &Type) -> Option<&MetaType> { + 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 { 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 { self.types .get(&self.query_type_name) .expect("Query type does not exist in schema") } - pub fn mutation_type(&self) -> Option { + pub fn mutation_type(&self) -> Option> { 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> { 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 { + pub fn type_list(&self) -> Vec> { 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> { self.types.values().collect() } - pub fn make_type(&self, t: &Type) -> TypeType { + pub fn make_type(&self, t: &Type) -> TypeType { 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> { self.directives.values().collect() } - pub fn directive_by_name(&self, name: &str) -> Option<&DirectiveType> { + pub fn directive_by_name(&self, name: &str) -> Option<&DirectiveType> { 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, t2: &MetaType) -> bool { + if (t1 as *const MetaType) == (t2 as *const MetaType) { 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) -> Vec<&MetaType> { 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, + possible_type: &MetaType, + ) -> 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) == (possible_type as *const MetaType)) } 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: fmt::Debug> TypeType<'a, S> { #[inline] - pub fn to_concrete(&self) -> Option<&'a MetaType> { + pub fn to_concrete(&self) -> Option<&'a MetaType> { 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 { 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::Debug> 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()), diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index fa67894ce..a3d2ebb8e 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -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 for RootNode<'a, S, QueryT, MutationT> where - QueryT: GraphQLType, - MutationT: GraphQLType, + S: ScalarValue, + QueryT: GraphQLType, + MutationT: GraphQLType, + 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, - ) -> ExecutionResult { + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { 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, - ) -> Value { - use value::Object; + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> Value { 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,180 +73,389 @@ where } } -graphql_object!(<'a> SchemaType<'a>: SchemaType<'a> as "__Schema" |&self| { - field types() -> Vec { - self.type_list() - .into_iter() - .filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false)) - .collect() - } - - field query_type() -> TypeType { - self.query_type() - } - - field mutation_type() -> Option { - self.mutation_type() - } - - // Included for compatibility with the introspection query in GraphQL.js - field subscription_type() -> Option { - None - } - - field directives() -> Vec<&DirectiveType> { - self.directive_list() - } -}); +impl<'a, S> GraphQLType for SchemaType<'a, S> +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + type Context = Self; + type TypeInfo = (); -graphql_object!(<'a> TypeType<'a>: SchemaType<'a> as "__Type" |&self| { - field name() -> Option<&str> { - match *self { - TypeType::Concrete(t) => t.name(), - _ => None, - } + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("__Schema") } - field description() -> Option<&String> { - match *self { - TypeType::Concrete(t) => t.description(), - _ => None, - } - } + fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + for<'b> &'b S: ScalarRefValue<'b>, + { + let types = registry.field::>>("types", info); + let query_type = registry.field::>("queryType", info); + let mutation_type = registry.field::>>("mutationType", info); + let subscription_type = registry.field::>>("subscriptionType", info); + let directives = registry.field::>>("directives", info); - field kind() -> TypeKind { - match *self { - TypeType::Concrete(t) => t.type_kind(), - TypeType::List(_) => TypeKind::List, - TypeType::NonNull(_) => TypeKind::NonNull, - } + let obj = registry.build_object_type::( + info, + &[ + types, + query_type, + mutation_type, + subscription_type, + directives, + ], + ); + obj.into_meta() } - field fields(include_deprecated = false: bool) -> Option> { - match *self { - TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) | - TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => - Some(fields - .iter() - .filter(|f| include_deprecated || f.deprecation_reason.is_none()) - .filter(|f| !f.name.starts_with("__")) - .collect()), - _ => None, - } + fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { + String::from("__Schema") } - field of_type() -> Option<&Box> { - match *self { - TypeType::Concrete(_) => None, - TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l), + fn resolve_field( + &self, + info: &Self::TypeInfo, + field: &str, + _args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field { + "types" => { + let r = self + .type_list() + .into_iter() + .filter(|t| { + t.to_concrete() + .map(|t| t.name() != Some("_EmptyMutation")) + .unwrap_or(false) + }).collect::>(); + executor.resolve(info, &r) + } + "queryType" => executor.resolve(info, &self.query_type()), + "mutationType" => executor.resolve(info, &self.mutation_type()), + "subscriptionType" => executor.resolve::>>(info, &None), + "directives" => executor.resolve(info, &self.directive_list()), + e => panic!("Field {} not found on type __Schema", e), } } +} - field input_fields() -> Option<&Vec> { - match *self { - TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) => - Some(input_fields), - _ => None, - } +impl<'a, S> GraphQLType for TypeType<'a, S> +where + S: ScalarValue + 'a, + for<'b> &'b S: ScalarRefValue<'b>, +{ + type Context = SchemaType<'a, S>; + type TypeInfo = (); + + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("__Type") + } + + fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + for<'b> &'b S: ScalarRefValue<'b>, + { + let name = registry.field::>("name", info); + let description = registry.field::>("description", info); + let kind = registry.field::("kind", info); + let fields = registry + .field::>>>("fields", info) + .argument(registry.arg_with_default("includeDeprecated", &false, info)); + let of_type = registry.field::>>>("ofType", info); + let input_fields = registry.field::>>>("inputFields", info); + let interfaces = registry.field::>>>("interfaces", info); + let possible_types = registry.field::>>>("possibleTypes", info); + let enum_values = registry + .field::>>("enumValues", info) + .argument(registry.arg_with_default("includeDeprecated", &false, info)); + + let obj = registry.build_object_type::( + info, + &[ + name, + description, + kind, + fields, + of_type, + input_fields, + interfaces, + possible_types, + enum_values, + ], + ); + obj.into_meta() + } + + fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { + String::from("__Type") } - field interfaces(&executor) -> Option> { - match *self { - TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => { - let schema = executor.context(); - Some(interface_names - .iter() - .filter_map(|n| schema.type_by_name(n)) - .collect()) + fn resolve_field( + &self, + info: &Self::TypeInfo, + field: &str, + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field { + "name" => { + let r = match *self { + TypeType::Concrete(t) => t.name(), + _ => None, + }; + executor.replaced_context(&()).resolve(info, &r) } - _ => None, - } - } - - field possible_types(&executor) -> Option> { - let schema = executor.context(); - match *self { - TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => { - Some(of_type_names - .iter() - .filter_map(|tn| schema.type_by_name(tn)) - .collect()) + "description" => { + let r = match *self { + TypeType::Concrete(t) => t.description(), + _ => None, + }; + executor.replaced_context(&()).resolve(info, &r) } - TypeType::Concrete(&MetaType::Interface(InterfaceMeta{name: ref iface_name, .. })) => { - Some(schema.concrete_type_list() - .iter() - .filter_map(|&ct| - if let MetaType::Object(ObjectMeta{ - ref name, - ref interface_names, - .. - }) = *ct { - if interface_names.contains(&iface_name.to_string()) { - schema.type_by_name(name) - } else { None } - } else { None } - ) - .collect()) + "kind" => { + let r = match *self { + TypeType::Concrete(t) => t.type_kind(), + TypeType::List(_) => TypeKind::List, + TypeType::NonNull(_) => TypeKind::NonNull, + }; + executor.replaced_context(&()).resolve(info, &r) } - _ => None, - } - } - - field enum_values(include_deprecated = false: bool) -> Option> { - match *self { - TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => - Some(values - .iter() - .filter(|f| include_deprecated || f.deprecation_reason.is_none()) - .collect()), - _ => None, + "fields" => { + let include_deprecated = args.get("includeDeprecated").unwrap_or(false); + let r: Option> = match *self { + TypeType::Concrete(&MetaType::Interface(InterfaceMeta { + ref fields, .. + })) + | TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some( + fields + .iter() + .filter(|f| include_deprecated || f.deprecation_reason.is_none()) + .filter(|f| !f.name.starts_with("__")) + .collect(), + ), + _ => None, + }; + executor.resolve(info, &r) + } + "ofType" => { + let r = match *self { + TypeType::Concrete(_) => None, + TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l), + }; + executor.resolve(info, &r) + } + "inputFields" => { + let r = match *self { + TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { + ref input_fields, + .. + })) => Some(input_fields), + _ => None, + }; + executor.resolve(info, &r) + } + "interfaces" => { + let r: Option> = match *self { + TypeType::Concrete(&MetaType::Object(ObjectMeta { + ref interface_names, + .. + })) => { + let schema = executor.context(); + Some( + interface_names + .iter() + .filter_map(|n| schema.type_by_name(n)) + .collect(), + ) + } + _ => None, + }; + executor.resolve(info, &r) + } + "possibleTypes" => { + let schema = executor.context(); + let r: Option> = match *self { + TypeType::Concrete(&MetaType::Union(UnionMeta { + ref of_type_names, .. + })) => Some( + of_type_names + .iter() + .filter_map(|tn| schema.type_by_name(tn)) + .collect(), + ), + TypeType::Concrete(&MetaType::Interface(InterfaceMeta { + name: ref iface_name, + .. + })) => Some( + schema + .concrete_type_list() + .iter() + .filter_map(|&ct| { + if let MetaType::Object(ObjectMeta { + ref name, + ref interface_names, + .. + }) = *ct + { + if interface_names.contains(&iface_name.to_string()) { + schema.type_by_name(name) + } else { + None + } + } else { + None + } + }).collect(), + ), + _ => None, + }; + executor.resolve(info, &r) + } + "enumValues" => { + let include_deprecated = args.get("includeDeprecated").unwrap_or(false); + let r: Option> = match *self { + TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => Some( + values + .iter() + .filter(|f| include_deprecated || f.deprecation_reason.is_none()) + .collect(), + ), + _ => None, + }; + executor.replaced_context(&()).resolve(info, &r) + } + e => panic!("Field {} not found on type __Type", e), } } -}); - -graphql_object!(<'a> Field<'a>: SchemaType<'a> as "__Field" |&self| { - field name() -> &String { - &self.name - } - - field description() -> &Option { - &self.description - } +} - field args() -> Vec<&Argument> { - self.arguments.as_ref().map_or_else(Vec::new, |v| v.iter().collect()) +impl<'a, S> GraphQLType for Field<'a, S> +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + type Context = SchemaType<'a, S>; + type TypeInfo = (); + + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("__Field") + } + + fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + for<'b> &'b S: ScalarRefValue<'b>, + { + let name = registry.field::>("name", info); + let description = registry.field::>("description", info); + let args = registry.field::>>("args", info); + let tpe = registry.field::>("type", info); + let is_deprecated = registry.field::("isDeprecated", info); + let deprecation_reason = registry.field::>("deprecationReason", info); + + let obj = registry.build_object_type::( + info, + &[ + name, + description, + args, + tpe, + is_deprecated, + deprecation_reason, + ], + ); + obj.into_meta() + } + + fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { + String::from("__Field") } - field type(&executor) -> TypeType { - executor.context().make_type(&self.field_type) + fn resolve_field( + &self, + info: &Self::TypeInfo, + field: &str, + _args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field { + "name" => executor.replaced_context(&()).resolve(info, &self.name), + "description" => executor + .replaced_context(&()) + .resolve(&(), &self.description), + "args" => executor.resolve( + info, + &self + .arguments + .as_ref() + .map_or_else(Vec::new, |v| v.iter().collect()), + ), + "type" => executor.resolve(info, &executor.context().make_type(&self.field_type)), + "isDeprecated" => executor + .replaced_context(&()) + .resolve(info, &self.deprecation_reason.is_some()), + "deprecationReason" => executor + .replaced_context(&()) + .resolve(info, &&self.deprecation_reason), + e => panic!("Field {} not found on type __Type", e), + } } +} - field is_deprecated() -> bool { - self.deprecation_reason.is_some() - } +impl<'a, S> GraphQLType for Argument<'a, S> +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + type Context = SchemaType<'a, S>; + type TypeInfo = (); - field deprecation_reason() -> &Option { - &self.deprecation_reason + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("__InputValue") } -}); -graphql_object!(<'a> Argument<'a>: SchemaType<'a> as "__InputValue" |&self| { - field name() -> &String { - &self.name - } + fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + for<'b> &'b S: ScalarRefValue<'b>, + { + let name = registry.field::>("name", info); + let description = registry.field::>("description", info); + let tpe = registry.field::>("type", info); + let default_value = registry.field::>("defaultValue", info); - field description() -> &Option { - &self.description + let obj = + registry.build_object_type::(info, &[name, description, tpe, default_value]); + obj.into_meta() } - field type(&executor) -> TypeType { - executor.context().make_type(&self.arg_type) + fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { + String::from("__InputValue") } - field default_value() -> Option { - self.default_value.as_ref().map(|v| format!("{}", v)) + fn resolve_field( + &self, + info: &Self::TypeInfo, + field: &str, + _args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field { + "name" => executor.replaced_context(&()).resolve(info, &self.name), + "description" => executor + .replaced_context(&()) + .resolve(info, &self.description), + "type" => executor.resolve(info, &executor.context().make_type(&self.arg_type)), + "defaultValue" => executor + .replaced_context(&()) + .resolve(info, &self.default_value.as_ref().map(|v| format!("{}", v))), + e => panic!("Field {} not found on type __Type", e), + } } -}); +} graphql_object!(EnumValue: () as "__EnumValue" |&self| { field name() -> &String { @@ -260,40 +475,87 @@ graphql_object!(EnumValue: () as "__EnumValue" |&self| { } }); -graphql_object!(<'a> DirectiveType<'a>: SchemaType<'a> as "__Directive" |&self| { - field name() -> &String { - &self.name - } - - field description() -> &Option { - &self.description - } - - field locations() -> &Vec { - &self.locations - } - - field args() -> &Vec { - &self.arguments - } - - // Included for compatibility with the introspection query in GraphQL.js - field deprecated "Use the locations array instead" - on_operation() -> bool { - self.locations.contains(&DirectiveLocation::Query) - } - - // Included for compatibility with the introspection query in GraphQL.js - field deprecated "Use the locations array instead" - on_fragment() -> bool { - self.locations.contains(&DirectiveLocation::FragmentDefinition) || - self.locations.contains(&DirectiveLocation::InlineFragment) || - self.locations.contains(&DirectiveLocation::FragmentSpread) +impl<'a, S> GraphQLType for DirectiveType<'a, S> +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + type Context = SchemaType<'a, S>; + type TypeInfo = (); + + fn name((): &Self::TypeInfo) -> Option<&str> { + Some("__Directive") + } + + fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + { + let name = registry.field::>("name", info); + let description = registry.field::>("description", info); + let locations = registry.field::<&Vec>("locations", info); + let args = registry.field::>>("args", info); + + let on_operation = registry + .field::("onOperation", info) + .deprecated("Use the locations array instead"); + let on_fragment = registry + .field::("onFragment", info) + .deprecated("Use the locations array instead"); + let on_field = registry + .field::("onField", info) + .deprecated("Use the locations array instead"); + + let obj = registry.build_object_type::( + info, + &[ + name, + description, + locations, + args, + on_operation, + on_fragment, + on_field, + ], + ); + obj.into_meta() + } + + fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { + String::from("__Directive") } - // Included for compatibility with the introspection query in GraphQL.js - field deprecated "Use the locations array instead" - on_field() -> bool { - self.locations.contains(&DirectiveLocation::Field) + fn resolve_field( + &self, + info: &Self::TypeInfo, + field: &str, + _args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { + match field { + "name" => executor.replaced_context(&()).resolve(info, &self.name), + "description" => executor + .replaced_context(&()) + .resolve(info, &self.description), + "locations" => executor + .replaced_context(&()) + .resolve(info, &self.locations), + "args" => executor.resolve(info, &self.arguments), + "onOperation" => executor + .replaced_context(&()) + .resolve(info, &self.locations.contains(&DirectiveLocation::Query)), + "onFragment" => executor.replaced_context(&()).resolve( + info, + &(self + .locations + .contains(&DirectiveLocation::FragmentDefinition) + || self.locations.contains(&DirectiveLocation::InlineFragment) + || self.locations.contains(&DirectiveLocation::FragmentSpread)), + ), + "onField" => executor + .replaced_context(&()) + .resolve(info, &self.locations.contains(&DirectiveLocation::Field)), + e => panic!("Field {} not found on type __Type", e), + } } -}); +} diff --git a/juniper/src/tests/introspection_tests.rs b/juniper/src/tests/introspection_tests.rs index fe2e527fa..b04bd281f 100644 --- a/juniper/src/tests/introspection_tests.rs +++ b/juniper/src/tests/introspection_tests.rs @@ -4,7 +4,7 @@ use executor::Variables; use schema::model::RootNode; use tests::model::Database; use types::scalars::EmptyMutation; -use value::Value; +use value::{DefaultScalarValue, Value}; #[test] fn test_query_type_name() { @@ -17,7 +17,8 @@ fn test_query_type_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -32,10 +33,10 @@ fn test_query_type_name() { vec![("name", Value::string("Query"))].into_iter().collect(), ), )].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -51,7 +52,8 @@ fn test_specific_type_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -61,7 +63,7 @@ fn test_specific_type_name() { "__type", Value::object(vec![("name", Value::string("Droid"))].into_iter().collect()), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -79,7 +81,8 @@ fn test_specific_object_type_name_and_kind() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -92,10 +95,10 @@ fn test_specific_object_type_name_and_kind() { ("name", Value::string("Droid")), ("kind", Value::string("OBJECT")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -113,7 +116,8 @@ fn test_specific_interface_type_name_and_kind() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -126,10 +130,10 @@ fn test_specific_interface_type_name_and_kind() { ("name", Value::string("Character")), ("kind", Value::string("INTERFACE")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -147,7 +151,8 @@ fn test_documentation() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -163,10 +168,10 @@ fn test_documentation() { Value::string("A mechanical creature in the Star Wars universe."), ), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -185,7 +190,8 @@ fn test_possible_types() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); let result = ::execute(doc, None, &schema, &Variables::new(), &database); @@ -214,8 +220,7 @@ fn test_possible_types() { .expect("'name' not present in type") .as_string_value() .expect("'name' not a string") - }) - .collect::>(); + }).collect::>(); assert_eq!(possible_types, vec!["Human", "Droid"].into_iter().collect()); } diff --git a/juniper/src/tests/model.rs b/juniper/src/tests/model.rs index b0be619c5..3d900b0c7 100644 --- a/juniper/src/tests/model.rs +++ b/juniper/src/tests/model.rs @@ -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, diff --git a/juniper/src/tests/query_tests.rs b/juniper/src/tests/query_tests.rs index 1cc2154df..bf2d1a866 100644 --- a/juniper/src/tests/query_tests.rs +++ b/juniper/src/tests/query_tests.rs @@ -3,7 +3,7 @@ use executor::Variables; use schema::model::RootNode; use tests::model::Database; use types::scalars::EmptyMutation; -use value::Value; +use value::{DefaultScalarValue, Value}; #[test] fn test_hero_name() { @@ -14,7 +14,8 @@ fn test_hero_name() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -24,7 +25,7 @@ fn test_hero_name() { "hero", Value::object(vec![("name", Value::string("R2-D2"))].into_iter().collect()), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -34,7 +35,8 @@ fn test_hero_name() { #[test] fn test_hero_field_order() { let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); let doc = r#" { @@ -54,10 +56,10 @@ fn test_hero_field_order() { ("id", Value::string("2001")), ("name", Value::string("R2-D2")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -81,10 +83,10 @@ fn test_hero_field_order() { ("name", Value::string("R2-D2")), ("id", Value::string("2001")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -104,7 +106,8 @@ fn test_hero_name_and_friends() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -137,10 +140,10 @@ fn test_hero_name_and_friends() { ]), ), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -164,7 +167,8 @@ fn test_hero_name_and_friends_and_friends_of_friends() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -203,7 +207,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { "name", Value::string("Leia Organa"), )].into_iter() - .collect(), + .collect(), ), Value::object( vec![("name", Value::string("C-3PO"))] @@ -218,7 +222,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { ]), ), ].into_iter() - .collect(), + .collect(), ), Value::object( vec![ @@ -239,14 +243,14 @@ fn test_hero_name_and_friends_and_friends_of_friends() { "name", Value::string("Luke Skywalker"), )].into_iter() - .collect(), + .collect(), ), Value::object( vec![( "name", Value::string("Leia Organa"), )].into_iter() - .collect(), + .collect(), ), Value::object( vec![("name", Value::string("R2-D2"))] @@ -256,7 +260,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { ]), ), ].into_iter() - .collect(), + .collect(), ), Value::object( vec![ @@ -277,7 +281,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { "name", Value::string("Luke Skywalker"), )].into_iter() - .collect(), + .collect(), ), Value::object( vec![("name", Value::string("Han Solo"))] @@ -297,15 +301,15 @@ fn test_hero_name_and_friends_and_friends_of_friends() { ]), ), ].into_iter() - .collect(), + .collect(), ), ]), ), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -316,7 +320,8 @@ fn test_hero_name_and_friends_and_friends_of_friends() { fn test_query_name() { let doc = r#"{ human(id: "1000") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -330,7 +335,7 @@ fn test_query_name() { .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -341,7 +346,8 @@ fn test_query_name() { fn test_query_alias_single() { let doc = r#"{ luke: human(id: "1000") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -355,7 +361,7 @@ fn test_query_alias_single() { .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -370,7 +376,8 @@ fn test_query_alias_multiple() { leia: human(id: "1003") { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -394,7 +401,7 @@ fn test_query_alias_multiple() { ), ), ].into_iter() - .collect() + .collect() ), vec![] )) @@ -414,7 +421,8 @@ fn test_query_alias_multiple_with_fragment() { homePlanet }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -428,7 +436,7 @@ fn test_query_alias_multiple_with_fragment() { ("name", Value::string("Luke Skywalker")), ("homePlanet", Value::string("Tatooine")), ].into_iter() - .collect(), + .collect(), ), ), ( @@ -438,11 +446,11 @@ fn test_query_alias_multiple_with_fragment() { ("name", Value::string("Leia Organa")), ("homePlanet", Value::string("Alderaan")), ].into_iter() - .collect(), + .collect(), ), ), ].into_iter() - .collect() + .collect() ), vec![] )) @@ -453,7 +461,8 @@ fn test_query_alias_multiple_with_fragment() { fn test_query_name_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); let vars = vec![("someId".to_owned(), InputValue::string("1000"))] .into_iter() @@ -471,7 +480,7 @@ fn test_query_name_variable() { .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -482,7 +491,8 @@ fn test_query_name_variable() { fn test_query_name_invalid_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); let vars = vec![("someId".to_owned(), InputValue::string("some invalid id"))] .into_iter() @@ -501,7 +511,8 @@ fn test_query_name_invalid_variable() { fn test_query_friends_names() { let doc = r#"{ human(id: "1000") { friends { name } } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -531,10 +542,10 @@ fn test_query_friends_names() { ), ]), )].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -556,7 +567,8 @@ fn test_query_inline_fragments_droid() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -570,10 +582,10 @@ fn test_query_inline_fragments_droid() { ("__typename", Value::string("Droid")), ("primaryFunction", Value::string("Astromech")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -591,7 +603,8 @@ fn test_query_inline_fragments_human() { } "#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -604,10 +617,10 @@ fn test_query_inline_fragments_human() { ("name", Value::string("Luke Skywalker")), ("__typename", Value::string("Human")), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -623,7 +636,8 @@ fn test_object_typename() { } }"#; let database = Database::new(); - let schema = RootNode::new(&database, EmptyMutation::::new()); + let schema: RootNode = + RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -637,7 +651,7 @@ fn test_object_typename() { .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) diff --git a/juniper/src/tests/type_info_tests.rs b/juniper/src/tests/type_info_tests.rs index 4c0750ac4..225d0a96b 100644 --- a/juniper/src/tests/type_info_tests.rs +++ b/juniper/src/tests/type_info_tests.rs @@ -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::{DefaultScalarValue, ScalarRefValue, ScalarValue, Value}; pub struct NodeTypeInfo { name: String, @@ -16,7 +16,11 @@ pub struct Node { attributes: IndexMap, } -impl GraphQLType for Node { +impl GraphQLType 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::(name, &())) .collect::>(); @@ -39,9 +47,9 @@ impl GraphQLType for Node { &self, _: &Self::TypeInfo, field_name: &str, - _: &Arguments, - executor: &Executor, - ) -> ExecutionResult { + _: &Arguments, + executor: &Executor, + ) -> ExecutionResult { executor.resolve(&(), &self.attributes.get(field_name).unwrap()) } } @@ -64,7 +72,8 @@ 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(), &()), @@ -75,7 +84,7 @@ fn test_node() { ("bar", Value::string("2")), ("baz", Value::string("3")), ].into_iter() - .collect() + .collect() ), vec![] )) diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index 970843b3e..11dd53616 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -2,19 +2,20 @@ use indexmap::IndexMap; use ast::{Directive, FromInputValue, InputValue, Selection}; use executor::Variables; -use value::{Object, Value}; +use value::{Object, ScalarRefValue, ScalarValue, Value}; use executor::{ExecutionResult, Executor, Registry}; use parser::Spanning; use schema::meta::{Argument, MetaType}; +use std::fmt::Debug; /// GraphQL type kind /// /// The GraphQL specification defines a number of type kinds - the meta type /// of a type. -#[derive(GraphQLEnum, Clone, Eq, PartialEq, Debug)] +#[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)] // Note: _internal flag needed to make derive work in juniper crate itself. -#[graphql(name = "__TypeKind", _internal)] +#[graphql(name = "__TypeKind")] pub enum TypeKind { /// ## Scalar types /// @@ -63,21 +64,26 @@ pub enum TypeKind { /// /// In GraphQL, nullable types are the default. By putting a `!` after a /// type, it becomes non-nullable. - #[graphql(name = "NON_NULL")] + #[graphql(name = "NON_NULL")] NonNull, } + /// Field argument container -pub struct Arguments<'a> { - args: Option>, +#[derive(Debug)] +pub struct Arguments<'a, S: Debug> { + args: Option>>, } -impl<'a> Arguments<'a> { +impl<'a, S> Arguments<'a, S> +where + S: ScalarValue, +{ #[doc(hidden)] pub fn new( - mut args: Option>, - meta_args: &'a Option>, - ) -> Arguments<'a> { + mut args: Option>>, + meta_args: &'a Option>>, + ) -> Self { if meta_args.is_some() && args.is_none() { args = Some(IndexMap::new()); } @@ -106,7 +112,8 @@ impl<'a> Arguments<'a> { /// succeeeds. pub fn get(&self, key: &str) -> Option where - T: FromInputValue, + T: FromInputValue, + for<'b> &'b S: ScalarRefValue<'b>, { match self.args { Some(ref args) => match args.get(key) { @@ -144,16 +151,22 @@ root: ```rust use juniper::{GraphQLType, Registry, FieldResult, Context, - Arguments, Executor, ExecutionResult}; + Arguments, Executor, ExecutionResult, + ScalarValue, ScalarRefValue}; use juniper::meta::MetaType; # use std::collections::HashMap; +#[derive(Debug)] struct User { id: String, name: String, friend_ids: Vec } +#[derive(Debug)] struct Database { users: HashMap } impl Context for Database {} -impl GraphQLType for User { +impl GraphQLType for User +where S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b> +{ type Context = Database; type TypeInfo = (); @@ -161,7 +174,9 @@ impl GraphQLType for User { Some("User") } - fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { + fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where S: '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 @@ -179,10 +194,10 @@ impl GraphQLType for User { &self, info: &(), field_name: &str, - args: &Arguments, - executor: &Executor + args: &Arguments, + executor: &Executor ) - -> ExecutionResult + -> ExecutionResult { // Next, we need to match the queried field name. All arms of this // match statement return `ExecutionResult`, which makes it hard to @@ -219,7 +234,11 @@ impl GraphQLType for User { ``` */ -pub trait GraphQLType: Sized { +pub trait GraphQLType: 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 +261,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 +278,9 @@ pub trait GraphQLType: Sized { &self, info: &Self::TypeInfo, field_name: &str, - arguments: &Arguments, - executor: &Executor, - ) -> ExecutionResult { + arguments: &Arguments, + executor: &Executor, + ) -> ExecutionResult { panic!("resolve_field must be implemented by object types"); } @@ -274,9 +295,9 @@ pub trait GraphQLType: Sized { &self, info: &Self::TypeInfo, type_name: &str, - selection_set: Option<&[Selection]>, - executor: &Executor, - ) -> ExecutionResult { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> ExecutionResult { if Self::name(info).unwrap() == type_name { Ok(self.resolve(info, selection_set, executor)) } else { @@ -305,9 +326,9 @@ pub trait GraphQLType: Sized { fn resolve( &self, info: &Self::TypeInfo, - selection_set: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> Value { 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 +342,17 @@ pub trait GraphQLType: Sized { } } -pub(crate) fn resolve_selection_set_into( +pub(crate) fn resolve_selection_set_into( instance: &T, info: &T::TypeInfo, - selection_set: &[Selection], - executor: &Executor, - result: &mut Object, + selection_set: &[Selection], + executor: &Executor, + result: &mut Object, ) -> bool where - T: GraphQLType, + T: GraphQLType, + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, { let meta_type = executor .schema() @@ -337,8 +360,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 +378,7 @@ where if f.name.item == "__typename" { result.add_field( response_name, - Value::string(instance.concrete_type_name(executor.context(), info)), + Value::string(&instance.concrete_type_name(executor.context(), info)), ); continue; } @@ -387,8 +409,7 @@ where .iter() .map(|&(ref k, ref v)| { (k.item, v.item.clone().into_const(exec_vars)) - }) - .collect() + }).collect() }), &meta_field.arguments, ), @@ -477,7 +498,11 @@ where true } -fn is_excluded(directives: &Option>>, vars: &Variables) -> bool { +fn is_excluded(directives: &Option>>>, vars: &Variables) -> bool +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ if let Some(ref directives) = *directives { for &Spanning { item: ref directive, @@ -502,8 +527,10 @@ fn is_excluded(directives: &Option>>, 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(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) { match *e { Value::Object(ref mut dest_obj) => { @@ -533,7 +560,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(dest: &mut Object, src: Object) { for (key, value) in src { if dest.contains_field(&key) { merge_key_into(dest, &key, value); diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index d73c32d8e..b04cde7b4 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -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 GraphQLType for Option +impl GraphQLType for Option where - T: GraphQLType, + S: ScalarValue, + T: GraphQLType, + 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::(info).into_meta() } fn resolve( &self, info: &T::TypeInfo, - _: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + _: Option<&[Selection]>, + executor: &Executor, + ) -> Value { match *self { Some(ref obj) => executor.resolve_into_value(info, obj), None => Value::null(), @@ -33,11 +39,15 @@ where } } -impl FromInputValue for Option +impl FromInputValue for Option where - T: FromInputValue, + T: FromInputValue, + S: ScalarValue, { - fn from_input_value(v: &InputValue) -> Option> { + fn from_input_value<'a>(v: &'a InputValue) -> Option> + where + for<'b> &'b S: ScalarRefValue<'b>, + { match v { &InputValue::Null => Some(None), v => match v.convert() { @@ -48,11 +58,12 @@ where } } -impl ToInputValue for Option +impl ToInputValue for Option where - T: ToInputValue, + T: ToInputValue, + S: ScalarValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { match *self { Some(ref v) => v.to_input_value(), None => InputValue::null(), @@ -60,9 +71,11 @@ where } } -impl GraphQLType for Vec +impl GraphQLType for Vec where - T: GraphQLType, + T: GraphQLType, + 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::(info).into_meta() } fn resolve( &self, info: &T::TypeInfo, - _: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + _: Option<&[Selection]>, + executor: &Executor, + ) -> Value { resolve_into_list(executor, info, self.iter()) } } -impl FromInputValue for Vec +impl FromInputValue for Vec where - T: FromInputValue, + T: FromInputValue, + S: ScalarValue, { - fn from_input_value(v: &InputValue) -> Option> { + fn from_input_value<'a>(v: &'a InputValue) -> Option> + 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 ToInputValue for Vec +impl ToInputValue for Vec where - T: ToInputValue, + T: ToInputValue, + S: ScalarValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { 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 for &'a [T] where - T: GraphQLType, + S: ScalarValue, + T: GraphQLType, + 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::(info).into_meta() } fn resolve( &self, info: &T::TypeInfo, - _: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + _: Option<&[Selection]>, + executor: &Executor, + ) -> Value { resolve_into_list(executor, info, self.iter()) } } -impl<'a, T> ToInputValue for &'a [T] +impl<'a, T, S> ToInputValue for &'a [T] where - T: ToInputValue, + T: ToInputValue, + S: ScalarValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { InputValue::list(self.iter().map(|v| v.to_input_value()).collect()) } } -fn resolve_into_list(executor: &Executor, info: &T::TypeInfo, iter: I) -> Value +fn resolve_into_list( + executor: &Executor, + info: &T::TypeInfo, + iter: I, +) -> Value where + S: ScalarValue, I: Iterator + ExactSizeIterator, - T: GraphQLType, + T: GraphQLType, + for<'b> &'b S: ScalarRefValue<'b>, { let stop_on_null = executor .current_type() diff --git a/juniper/src/types/pointers.rs b/juniper/src/types/pointers.rs index 1563c7cff..c39041916 100644 --- a/juniper/src/types/pointers.rs +++ b/juniper/src/types/pointers.rs @@ -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 GraphQLType for Box +impl GraphQLType for Box where - T: GraphQLType, + S: ScalarValue, + T: GraphQLType, + 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, - ) -> ExecutionResult { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -35,46 +42,53 @@ where &self, info: &T::TypeInfo, field: &str, - args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } fn resolve( &self, info: &T::TypeInfo, - selection_set: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> Value { (**self).resolve(info, selection_set, executor) } } -impl FromInputValue for Box +impl FromInputValue for Box where - T: FromInputValue, + S: ScalarValue, + T: FromInputValue, { - fn from_input_value(v: &InputValue) -> Option> { - match ::from_input_value(v) { + fn from_input_value<'a>(v: &'a InputValue) -> Option> + where + for<'b> &'b S: ScalarRefValue<'b> + { + match >::from_input_value(v) { Some(v) => Some(Box::new(v)), None => None, } } } -impl ToInputValue for Box +impl ToInputValue for Box where - T: ToInputValue, + S: Debug, + T: ToInputValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { (**self).to_input_value() } } -impl<'a, T, CtxT> GraphQLType for &'a T +impl<'a, S, T, CtxT> GraphQLType for &'a T where - T: GraphQLType, + S: ScalarValue, + T: GraphQLType, + 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, - ) -> ExecutionResult { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -101,43 +119,50 @@ where &self, info: &T::TypeInfo, field: &str, - args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } fn resolve( &self, info: &T::TypeInfo, - selection_set: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> Value { (**self).resolve(info, selection_set, executor) } } -impl<'a, T> ToInputValue for &'a T +impl<'a, T, S> ToInputValue for &'a T where - T: ToInputValue, + S: Debug, + T: ToInputValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { (**self).to_input_value() } } -impl GraphQLType for Arc +impl GraphQLType for Arc where - T: GraphQLType, + S: ScalarValue, + T: GraphQLType, + 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, - ) -> ExecutionResult { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -155,27 +180,28 @@ where &self, info: &T::TypeInfo, field: &str, - args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { + args: &Arguments, + executor: &Executor, + ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } fn resolve( &self, info: &T::TypeInfo, - selection_set: Option<&[Selection]>, - executor: &Executor, - ) -> Value { + selection_set: Option<&[Selection]>, + executor: &Executor, + ) -> Value { (**self).resolve(info, selection_set, executor) } } -impl ToInputValue for Arc +impl ToInputValue for Arc where - T: ToInputValue, + S: Debug, + T: ToInputValue, { - fn to_input_value(&self) -> InputValue { + fn to_input_value(&self) -> InputValue { (**self).to_input_value() } } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 31042b5ad..7f8cbe427 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -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, Token}; +use schema::meta::MetaType; use types::base::GraphQLType; +use value::{ParseScalarValue, ScalarRefValue, ScalarValue, Value}; /// An ID as defined by the GraphQL specification /// @@ -30,34 +31,127 @@ impl Deref for ID { } } -graphql_scalar!(ID as "ID" { +graphql_scalar!(ID as "ID" where Scalar = { resolve(&self) -> Value { Value::string(&self.0) } from_input_value(v: &InputValue) -> Option { match *v { - InputValue::String(ref s) => Some(ID(s.to_owned())), - InputValue::Int(i) => Some(ID(format!("{}", i))), + InputValue::Scalar(ref s) => { + <_ as Into>>::into(s.clone()).or_else(||{ + <_ as Into>>::into(s.clone()).map(|i| i.to_string()) + }).map(ID) + } _ => None } } + + from_str(value: &str) -> Result { + Ok(S::from(value)) + } }); -graphql_scalar!(String as "String" { +graphql_scalar!(String as "String" where Scalar = { resolve(&self) -> Value { Value::string(self) } from_input_value(v: &InputValue) -> Option { match *v { - InputValue::String(ref s) => Some(s.clone()), + InputValue::Scalar(ref s) => <_ as Into>>::into(s.clone()), _ => None, } } + + from_str(value: &str) -> Result { + 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()) + } }); -impl<'a> GraphQLType for &'a str { +fn parse_unicode_codepoint<'a, I>(char_iter: &mut I) -> Result> +where + I: Iterator, +{ + 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 for &'a str +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ type Context = (); type TypeInfo = (); @@ -65,62 +159,100 @@ 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::(&()).into_meta() } - fn resolve(&self, _: &(), _: Option<&[Selection]>, _: &Executor) -> Value { + fn resolve( + &self, + _: &(), + _: Option<&[Selection]>, + _: &Executor, + ) -> Value { Value::string(self) } } -impl<'a> ToInputValue for &'a str { - fn to_input_value(&self) -> InputValue { +impl<'a, S> ToInputValue for &'a str +where + S: ScalarValue, +{ + fn to_input_value(&self) -> InputValue { InputValue::string(self) } } -graphql_scalar!(bool as "Boolean" { +graphql_scalar!(bool as "Boolean" where Scalar = { resolve(&self) -> Value { Value::boolean(*self) } from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Boolean(b) => Some(b), + InputValue::Scalar(ref b) => <_ as Into>>::into(b), _ => None, } } + + from_str(value: &str) -> Result { + value + .parse() + .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) + .map(|s: bool| s.into()) + } }); -graphql_scalar!(i32 as "Int" { +graphql_scalar!(i32 as "Int" where Scalar = { resolve(&self) -> Value { Value::int(*self) } from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Int(i) => Some(i), + InputValue::Scalar(ref i) => <_ as Into>>::into(i), _ => None, } } + + from_str(value: &str) -> Result { + value + .parse() + .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) + .map(|s: i32| s.into()) + } }); -graphql_scalar!(f64 as "Float" { +graphql_scalar!(f64 as "Float" where Scalar = { resolve(&self) -> Value { Value::float(*self) } from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Int(i) => Some(f64::from(i)), - InputValue::Float(f) => Some(f), + InputValue::Scalar(ref s) => { + <_ as Into>>::into(s) + } _ => None, } } + + from_str(value: &str) -> Result { + value + .parse() + .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) + .map(|s: f64| s.into()) + } }); -impl GraphQLType for () { +impl GraphQLType for () +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ type Context = (); type TypeInfo = (); @@ -128,21 +260,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::(&()).into_meta() } } -impl FromInputValue for () { - fn from_input_value(_: &InputValue) -> Option<()> { +impl ParseScalarValue for () +where + S: ScalarValue, +{ + fn from_str(_value: &str) -> Result { + Ok(S::from(0)) + } +} + +impl FromInputValue for () { + fn from_input_value<'a>(_: &'a InputValue) -> 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 { phantom: PhantomData, } @@ -156,7 +305,11 @@ impl EmptyMutation { } } -impl GraphQLType for EmptyMutation { +impl GraphQLType for EmptyMutation +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ type Context = T; type TypeInfo = (); @@ -164,7 +317,11 @@ impl GraphQLType for EmptyMutation { Some("_EmptyMutation") } - fn meta<'r>(_: &(), registry: &mut Registry<'r>) -> MetaType<'r> { + fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S> + where + S: 'r, + for<'b> &'b S: ScalarRefValue<'b>, + { registry.build_object_type::(&(), &[]).into_meta() } } diff --git a/juniper/src/types/utilities.rs b/juniper/src/types/utilities.rs index 6f6d56219..0997d4c8b 100644 --- a/juniper/src/types/utilities.rs +++ b/juniper/src/types/utilities.rs @@ -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( + schema: &SchemaType, + arg_type: &TypeType, + arg_value: &InputValue, +) -> 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 { diff --git a/juniper/src/validation/context.rs b/juniper/src/validation/context.rs index 38e6d162d..b59a45ecf 100644 --- a/juniper/src/validation/context.rs +++ b/juniper/src/validation/context.rs @@ -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, - type_stack: Vec>>, + type_stack: Vec>>, type_literal_stack: Vec>>, - input_type_stack: Vec>>, + input_type_stack: Vec>>, input_type_literal_stack: Vec>>, - parent_type_stack: Vec>>, + parent_type_stack: Vec>>, 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, 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(&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(&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(&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) } diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs index 2051b1a46..9236e9635 100644 --- a/juniper/src/validation/input_value.rs +++ b/juniper/src/validation/input_value.rs @@ -7,6 +7,7 @@ use parser::SourcePosition; use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta}; use schema::model::{SchemaType, TypeType}; use validation::RuleError; +use value::{ScalarValue, ScalarRefValue}; #[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 { - let mut errors: Vec = vec![]; +pub fn validate_input_values( + values: &Variables, + document: &Document, + schema: &SchemaType, +) -> Vec +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( + values: &Variables, + var_defs: &VariableDefinitions, + schema: &SchemaType, errors: &mut Vec, -) { +) 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, + meta_type: &TypeType<'a, S>, + schema: &SchemaType, path: Path<'a>, -) -> Vec { +) -> Vec + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b> +{ let mut errors: Vec = 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, + meta: &ScalarMeta, path: &Path<'a>, -) -> Vec { +) -> Vec +where + S: fmt::Debug, +{ let mut errors: Vec = vec![]; if !(meta.try_parse_fn)(value) { @@ -191,13 +206,17 @@ 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, + meta: &EnumMeta, path: &Path<'a>, -) -> Vec { +) -> Vec +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b> +{ let mut errors: Vec = vec![]; match *value { @@ -221,14 +240,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, + meta: &InputObjectMeta, + schema: &SchemaType, path: &Path<'a>, -) -> Vec { +) -> Vec +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b> +{ let mut errors: Vec = vec![]; if let Some(ref obj) = value.to_object_value() { @@ -282,7 +305,10 @@ fn unify_input_object<'a>( errors } -fn is_absent_or_null(v: Option<&InputValue>) -> bool { +fn is_absent_or_null(v: Option<&InputValue>) -> bool +where + S: ScalarValue, +{ v.map_or(true, InputValue::is_null) } diff --git a/juniper/src/validation/mod.rs b/juniper/src/validation/mod.rs index eb9f68080..e5f9bad9e 100644 --- a/juniper/src/validation/mod.rs +++ b/juniper/src/validation/mod.rs @@ -12,8 +12,8 @@ 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; diff --git a/juniper/src/validation/multi_visitor.rs b/juniper/src/validation/multi_visitor.rs index 6fe400ad7..ab24563e7 100644 --- a/juniper/src/validation/multi_visitor.rs +++ b/juniper/src/validation/multi_visitor.rs @@ -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) -> ()>(&mut self, f: F); +pub struct MultiVisitorNil; - fn with>(self, visitor: V) -> MultiVisitorCons - where - Self: Sized, - { +impl MultiVisitorNil { + pub fn with(self, visitor: V) -> MultiVisitorCons { MultiVisitorCons(visitor, self) } } -#[doc(hidden)] -pub struct MultiVisitorNil; - -impl<'a> MultiVisitor<'a> for MultiVisitorNil { - fn visit_all) -> ()>(&mut self, _: F) {} -} - #[doc(hidden)] pub struct MultiVisitorCons(A, B); -impl<'a, A: Visitor<'a>, B: MultiVisitor<'a>> MultiVisitor<'a> for MultiVisitorCons { - fn visit_all) -> ()>(&mut self, mut f: F) { - f(&mut self.0); - self.1.visit_all(f); +impl MultiVisitorCons { + pub fn with(self, visitor: V) -> MultiVisitorCons { + MultiVisitorCons(visitor, self) } } -impl<'a, M> Visitor<'a> for M +impl<'a, S> Visitor<'a, S> for MultiVisitorNil where S: ScalarValue {} + +impl<'a, A, B, S> Visitor<'a, S> for MultiVisitorCons where - M: MultiVisitor<'a>, + S: ScalarValue, + A: Visitor<'a, S> + 'a, + B: Visitor<'a, S> + 'a, { - fn enter_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { - self.visit_all(|v| v.enter_document(ctx, doc)); + fn enter_document(&mut self, ctx: &mut ValidatorContext<'a, S>, doc: &'a Document) { + self.0.enter_document(ctx, doc); + self.1.enter_document(ctx, doc); } - - fn exit_document(&mut self, ctx: &mut ValidatorContext<'a>, doc: &'a Document) { - self.visit_all(|v| v.exit_document(ctx, doc)); + fn exit_document(&mut self, ctx: &mut ValidatorContext<'a, S>, doc: &'a Document) { + 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, + ctx: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { - 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), ) { - 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), ) { - 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) { - self.visit_all(|v| v.enter_directive(ctx, d)); + fn enter_directive( + &mut self, + ctx: &mut ValidatorContext<'a, S>, + d: &'a Spanning>, + ) { + self.0.enter_directive(ctx, d); + self.1.enter_directive(ctx, d); } - fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a>, d: &'a Spanning) { - self.visit_all(|v| v.exit_directive(ctx, d)); + fn exit_directive(&mut self, ctx: &mut ValidatorContext<'a, S>, d: &'a Spanning>) { + 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), + ctx: &mut ValidatorContext<'a, S>, + arg: &'a (Spanning<&'a str>, Spanning>), ) { - 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), + ctx: &mut ValidatorContext<'a, S>, + arg: &'a (Spanning<&'a str>, Spanning>), ) { - 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) { - self.visit_all(|v| v.enter_selection_set(ctx, s)); + fn enter_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec>) { + 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) { - self.visit_all(|v| v.exit_selection_set(ctx, s)); + fn exit_selection_set(&mut self, ctx: &mut ValidatorContext<'a, S>, s: &'a Vec>) { + 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) { - self.visit_all(|v| v.enter_field(ctx, f)); + fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning>) { + self.0.enter_field(ctx, f); + self.1.enter_field(ctx, f); } - fn exit_field(&mut self, ctx: &mut ValidatorContext<'a>, f: &'a Spanning) { - self.visit_all(|v| v.exit_field(ctx, f)); + fn exit_field(&mut self, ctx: &mut ValidatorContext<'a, S>, f: &'a Spanning>) { + 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, + ctx: &mut ValidatorContext<'a, S>, + s: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + s: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { - 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, + ctx: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { - self.visit_all(|v| v.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 exit_null_value(&mut self, ctx: &mut ValidatorContext<'a>, n: Spanning<()>) { - self.visit_all(|v| v.exit_null_value(ctx, n.clone())); - } - - fn enter_int_value(&mut self, ctx: &mut ValidatorContext<'a>, i: Spanning) { - self.visit_all(|v| v.enter_int_value(ctx, i.clone())); - } - fn exit_int_value(&mut self, ctx: &mut ValidatorContext<'a>, i: Spanning) { - self.visit_all(|v| v.exit_int_value(ctx, i.clone())); - } - - fn enter_float_value(&mut self, ctx: &mut ValidatorContext<'a>, f: Spanning) { - self.visit_all(|v| v.enter_float_value(ctx, f.clone())); - } - fn exit_float_value(&mut self, ctx: &mut ValidatorContext<'a>, f: Spanning) { - self.visit_all(|v| v.exit_float_value(ctx, f.clone())); + self.0.exit_inline_fragment(ctx, f); + self.1.exit_inline_fragment(ctx, f); } - 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_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_string_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { - self.visit_all(|v| v.exit_string_value(ctx, s.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_boolean_value(&mut self, ctx: &mut ValidatorContext<'a>, b: Spanning) { - self.visit_all(|v| v.enter_boolean_value(ctx, b.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_boolean_value(&mut self, ctx: &mut ValidatorContext<'a>, b: Spanning) { - self.visit_all(|v| v.exit_boolean_value(ctx, b.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_enum_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { - self.visit_all(|v| v.enter_enum_value(ctx, s.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_enum_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { - self.visit_all(|v| v.exit_enum_value(ctx, s.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_variable_value(&mut self, ctx: &mut ValidatorContext<'a>, s: Spanning<&'a String>) { - self.visit_all(|v| v.enter_variable_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_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>>, + ctx: &mut ValidatorContext<'a, S>, + l: Spanning<&'a Vec>>>, ) { - 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>>, + ctx: &mut ValidatorContext<'a, S>, + l: Spanning<&'a Vec>>>, ) { - 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, Spanning)>>, + ctx: &mut ValidatorContext<'a, S>, + o: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { - 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, Spanning)>>, + ctx: &mut ValidatorContext<'a, S>, + o: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { - 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, Spanning), + ctx: &mut ValidatorContext<'a, S>, + f: &'a (Spanning, Spanning>), ) { - 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, Spanning), + ctx: &mut ValidatorContext<'a, S>, + f: &'a (Spanning, Spanning>), ) { - self.visit_all(|v| v.exit_object_field(ctx, f)); + self.0.exit_object_field(ctx, f); + self.1.exit_object_field(ctx, f); } } diff --git a/juniper/src/validation/rules/arguments_of_correct_type.rs b/juniper/src/validation/rules/arguments_of_correct_type.rs index 25b533030..a8f2b8c19 100644 --- a/juniper/src/validation/rules/arguments_of_correct_type.rs +++ b/juniper/src/validation/rules/arguments_of_correct_type.rs @@ -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>>, +pub struct ArgumentsOfCorrectType<'a, S: Debug + 'a> { + current_args: Option<&'a Vec>>, } -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, + ctx: &mut ValidatorContext<'a, S>, + directive: &'a Spanning>, ) { - 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) { + fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { self.current_args = None; } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning) { - self.current_args = ctx.parent_type() + fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning>) { + 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) { + fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { self.current_args = None; } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, - &(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning), + ctx: &mut ValidatorContext<'a, S>, + &(ref arg_name, ref arg_value): &'a (Spanning<&'a str>, Spanning>), ) { - 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#" { diff --git a/juniper/src/validation/rules/default_values_of_correct_type.rs b/juniper/src/validation/rules/default_values_of_correct_type.rs index 5fd14eb2a..36364830a 100644 --- a/juniper/src/validation/rules/default_values_of_correct_type.rs +++ b/juniper/src/validation/rules/default_values_of_correct_type.rs @@ -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), ) { 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]) { diff --git a/juniper/src/validation/rules/fields_on_correct_type.rs b/juniper/src/validation/rules/fields_on_correct_type.rs index ecb933071..e3bef3d90 100644 --- a/juniper/src/validation/rules/fields_on_correct_type.rs +++ b/juniper/src/validation/rules/fields_on_correct_type.rs @@ -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) { +impl<'a, S> Visitor<'a, S> for FieldsOnCorrectType +where + S: ScalarValue, +{ + + fn enter_field( + &mut self, + context: &mut ValidatorContext<'a, S>, + field: &'a Spanning>, + ) { { 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 { diff --git a/juniper/src/validation/rules/fragments_on_composite_types.rs b/juniper/src/validation/rules/fragments_on_composite_types.rs index aac5c12e0..c9fe8cd96 100644 --- a/juniper/src/validation/rules/fragments_on_composite_types.rs +++ b/juniper/src/validation/rules/fragments_on_composite_types.rs @@ -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, + context: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { { 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, + context: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { { 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 { diff --git a/juniper/src/validation/rules/known_argument_names.rs b/juniper/src/validation/rules/known_argument_names.rs index cd136eba4..f31cc00c7 100644 --- a/juniper/src/validation/rules/known_argument_names.rs +++ b/juniper/src/validation/rules/known_argument_names.rs @@ -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>)>, +pub struct KnownArgumentNames<'a, S: Debug + 'a> { + current_args: Option<(ArgumentPosition<'a>, &'a Vec>)>, } -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, + ctx: &mut ValidatorContext<'a, S>, + directive: &'a Spanning>, ) { - 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) { + fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { self.current_args = None; } - fn enter_field(&mut self, ctx: &mut ValidatorContext<'a>, field: &'a Spanning) { - self.current_args = ctx.parent_type() + fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning>) { + 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) { + fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { self.current_args = None; } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, - &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning), + ctx: &mut ValidatorContext<'a, S>, + &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning>), ) { 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#" { diff --git a/juniper/src/validation/rules/known_directives.rs b/juniper/src/validation/rules/known_directives.rs index b5765df9f..6e93098b5 100644 --- a/juniper/src/validation/rules/known_directives.rs +++ b/juniper/src/validation/rules/known_directives.rs @@ -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, @@ -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, + _: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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) { + fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { self.location_stack.push(DirectiveLocation::Field); } - fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) { + fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) { let top = self.location_stack.pop(); assert_eq!(top, Some(DirectiveLocation::Field)); } fn enter_fragment_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { self.location_stack.push(DirectiveLocation::FragmentSpread); } fn exit_fragment_spread( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { self.location_stack.push(DirectiveLocation::InlineFragment); } fn exit_inline_fragment( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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, + ctx: &mut ValidatorContext<'a, S>, + directive: &'a Spanning>, ) { 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) { diff --git a/juniper/src/validation/rules/known_fragment_names.rs b/juniper/src/validation/rules/known_fragment_names.rs index f91afc8fd..60301854f 100644 --- a/juniper/src/validation/rules/known_fragment_names.rs +++ b/juniper/src/validation/rules/known_fragment_names.rs @@ -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, + context: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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#" { diff --git a/juniper/src/validation/rules/known_type_names.rs b/juniper/src/validation/rules/known_type_names.rs index 9db2c2db9..35bc2a841 100644 --- a/juniper/src/validation/rules/known_type_names.rs +++ b/juniper/src/validation/rules/known_type_names.rs @@ -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, + ctx: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, ) { 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, + ctx: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, ) { 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), ) { 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) { diff --git a/juniper/src/validation/rules/lone_anonymous_operation.rs b/juniper/src/validation/rules/lone_anonymous_operation.rs index e3ce8d6b3..dae465180 100644 --- a/juniper/src/validation/rules/lone_anonymous_operation.rs +++ b/juniper/src/validation/rules/lone_anonymous_operation.rs @@ -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, @@ -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) { 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, + ctx: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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#" { diff --git a/juniper/src/validation/rules/mod.rs b/juniper/src/validation/rules/mod.rs index 1e309465d..cdae7559b 100644 --- a/juniper/src/validation/rules/mod.rs +++ b/juniper/src/validation/rules/mod.rs @@ -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) +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) } diff --git a/juniper/src/validation/rules/no_fragment_cycles.rs b/juniper/src/validation/rules/no_fragment_cycles.rs index 2e397e40d..dae0c7f3e 100644 --- a/juniper/src/validation/rules/no_fragment_cycles.rs +++ b/juniper/src/validation/rules/no_fragment_cycles.rs @@ -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) { 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, + _: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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 } diff --git a/juniper/src/validation/rules/no_undefined_variables.rs b/juniper/src/validation/rules/no_undefined_variables.rs index 66c82a5cc..e3b40be38 100644 --- a/juniper/src/validation/rules/no_undefined_variables.rs +++ b/juniper/src/validation/rules/no_undefined_variables.rs @@ -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) { 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, + _: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { self.current_scope = Some(Scope::Fragment(f.item.name.item)); } fn enter_fragment_spread( &mut self, - _: &mut ValidatorContext<'a>, - spread: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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), ) { 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), + _: &mut ValidatorContext<'a, S>, + &(_, ref value): &'a (Spanning<&'a str>, Spanning>), ) { 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) { diff --git a/juniper/src/validation/rules/no_unused_fragments.rs b/juniper/src/validation/rules/no_unused_fragments.rs index 0242fec03..7fced6aa0 100644 --- a/juniper/src/validation/rules/no_unused_fragments.rs +++ b/juniper/src/validation/rules/no_unused_fragments.rs @@ -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) { 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, + _: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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 { diff --git a/juniper/src/validation/rules/no_unused_variables.rs b/juniper/src/validation/rules/no_unused_variables.rs index 8e7caa6f9..5eb2fb01b 100644 --- a/juniper/src/validation/rules/no_unused_variables.rs +++ b/juniper/src/validation/rules/no_unused_variables.rs @@ -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) { 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, + _: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { self.current_scope = Some(Scope::Fragment(f.item.name.item)); } fn enter_fragment_spread( &mut self, - _: &mut ValidatorContext<'a>, - spread: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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), ) { 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), + _: &mut ValidatorContext<'a, S>, + &(_, ref value): &'a (Spanning<&'a str>, Spanning>), ) { 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) { diff --git a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs index 5afdc5327..fd6469cd8 100644 --- a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs +++ b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs @@ -4,8 +4,10 @@ use schema::meta::{Field as FieldType, MetaType}; use std::borrow::Borrow; use std::cell::RefCell; use std::collections::HashMap; +use std::fmt::Debug; use std::hash::Hash; use validation::{ValidatorContext, Visitor}; +use value::ScalarValue; #[derive(Debug)] struct Conflict(ConflictReason, Vec, Vec); @@ -14,13 +16,13 @@ struct Conflict(ConflictReason, Vec, Vec); struct ConflictReason(String, ConflictReasonMessage); #[derive(Debug)] -struct AstAndDef<'a>( +struct AstAndDef<'a, S: Debug + 'a>( Option<&'a str>, - &'a Spanning>, - Option<&'a FieldType<'a>>, + &'a Spanning>, + Option<&'a FieldType<'a, S>>, ); -type AstAndDefCollection<'a> = OrderedMap<&'a str, Vec>>; +type AstAndDefCollection<'a, S> = OrderedMap<&'a str, Vec>>; #[derive(Debug, Clone, PartialEq, Eq, Hash)] enum ConflictReasonMessage { @@ -132,25 +134,28 @@ impl<'a> PairSet<'a> { } } -pub struct OverlappingFieldsCanBeMerged<'a> { - named_fragments: HashMap<&'a str, &'a Fragment<'a>>, +pub struct OverlappingFieldsCanBeMerged<'a, S: Debug + 'a> { + named_fragments: HashMap<&'a str, &'a Fragment<'a, S>>, compared_fragments: RefCell>, } -pub fn factory<'a>() -> OverlappingFieldsCanBeMerged<'a> { +pub fn factory<'a, S: Debug>() -> OverlappingFieldsCanBeMerged<'a, S> { OverlappingFieldsCanBeMerged { named_fragments: HashMap::new(), compared_fragments: RefCell::new(PairSet::new()), } } -impl<'a> OverlappingFieldsCanBeMerged<'a> { +impl<'a, S: Debug> OverlappingFieldsCanBeMerged<'a, S> { fn find_conflicts_within_selection_set( &self, - parent_type: Option<&'a MetaType>, - selection_set: &'a [Selection], - ctx: &ValidatorContext<'a>, - ) -> Vec { + parent_type: Option<&'a MetaType>, + selection_set: &'a [Selection], + ctx: &ValidatorContext<'a, S>, + ) -> Vec + where + S: ScalarValue, + { let mut conflicts = Vec::new(); let (field_map, fragment_names) = @@ -187,8 +192,10 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fragment_name1: &'a str, fragment_name2: &'a str, mutually_exclusive: bool, - ctx: &ValidatorContext<'a>, - ) { + ctx: &ValidatorContext<'a, S>, + ) where + S: ScalarValue, + { if fragment_name1 == fragment_name2 { return; } @@ -258,11 +265,13 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn collect_conflicts_between_fields_and_fragment( &self, conflicts: &mut Vec, - field_map: &AstAndDefCollection<'a>, + field_map: &AstAndDefCollection<'a, S>, fragment_name: &str, mutually_exclusive: bool, - ctx: &ValidatorContext<'a>, - ) { + ctx: &ValidatorContext<'a, S>, + ) where + S: ScalarValue, + { let fragment = match self.named_fragments.get(fragment_name) { Some(f) => f, None => return, @@ -288,10 +297,12 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { &self, conflicts: &mut Vec, mutually_exclusive: bool, - field_map1: &AstAndDefCollection<'a>, - field_map2: &AstAndDefCollection<'a>, - ctx: &ValidatorContext<'a>, - ) { + field_map1: &AstAndDefCollection<'a, S>, + field_map2: &AstAndDefCollection<'a, S>, + ctx: &ValidatorContext<'a, S>, + ) where + S: ScalarValue, + { for (response_name, fields1) in field_map1.iter() { if let Some(fields2) = field_map2.get(response_name) { for field1 in fields1 { @@ -314,9 +325,11 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn collect_conflicts_within( &self, conflicts: &mut Vec, - field_map: &AstAndDefCollection<'a>, - ctx: &ValidatorContext<'a>, - ) { + field_map: &AstAndDefCollection<'a, S>, + ctx: &ValidatorContext<'a, S>, + ) where + S: ScalarValue, + { for (response_name, fields) in field_map.iter() { for (i, field1) in fields.iter().enumerate() { for field2 in &fields[i + 1..] { @@ -333,11 +346,14 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn find_conflict( &self, response_name: &str, - field1: &AstAndDef<'a>, - field2: &AstAndDef<'a>, + field1: &AstAndDef<'a, S>, + field2: &AstAndDef<'a, S>, parents_mutually_exclusive: bool, - ctx: &ValidatorContext<'a>, - ) -> Option { + ctx: &ValidatorContext<'a, S>, + ) -> Option + where + S: ScalarValue, + { let AstAndDef(ref parent_type1, ast1, ref def1) = *field1; let AstAndDef(ref parent_type2, ast2, ref def2) = *field2; @@ -416,11 +432,14 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { &self, mutually_exclusive: bool, parent_type1: Option<&str>, - selection_set1: &'a [Selection], + selection_set1: &'a [Selection], parent_type2: Option<&str>, - selection_set2: &'a [Selection], - ctx: &ValidatorContext<'a>, - ) -> Vec { + selection_set2: &'a [Selection], + ctx: &ValidatorContext<'a, S>, + ) -> Vec + where + S: ScalarValue, + { let mut conflicts = Vec::new(); let parent_type1 = parent_type1.and_then(|t| ctx.schema.concrete_type_by_name(t)); @@ -496,20 +515,18 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { conflicts .iter() .flat_map(|&Conflict(_, ref fs1, _)| fs1.clone()), - ) - .collect(), + ).collect(), vec![pos2.clone()] .into_iter() .chain( conflicts .iter() .flat_map(|&Conflict(_, _, ref fs2)| fs2.clone()), - ) - .collect(), + ).collect(), )) } - fn is_type_conflict(&self, ctx: &ValidatorContext<'a>, t1: &Type, t2: &Type) -> bool { + fn is_type_conflict(&self, ctx: &ValidatorContext<'a, S>, t1: &Type, t2: &Type) -> bool { match (t1, t2) { (&Type::List(ref inner1), &Type::List(ref inner2)) | (&Type::NonNullList(ref inner1), &Type::NonNullList(ref inner2)) => { @@ -534,9 +551,12 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn is_same_arguments( &self, - args1: &Option>, - args2: &Option>, - ) -> bool { + args1: &Option>>, + args2: &Option>>, + ) -> bool + where + S: ScalarValue, + { match (args1, args2) { (&None, &None) => true, ( @@ -565,7 +585,7 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { } } - fn is_object_type(&self, ctx: &ValidatorContext<'a>, type_name: Option<&str>) -> bool { + fn is_object_type(&self, ctx: &ValidatorContext<'a, S>, type_name: Option<&str>) -> bool { match type_name.and_then(|n| ctx.schema.concrete_type_by_name(n)) { Some(&MetaType::Object(_)) => true, _ => false, @@ -574,10 +594,11 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn get_referenced_fields_and_fragment_names( &self, - fragment: &'a Fragment, - ctx: &ValidatorContext<'a>, - ) -> (AstAndDefCollection<'a>, Vec<&'a str>) { - let fragment_type = ctx.schema + fragment: &'a Fragment, + ctx: &ValidatorContext<'a, S>, + ) -> (AstAndDefCollection<'a, S>, Vec<&'a str>) { + let fragment_type = ctx + .schema .concrete_type_by_name(fragment.type_condition.item); self.get_fields_and_fragment_names(fragment_type, &fragment.selection_set, ctx) @@ -585,10 +606,10 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn get_fields_and_fragment_names( &self, - parent_type: Option<&'a MetaType>, - selection_set: &'a [Selection], - ctx: &ValidatorContext<'a>, - ) -> (AstAndDefCollection<'a>, Vec<&'a str>) { + parent_type: Option<&'a MetaType>, + selection_set: &'a [Selection], + ctx: &ValidatorContext<'a, S>, + ) -> (AstAndDefCollection<'a, S>, Vec<&'a str>) { let mut ast_and_defs = OrderedMap::new(); let mut fragment_names = Vec::new(); @@ -605,10 +626,10 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { fn collect_fields_and_fragment_names( &self, - parent_type: Option<&'a MetaType>, - selection_set: &'a [Selection], - ctx: &ValidatorContext<'a>, - ast_and_defs: &mut AstAndDefCollection<'a>, + parent_type: Option<&'a MetaType>, + selection_set: &'a [Selection], + ctx: &ValidatorContext<'a, S>, + ast_and_defs: &mut AstAndDefCollection<'a, S>, fragment_names: &mut Vec<&'a str>, ) { for selection in selection_set { @@ -657,8 +678,11 @@ impl<'a> OverlappingFieldsCanBeMerged<'a> { } } -impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged<'a> { - fn enter_document(&mut self, _: &mut ValidatorContext<'a>, defs: &'a Document) { +impl<'a, S> Visitor<'a, S> for OverlappingFieldsCanBeMerged<'a, S> +where + S: ScalarValue, +{ + fn enter_document(&mut self, _: &mut ValidatorContext<'a, S>, defs: &'a Document) { for def in defs { if let Definition::Fragment(Spanning { ref item, .. }) = *def { self.named_fragments.insert(item.name.item, item); @@ -668,8 +692,8 @@ impl<'a> Visitor<'a> for OverlappingFieldsCanBeMerged<'a> { fn enter_selection_set( &mut self, - ctx: &mut ValidatorContext<'a>, - selection_set: &'a Vec, + ctx: &mut ValidatorContext<'a, S>, + selection_set: &'a Vec>, ) { for Conflict(ConflictReason(reason_name, reason_msg), mut p1, mut p2) in self.find_conflicts_within_selection_set(ctx.parent_type(), selection_set, ctx) @@ -701,8 +725,7 @@ fn format_reason(reason: &ConflictReasonMessage) -> String { name, format_reason(subreason) ) - }) - .collect::>() + }).collect::>() .join(" and "), } } @@ -715,17 +738,18 @@ mod tests { use executor::Registry; use schema::meta::MetaType; use types::base::GraphQLType; - use types::scalars::ID; + use types::scalars::{EmptyMutation, ID}; use parser::SourcePosition; use validation::{ expect_fails_rule, expect_fails_rule_with_schema, expect_passes_rule, expect_passes_rule_with_schema, RuleError, }; + use value::{DefaultScalarValue, ScalarRefValue, ScalarValue}; #[test] fn unique_fields() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment uniqueFields on Dog { @@ -738,7 +762,7 @@ mod tests { #[test] fn identical_fields() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment mergeIdenticalFields on Dog { @@ -751,7 +775,7 @@ mod tests { #[test] fn identical_fields_with_identical_args() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment mergeIdenticalFieldsWithIdenticalArgs on Dog { @@ -764,7 +788,7 @@ mod tests { #[test] fn identical_fields_with_identical_directives() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment mergeSameFieldsWithSameDirectives on Dog { @@ -777,7 +801,7 @@ mod tests { #[test] fn different_args_with_different_aliases() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment differentArgsWithDifferentAliases on Dog { @@ -790,7 +814,7 @@ mod tests { #[test] fn different_directives_with_different_aliases() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment differentDirectivesWithDifferentAliases on Dog { @@ -803,7 +827,7 @@ mod tests { #[test] fn different_skip_include_directives_accepted() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment differentDirectivesWithDifferentAliases on Dog { @@ -816,7 +840,7 @@ mod tests { #[test] fn same_aliases_with_different_field_targets() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" fragment sameAliasesWithDifferentFieldTargets on Dog { @@ -839,7 +863,7 @@ mod tests { #[test] fn same_aliases_allowed_on_nonoverlapping_fields() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment sameAliasesWithDifferentFieldTargets on Pet { @@ -856,7 +880,7 @@ mod tests { #[test] fn alias_masking_direct_field_access() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" fragment aliasMaskingDirectFieldAccess on Dog { @@ -879,7 +903,7 @@ mod tests { #[test] fn different_args_second_adds_an_argument() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" fragment conflictingArgs on Dog { @@ -902,7 +926,7 @@ mod tests { #[test] fn different_args_second_missing_an_argument() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" fragment conflictingArgs on Dog { @@ -925,7 +949,7 @@ mod tests { #[test] fn conflicting_args() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" fragment conflictingArgs on Dog { @@ -948,7 +972,7 @@ mod tests { #[test] fn allows_different_args_where_no_conflict_is_possible() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" fragment conflictingArgs on Pet { @@ -965,7 +989,7 @@ mod tests { #[test] fn encounters_conflict_in_fragments() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -991,7 +1015,7 @@ mod tests { #[test] fn reports_each_conflict_once() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1044,7 +1068,7 @@ mod tests { #[test] fn deep_conflict() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1076,7 +1100,7 @@ mod tests { #[test] fn deep_conflict_with_multiple_issues() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1118,7 +1142,7 @@ mod tests { #[test] fn very_deep_conflict() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1159,7 +1183,7 @@ mod tests { #[test] fn reports_deep_conflict_to_nearest_common_ancestor() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1198,7 +1222,7 @@ mod tests { #[test] fn reports_deep_conflict_to_nearest_common_ancestor_in_fragments() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1245,7 +1269,7 @@ mod tests { #[test] fn reports_deep_conflict_in_nested_fragments() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1299,7 +1323,7 @@ mod tests { #[test] fn ignores_unknown_fragments() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -1328,7 +1352,11 @@ mod tests { struct Node; struct QueryRoot; - impl GraphQLType for SomeBox { + impl GraphQLType for SomeBox + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1336,7 +1364,10 @@ mod tests { Some("SomeBox") } - 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::>("deepBox", i), registry.field::>("unrelatedField", i), @@ -1346,7 +1377,11 @@ mod tests { } } - impl GraphQLType for StringBox { + impl GraphQLType for StringBox + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1354,7 +1389,10 @@ mod tests { Some("StringBox") } - 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::>("scalar", i), registry.field::>("deepBox", i), @@ -1371,7 +1409,11 @@ mod tests { } } - impl GraphQLType for IntBox { + impl GraphQLType for IntBox + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1379,7 +1421,10 @@ mod tests { Some("IntBox") } - 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::>("scalar", i), registry.field::>("deepBox", i), @@ -1396,7 +1441,11 @@ mod tests { } } - impl GraphQLType for NonNullStringBox1 { + impl GraphQLType for NonNullStringBox1 + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1404,14 +1453,21 @@ mod tests { Some("NonNullStringBox1") } - 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::("scalar", i)]; registry.build_interface_type::(i, fields).into_meta() } } - impl GraphQLType for NonNullStringBox1Impl { + impl GraphQLType for NonNullStringBox1Impl + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1419,7 +1475,10 @@ mod tests { Some("NonNullStringBox1Impl") } - 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::("scalar", i), registry.field::>("deepBox", i), @@ -1431,12 +1490,15 @@ mod tests { .interfaces(&[ registry.get_type::(i), registry.get_type::(i), - ]) - .into_meta() + ]).into_meta() } } - impl GraphQLType for NonNullStringBox2 { + impl GraphQLType for NonNullStringBox2 + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1444,14 +1506,21 @@ mod tests { Some("NonNullStringBox2") } - 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::("scalar", i)]; registry.build_interface_type::(i, fields).into_meta() } } - impl GraphQLType for NonNullStringBox2Impl { + impl GraphQLType for NonNullStringBox2Impl + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1459,7 +1528,10 @@ mod tests { Some("NonNullStringBox2Impl") } - 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::("scalar", i), registry.field::>("deepBox", i), @@ -1471,12 +1543,15 @@ mod tests { .interfaces(&[ registry.get_type::(i), registry.get_type::(i), - ]) - .into_meta() + ]).into_meta() } } - impl GraphQLType for Node { + impl GraphQLType for Node + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1484,7 +1559,10 @@ mod tests { Some("Node") } - 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::>("id", i), registry.field::>("name", i), @@ -1494,7 +1572,11 @@ mod tests { } } - impl GraphQLType for Edge { + impl GraphQLType for Edge + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1502,14 +1584,21 @@ mod tests { Some("Edge") } - 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::>("node", i)]; registry.build_object_type::(i, fields).into_meta() } } - impl GraphQLType for Connection { + impl GraphQLType for Connection + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1517,14 +1606,21 @@ mod tests { Some("Connection") } - 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::>>>("edges", i)]; registry.build_object_type::(i, fields).into_meta() } } - impl GraphQLType for QueryRoot { + impl GraphQLType for QueryRoot + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { type Context = (); type TypeInfo = (); @@ -1532,7 +1628,10 @@ mod tests { 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, + { registry.get_type::(i); registry.get_type::(i); registry.get_type::(i); @@ -1548,8 +1647,9 @@ mod tests { #[test] fn conflicting_return_types_which_potentially_overlap() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1578,8 +1678,9 @@ mod tests { #[test] fn compatible_return_shapes_on_different_return_types() { - expect_passes_rule_with_schema( + expect_passes_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1602,8 +1703,9 @@ mod tests { #[test] fn disallows_differing_return_types_despite_no_overlap() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1632,8 +1734,9 @@ mod tests { #[test] fn reports_correctly_when_a_non_exclusive_follows_an_exclusive() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1699,8 +1802,9 @@ mod tests { #[test] fn disallows_differing_return_type_nullability_despite_no_overlap() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1729,8 +1833,9 @@ mod tests { #[test] fn disallows_differing_return_type_list_despite_no_overlap() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1760,8 +1865,9 @@ mod tests { )], ); - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1794,8 +1900,9 @@ mod tests { #[test] fn disallows_differing_subfields() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1829,8 +1936,9 @@ mod tests { #[test] fn disallows_differing_deep_return_types_despite_no_overlap() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1868,8 +1976,9 @@ mod tests { #[test] fn allows_non_conflicting_overlapping_types() { - expect_passes_rule_with_schema( + expect_passes_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1888,8 +1997,9 @@ mod tests { #[test] fn same_wrapped_scalar_return_types() { - expect_passes_rule_with_schema( + expect_passes_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1908,8 +2018,9 @@ mod tests { #[test] fn allows_inline_typeless_fragments() { - expect_passes_rule_with_schema( + expect_passes_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1924,8 +2035,9 @@ mod tests { #[test] fn compares_deep_types_including_list() { - expect_fails_rule_with_schema( + expect_fails_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { @@ -1972,8 +2084,9 @@ mod tests { #[test] fn ignores_unknown_types() { - expect_passes_rule_with_schema( + expect_passes_rule_with_schema::<_, EmptyMutation<()>, _, _, DefaultScalarValue>( QueryRoot, + EmptyMutation::new(), factory, r#" { diff --git a/juniper/src/validation/rules/possible_fragment_spreads.rs b/juniper/src/validation/rules/possible_fragment_spreads.rs index b455b3ac7..a8e614e0c 100644 --- a/juniper/src/validation/rules/possible_fragment_spreads.rs +++ b/juniper/src/validation/rules/possible_fragment_spreads.rs @@ -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) { 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, + ctx: &mut ValidatorContext<'a, S>, + frag: &'a Spanning>, ) { 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, + ctx: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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 } diff --git a/juniper/src/validation/rules/provided_non_null_arguments.rs b/juniper/src/validation/rules/provided_non_null_arguments.rs index 8b2f2cb3f..5151217fd 100644 --- a/juniper/src/validation/rules/provided_non_null_arguments.rs +++ b/juniper/src/validation/rules/provided_non_null_arguments.rs @@ -3,15 +3,24 @@ 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) { +impl<'a, S> Visitor<'a, S> for ProvidedNonNullArguments +where + S: ScalarValue, +{ + + fn enter_field( + &mut self, + ctx: &mut ValidatorContext<'a, S>, + field: &'a Spanning>, + ) { let field_name = &field.item.name.item; if let Some(&FieldType { @@ -43,8 +52,8 @@ impl<'a> Visitor<'a> for ProvidedNonNullArguments { fn enter_directive( &mut self, - ctx: &mut ValidatorContext<'a>, - directive: &'a Spanning, + ctx: &mut ValidatorContext<'a, S>, + directive: &'a Spanning>, ) { let directive_name = &directive.item.name.item; @@ -96,10 +105,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 +123,7 @@ mod tests { #[test] fn arg_on_optional_arg() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -127,7 +137,7 @@ mod tests { #[test] fn no_arg_on_optional_arg() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -141,7 +151,7 @@ mod tests { #[test] fn multiple_args() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -155,7 +165,7 @@ mod tests { #[test] fn multiple_args_reverse_order() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -169,7 +179,7 @@ mod tests { #[test] fn no_args_on_multiple_optional() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -183,7 +193,7 @@ mod tests { #[test] fn one_arg_on_multiple_optional() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -197,7 +207,7 @@ mod tests { #[test] fn second_arg_on_multiple_optional() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -211,7 +221,7 @@ mod tests { #[test] fn muliple_reqs_on_mixed_list() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -225,7 +235,7 @@ mod tests { #[test] fn multiple_reqs_and_one_opt_on_mixed_list() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -239,7 +249,7 @@ mod tests { #[test] fn all_reqs_on_opts_on_mixed_list() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -253,7 +263,7 @@ mod tests { #[test] fn missing_one_non_nullable_argument() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -271,7 +281,7 @@ mod tests { #[test] fn missing_multiple_non_nullable_arguments() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -295,7 +305,7 @@ mod tests { #[test] fn incorrect_value_and_missing_argument() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -313,7 +323,7 @@ mod tests { #[test] fn ignores_unknown_directives() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -325,7 +335,7 @@ mod tests { #[test] fn with_directives_of_valid_types() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -342,7 +352,7 @@ mod tests { #[test] fn with_directive_with_missing_types() { - expect_fails_rule( + expect_fails_rule::<_, _, DefaultScalarValue>( factory, r#" { diff --git a/juniper/src/validation/rules/scalar_leafs.rs b/juniper/src/validation/rules/scalar_leafs.rs index 2357c7998..e916d29bb 100644 --- a/juniper/src/validation/rules/scalar_leafs.rs +++ b/juniper/src/validation/rules/scalar_leafs.rs @@ -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) { +impl<'a, S> Visitor<'a, S> for ScalarLeafs +where + S: ScalarValue, +{ + + fn enter_field( + &mut self, + ctx: &mut ValidatorContext<'a, S>, + field: &'a Spanning>, + ) { 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 { diff --git a/juniper/src/validation/rules/unique_argument_names.rs b/juniper/src/validation/rules/unique_argument_names.rs index e31585ba9..4a0f6b281 100644 --- a/juniper/src/validation/rules/unique_argument_names.rs +++ b/juniper/src/validation/rules/unique_argument_names.rs @@ -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) { +impl<'a, S> Visitor<'a, S> for UniqueArgumentNames<'a> +where + S: ScalarValue, +{ + + fn enter_directive( + &mut self, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, + ) { self.known_names = HashMap::new(); } - fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) { + fn enter_field( + &mut self, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, + ) { self.known_names = HashMap::new(); } fn enter_argument( &mut self, - ctx: &mut ValidatorContext<'a>, - &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning), + ctx: &mut ValidatorContext<'a, S>, + &(ref arg_name, _): &'a (Spanning<&'a str>, Spanning>), ) { 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,7 +81,7 @@ mod tests { #[test] fn no_arguments_on_directive() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -79,7 +93,7 @@ mod tests { #[test] fn argument_on_field() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -91,7 +105,7 @@ mod tests { #[test] fn argument_on_directive() { - expect_passes_rule( + expect_passes_rule::<_, _, DefaultScalarValue>( factory, r#" { @@ -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#" { diff --git a/juniper/src/validation/rules/unique_fragment_names.rs b/juniper/src/validation/rules/unique_fragment_names.rs index 21873c889..02fcc438c 100644 --- a/juniper/src/validation/rules/unique_fragment_names.rs +++ b/juniper/src/validation/rules/unique_fragment_names.rs @@ -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, + context: &mut ValidatorContext<'a, S>, + f: &'a Spanning>, ) { 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), ], )], ); diff --git a/juniper/src/validation/rules/unique_input_field_names.rs b/juniper/src/validation/rules/unique_input_field_names.rs index cd1e25303..8035f7b5b 100644 --- a/juniper/src/validation/rules/unique_input_field_names.rs +++ b/juniper/src/validation/rules/unique_input_field_names.rs @@ -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>, @@ -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, Spanning)>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { self.known_name_stack.push(HashMap::new()); } fn exit_object_value( &mut self, - _: &mut ValidatorContext<'a>, - _: Spanning<&'a Vec<(Spanning, Spanning)>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { self.known_name_stack.pop(); } fn enter_object_field( &mut self, - ctx: &mut ValidatorContext<'a>, - &(ref field_name, _): &'a (Spanning, Spanning), + ctx: &mut ValidatorContext<'a, S>, + &(ref field_name, _): &'a (Spanning, Spanning>), ) { 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#" { diff --git a/juniper/src/validation/rules/unique_operation_names.rs b/juniper/src/validation/rules/unique_operation_names.rs index 3ca6ac8de..22742e290 100644 --- a/juniper/src/validation/rules/unique_operation_names.rs +++ b/juniper/src/validation/rules/unique_operation_names.rs @@ -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, + ctx: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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,14 +144,18 @@ 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( @@ -154,14 +170,16 @@ 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( diff --git a/juniper/src/validation/rules/unique_variable_names.rs b/juniper/src/validation/rules/unique_variable_names.rs index 24e72e26e..0730ce93b 100644 --- a/juniper/src/validation/rules/unique_variable_names.rs +++ b/juniper/src/validation/rules/unique_variable_names.rs @@ -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, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { 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), ) { 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 } diff --git a/juniper/src/validation/rules/variables_are_input_types.rs b/juniper/src/validation/rules/variables_are_input_types.rs index c58376097..3ff8a5022 100644 --- a/juniper/src/validation/rules/variables_are_input_types.rs +++ b/juniper/src/validation/rules/variables_are_input_types.rs @@ -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), ) { - 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) { diff --git a/juniper/src/validation/rules/variables_in_allowed_position.rs b/juniper/src/validation/rules/variables_in_allowed_position.rs index 0bffb3469..ba923c047 100644 --- a/juniper/src/validation/rules/variables_in_allowed_position.rs +++ b/juniper/src/validation/rules/variables_in_allowed_position.rs @@ -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, HashSet<&'a str>>, variable_usages: HashMap, Vec<(Spanning<&'a String>, Type<'a>)>>, - variable_defs: HashMap, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a>)>>, + variable_defs: HashMap, Vec<&'a (Spanning<&'a str>, VariableDefinition<'a, S>)>>, current_scope: Option>, } -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)>, + ctx: &mut ValidatorContext<'a, S>, visited: &mut HashSet>, ) { 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) { 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, + _: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, ) { self.current_scope = Some(Scope::Fragment(fragment.item.name.item)); } fn enter_operation_definition( &mut self, - _: &mut ValidatorContext<'a>, - op: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + op: &'a Spanning>, ) { 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, + _: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, ) { 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), ) { 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) { diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index 47331b0c0..ca3219aa2 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -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; @@ -26,6 +27,14 @@ struct ComplicatedArgs; struct QueryRoot; +#[derive(Debug, GraphQLInputObject)] +struct TestInput { + id: i32, + name: String, +} + +struct MutationRoot; + #[derive(Debug)] enum DogCommand { Sit, @@ -51,7 +60,11 @@ struct ComplexInput { string_list_field: Option>>, } -impl GraphQLType for Being { +impl GraphQLType 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::>("name", i) .argument(registry.arg::>("surname", i))]; @@ -68,7 +84,11 @@ impl GraphQLType for Being { } } -impl GraphQLType for Pet { +impl GraphQLType 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::>("name", i) .argument(registry.arg::>("surname", i))]; @@ -85,7 +108,11 @@ impl GraphQLType for Pet { } } -impl GraphQLType for Canine { +impl GraphQLType 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::>("name", i) .argument(registry.arg::>("surname", i))]; @@ -102,7 +132,11 @@ impl GraphQLType for Canine { } } -impl GraphQLType for DogCommand { +impl GraphQLType 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::( 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 { +impl FromInputValue for DogCommand +where + S: ScalarValue, +{ + fn from_input_value<'a>(v: &InputValue) -> Option + 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 GraphQLType 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::>("name", i) @@ -169,12 +218,15 @@ impl GraphQLType for Dog { registry.get_type::(i), registry.get_type::(i), registry.get_type::(i), - ]) - .into_meta() + ]).into_meta() } } -impl GraphQLType for FurColor { +impl GraphQLType 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::( 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 { +impl FromInputValue for FurColor +where + S: ScalarValue, +{ + fn from_input_value<'a>(v: &InputValue) -> Option + 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 GraphQLType 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::>("name", i) @@ -235,7 +304,11 @@ impl GraphQLType for Cat { } } -impl GraphQLType for CatOrDog { +impl GraphQLType 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::(i), registry.get_type::(i)]; registry.build_union_type::(i, types).into_meta() } } -impl GraphQLType for Intelligent { +impl GraphQLType 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::>("iq", i)]; registry.build_interface_type::(i, fields).into_meta() } } -impl GraphQLType for Human { +impl GraphQLType 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::>("name", i) @@ -287,12 +377,15 @@ impl GraphQLType for Human { .interfaces(&[ registry.get_type::(i), registry.get_type::(i), - ]) - .into_meta() + ]).into_meta() } } -impl GraphQLType for Alien { +impl GraphQLType 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::>("name", i) @@ -314,12 +410,15 @@ impl GraphQLType for Alien { .interfaces(&[ registry.get_type::(i), registry.get_type::(i), - ]) - .into_meta() + ]).into_meta() } } -impl GraphQLType for DogOrHuman { +impl GraphQLType 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::(i), registry.get_type::(i)]; registry.build_union_type::(i, types).into_meta() } } -impl GraphQLType for HumanOrAlien { +impl GraphQLType 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::(i), registry.get_type::(i)]; registry.build_union_type::(i, types).into_meta() } } -impl GraphQLType for ComplexInput { +impl GraphQLType 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::("requiredField", i), registry.arg::>("intField", i), @@ -372,8 +488,15 @@ impl GraphQLType for ComplexInput { } } -impl FromInputValue for ComplexInput { - fn from_input_value(v: &InputValue) -> Option { +impl FromInputValue for ComplexInput +where + S: ScalarValue, +{ + fn from_input_value<'a>(v: &InputValue) -> Option + 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 GraphQLType 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::>("intArgField", i) @@ -449,7 +579,11 @@ impl GraphQLType for ComplicatedArgs { } } -impl GraphQLType for QueryRoot { +impl GraphQLType 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::>("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 +impl GraphQLType for MutationRoot +where + 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::("testInput", i).argument( + registry.arg_with_default::( + "input", + &TestInput { + id: 423, + name: String::from("foo"), + }, + i, + ), + )]; + + registry.build_object_type::(i, &fields).into_meta() + } +} + +pub fn validate<'a, Q, M, V, F, S>(r: Q, m: M, q: &'a str, factory: F) -> Vec where - R: GraphQLType, - V: Visitor<'a> + 'a, + for<'b> &'b S: ScalarRefValue<'b>, + S: ScalarValue + 'a, + Q: GraphQLType, + M: GraphQLType, + 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,66 +696,81 @@ 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, - V: Visitor<'a> + 'a, + S: ScalarValue + 'a, + for<'b> &'b S: ScalarRefValue<'b>, + Q: GraphQLType, + M: GraphQLType, + 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); + print_errors(&errs, q); panic!("Expected rule to pass, but errors found"); } } -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, - V: Visitor<'a> + 'a, + S: ScalarValue + 'a, + for<'b> &'b S: ScalarRefValue<'b>, + Q: GraphQLType, + M: GraphQLType, + 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"); } else if errs != expected_errors { println!("==> Expected errors:"); - print_errors(expected_errors); + print_errors(expected_errors, q); println!("\n==> Actual errors:"); - print_errors(&errs); + print_errors(&errs, q); panic!("Unexpected set of errors found"); } } -fn print_errors(errs: &[RuleError]) { +fn print_errors(errs: &[RuleError], query: &str) { for err in errs { for p in err.locations() { print!("[{:>3},{:>3},{:>3}] ", p.index(), p.line(), p.column()); } println!("{}", err.message()); + + for p in err.locations() { + println!("{}", &query[p.index()..]); + } } } diff --git a/juniper/src/validation/traits.rs b/juniper/src/validation/traits.rs index 624d17782..7f48c6aca 100644 --- a/juniper/src/validation/traits.rs +++ b/juniper/src/validation/traits.rs @@ -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) {} + fn exit_document(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Document) {} fn enter_operation_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn exit_operation_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn enter_fragment_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn exit_fragment_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn enter_variable_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning<&'a str>, VariableDefinition), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning<&'a str>, VariableDefinition), ) { } fn exit_variable_definition( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning<&'a str>, VariableDefinition), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning<&'a str>, VariableDefinition), ) { } - fn enter_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) {} - fn exit_directive(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) {} + fn enter_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) {} + fn exit_directive(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) {} fn enter_argument( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning<&'a str>, Spanning), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning<&'a str>, Spanning>), ) { } fn exit_argument( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning<&'a str>, Spanning), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning<&'a str>, Spanning>), ) { } - fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec) {} - fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a>, _: &'a Vec) {} + fn enter_selection_set(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Vec>) {} + fn exit_selection_set(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Vec>) {} - fn enter_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) {} - fn exit_field(&mut self, _: &mut ValidatorContext<'a>, _: &'a Spanning) {} + fn enter_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) {} + fn exit_field(&mut self, _: &mut ValidatorContext<'a, S>, _: &'a Spanning>) {} fn enter_fragment_spread( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn exit_fragment_spread( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn enter_inline_fragment( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } fn exit_inline_fragment( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a Spanning, + _: &mut ValidatorContext<'a, S>, + _: &'a Spanning>, ) { } - 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) {} - fn exit_int_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning) {} + 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) {} - fn exit_float_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning) {} + 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) {} - fn exit_boolean_value(&mut self, _: &mut ValidatorContext<'a>, _: Spanning) {} - - 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>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec>>>, ) { } fn exit_list_value( &mut self, - _: &mut ValidatorContext<'a>, - _: Spanning<&'a Vec>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec>>>, ) { } fn enter_object_value( &mut self, - _: &mut ValidatorContext<'a>, - _: Spanning<&'a Vec<(Spanning, Spanning)>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { } fn exit_object_value( &mut self, - _: &mut ValidatorContext<'a>, - _: Spanning<&'a Vec<(Spanning, Spanning)>>, + _: &mut ValidatorContext<'a, S>, + _: Spanning<&'a Vec<(Spanning, Spanning>)>>, ) { } fn enter_object_field( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning, Spanning), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning, Spanning>), ) { } fn exit_object_field( &mut self, - _: &mut ValidatorContext<'a>, - _: &'a (Spanning, Spanning), + _: &mut ValidatorContext<'a, S>, + _: &'a (Spanning, Spanning>), ) { } } diff --git a/juniper/src/validation/visitor.rs b/juniper/src/validation/visitor.rs index f7f9a7866..12417473c 100644 --- a/juniper/src/validation/visitor.rs +++ b/juniper/src/validation/visitor.rs @@ -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, + ctx: &mut ValidatorContext<'a, S>, + d: &'a Document, +) where + S: ScalarValue, + MultiVisitorCons: 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, -) { + ctx: &mut ValidatorContext<'a, S>, + d: &'a Vec>, +) 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) +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) +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) +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>, -) { + ctx: &mut ValidatorContext<'a, S>, + defs: &'a Option>>, +) 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>>, -) { + ctx: &mut ValidatorContext<'a, S>, + directives: &'a Option>>>, +) 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>>, - arguments: &'a Option>, -) { + ctx: &mut ValidatorContext<'a, S>, + meta_args: &Option<&Vec>>, + arguments: &'a Option>>, +) 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, -) { + ctx: &mut ValidatorContext<'a, S>, + selection_set: &'a Vec>, +) 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, +) 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, -) { - let meta_field = ctx.parent_type() + ctx: &mut ValidatorContext<'a, S>, + field: &'a Spanning>, +) 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, -) { + ctx: &mut ValidatorContext<'a, S>, + spread: &'a Spanning>, +) 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, -) { - let mut visit_fn = move |ctx: &mut ValidatorContext<'a>| { + ctx: &mut ValidatorContext<'a, S>, + fragment: &'a Spanning>, +) 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, -) { + ctx: &mut ValidatorContext<'a, S>, + input_value: &'a Spanning>, +) 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, -) { + ctx: &mut ValidatorContext<'a, S>, + input_value: &'a Spanning>, +) 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, -) { + ctx: &mut ValidatorContext<'a, S>, + input_value: &'a Spanning>, +) 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)), diff --git a/juniper/src/value.rs b/juniper/src/value.rs deleted file mode 100644 index 0f482db64..000000000 --- a/juniper/src/value.rs +++ /dev/null @@ -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), - 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(&mut self, k: K, value: Value) -> Option - where - K: Into, - for<'a> &'a str: PartialEq, - { - 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(&self, f: K) -> bool - where - for<'a> &'a str: PartialEq, - { - 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` - // 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` - // 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(&self, key: K) -> Option<&Value> - where - for<'a> &'a str: PartialEq, - { - 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; - - fn into_iter(self) -> Self::IntoIter { - self.key_value_list.into_iter() - } -} - -impl From for Value { - fn from(o: Object) -> Self { - Value::Object(o) - } -} - -impl FromIterator<(K, Value)> for Object -where - K: Into, - for<'a> &'a str: PartialEq, -{ - fn from_iter(iter: I) -> Self - where - I: IntoIterator, - { - 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.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.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>(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::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> { - 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 for Value { - fn from(s: String) -> Value { - Value::string(s) - } -} - -impl From for Value { - fn from(b: bool) -> Value { - Value::boolean(b) - } -} - -impl From for Value { - fn from(i: i32) -> Value { - Value::int(i) - } -} - -impl From for Value { - fn from(f: f64) -> Value { - Value::float(f) - } -} - -impl From> for Value -where - Value: From, -{ - fn from(v: Option) -> 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(), - ) - ); - } -} diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs new file mode 100644 index 000000000..145910fda --- /dev/null +++ b/juniper/src/value/mod.rs @@ -0,0 +1,338 @@ +use ast::{InputValue, ToInputValue}; +use parser::Spanning; +mod object; +mod scalar; + +pub use self::object::Object; + +pub use self::scalar::{DefaultScalarValue, 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 { + Null, + Scalar(S), + List(Vec>), + Object(Object), +} + +impl Value +where + S: ScalarValue, +{ + // CONSTRUCTORS + + /// Construct a null value. + pub fn null() -> Self { + Value::Null + } + + /// Construct an integer value. + pub fn int(i: i32) -> Self { + Self::scalar(i) + } + + /// Construct a floating point value. + pub fn float(f: f64) -> Self { + Self::scalar(f) + } + + /// Construct a string value. + pub fn string(s: &str) -> Self { + Self::scalar(s) + } + + /// Construct a boolean value. + pub fn boolean(b: bool) -> Self { + Self::scalar(b) + } + + /// Construct a list value. + pub fn list(l: Vec) -> Self { + Value::List(l) + } + + /// Construct an object value. + pub fn object(o: Object) -> Self { + Value::Object(o) + } + + pub fn scalar(s: T) -> Self + where + T: Into, + { + Value::Scalar(s.into()) + } + + // DISCRIMINATORS + + /// Does this value represent null? + pub fn is_null(&self) -> bool { + match *self { + Value::Null => true, + _ => false, + } + } + + pub fn as_scalar_value<'a, T>(&self) -> Option + where + for<'b> &'b S: Into>, + S: 'a, + { + match *self { + Value::Scalar(ref s) => s.into(), + _ => None, + } + } + + /// View the underlying float value, if present. + pub fn as_float_value(&self) -> Option + where + for<'a> &'a S: ScalarRefValue<'a>, + { + self.as_scalar_value::() + } + + /// 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> { + match *self { + Value::List(ref l) => Some(l), + _ => None, + } + } + + pub fn as_scalar(&self) -> Option<&S> { + match *self { + Value::Scalar(ref s) => Some(s), + _ => None, + } + } + + /// View the underlying string value, if present. + pub fn as_string_value<'a>(&'a self) -> Option<&'a str> + where + Option<&'a str>: From<&'a S>, + { + match *self { + Value::Scalar(ref s) => <_ as Into>>::into(s), + _ => None, + } + } +} + +impl ToInputValue for Value { + fn to_input_value(&self) -> InputValue { + 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 From> for Value +where + S: ScalarValue, + Value: From, +{ + fn from(v: Option) -> Value { + match v { + Some(v) => v.into(), + None => Value::null(), + } + } +} + +impl<'a, S> From<&'a str> for Value +where + S: ScalarValue, +{ + fn from(s: &'a str) -> Self { + Value::scalar(s) + } +} + +impl From for Value +where + S: ScalarValue, +{ + fn from(s: String) -> Self { + Value::scalar(s) + } +} + +impl From for Value +where + S: ScalarValue, +{ + fn from(i: i32) -> Self { + Value::scalar(i) + } +} + +impl From for Value +where + S: ScalarValue, +{ + fn from(f: f64) -> Self { + Value::scalar(f) + } +} + +impl From for Value +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; +/// +/// # 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 = graphql_value!("test"); + assert_eq!(s, Value::string("test")); + } + + #[test] + fn value_macro_int() { + let s: Value = graphql_value!(123); + assert_eq!(s, Value::int(123)); + } + + #[test] + fn value_macro_float() { + let s: Value = graphql_value!(123.5); + assert_eq!(s, Value::float(123.5)); + } + + #[test] + fn value_macro_boolean() { + let s: Value = graphql_value!(false); + assert_eq!(s, Value::boolean(false)); + } + + #[test] + fn value_macro_option() { + let s: Value = graphql_value!(Some("test")); + assert_eq!(s, Value::string("test")); + let s: Value = graphql_value!(None); + assert_eq!(s, Value::null()); + } + + #[test] + fn value_macro_list() { + let s: Value = graphql_value!([123, "Test", false]); + assert_eq!( + s, + Value::list(vec![ + Value::int(123), + Value::string("Test"), + Value::boolean(false), + ]) + ); + let s: Value = graphql_value!([123, [456], 789]); + assert_eq!( + s, + Value::list(vec![ + Value::int(123), + Value::list(vec![Value::int(456)]), + Value::int(789), + ]) + ); + } + + #[test] + fn value_macro_object() { + let s: Value = graphql_value!({ "key": 123, "next": true }); + assert_eq!( + s, + Value::object( + vec![("key", Value::int(123)), ("next", Value::boolean(true))] + .into_iter() + .collect(), + ) + ); + } +} diff --git a/juniper/src/value/object.rs b/juniper/src/value/object.rs new file mode 100644 index 000000000..2a2985655 --- /dev/null +++ b/juniper/src/value/object.rs @@ -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 { + 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(&mut self, k: K, value: Value) -> Option> + where + K: Into, + for<'a> &'a str: PartialEq, + { + 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(&self, f: K) -> bool + where + for<'a> &'a str: PartialEq, + { + 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` + // 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` + // 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(&self, key: K) -> Option<&Value> + where + for<'a> &'a str: PartialEq, + { + 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; + + fn into_iter(self) -> Self::IntoIter { + self.key_value_list.into_iter() + } +} + +impl From> for Value { + fn from(o: Object) -> Self { + Value::Object(o) + } +} + +impl FromIterator<(K, Value)> for Object +where + K: Into, + for<'a> &'a str: PartialEq, +{ + fn from_iter(iter: I) -> Self + where + I: IntoIterator)>, + { + 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)>, +} + +impl<'a, S> Iterator for FieldIter<'a, S> { + type Item = &'a (String, Value); + + fn next(&mut self) -> Option { + self.inner.next() + } +} + +#[doc(hidden)] +pub struct FieldIterMut<'a, S: 'a> { + inner: ::std::slice::IterMut<'a, (String, Value)>, +} + +impl<'a, S> Iterator for FieldIterMut<'a, S> { + type Item = &'a mut (String, Value); + + fn next(&mut self) -> Option { + self.inner.next() + } +} diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs new file mode 100644 index 000000000..3e8380df7 --- /dev/null +++ b/juniper/src/value/scalar.rs @@ -0,0 +1,253 @@ +use parser::ParseError; +use schema::meta::ScalarMeta; +use serde::de::{self, Deserialize, Deserializer}; +use serde::ser::{Serialize, Serializer}; +use std::fmt::{self, Debug, Display}; + +pub trait ParseScalarValue { + fn from_str(value: &str) -> Result; +} + +pub trait ScalarValue: + Debug + + Display + + PartialEq + + Clone + + Serialize + + for<'a> From<&'a str> + + From + + From + + From + + From + + Into> + + Into> + + Into> + + Into> +{ + fn is_type<'a, T>(&'a self) -> bool + where + &'a Self: Into>, + { + self.into().is_some() + } +} + +pub trait ScalarRefValue<'a>: + Debug + Into> + Into> + Into> + Into> +{ +} + +impl<'a, T> ScalarRefValue<'a> for &'a T +where + T: ScalarValue, + &'a T: Into> + Into> + Into> + Into>, +{} + +#[derive(Debug, PartialEq, Clone)] +pub enum DefaultScalarValue { + Int(i32), + Float(f64), + String(String), + Boolean(bool), +} + +impl ScalarValue for DefaultScalarValue {} + +impl Display for DefaultScalarValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + DefaultScalarValue::Int(i) => write!(f, "{}", i), + DefaultScalarValue::Float(n) => write!(f, "{}", n), + DefaultScalarValue::String(ref s) => write!(f, "\"{}\"", s), + DefaultScalarValue::Boolean(b) => write!(f, "{}", b), + } + } +} + +impl<'a> From<&'a str> for DefaultScalarValue { + fn from(s: &'a str) -> Self { + DefaultScalarValue::String(s.into()) + } +} + +impl From for DefaultScalarValue { + fn from(s: String) -> Self { + (&s as &str).into() + } +} + +impl From for DefaultScalarValue { + fn from(b: bool) -> Self { + DefaultScalarValue::Boolean(b) + } +} + +impl From for DefaultScalarValue { + fn from(i: i32) -> Self { + DefaultScalarValue::Int(i) + } +} + +impl From for DefaultScalarValue { + fn from(f: f64) -> Self { + DefaultScalarValue::Float(f) + } +} + +impl From for Option { + fn from(s: DefaultScalarValue) -> Self { + match s { + DefaultScalarValue::Boolean(b) => Some(b), + _ => None, + } + } +} + +impl From for Option { + fn from(s: DefaultScalarValue) -> Self { + match s { + DefaultScalarValue::Int(i) => Some(i), + _ => None, + } + } +} + +impl From for Option { + fn from(s: DefaultScalarValue) -> Self { + match s { + DefaultScalarValue::Float(s) => Some(s), + DefaultScalarValue::Int(i) => Some(i as f64), + _ => None, + } + } +} + +impl From for Option { + fn from(s: DefaultScalarValue) -> Self { + match s { + DefaultScalarValue::String(s) => Some(s.clone()), + _ => None, + } + } +} + +impl<'a> From<&'a DefaultScalarValue> for Option { + fn from(s: &'a DefaultScalarValue) -> Self { + match *s { + DefaultScalarValue::Boolean(b) => Some(b), + _ => None, + } + } +} + +impl<'a> From<&'a DefaultScalarValue> for Option { + fn from(s: &'a DefaultScalarValue) -> Self { + match *s { + DefaultScalarValue::Float(b) => Some(b), + DefaultScalarValue::Int(i) => Some(i as f64), + _ => None, + } + } +} + +impl<'a> From<&'a DefaultScalarValue> for Option { + fn from(s: &'a DefaultScalarValue) -> Self { + match *s { + DefaultScalarValue::Int(b) => Some(b), + _ => None, + } + } +} + +impl<'a> From<&'a DefaultScalarValue> for Option<&'a str> { + fn from(s: &'a DefaultScalarValue) -> Self { + match *s { + DefaultScalarValue::String(ref s) => Some(s), + _ => None, + } + } +} + +impl Serialize for DefaultScalarValue { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + match *self { + DefaultScalarValue::Int(v) => serializer.serialize_i32(v), + DefaultScalarValue::Float(v) => serializer.serialize_f64(v), + DefaultScalarValue::String(ref v) => serializer.serialize_str(v), + DefaultScalarValue::Boolean(v) => serializer.serialize_bool(v), + } + } +} + +impl<'de> Deserialize<'de> for DefaultScalarValue { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + 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(self, value: bool) -> Result { + Ok(DefaultScalarValue::Boolean(value)) + } + + fn visit_i64(self, value: i64) -> Result + 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(self, value: u64) -> Result + 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(self, value: f64) -> Result { + Ok(DefaultScalarValue::Float(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.into()) + } + + fn visit_string(self, value: String) -> Result { + Ok(DefaultScalarValue::String(value)) + } + } + + deserializer.deserialize_any(DefaultScalarValueVisitor) + } +} diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 7aed453f9..d52db5082 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -182,7 +182,10 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { } 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 +193,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,17 +207,19 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { fn resolve( &self, _: &(), - _: Option<&[_juniper::Selection]>, - _: &_juniper::Executor - ) -> _juniper::Value { + _: Option<&[_juniper::Selection<__S>]>, + _: &_juniper::Executor<__S, Self::Context> + ) -> _juniper::Value<__S> { match self { #(#resolves)* } } } - impl _juniper::FromInputValue for #ident { - fn from_input_value(v: &_juniper::InputValue) -> Option<#ident> { + 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_string_value()) { #(#from_inputs)* _ => None, @@ -221,8 +227,8 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { } } - 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 }; }; diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs index 40e98f10a..e1a7fb74c 100644 --- a/juniper_codegen/src/derive_input_object.rs +++ b/juniper_codegen/src/derive_input_object.rs @@ -225,8 +225,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() }, @@ -241,81 +241,78 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { } let body = quote! { - impl #generics _juniper::GraphQLType for #ident #generics { - type Context = (); - type TypeInfo = (); + impl _juniper::GraphQLType for #ident + where S: _juniper::ScalarValue, + for<'b> &'b S: _juniper::ScalarRefValue<'b> + { + type Context = (); + type TypeInfo = (); - fn name(_: &()) -> Option<&'static str> { - Some(#name) - } + fn name(_: &()) -> Option<&'static str> { + Some(#name) + } - fn meta<'r>( - _: &(), - registry: &mut _juniper::Registry<'r> - ) -> _juniper::meta::MetaType<'r> { - let fields = &[ - #(#meta_fields)* - ]; - let meta = registry.build_input_object_type::<#ident>(&(), fields); - #meta_description - meta.into_meta() + fn meta<'r>( + _: &(), + registry: &mut _juniper::Registry<'r, S> + ) -> _juniper::meta::MetaType<'r, S> + where S: 'r + { + let fields = &[ + #(#meta_fields)* + ]; + let meta = registry.build_input_object_type::<#ident>(&(), fields); + #meta_description + meta.into_meta() + } } - } - impl #generics _juniper::FromInputValue for #ident #generics { - fn from_input_value(value: &_juniper::InputValue) -> Option<#ident #generics> { - if let Some(obj) = value.to_object_value() { - let item = #ident { - #(#from_inputs)* - }; - Some(item) - } - else { - None + impl _juniper::FromInputValue for #ident + where S: _juniper::ScalarValue, + for<'__b> &'__b S: _juniper::ScalarRefValue<'__b> + { + fn from_input_value(value: &_juniper::InputValue) -> Option<#ident> + where + for<'b> &'b S: _juniper::ScalarRefValue<'b> + // S: 'a, + // &'a S: _juniper::ScalarRefValue<'a>, + { + if let Some(obj) = value.to_object_value() { + let item = #ident { + #(#from_inputs)* + }; + Some(item) + } + else { + None + } } } - } - impl #generics _juniper::ToInputValue for #ident #generics { - fn to_input_value(&self) -> _juniper::InputValue { - _juniper::InputValue::object(vec![ - #(#to_inputs)* - ].into_iter().collect()) + impl _juniper::ToInputValue for #ident + where S: _juniper::ScalarValue, + for<'__b> &'__b S: _juniper::ScalarRefValue<'__b> + { + fn to_input_value(&self) -> _juniper::InputValue { + _juniper::InputValue::object(vec![ + #(#to_inputs)* + ].into_iter().collect()) + } } - } - }; + }; let dummy_const = Ident::new( &format!("_IMPL_GRAPHQLINPUTOBJECT_FOR_{}", ident), 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 }; }; From 555ab37a8be6c52e3189033d0adfbb97692d4f90 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 18 Sep 2018 12:27:18 +0200 Subject: [PATCH 02/20] Fix all tests in juniper itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reworks the parser again, because the previous approach was to strict. Now the strategy is as following: * 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 does now again distinguish between several fundamental scalar types (String, Float, Int). This is done on a fundamental level. It does not try to actually parse those values, just annotate them that this is a floating point number, a integer number or a string value * If those type information exist 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 exist try parsing the scalar value using the inferred type from the lexer --- juniper/src/ast.rs | 2 +- juniper/src/executor/look_ahead.rs | 47 ++-- juniper/src/executor_tests/custom_scalar.rs | 26 +- .../src/executor_tests/introspection/mod.rs | 2 +- juniper/src/executor_tests/variables.rs | 2 +- juniper/src/integrations/chrono.rs | 31 ++- juniper/src/integrations/url.rs | 5 +- juniper/src/integrations/uuid.rs | 12 +- juniper/src/lib.rs | 32 +-- juniper/src/macros/scalar.rs | 15 +- juniper/src/macros/tests/scalar.rs | 10 +- juniper/src/parser/document.rs | 107 ++++---- juniper/src/parser/lexer.rs | 70 ++++- juniper/src/parser/mod.rs | 2 +- juniper/src/parser/tests/document.rs | 28 +- juniper/src/parser/tests/lexer.rs | 32 +-- juniper/src/parser/tests/value.rs | 71 +++--- juniper/src/parser/value.rs | 103 +++++--- juniper/src/schema/meta.rs | 4 +- juniper/src/types/scalars.rs | 123 ++++++--- juniper/src/validation/mod.rs | 2 +- .../rules/overlapping_fields_can_be_merged.rs | 241 +++++++++--------- .../validation/rules/unique_argument_names.rs | 4 +- .../rules/unique_operation_names.rs | 4 +- juniper/src/validation/test_harness.rs | 7 +- juniper/src/value/scalar.rs | 5 +- 26 files changed, 575 insertions(+), 412 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index d11e05ef9..84f2b753e 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -519,7 +519,7 @@ impl<'a, S> VariableDefinitions<'a, S> { mod tests { use super::InputValue; use parser::Spanning; - use value::{DefaultScalarValue, Value}; + use value::DefaultScalarValue; type TestValue = InputValue; diff --git a/juniper/src/executor/look_ahead.rs b/juniper/src/executor/look_ahead.rs index 4e199bbff..20522976f 100644 --- a/juniper/src/executor/look_ahead.rs +++ b/juniper/src/executor/look_ahead.rs @@ -350,9 +350,21 @@ impl<'a, S> LookAheadMethods for LookAheadSelection<'a, S> { 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(q: &str) -> UnlocatedParseResult> + where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, + { + ::parse_document_source(q, &SchemaType::new::(&(), &())) + } + + fn extract_fragments<'a, S>(doc: &'a Document) -> HashMap<&'a str, &'a Fragment<'a, S>> { let mut fragments = HashMap::new(); for d in doc { if let ::ast::Definition::Fragment(ref f) = *d { @@ -365,7 +377,7 @@ mod tests { #[test] fn check_simple_query() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -417,7 +429,7 @@ query Hero { #[test] fn check_query_with_alias() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { custom_hero: hero { @@ -469,7 +481,7 @@ query Hero { #[test] fn check_query_with_child() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -553,7 +565,7 @@ query Hero { #[test] fn check_query_with_argument() { - let docs = ::parse_document_source( + let docs = parse_document_source( " query Hero { hero(episode: EMPIRE) { @@ -595,7 +607,7 @@ query Hero { alias: None, arguments: vec![LookAheadArgument { name: "uppercase", - value: LookAheadValue::Boolean(true), + value: LookAheadValue::Scalar(&DefaultScalarValue::Boolean(true)), }], children: Vec::new(), }, @@ -611,7 +623,7 @@ query Hero { #[test] fn check_query_with_variable() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero($episode: Episode) { hero(episode: $episode) { @@ -667,7 +679,7 @@ query Hero($episode: Episode) { #[test] fn check_query_with_fragment() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -733,7 +745,7 @@ fragment commonFields on Character { #[test] fn check_query_with_directives() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -786,7 +798,7 @@ query Hero { #[test] fn check_query_with_inline_fragments() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -851,7 +863,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) { @@ -876,9 +888,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, @@ -889,7 +904,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 { @@ -1011,7 +1026,7 @@ fragment comparisonFields on Character { #[test] fn check_resolve_concrete_type() { - let docs = ::parse_document_source( + let docs = parse_document_source::( " query Hero { hero { @@ -1061,7 +1076,7 @@ query Hero { #[test] fn check_select_child() { - let lookahead = LookAheadSelection { + let lookahead: LookAheadSelection = LookAheadSelection { name: "hero", alias: None, arguments: Vec::new(), diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs index b0cb65aae..d025cf6ef 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -1,9 +1,8 @@ -use ast::{FromInputValue, InputValue, Selection, ToInputValue}; +use ast::InputValue; use executor::{ExecutionResult, Executor, Registry, Variables}; -use parser::ParseError; -use parser::Token; -use schema::meta::{MetaType, ScalarMeta}; +use parser::{ParseError, ScalarToken, Token}; use schema::model::RootNode; +use schema::meta::MetaType; use serde::de::{self, Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use std::fmt::{self, Display}; @@ -101,7 +100,7 @@ impl From for Option { impl From for Option { fn from(s: MyScalarValue) -> Self { match s { - MyScalarValue::String(s) => Some(s.clone()), + MyScalarValue::String(s) => Some(s), _ => None, } } @@ -259,11 +258,14 @@ graphql_scalar!(i64 as "Long" where Scalar = MyScalarValue { } } - from_str(value: &str) -> Result { - value - .parse() - .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) - .map(|s: i64| s.into()) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + if let ScalarToken::Int(v) = value { + v.parse() + .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) + .map(|s: i64| s.into()) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) + } } }); @@ -300,10 +302,10 @@ impl GraphQLType for TestType { fn resolve_field( &self, - info: &Self::TypeInfo, + _info: &Self::TypeInfo, field_name: &str, args: &Arguments, - executor: &Executor, + _executor: &Executor, ) -> ExecutionResult { match field_name { "longField" => Ok(Value::Scalar(MyScalarValue::Long( diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index ef7ba5a42..97ac490bc 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -33,7 +33,7 @@ graphql_scalar!(Scalar as "SampleScalar" where Scalar = { v.as_int_value().map(|i| Scalar(i)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 1d5ef860a..9bdc2fe5e 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -28,7 +28,7 @@ graphql_scalar!(TestComplexScalar where Scalar = { None } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index 07dd24452..c77984b85 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -15,8 +15,8 @@ */ use chrono::prelude::*; -use parser::ParseError; -use value::{ParseScalarValue, ScalarValue}; +use parser::{ParseError, ScalarToken, Token}; +use value::ParseScalarValue; use Value; #[doc(hidden)] @@ -34,8 +34,12 @@ graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = Result { - Ok(S::from(value)) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + if let ScalarToken::String(value) = value { + Ok(S::from(value)) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) + } } }); @@ -51,12 +55,15 @@ graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { .and_then(|s| (s.parse::>().ok())) } - from_str(value: &str) -> Result { - Ok(S::from(value)) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + if let ScalarToken::String(value) = value { + Ok(S::from(value)) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) + } } }); - // Don't use `Date` as the docs say: // "[Date] should be considered ambiguous at best, due to the " // inherent lack of precision required for the time zone resolution. @@ -74,8 +81,12 @@ graphql_scalar!(NaiveDate where Scalar = { .and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) } - from_str(value: &str) -> Result { - Ok(S::from(value)) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + if let ScalarToken::String(value) = value { + Ok(S::from(value)) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) + } } }); @@ -93,7 +104,7 @@ graphql_scalar!(NaiveDateTime where Scalar = { .and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index dc82118c1..608e0fd16 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -1,7 +1,7 @@ use url::Url; use parser::ParseError; -use value::{ParseScalarValue, ScalarValue}; +use value::ParseScalarValue; use Value; graphql_scalar!(Url where Scalar = { @@ -16,12 +16,11 @@ graphql_scalar!(Url where Scalar = { .and_then(|s| Url::parse(s).ok()) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); - #[cfg(test)] mod test { use url::Url; diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index 290c14fe4..f6d0f77b4 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -1,7 +1,6 @@ use uuid::Uuid; -use parser::ParseError; -use value::{ParseScalarValue, ScalarValue}; +use parser::{ParseError, ScalarToken, Token}; use Value; graphql_scalar!(Uuid where Scalar = { @@ -16,12 +15,15 @@ graphql_scalar!(Uuid where Scalar = { .and_then(|s| Uuid::parse_str(s).ok()) } - from_str(value: &str) -> Result { - Ok(S::from(value)) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + if let ScalarToken::String(value) = value { + Ok(S::from(value)) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) + } } }); - #[cfg(test)] mod test { use uuid::Uuid; diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index a4269a5be..778888ce6 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -133,7 +133,7 @@ mod validation; // https://github.com/rust-lang/cargo/issues/1520 pub mod http; pub mod integrations; -// // TODO: remove this alias export in 0.10. (breaking change) +// TODO: remove this alias export in 0.10. (breaking change) pub use http::graphiql; #[cfg(all(test, not(feature = "expose-test-schema")))] @@ -144,7 +144,7 @@ pub mod tests; #[cfg(test)] mod executor_tests; -// // Needs to be public because macros use it. +// Needs to be public because macros use it. pub use util::to_camel_case; use executor::execute_validated_query; @@ -164,7 +164,7 @@ 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::{DefaultScalarValue, Object, ScalarRefValue, ScalarValue, Value, ParseScalarValue}; +pub use value::{DefaultScalarValue, Object, ParseScalarValue, ScalarRefValue, ScalarValue, Value}; /// An error that prevented query execution #[derive(Debug, PartialEq)] @@ -191,25 +191,25 @@ where QueryT: GraphQLType, MutationT: GraphQLType, { - let document = parse_document_source(document_source)?; + let document = parse_document_source(document_source, &root_node.schema)?; - { - let errors = validate_input_values(variables, &document, &root_node.schema); + { + let errors = validate_input_values(variables, &document, &root_node.schema); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } + if !errors.is_empty() { + return Err(GraphQLError::ValidationError(errors)); } + } - { - let mut ctx = ValidatorContext::new(&root_node.schema, &document); - visit_all_rules(&mut ctx, &document); + { + let mut ctx = ValidatorContext::new(&root_node.schema, &document); + visit_all_rules(&mut ctx, &document); - let errors = ctx.into_errors(); - if !errors.is_empty() { - return Err(GraphQLError::ValidationError(errors)); - } + let errors = ctx.into_errors(); + if !errors.is_empty() { + return Err(GraphQLError::ValidationError(errors)); } + } execute_validated_query(document, operation_name, root_node, variables, context) } diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index d1878056a..f4c4f4836 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -13,10 +13,11 @@ datatype appropriate for that platform. ```rust # #[macro_use] extern crate juniper; -# use juniper::{Value, FieldResult}; +# use juniper::{Value, FieldResult, ParseScalarValue}; +# use juniper::parser::ParseError; struct UserID(String); -graphql_scalar!(UserID { +graphql_scalar!(UserID where Scalar = { description: "An opaque identifier, represented as a string" resolve(&self) -> Value { @@ -26,6 +27,10 @@ graphql_scalar!(UserID { from_input_value(v: &InputValue) -> Option { v.as_string_value().map(|s| UserID(s.to_owned())) } + + from_str<'a>(value: ScalarToken<'a>) -> Result> { + >::from_str(value) + } }); # fn main() { } @@ -103,6 +108,7 @@ macro_rules! graphql_scalar { value_arg = $from_str_arg: ident, result = $from_str_result: ty, body = $from_str_body: block, + lifetime = $from_str_lt: tt, }, ) => { @@ -163,7 +169,7 @@ macro_rules! graphql_scalar { graphql_scalar!( @impl_trait impl<$($scalar)+> ParseScalarValue for $name { - fn from_str($from_str_arg: &str) -> $from_str_result { + fn from_str<$from_str_lt>($from_str_arg: $crate::parser::ScalarToken<$from_str_lt>) -> $from_str_result { $from_str_body } } @@ -302,7 +308,7 @@ macro_rules! graphql_scalar { $(resolve = {$($resolve_body:tt)+},)* $(from_input_value = {$($from_input_value_body:tt)+},)* $(from_str = {$($from_str_body:tt)+},)* - rest = from_str($value_arg:ident: &str) -> $result:ty $body:block $($rest:tt)* + rest = from_str<$from_str_lt: tt>($value_arg:ident: ScalarToken<$ignored_lt2: tt>) -> $result:ty $body:block $($rest:tt)* ) => { graphql_scalar!( @parse, @@ -313,6 +319,7 @@ macro_rules! graphql_scalar { value_arg = $value_arg, result = $result, body = $body, + lifetime = $from_str_lt, }, rest = $($rest)* ); diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index 36e23935e..a771130db 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -29,7 +29,7 @@ graphql_scalar!(DefaultName where Scalar = { v.as_int_value().map(|i| DefaultName(i)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); @@ -38,12 +38,12 @@ graphql_scalar!(OtherOrder where Scalar = { Value::int(self.0) } - from_input_value(v: &InputValue) -> Option { + from_input_value(v: &InputValue) -> Option { v.as_int_value().map(|i| OtherOrder(i)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); @@ -56,7 +56,7 @@ graphql_scalar!(Named as "ANamedScalar" where Scalar = { v.as_int_value().map(|i| Named(i)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); @@ -72,7 +72,7 @@ graphql_scalar!(ScalarDescription where Scalar = { v.as_int_value().map(|i| ScalarDescription(i)) } - from_str(value: &str) -> Result { + from_str<'a>(value: ScalarToken<'a>) -> Result> { >::from_str(value) } }); diff --git a/juniper/src/parser/document.rs b/juniper/src/parser/document.rs index 16cedaa1a..0a7b7fbed 100644 --- a/juniper/src/parser/document.rs +++ b/juniper/src/parser/document.rs @@ -10,7 +10,7 @@ use parser::{ Lexer, OptionParseResult, ParseError, ParseResult, Parser, Spanning, Token, UnlocatedParseResult, }; -use schema::meta::{Argument, Field as MetaField, MetaType}; +use schema::meta::{Argument, Field as MetaField}; use schema::model::SchemaType; use value::ScalarValue; @@ -71,11 +71,9 @@ where S: ScalarValue, { if parser.peek().item == Token::CurlyOpen { - let fields = schema - .concrete_query_type() - .fields(schema) - .unwrap_or_else(Vec::new); - let selection_set = parse_selection_set(parser, schema, &fields)?; + 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, @@ -95,7 +93,8 @@ where OperationType::Query => Some(schema.concrete_query_type()), OperationType::Mutation => schema.concrete_mutation_type(), }; - let fields = op.and_then(|m| m.fields(schema)).unwrap_or_else(Vec::new); + 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()?), @@ -103,7 +102,7 @@ where }; let variable_definitions = parse_variable_definitions(parser, schema)?; let directives = parse_directives(parser, schema)?; - let selection_set = parse_selection_set(parser, schema, &fields)?; + let selection_set = parse_selection_set(parser, schema, fields)?; Ok(Spanning::start_end( &start_pos, @@ -143,18 +142,11 @@ where let fields = schema .concrete_type_by_name(type_cond.item) - .and_then(|m| m.fields(schema)) - .ok_or_else(|| { - println!( - "{:?}", - schema.types.values().map(|v| v.name()).collect::>() - ); - println!("{}", type_cond.item); - unimplemented!() - })?; + .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)?; + let selection_set = parse_selection_set(parser, schema, fields)?; Ok(Spanning::start_end( &start_pos, @@ -171,7 +163,7 @@ where fn parse_optional_selection_set<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - fields: &[&MetaField<'b, S>], + fields: Option<&[&MetaField<'b, S>]>, ) -> OptionParseResult<'a, Vec>> where S: ScalarValue, @@ -186,7 +178,7 @@ where fn parse_selection_set<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - fields: &[&MetaField<'b, S>], + fields: Option<&[&MetaField<'b, S>]>, ) -> ParseResult<'a, Vec>> where S: ScalarValue, @@ -201,7 +193,7 @@ where fn parse_selection<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - fields: &[&MetaField<'b, S>], + fields: Option<&[&MetaField<'b, S>]>, ) -> UnlocatedParseResult<'a, Selection<'a, S>> where S: ScalarValue, @@ -215,7 +207,7 @@ where fn parse_fragment<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - fields: &[&MetaField<'b, S>], + fields: Option<&[&MetaField<'b, S>]>, ) -> UnlocatedParseResult<'a, Selection<'a, S>> where S: ScalarValue, @@ -232,10 +224,10 @@ where let fields = schema .concrete_type_by_name(name.item) - .and_then(|m| m.fields(schema)) - .ok_or_else(|| unimplemented!())?; + .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)?; + let selection_set = parse_selection_set(parser, schema, fields)?; Ok(Selection::InlineFragment(Spanning::start_end( &start_pos.clone(), @@ -297,7 +289,7 @@ where fn parse_field<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - fields: &[&MetaField<'b, S>], + fields: Option<&[&MetaField<'b, S>]>, ) -> ParseResult<'a, Field<'a, S>> where S: ScalarValue, @@ -310,26 +302,21 @@ where alias.take().unwrap() }; - let field = fields.iter().find(|f| f.name == name.item).ok_or_else(|| { - println!("Field: {}", name.item); - println!( - "Fields: {:?}", - fields.iter().map(|f| &f.name).collect::>() - ); - unimplemented!() - })?; + 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 arguments = parse_arguments( - parser, - schema, - &field.arguments.as_ref().map(|a| a as &[_]).unwrap_or(&[]), - )?; - let fields = schema - .lookup_type(&field.field_type) - .and_then(|m| m.fields(schema)) - .unwrap_or_else(Vec::new); let directives = parse_directives(parser, schema)?; - let selection_set = parse_optional_selection_set(parser, schema, &fields)?; + let selection_set = parse_optional_selection_set(parser, schema, fields)?; Ok(Spanning::start_end( &alias.as_ref().unwrap_or(&name).start.clone(), @@ -353,7 +340,7 @@ where fn parse_arguments<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - arguments: &[Argument<'b, S>], + arguments: Option<&[Argument<'b, S>]>, ) -> OptionParseResult<'a, Arguments<'a, S>> where S: ScalarValue, @@ -377,20 +364,15 @@ where fn parse_argument<'a, 'b, S>( parser: &mut Parser<'a>, schema: &'b SchemaType<'b, S>, - arguments: &[Argument<'b, S>], + arguments: Option<&[Argument<'b, S>]>, ) -> ParseResult<'a, (Spanning<&'a str>, Spanning>)> where S: ScalarValue, { let name = parser.expect_name()?; - let arg = arguments - .iter() - .find(|a| a.name == name.item) - .ok_or_else(|| unimplemented!())?; - - let tpe = schema - .lookup_type(&arg.arg_type) - .ok_or_else(|| unimplemented!())?; + 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, schema, tpe)?; @@ -446,12 +428,10 @@ where let var_name = parser.expect_name()?; parser.expect(&Token::Colon)?; let var_type = parse_type(parser)?; - let tpe = schema - .lookup_type(&var_type.item) - .ok_or_else(|| unimplemented!())?; + let tpe = schema.lookup_type(&var_type.item); let default_value = if parser.skip(&Token::Equals)?.is_some() { - Some(parse_value_literal(parser, true, schema, &tpe)?) + Some(parse_value_literal(parser, true, schema, tpe)?) } else { None }; @@ -502,11 +482,14 @@ where start: start_pos, .. } = parser.expect(&Token::At)?; let name = parser.expect_name()?; - let directive = schema - .directive_by_name(name.item) - .ok_or_else(|| unimplemented!())?; - let arguments = parse_arguments(parser, schema, &directive.arguments)?; + 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, diff --git a/juniper/src/parser/lexer.rs b/juniper/src/parser/lexer.rs index 6c7142443..e872be3c1 100644 --- a/juniper/src/parser/lexer.rs +++ b/juniper/src/parser/lexer.rs @@ -16,12 +16,19 @@ pub struct Lexer<'a> { has_reached_eof: bool, } +#[derive(Debug, PartialEq, Clone, Copy)] +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), - Scalar(&'a str), + Scalar(ScalarToken<'a>), ExclamationMark, Dollar, ParenOpen, @@ -217,19 +224,45 @@ impl<'a> Lexer<'a> { } 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; + } + '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, - Token::Scalar(&self.source[start_idx+1..idx]), + Token::Scalar(ScalarToken::String(&self.source[start_idx+1..idx])), )); } - _ => escaped = false, + '\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( @@ -241,7 +274,7 @@ impl<'a> Lexer<'a> { fn scan_escaped_unicode( &mut self, start_pos: &SourcePosition, - ) -> Result> { + ) -> Result<(), Spanning> { let (start_idx, _) = self.peek_char().ok_or(Spanning::zero_width( &self.position, LexerError::UnterminatedString, @@ -284,7 +317,7 @@ impl<'a> Lexer<'a> { start_pos, LexerError::UnknownEscapeSequence("\\u".to_owned() + escape), ) - }) + }).map(|_|()) } fn scan_number(&mut self) -> LexerResult<'a> { @@ -296,6 +329,7 @@ impl<'a> Lexer<'a> { 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() { @@ -318,11 +352,12 @@ impl<'a> Lexer<'a> { } last_idx = idx; } else { - break last_idx; + break last_idx + 1; } }; if let Some((start_idx, '.')) = self.peek_char() { + is_float = true; let mut last_idx = start_idx; self.next_char(); end_idx = loop { @@ -344,12 +379,13 @@ impl<'a> Lexer<'a> { LexerError::UnexpectedEndOfFile, )); } else { - break last_idx; + break last_idx + 1; } }; } if let Some((start_idx, ch)) = self.peek_char() { if ch == 'e' || ch == 'E' { + is_float = true; self.next_char(); let mut last_idx = start_idx; @@ -374,17 +410,24 @@ impl<'a> Lexer<'a> { LexerError::UnexpectedEndOfFile, )); } else { - break last_idx; + break last_idx + 1; } }; } } let number = &self.source[start_idx..end_idx]; let end_pos = &self.position; + + 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::Scalar(number), + token, )) } } @@ -438,7 +481,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::Scalar(s) => write!(f, "{}", 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, "("), diff --git a/juniper/src/parser/mod.rs b/juniper/src/parser/mod.rs index 18676fded..45865d31c 100644 --- a/juniper/src/parser/mod.rs +++ b/juniper/src/parser/mod.rs @@ -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}; diff --git a/juniper/src/parser/tests/document.rs b/juniper/src/parser/tests/document.rs index 5b559beed..8ac85c706 100644 --- a/juniper/src/parser/tests/document.rs +++ b/juniper/src/parser/tests/document.rs @@ -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: &str) -> Document +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + parse_document_source(s, &SchemaType::new::(&(), &())) + .expect(&format!("Parse error on input {:#?}", s)) } -fn parse_document_error<'a>(s: &'a str) -> Spanning> { - match parse_document_source(s) { +fn parse_document_error<'a, S>(s: &'a str) -> Spanning> +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + match parse_document_source::(s, &SchemaType::new::(&(), &())) { 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> { #[test] fn simple_ast() { assert_eq!( - parse_document( + parse_document::( r#" { node(id: 4) { @@ -107,7 +119,7 @@ fn simple_ast() { #[test] fn errors() { assert_eq!( - parse_document_error("{"), + parse_document_error::("{"), 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::("{ ...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::("{ ...on }"), Spanning::start_end( &SourcePosition::new(8, 0, 8), &SourcePosition::new(9, 0, 9), diff --git a/juniper/src/parser/tests/lexer.rs b/juniper/src/parser/tests/lexer.rs index ca1bf2429..283607538 100644 --- a/juniper/src/parser/tests/lexer.rs +++ b/juniper/src/parser/tests/lexer.rs @@ -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>> { 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::Scalar("simple") + 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::Scalar(" white space ") + 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::Scalar("quote \"") + 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::Scalar("escaped \n\r\u{0008}\t\u{000c}") + 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::Scalar("slashes \\ /") + 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::Scalar("unicode \u{1234}\u{5678}\u{90ab}\u{cdef}") + Token::Scalar(ScalarToken::String(r#"unicode \u1234\u5678\u90AB\uCDEF"#)) ) ); } @@ -334,7 +334,7 @@ fn numbers() { assert_eq!(parsed.end, end); match parsed.item { - Token::Scalar(actual) => { + Token::Scalar(ScalarToken::Float(actual)) => { assert!( expected == actual, "[expected] {} != {} [actual]", @@ -351,7 +351,7 @@ fn numbers() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(1, 0, 1), - Token::Scalar("4") + Token::Scalar(ScalarToken::Int("4")) ) ); @@ -374,7 +374,7 @@ fn numbers() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(2, 0, 2), - Token::Scalar("-4") + Token::Scalar(ScalarToken::Int("-4")) ) ); @@ -383,7 +383,7 @@ fn numbers() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(1, 0, 1), - Token::Scalar("9") + Token::Scalar(ScalarToken::Int("9")) ) ); @@ -392,7 +392,7 @@ fn numbers() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(1, 0, 1), - Token::Scalar("0") + Token::Scalar(ScalarToken::Int("0")) ) ); @@ -652,19 +652,19 @@ fn punctuation_error() { fn display() { assert_eq!(format!("{}", Token::Name("identifier")), "identifier"); - assert_eq!(format!("{}", Token::Scalar("123")), "123"); + assert_eq!(format!("{}", Token::Scalar(ScalarToken::Int("123"))), "123"); - assert_eq!(format!("{}", Token::Scalar("4.5")), "4.5"); + assert_eq!(format!("{}", Token::Scalar(ScalarToken::Float("4.5"))), "4.5"); assert_eq!( - format!("{}", Token::Scalar("some string")), + format!("{}", Token::Scalar(ScalarToken::String("some string"))), "\"some string\"" ); assert_eq!( format!( "{}", - Token::Scalar("string with \\ escape and \" quote") + Token::Scalar(ScalarToken::String("string with \\ escape and \" quote")) ), "\"string with \\\\ escape and \\\" quote\"" ); diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index dc0101be9..160842fdb 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -5,10 +5,26 @@ use parser::value::parse_value_literal; use parser::{Lexer, Parser, SourcePosition, Spanning}; use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue}; -use schema::meta::{Field, MetaType, ObjectMeta, ScalarMeta}; +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: () |&self| { @@ -27,6 +43,10 @@ graphql_object!(Query: () |&self| { field bool_field() -> bool { true } + + field enum_field(_foo: Foo) -> Enum { + Enum::EnumValue + } }); fn scalar_meta(name: &'static str) -> MetaType @@ -36,7 +56,7 @@ where MetaType::Scalar(ScalarMeta::new::(name.into())) } -fn parse_value(s: &str, meta: MetaType) -> Spanning> +fn parse_value(s: &str, meta: &MetaType) -> Spanning> where S: ScalarValue, for<'a> &'a S: ScalarRefValue<'a>, @@ -45,14 +65,14 @@ where let mut parser = Parser::new(&mut lexer).expect(&format!("Lexer error on input {:#?}", s)); let schema = SchemaType::new::>(&(), &()); - parse_value_literal(&mut parser, false, &schema, &meta) + 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", scalar_meta::("Int")), + parse_value::("123", &scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(3, 0, 3), @@ -60,7 +80,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("123.45", scalar_meta::("Float")), + parse_value::("123.45", &scalar_meta::("Float")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(6, 0, 6), @@ -68,7 +88,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("true", scalar_meta::("Bool")), + parse_value::("true", &scalar_meta::("Bool")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(4, 0, 4), @@ -76,7 +96,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("false", scalar_meta::("Bool")), + parse_value::("false", &scalar_meta::("Bool")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(5, 0, 5), @@ -84,15 +104,18 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::(r#""test""#, scalar_meta::("String")), + parse_value::(r#""test""#, &scalar_meta::("String")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(6, 0, 6), InputValue::string("test") ) ); + let values = &[EnumValue::new("enum_value")]; + let e: EnumMeta = EnumMeta::new::("TestEnum".into(), values); + assert_eq!( - parse_value::("enum_value", scalar_meta::("Int")), + parse_value::("enum_value", &MetaType::Enum(e)), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(10, 0, 10), @@ -100,7 +123,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("$variable", scalar_meta::("Int")), + parse_value::("$variable", &scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(9, 0, 9), @@ -108,7 +131,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("[]", scalar_meta::("Int")), + parse_value::("[]", &scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(2, 0, 2), @@ -116,7 +139,7 @@ fn input_value_literals() { ) ); assert_eq!( - parse_value::("[1, [2, 3]]", scalar_meta::("Int")), + parse_value::("[1, [2, 3]]", &scalar_meta::("Int")), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(11, 0, 11), @@ -145,36 +168,22 @@ fn input_value_literals() { ]) ) ); + let fields = [ Argument::new("key", Type::NonNullNamed("Int".into())), + Argument::new("other", Type::NonNullNamed("Bar".into()))]; + let meta = &MetaType::InputObject(InputObjectMeta::new::("foo".into(), &fields)); assert_eq!( - parse_value::("{}", scalar_meta::("Int")), + parse_value::("{}", meta), Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(2, 0, 2), InputValue::object(IndexMap::>::new()) ) ); - let fields = [ - Field { - name: "key".into(), - description: None, - arguments: None, - field_type: Type::NonNullNamed("Int".into()), - deprecation_reason: None, - }, - Field { - name: "other".into(), - description: None, - arguments: None, - field_type: Type::NonNullNamed("Bar".into()), - deprecation_reason: None, - }, - ]; - let meta = ObjectMeta::new("foo".into(), &fields); assert_eq!( parse_value::( r#"{key: 123, other: {foo: "bar"}}"#, - MetaType::Object(meta) + meta ), Spanning::start_end( &SourcePosition::new(0, 0, 0), diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs index f4d4f9fae..1fe2f4998 100644 --- a/juniper/src/parser/value.rs +++ b/juniper/src/parser/value.rs @@ -1,8 +1,7 @@ -use ast::{InputValue, Type}; +use ast::InputValue; -use parser::lexer::LexerError; -use parser::{ParseError, ParseResult, Parser, Spanning, Token}; -use schema::meta::{MetaType, ObjectMeta}; +use parser::{ParseError, ParseResult, Parser, ScalarToken, SourcePosition, Spanning, Token}; +use schema::meta::{InputObjectMeta, MetaType}; use schema::model::SchemaType; use value::ScalarValue; @@ -10,7 +9,7 @@ pub fn parse_value_literal<'a, 'b, S>( parser: &mut Parser<'a>, is_const: bool, schema: &'b SchemaType<'b, S>, - tpe: &MetaType<'b, S>, + tpe: Option<&MetaType<'b, S>>, ) -> ParseResult<'a, InputValue> where S: ScalarValue, @@ -28,8 +27,15 @@ where item: Token::CurlyOpen, .. }, - MetaType::Object(ref o), - ) => parse_object_literal(parser, is_const, schema, o), + 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, @@ -46,7 +52,7 @@ where item: Token::Scalar(_), .. }, - MetaType::Scalar(ref s), + Some(&MetaType::Scalar(ref s)), ) => { if let Spanning { item: Token::Scalar(scalar), @@ -54,10 +60,9 @@ where end, } = parser.next()? { - println!("{:?}", scalar); (s.parse_fn)(scalar) .map(|s| Spanning::start_end(&start, &end, InputValue::Scalar(s))) - .map_err(|e| Spanning::start_end(&start, &end, e)) + .or_else(|_| parse_scalar_literal_by_infered_type(scalar, &start, &end, schema)) } else { unreachable!() } @@ -67,21 +72,15 @@ where item: Token::Scalar(_), .. }, - MetaType::Enum(_), + _, ) => { if let Spanning { - item: Token::Scalar(scalar), + item: Token::Scalar(token), start, end, } = parser.next()? { - if let Some(MetaType::Scalar(s)) = schema.concrete_type_by_name("String") { - (s.parse_fn)(scalar) - .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") - } + parse_scalar_literal_by_infered_type(token, &start, &end, schema) } else { unreachable!() } @@ -91,14 +90,14 @@ where item: Token::Name("true"), .. }, - MetaType::Scalar(ref _s), + _, ) => Ok(parser.next()?.map(|_| InputValue::boolean(true))), ( &Spanning { item: Token::Name("false"), .. }, - &MetaType::Scalar(ref _s), + _, ) => Ok(parser.next()?.map(|_| InputValue::boolean(false))), ( &Spanning { @@ -112,7 +111,7 @@ where item: Token::Name(name), .. }, - MetaType::Enum(_), + _, ) => Ok(parser .next()? .map(|_| InputValue::enum_value(name.to_owned()))), @@ -124,7 +123,7 @@ fn parse_list_literal<'a, 'b, S>( parser: &mut Parser<'a>, is_const: bool, schema: &'b SchemaType<'b, S>, - tpe: &MetaType<'b, S>, + tpe: Option<&MetaType<'b, S>>, ) -> ParseResult<'a, InputValue> where S: ScalarValue, @@ -141,7 +140,7 @@ fn parse_object_literal<'a, 'b, S>( parser: &mut Parser<'a>, is_const: bool, schema: &'b SchemaType<'b, S>, - object_tpe: &ObjectMeta<'b, S>, + object_tpe: Option<&InputObjectMeta<'b, S>>, ) -> ParseResult<'a, InputValue> where S: ScalarValue, @@ -158,26 +157,20 @@ fn parse_object_field<'a, 'b, S>( parser: &mut Parser<'a>, is_const: bool, schema: &'b SchemaType<'b, S>, - object_meta: &ObjectMeta<'b, S>, + object_meta: Option<&InputObjectMeta<'b, S>>, ) -> ParseResult<'a, (Spanning, Spanning>)> where S: ScalarValue, { let key = parser.expect_name()?; - let field = object_meta - .fields - .iter() - .find(|f| f.name == key.item) - .ok_or_else(|| unimplemented!())?; - - let tpe = schema - .lookup_type(&field.field_type) - .ok_or_else(|| unimplemented!())?; + 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, schema, &tpe)?; + let value = parse_value_literal(parser, is_const, schema, tpe)?; Ok(Spanning::start_end( &key.start.clone(), @@ -205,3 +198,43 @@ where 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> +where + S: ScalarValue, +{ + match token { + ScalarToken::String(_) => { + if let Some(MetaType::Scalar(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(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(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") + } + } + } +} diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 8061ae710..3684f6e28 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::fmt; use ast::{FromInputValue, InputValue, Type}; -use parser::ParseError; +use parser::{ParseError, ScalarToken}; use schema::model::SchemaType; use types::base::TypeKind; use value::{ParseScalarValue, ScalarRefValue, ScalarValue}; @@ -17,7 +17,7 @@ pub struct ScalarMeta<'a, S: fmt::Debug> { pub description: Option, #[doc(hidden)] pub try_parse_fn: Box) -> bool + Send + Sync>, - pub(crate) parse_fn: fn(&str) -> Result, + pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result>, } /// List type metadata diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 7f8cbe427..7445613c1 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -6,7 +6,7 @@ use std::{char, u32}; use ast::{FromInputValue, InputValue, Selection, ToInputValue}; use executor::{Executor, Registry}; -use parser::{LexerError, ParseError, Token}; +use parser::{LexerError, ParseError, Token, ScalarToken}; use schema::meta::MetaType; use types::base::GraphQLType; use value::{ParseScalarValue, ScalarRefValue, ScalarValue, Value}; @@ -47,8 +47,13 @@ graphql_scalar!(ID as "ID" where Scalar = { } } - from_str(value: &str) -> Result { - Ok(S::from(value)) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + match value { + ScalarToken::String(value) | ScalarToken::Int(value) => { + Ok(S::from(value)) + } + _ => Err(ParseError::UnexpectedToken(Token::Scalar(value))), + } } }); @@ -64,32 +69,36 @@ graphql_scalar!(String as "String" where Scalar = { } } - from_str(value: &str) -> Result { - 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)?); + from_str<'a>(value: ScalarToken<'a>) -> Result> { + 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)), } - Some(s) => return Err(ParseError::LexerError(LexerError::UnknownEscapeSequence(format!("\\{}", s)))), - None => return Err(ParseError::LexerError(LexerError::UnterminatedString)), - } - }, - ch => {ret.push(ch);} + }, + ch => {ret.push(ch);} + } } + Ok(ret.into()) + } else { + Err(ParseError::UnexpectedToken(Token::Scalar(value))) } - Ok(ret.into()) } }); @@ -198,11 +207,9 @@ graphql_scalar!(bool as "Boolean" where Scalar = { } } - from_str(value: &str) -> Result { - value - .parse() - .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) - .map(|s: bool| s.into()) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + // Bools are parsed on it's own. This should not hit this code path + Err(ParseError::UnexpectedToken(Token::Scalar(value))) } }); @@ -218,11 +225,14 @@ graphql_scalar!(i32 as "Int" where Scalar = { } } - from_str(value: &str) -> Result { - value - .parse() - .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) - .map(|s: i32| s.into()) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + 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))) + } } }); @@ -240,11 +250,17 @@ graphql_scalar!(f64 as "Float" where Scalar = { } } - from_str(value: &str) -> Result { - value - .parse() - .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) - .map(|s: f64| s.into()) + from_str<'a>(value: ScalarToken<'a>) -> Result> { + 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))) + } + } } }); @@ -273,7 +289,7 @@ impl ParseScalarValue for () where S: ScalarValue, { - fn from_str(_value: &str) -> Result { + fn from_str<'a>(_value: ScalarToken<'a>) -> Result> { Ok(S::from(0)) } } @@ -329,6 +345,8 @@ where #[cfg(test)] mod tests { use super::ID; + use value::{DefaultScalarValue, ParseScalarValue}; + use parser::ScalarToken; #[test] fn test_id_from_string() { @@ -342,4 +360,25 @@ 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 = >::from_str(ScalarToken::String(s)); + assert!(s.is_ok(), "A parsing error occurred: {:?}", s); + let s: Option = 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}", + ); + } } diff --git a/juniper/src/validation/mod.rs b/juniper/src/validation/mod.rs index e5f9bad9e..9d9da0f90 100644 --- a/juniper/src/validation/mod.rs +++ b/juniper/src/validation/mod.rs @@ -8,7 +8,7 @@ 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; diff --git a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs index fd6469cd8..dd9b12862 100644 --- a/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs +++ b/juniper/src/validation/rules/overlapping_fields_can_be_merged.rs @@ -996,18 +996,18 @@ mod tests { ...A ...B } - fragment A on Type { - x: a + fragment A on Dog { + x: name } - fragment B on Type { - x: b + fragment B on Dog { + x: barks } "#, &[RuleError::new( - &error_message("x", &Message("a and b are different fields".to_owned())), + &error_message("x", &Message("name and barks are different fields".to_owned())), &[ - SourcePosition::new(102, 6, 12), - SourcePosition::new(162, 9, 12), + SourcePosition::new(101, 6, 12), + SourcePosition::new(163, 9, 12), ], )], ); @@ -1019,47 +1019,47 @@ mod tests { factory, r#" { - f1 { + dorOrHuman { ...A ...B } - f2 { + catOrDog { ...B ...A } - f3 { + dog { ...A ...B - x: c + x: name } } - fragment A on Type { - x: a + fragment A on Dog { + x: barks } - fragment B on Type { - x: b + fragment B on Dog { + x: nickname } "#, &[ RuleError::new( - &error_message("x", &Message("c and a are different fields".to_owned())), + &error_message("x", &Message("name and barks are different fields".to_owned())), &[ - SourcePosition::new(220, 13, 14), - SourcePosition::new(294, 17, 12), + SourcePosition::new(235, 13, 14), + SourcePosition::new(311, 17, 12), ], ), RuleError::new( - &error_message("x", &Message("c and b are different fields".to_owned())), + &error_message("x", &Message("name and nickname are different fields".to_owned())), &[ - SourcePosition::new(220, 13, 14), - SourcePosition::new(354, 20, 12), + SourcePosition::new(235, 13, 14), + SourcePosition::new(374, 20, 12), ], ), RuleError::new( - &error_message("x", &Message("a and b are different fields".to_owned())), + &error_message("x", &Message("barks and nickname are different fields".to_owned())), &[ - SourcePosition::new(294, 17, 12), - SourcePosition::new(354, 20, 12), + SourcePosition::new(311, 17, 12), + SourcePosition::new(374, 20, 12), ], ), ], @@ -1072,27 +1072,27 @@ mod tests { factory, r#" { - field { - x: a + dog { + x: name }, - field { - x: b + dog { + x: barks } } "#, &[RuleError::new( &error_message( - "field", + "dog", &Nested(vec![ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("name and barks are different fields".to_owned()), )]), ), &[ SourcePosition::new(25, 2, 12), - SourcePosition::new(47, 3, 14), - SourcePosition::new(79, 5, 12), - SourcePosition::new(101, 6, 14), + SourcePosition::new(45, 3, 14), + SourcePosition::new(80, 5, 12), + SourcePosition::new(100, 6, 14), ], )], ); @@ -1104,37 +1104,37 @@ mod tests { factory, r#" { - field { - x: a - y: c + dog { + x: barks + y: name }, - field { - x: b - y: d + dog { + x: nickname + y: barkVolume } } "#, &[RuleError::new( &error_message( - "field", + "dog", &Nested(vec![ ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("barks and nickname are different fields".to_owned()), ), ConflictReason( "y".to_owned(), - Message("c and d are different fields".to_owned()), + Message("name and barkVolume are different fields".to_owned()), ), ]), ), &[ SourcePosition::new(25, 2, 12), - SourcePosition::new(47, 3, 14), - SourcePosition::new(66, 4, 14), - SourcePosition::new(98, 6, 12), - SourcePosition::new(120, 7, 14), - SourcePosition::new(139, 8, 14), + SourcePosition::new(45, 3, 14), + SourcePosition::new(68, 4, 14), + SourcePosition::new(105, 6, 14), + SourcePosition::new(125, 7, 14), + SourcePosition::new(151, 8, 14), ], )], ); @@ -1146,26 +1146,26 @@ mod tests { factory, r#" { - field { - deepField { - x: a + human { + relatives { + x: name } }, - field { - deepField { - x: b + human { + relatives { + x: iq } } } "#, &[RuleError::new( &error_message( - "field", + "human", &Nested(vec![ConflictReason( - "deepField".to_owned(), + "relatives".to_owned(), Nested(vec![ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("name and iq are different fields".to_owned()), )]), )]), ), @@ -1173,9 +1173,9 @@ mod tests { SourcePosition::new(25, 2, 12), SourcePosition::new(47, 3, 14), SourcePosition::new(75, 4, 16), - SourcePosition::new(123, 7, 12), - SourcePosition::new(145, 8, 14), - SourcePosition::new(173, 9, 16), + SourcePosition::new(126, 7, 12), + SourcePosition::new(148, 8, 14), + SourcePosition::new(176, 9, 16), ], )], ); @@ -1187,34 +1187,34 @@ mod tests { factory, r#" { - field { - deepField { - x: a + human { + relatives { + x: iq } - deepField { - x: b + relatives { + x: name } }, - field { - deepField { - y + human { + relatives { + iq } } } "#, &[RuleError::new( &error_message( - "deepField", + "relatives", &Nested(vec![ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("iq and name are different fields".to_owned()), )]), ), &[ SourcePosition::new(47, 3, 14), SourcePosition::new(75, 4, 16), - SourcePosition::new(110, 6, 14), - SourcePosition::new(138, 7, 16), + SourcePosition::new(111, 6, 14), + SourcePosition::new(139, 7, 16), ], )], ); @@ -1226,42 +1226,42 @@ mod tests { factory, r#" { - field { + human { ...F } - field { + human { ...F } } - fragment F on T { - deepField { - deeperField { - x: a + fragment F on Human { + relatives { + relatives { + x: iq } - deeperField { - x: b + relatives { + x: name } }, - deepField { - deeperField { - y + relatives { + relatives { + iq } } } "#, &[RuleError::new( &error_message( - "deeperField", + "relatives", &Nested(vec![ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("iq and name are different fields".to_owned()), )]), ), &[ - SourcePosition::new(197, 11, 14), - SourcePosition::new(227, 12, 16), - SourcePosition::new(262, 14, 14), - SourcePosition::new(292, 15, 16), + SourcePosition::new(201, 11, 14), + SourcePosition::new(229, 12, 16), + SourcePosition::new(265, 14, 14), + SourcePosition::new(293, 15, 16), ], )], ); @@ -1273,49 +1273,49 @@ mod tests { factory, r#" { - field { + dog { ...F } - field { + dog { ...I } } - fragment F on T { - x: a + fragment F on Dog { + x: name ...G } - fragment G on T { - y: c + fragment G on Dog { + y: barkVolume } - fragment I on T { - y: d + fragment I on Dog { + y: nickname ...J } - fragment J on T { - x: b + fragment J on Dog { + x: barks } "#, &[RuleError::new( &error_message( - "field", + "dog", &Nested(vec![ ConflictReason( "x".to_owned(), - Message("a and b are different fields".to_owned()), + Message("name and barks are different fields".to_owned()), ), ConflictReason( "y".to_owned(), - Message("c and d are different fields".to_owned()), + Message("barkVolume and nickname are different fields".to_owned()), ), ]), ), &[ SourcePosition::new(25, 2, 12), - SourcePosition::new(171, 10, 12), - SourcePosition::new(245, 14, 12), - SourcePosition::new(78, 5, 12), - SourcePosition::new(376, 21, 12), - SourcePosition::new(302, 17, 12), + SourcePosition::new(169, 10, 12), + SourcePosition::new(248, 14, 12), + SourcePosition::new(76, 5, 12), + SourcePosition::new(399, 21, 12), + SourcePosition::new(316, 17, 12), ], )], ); @@ -1327,13 +1327,17 @@ mod tests { factory, r#" { - field + dog { + name + } ...Unknown ...Known } - fragment Known on T { - field + fragment Known on QueryRoot { + dog { + name + } ...OtherUnknown } "#, @@ -1371,6 +1375,7 @@ mod tests { let fields = &[ registry.field::>("deepBox", i), registry.field::>("unrelatedField", i), + registry.field::>("otherField", i), ]; registry.build_interface_type::(i, fields).into_meta() @@ -1776,25 +1781,25 @@ mod tests { } } fragment X on SomeBox { - scalar + otherField } fragment Y on SomeBox { - scalar: unrelatedField + otherField: unrelatedField } "#, &[RuleError::new( &error_message( "other", &Nested(vec![ConflictReason( - "scalar".to_owned(), - Message("scalar and unrelatedField are different fields".to_owned()), + "otherField".to_owned(), + Message("otherField and unrelatedField are different fields".to_owned()), )]), ), &[ SourcePosition::new(703, 30, 14), SourcePosition::new(889, 38, 14), SourcePosition::new(771, 33, 14), - SourcePosition::new(960, 41, 14), + SourcePosition::new(964, 41, 14), ], )], ); @@ -2024,9 +2029,13 @@ mod tests { factory, r#" { - a + someBox { + unrelatedField + } ... { - a + someBox { + unrelatedField + } } } "#, diff --git a/juniper/src/validation/rules/unique_argument_names.rs b/juniper/src/validation/rules/unique_argument_names.rs index 4a0f6b281..a5f304a7d 100644 --- a/juniper/src/validation/rules/unique_argument_names.rs +++ b/juniper/src/validation/rules/unique_argument_names.rs @@ -85,7 +85,7 @@ mod tests { factory, r#" { - field @directive + dog @directive } "#, ); @@ -109,7 +109,7 @@ mod tests { factory, r#" { - field @directive(arg: "value") + dog @directive(arg: "value") } "#, ); diff --git a/juniper/src/validation/rules/unique_operation_names.rs b/juniper/src/validation/rules/unique_operation_names.rs index 22742e290..0b86191c3 100644 --- a/juniper/src/validation/rules/unique_operation_names.rs +++ b/juniper/src/validation/rules/unique_operation_names.rs @@ -162,7 +162,7 @@ mod tests { &error_message("Foo"), &[ SourcePosition::new(11, 1, 10), - SourcePosition::new(64, 4, 10), + SourcePosition::new(96, 6, 10), ], )], ); @@ -186,7 +186,7 @@ mod tests { &error_message("Foo"), &[ SourcePosition::new(11, 1, 10), - SourcePosition::new(64, 4, 10), + SourcePosition::new(96, 6, 10), ], )], ); diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index ca3219aa2..b4f8036cd 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -25,7 +25,7 @@ struct HumanOrAlien; struct ComplicatedArgs; -struct QueryRoot; +pub(crate) struct QueryRoot; #[derive(Debug, GraphQLInputObject)] struct TestInput { @@ -33,7 +33,7 @@ struct TestInput { name: String, } -struct MutationRoot; +pub(crate) struct MutationRoot; #[derive(Debug)] enum DogCommand { @@ -769,8 +769,5 @@ fn print_errors(errs: &[RuleError], query: &str) { } println!("{}", err.message()); - for p in err.locations() { - println!("{}", &query[p.index()..]); - } } } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 3e8380df7..e7e990821 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -1,11 +1,10 @@ -use parser::ParseError; -use schema::meta::ScalarMeta; +use parser::{ParseError, ScalarToken}; use serde::de::{self, Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use std::fmt::{self, Debug, Display}; pub trait ParseScalarValue { - fn from_str(value: &str) -> Result; + fn from_str<'a>(value: ScalarToken<'a>) -> Result>; } pub trait ScalarValue: From 159d8664fbcaf98174d7f1e2d5259aef788e774f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 18 Sep 2018 15:54:05 +0200 Subject: [PATCH 03/20] Remove the allocation from the `try_parse_fn` callback --- juniper/src/schema/meta.rs | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 3684f6e28..d2474bafd 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -15,8 +15,7 @@ pub struct ScalarMeta<'a, S: fmt::Debug> { pub name: Cow<'a, str>, #[doc(hidden)] pub description: Option, - #[doc(hidden)] - pub try_parse_fn: Box) -> bool + Send + Sync>, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, pub(crate) parse_fn: for<'b> fn(ScalarToken<'b>) -> Result>, } @@ -55,8 +54,7 @@ pub struct EnumMeta<'a, S: fmt::Debug> { pub description: Option, #[doc(hidden)] pub values: Vec, - #[doc(hidden)] - pub try_parse_fn: Box) -> bool + Send + Sync>, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, } /// Interface type metadata @@ -89,8 +87,7 @@ pub struct InputObjectMeta<'a, S: fmt::Debug> { pub description: Option, #[doc(hidden)] pub input_fields: Vec>, - #[doc(hidden)] - pub try_parse_fn: Box) -> bool + Send + Sync>, + pub(crate) try_parse_fn: for<'b> fn(&'b InputValue) -> bool, } /// A placeholder for not-yet-registered types @@ -287,7 +284,7 @@ impl<'a, S: fmt::Debug> MetaType<'a, S> { /// `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) -> bool + Send + Sync>> { + pub fn input_value_parse_fn(&self) -> Option fn(&'b InputValue) -> bool> { match *self { MetaType::Scalar(ScalarMeta { ref try_parse_fn, .. @@ -297,7 +294,7 @@ impl<'a, S: fmt::Debug> MetaType<'a, S> { }) | MetaType::InputObject(InputObjectMeta { ref try_parse_fn, .. - }) => Some(try_parse_fn), + }) => Some(*try_parse_fn), _ => None, } } @@ -372,9 +369,7 @@ where ScalarMeta { name: name, description: None, - try_parse_fn: Box::new(|v: &InputValue| { - >::from_input_value(v).is_some() - }), + try_parse_fn: try_parse_fn::, parse_fn: >::from_str, } } @@ -462,17 +457,16 @@ where S: ScalarValue + 'a, { /// Build a new enum type with the specified name and possible values - pub fn new>(name: Cow<'a, str>, values: &[EnumValue]) -> Self + pub fn new(name: Cow<'a, str>, values: &[EnumValue]) -> Self where + T: FromInputValue, for<'b> &'b S: ScalarRefValue<'b>, { EnumMeta { name: name, description: None, values: values.to_vec(), - try_parse_fn: Box::new(|v: &InputValue| { - >::from_input_value(v).is_some() - }), + try_parse_fn: try_parse_fn::, } } @@ -557,9 +551,7 @@ where name: name, description: None, input_fields: input_fields.to_vec(), - try_parse_fn: Box::new(|v: &InputValue| { - >::from_input_value(v).is_some() - }), + try_parse_fn: try_parse_fn::, } } @@ -694,3 +686,11 @@ impl<'a, S: fmt::Debug> fmt::Debug for InputObjectMeta<'a, S> { .finish() } } + +fn try_parse_fn(v: &InputValue) -> bool +where + T: FromInputValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ + >::from_input_value(v).is_some() +} From a382da13673fb0f7dbc82a8b39a2b13af7a55798 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 18 Sep 2018 15:56:55 +0200 Subject: [PATCH 04/20] Add a custom derive to simplify the setup of new custom scalar value type To do this some refactoring/moving of some internal transformations are required The only thing left for manual implementation is `Deserialize` because I see no way to infer that just from the enum. --- juniper/src/ast.rs | 16 +- juniper/src/executor/look_ahead.rs | 12 +- juniper/src/executor_tests/custom_scalar.rs | 154 +----------------- juniper/src/integrations/chrono.rs | 6 +- juniper/src/integrations/uuid.rs | 2 +- juniper/src/lib.rs | 11 +- juniper/src/macros/mod.rs | 7 - juniper/src/types/scalars.rs | 16 +- juniper/src/value/mod.rs | 18 +- juniper/src/value/scalar.rs | 147 ++--------------- .../src/derive_juniper_scalar_value.rs | 123 ++++++++++++++ juniper_codegen/src/lib.rs | 8 + 12 files changed, 192 insertions(+), 328 deletions(-) create mode 100644 juniper_codegen/src/derive_juniper_scalar_value.rs diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 84f2b753e..d8c89dcfa 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -231,7 +231,7 @@ where /// Construct a string value. pub fn string>(s: T) -> Self { - InputValue::scalar(s.as_ref()) + InputValue::scalar(s.as_ref().to_owned()) } pub fn scalar(v: T) -> Self @@ -343,25 +343,25 @@ where /// View the underlying int value, if present. pub fn as_int_value<'a>(&'a self) -> Option where - &'a S: Into>, + &'a S: Into>, { - self.as_scalar_value() + self.as_scalar_value().map(|i| *i) } /// View the underlying float value, if present. pub fn as_float_value<'a>(&'a self) -> Option where - &'a S: Into>, + &'a S: Into>, { - self.as_scalar_value() + self.as_scalar_value().map(|f| *f) } /// View the underlying string value, if present. pub fn as_string_value<'a>(&'a self) -> Option<&'a str> where - &'a S: Into>, + &'a S: Into>, { - self.as_scalar_value() + self.as_scalar_value().map(|s| s as &str) } pub fn as_scalar(&self) -> Option<&S> { @@ -447,10 +447,12 @@ where impl fmt::Display for InputValue 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::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), diff --git a/juniper/src/executor/look_ahead.rs b/juniper/src/executor/look_ahead.rs index 20522976f..a1245f727 100644 --- a/juniper/src/executor/look_ahead.rs +++ b/juniper/src/executor/look_ahead.rs @@ -1,6 +1,6 @@ use ast::{Directive, Fragment, InputValue, Selection}; use parser::Spanning; -use value::ScalarValue; +use value::{ScalarRefValue, ScalarValue}; use std::collections::HashMap; @@ -103,7 +103,7 @@ pub struct LookAheadSelection<'a, S: 'a> { impl<'a, S> LookAheadSelection<'a, S> where S: ScalarValue, - &'a S: Into>, + &'a S: ScalarRefValue<'a>, { fn should_include<'b, 'c>( directives: Option<&'b Vec>>>, @@ -128,7 +128,9 @@ where if let LookAheadValue::Scalar(s) = LookAheadValue::from_input_value(&v.item, vars) { - s.into().unwrap_or(false) + <&S as Into>>::into(s) + .map(|b| *b) + .unwrap_or(false) } else { false } @@ -142,7 +144,9 @@ where if let LookAheadValue::Scalar(b) = LookAheadValue::from_input_value(&v.item, vars) { - b.into().map(::std::ops::Not::not).unwrap_or(false) + <&S as Into>>::into(b) + .map(::std::ops::Not::not) + .unwrap_or(false) } else { false } diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs index d025cf6ef..14006251b 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -1,8 +1,8 @@ use ast::InputValue; use executor::{ExecutionResult, Executor, Registry, Variables}; use parser::{ParseError, ScalarToken, Token}; -use schema::model::RootNode; use schema::meta::MetaType; +use schema::model::RootNode; use serde::de::{self, Deserialize, Deserializer}; use serde::ser::{Serialize, Serializer}; use std::fmt::{self, Display}; @@ -10,7 +10,7 @@ use types::base::{Arguments, GraphQLType}; use types::scalars::EmptyMutation; use value::{Object, ScalarRefValue, ScalarValue, Value}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, PartialEq, ScalarValue)] enum MyScalarValue { Int(i32), Long(i64), @@ -19,154 +19,6 @@ enum MyScalarValue { Boolean(bool), } -impl ScalarValue for MyScalarValue {} - -impl Display for MyScalarValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - MyScalarValue::Int(i) => write!(f, "{}", i), - MyScalarValue::Long(i) => write!(f, "{}", i), - MyScalarValue::Float(n) => write!(f, "{}", n), - MyScalarValue::String(ref s) => write!(f, "\"{}\"", s), - MyScalarValue::Boolean(b) => write!(f, "{}", b), - } - } -} - -impl<'a> From<&'a str> for MyScalarValue { - fn from(s: &'a str) -> Self { - MyScalarValue::String(s.into()) - } -} - -impl From for MyScalarValue { - fn from(s: String) -> Self { - (&s as &str).into() - } -} - -impl From for MyScalarValue { - fn from(b: bool) -> Self { - MyScalarValue::Boolean(b) - } -} - -impl From for MyScalarValue { - fn from(i: i32) -> Self { - MyScalarValue::Int(i) - } -} - -impl From for MyScalarValue { - fn from(i: i64) -> Self { - MyScalarValue::Long(i) - } -} - -impl From for MyScalarValue { - fn from(f: f64) -> Self { - MyScalarValue::Float(f) - } -} - -impl From for Option { - fn from(s: MyScalarValue) -> Self { - match s { - MyScalarValue::Boolean(b) => Some(b), - _ => None, - } - } -} - -impl From for Option { - fn from(s: MyScalarValue) -> Self { - match s { - MyScalarValue::Int(i) => Some(i), - _ => None, - } - } -} - -impl From for Option { - fn from(s: MyScalarValue) -> Self { - match s { - MyScalarValue::Float(s) => Some(s), - MyScalarValue::Int(i) => Some(i as f64), - _ => None, - } - } -} - -impl From for Option { - fn from(s: MyScalarValue) -> Self { - match s { - MyScalarValue::String(s) => Some(s), - _ => None, - } - } -} - -impl<'a> From<&'a MyScalarValue> for Option { - fn from(s: &'a MyScalarValue) -> Self { - match *s { - MyScalarValue::Boolean(b) => Some(b), - _ => None, - } - } -} - -impl<'a> From<&'a MyScalarValue> for Option { - fn from(s: &'a MyScalarValue) -> Self { - match *s { - MyScalarValue::Float(b) => Some(b), - MyScalarValue::Int(i) => Some(i as f64), - _ => None, - } - } -} - -impl<'a> From<&'a MyScalarValue> for Option { - fn from(s: &'a MyScalarValue) -> Self { - match *s { - MyScalarValue::Int(b) => Some(b), - _ => None, - } - } -} - -impl<'a> From<&'a MyScalarValue> for Option { - fn from(s: &'a MyScalarValue) -> Self { - match *s { - MyScalarValue::Long(l) => Some(l), - _ => None, - } - } -} - -impl<'a> From<&'a MyScalarValue> for Option<&'a str> { - fn from(s: &'a MyScalarValue) -> Self { - match *s { - MyScalarValue::String(ref s) => Some(s), - _ => None, - } - } -} - -impl Serialize for MyScalarValue { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - MyScalarValue::Int(v) => serializer.serialize_i32(v), - MyScalarValue::Long(v) => serializer.serialize_i64(v), - MyScalarValue::Float(v) => serializer.serialize_f64(v), - MyScalarValue::String(ref v) => serializer.serialize_str(v), - MyScalarValue::Boolean(v) => serializer.serialize_bool(v), - } - } -} - impl<'de> Deserialize<'de> for MyScalarValue { fn deserialize(deserializer: D) -> Result where @@ -253,7 +105,7 @@ graphql_scalar!(i64 as "Long" where Scalar = MyScalarValue { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref i) => <_ as Into>>::into(i), + InputValue::Scalar(MyScalarValue::Long(i)) => Some(i), _ => None, } } diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index c77984b85..e16fb2b95 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -36,7 +36,7 @@ graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = (value: ScalarToken<'a>) -> Result> { if let ScalarToken::String(value) = value { - Ok(S::from(value)) + Ok(S::from(value.to_owned())) } else { Err(ParseError::UnexpectedToken(Token::Scalar(value))) } @@ -57,7 +57,7 @@ graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { from_str<'a>(value: ScalarToken<'a>) -> Result> { if let ScalarToken::String(value) = value { - Ok(S::from(value)) + Ok(S::from(value.to_owned())) } else { Err(ParseError::UnexpectedToken(Token::Scalar(value))) } @@ -83,7 +83,7 @@ graphql_scalar!(NaiveDate where Scalar = { from_str<'a>(value: ScalarToken<'a>) -> Result> { if let ScalarToken::String(value) = value { - Ok(S::from(value)) + Ok(S::from(value.to_owned())) } else { Err(ParseError::UnexpectedToken(Token::Scalar(value))) } diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index f6d0f77b4..ed0cf85a2 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -17,7 +17,7 @@ graphql_scalar!(Uuid where Scalar = { from_str<'a>(value: ScalarToken<'a>) -> Result> { if let ScalarToken::String(value) = value { - Ok(S::from(value)) + Ok(S::from(value.to_owned())) } else { Err(ParseError::UnexpectedToken(Token::Scalar(value))) } diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 778888ce6..f5ebb27e8 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -90,7 +90,8 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected. */ #![warn(missing_docs)] -extern crate serde; +#[doc(hidden)] +pub extern crate serde; #[macro_use] extern crate serde_derive; @@ -118,6 +119,14 @@ extern crate juniper_codegen; #[doc(hidden)] pub use juniper_codegen::*; +#[macro_export] +#[doc(hidden)] +macro_rules! __juniper_use_everything { + () => { + pub use $crate::*; + }; +} + #[macro_use] mod value; #[macro_use] diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index f9e4b19d5..5347fbac6 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -31,12 +31,5 @@ mod field; #[macro_use] mod union; -#[macro_export] -macro_rules! __juniper_use_everything { - () => { - pub use $crate::*; - }; -} - #[cfg(test)] mod tests; diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 7445613c1..8ac2924a6 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -6,7 +6,7 @@ use std::{char, u32}; use ast::{FromInputValue, InputValue, Selection, ToInputValue}; use executor::{Executor, Registry}; -use parser::{LexerError, ParseError, Token, ScalarToken}; +use parser::{LexerError, ParseError, ScalarToken, Token}; use schema::meta::MetaType; use types::base::GraphQLType; use value::{ParseScalarValue, ScalarRefValue, ScalarValue, Value}; @@ -50,7 +50,7 @@ graphql_scalar!(ID as "ID" where Scalar = { from_str<'a>(value: ScalarToken<'a>) -> Result> { match value { ScalarToken::String(value) | ScalarToken::Int(value) => { - Ok(S::from(value)) + Ok(S::from(value.to_owned())) } _ => Err(ParseError::UnexpectedToken(Token::Scalar(value))), } @@ -202,7 +202,7 @@ graphql_scalar!(bool as "Boolean" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref b) => <_ as Into>>::into(b), + InputValue::Scalar(ref b) => <_ as Into>>::into(b).map(|b| *b), _ => None, } } @@ -220,7 +220,7 @@ graphql_scalar!(i32 as "Int" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref i) => <_ as Into>>::into(i), + InputValue::Scalar(ref i) => <_ as Into>>::into(i).map(|i| *i), _ => None, } } @@ -244,7 +244,8 @@ graphql_scalar!(f64 as "Float" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { InputValue::Scalar(ref s) => { - <_ as Into>>::into(s) + <_ as Into>>::into(s).map(|i| *i) + .or_else(|| <_ as Into>>::into(s).map(|i| *i as f64)) } _ => None, } @@ -345,8 +346,8 @@ where #[cfg(test)] mod tests { use super::ID; - use value::{DefaultScalarValue, ParseScalarValue}; use parser::ScalarToken; + use value::{DefaultScalarValue, ParseScalarValue}; #[test] fn test_id_from_string() { @@ -364,7 +365,8 @@ mod tests { #[test] fn parse_strings() { fn parse_string(s: &str, expected: &str) { - let s = >::from_str(ScalarToken::String(s)); + let s = + >::from_str(ScalarToken::String(s)); assert!(s.is_ok(), "A parsing error occurred: {:?}", s); let s: Option = s.unwrap().into(); assert!(s.is_some(), "No string returned"); diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index 145910fda..96d333b1a 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -48,7 +48,7 @@ where /// Construct a string value. pub fn string(s: &str) -> Self { - Self::scalar(s) + Self::scalar(s.to_owned()) } /// Construct a boolean value. @@ -83,10 +83,9 @@ where } } - pub fn as_scalar_value<'a, T>(&self) -> Option + pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T> where - for<'b> &'b S: Into>, - S: 'a, + &'a S: Into>, { match *self { Value::Scalar(ref s) => s.into(), @@ -99,7 +98,7 @@ where where for<'a> &'a S: ScalarRefValue<'a>, { - self.as_scalar_value::() + self.as_scalar_value::().map(|v| *v) } /// View the underlying object value, if present. @@ -136,12 +135,9 @@ where /// View the underlying string value, if present. pub fn as_string_value<'a>(&'a self) -> Option<&'a str> where - Option<&'a str>: From<&'a S>, + Option<&'a String>: From<&'a S>, { - match *self { - Value::Scalar(ref s) => <_ as Into>>::into(s), - _ => None, - } + self.as_scalar_value::().map(|s| s as &str) } } @@ -186,7 +182,7 @@ where S: ScalarValue, { fn from(s: &'a str) -> Self { - Value::scalar(s) + Value::scalar(s.to_owned()) } } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index e7e990821..4eb07218b 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -1,6 +1,6 @@ use parser::{ParseError, ScalarToken}; use serde::de::{self, Deserialize, Deserializer}; -use serde::ser::{Serialize, Serializer}; +use serde::ser::Serialize; use std::fmt::{self, Debug, Display}; pub trait ParseScalarValue { @@ -13,7 +13,6 @@ pub trait ScalarValue: + PartialEq + Clone + Serialize - + for<'a> From<&'a str> + From + From + From @@ -32,17 +31,24 @@ pub trait ScalarValue: } pub trait ScalarRefValue<'a>: - Debug + Into> + Into> + Into> + Into> + Debug + + Into> + + Into> + + Into> + + Into> { } impl<'a, T> ScalarRefValue<'a> for &'a T where T: ScalarValue, - &'a T: Into> + Into> + Into> + Into>, + &'a T: Into> + + Into> + + Into> + + Into>, {} -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, PartialEq, Clone, ScalarValue)] pub enum DefaultScalarValue { Int(i32), Float(f64), @@ -50,137 +56,6 @@ pub enum DefaultScalarValue { Boolean(bool), } -impl ScalarValue for DefaultScalarValue {} - -impl Display for DefaultScalarValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - DefaultScalarValue::Int(i) => write!(f, "{}", i), - DefaultScalarValue::Float(n) => write!(f, "{}", n), - DefaultScalarValue::String(ref s) => write!(f, "\"{}\"", s), - DefaultScalarValue::Boolean(b) => write!(f, "{}", b), - } - } -} - -impl<'a> From<&'a str> for DefaultScalarValue { - fn from(s: &'a str) -> Self { - DefaultScalarValue::String(s.into()) - } -} - -impl From for DefaultScalarValue { - fn from(s: String) -> Self { - (&s as &str).into() - } -} - -impl From for DefaultScalarValue { - fn from(b: bool) -> Self { - DefaultScalarValue::Boolean(b) - } -} - -impl From for DefaultScalarValue { - fn from(i: i32) -> Self { - DefaultScalarValue::Int(i) - } -} - -impl From for DefaultScalarValue { - fn from(f: f64) -> Self { - DefaultScalarValue::Float(f) - } -} - -impl From for Option { - fn from(s: DefaultScalarValue) -> Self { - match s { - DefaultScalarValue::Boolean(b) => Some(b), - _ => None, - } - } -} - -impl From for Option { - fn from(s: DefaultScalarValue) -> Self { - match s { - DefaultScalarValue::Int(i) => Some(i), - _ => None, - } - } -} - -impl From for Option { - fn from(s: DefaultScalarValue) -> Self { - match s { - DefaultScalarValue::Float(s) => Some(s), - DefaultScalarValue::Int(i) => Some(i as f64), - _ => None, - } - } -} - -impl From for Option { - fn from(s: DefaultScalarValue) -> Self { - match s { - DefaultScalarValue::String(s) => Some(s.clone()), - _ => None, - } - } -} - -impl<'a> From<&'a DefaultScalarValue> for Option { - fn from(s: &'a DefaultScalarValue) -> Self { - match *s { - DefaultScalarValue::Boolean(b) => Some(b), - _ => None, - } - } -} - -impl<'a> From<&'a DefaultScalarValue> for Option { - fn from(s: &'a DefaultScalarValue) -> Self { - match *s { - DefaultScalarValue::Float(b) => Some(b), - DefaultScalarValue::Int(i) => Some(i as f64), - _ => None, - } - } -} - -impl<'a> From<&'a DefaultScalarValue> for Option { - fn from(s: &'a DefaultScalarValue) -> Self { - match *s { - DefaultScalarValue::Int(b) => Some(b), - _ => None, - } - } -} - -impl<'a> From<&'a DefaultScalarValue> for Option<&'a str> { - fn from(s: &'a DefaultScalarValue) -> Self { - match *s { - DefaultScalarValue::String(ref s) => Some(s), - _ => None, - } - } -} - -impl Serialize for DefaultScalarValue { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - match *self { - DefaultScalarValue::Int(v) => serializer.serialize_i32(v), - DefaultScalarValue::Float(v) => serializer.serialize_f64(v), - DefaultScalarValue::String(ref v) => serializer.serialize_str(v), - DefaultScalarValue::Boolean(v) => serializer.serialize_bool(v), - } - } -} - impl<'de> Deserialize<'de> for DefaultScalarValue { fn deserialize(deserializer: D) -> Result where diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs new file mode 100644 index 000000000..3b03f9f5d --- /dev/null +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -0,0 +1,123 @@ +use quote::Tokens; +use syn::{self, Data, Fields, Ident, Variant}; + +pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { + 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::, 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::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + + quote!{ + const #dummy_const: () = { + mod juniper { + __juniper_use_everything!(); + } + + extern crate std; + + #(#froms)* + + #serialize + #display + + impl juniper::ScalarValue for #ident {} + + }; + } +} + +fn derive_display<'a, I>(variants: I, ident: &Ident) -> Tokens +where + I: Iterator, +{ + 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) -> Tokens +where + I: Iterator, +{ + 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(&self, serializer: S) -> std::result::Result + where S: juniper::serde::Serializer + { + match *self { + #(#arms)* + } + } + } + } +} + +fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result { + 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 + } + } + } + }) +} diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index e814cd0ed..712f22bc6 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -18,6 +18,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 +43,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::(input).unwrap(); + let gen = derive_juniper_scalar_value::impl_scalar_value(&ast); + gen.into() +} From b7d21f6156b387be063cb761a8ad878d0fc11122 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 18 Sep 2018 17:21:19 +0200 Subject: [PATCH 05/20] Add some documentation --- juniper/src/ast.rs | 10 ++- juniper/src/value/mod.rs | 2 + juniper/src/value/scalar.rs | 146 +++++++++++++++++++++++++++++++++++- 3 files changed, 154 insertions(+), 4 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index d8c89dcfa..758ae6100 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -234,6 +234,7 @@ where InputValue::scalar(s.as_ref().to_owned()) } + /// Construct a scalar value pub fn scalar(v: T) -> Self where T: Into, @@ -364,6 +365,7 @@ where 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::Scalar(ref s) => Some(s), @@ -371,9 +373,11 @@ where } } - pub fn as_scalar_value<'a, T>(&'a self) -> Option + /// View the underlying scalar value, if present. + pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T> where - &'a S: Into>, + T: 'a, + &'a S: Into>, { self.as_scalar().and_then(Into::into) } @@ -452,7 +456,7 @@ where fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { InputValue::Null => write!(f, "null"), - InputValue::Scalar(ref s) if s.is_type::<&String>() => write!(f, "\"{}\"", s), + InputValue::Scalar(ref s) if s.is_type::() => write!(f, "\"{}\"", s), InputValue::Scalar(ref s) => write!(f, "{}", s), InputValue::Enum(ref v) => write!(f, "{}", v), InputValue::Variable(ref v) => write!(f, "${}", v), diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index 96d333b1a..e7785580d 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -83,6 +83,7 @@ where } } + /// View the underlying scalar value if present pub fn as_scalar_value<'a, T>(&'a self) -> Option<&'a T> where &'a S: Into>, @@ -125,6 +126,7 @@ where } } + /// View the underlying scalar value, if present pub fn as_scalar(&self) -> Option<&S> { match *self { Value::Scalar(ref s) => Some(s), diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 4eb07218b..25d275a4e 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -3,10 +3,124 @@ use serde::de::{self, Deserialize, Deserializer}; use serde::ser::Serialize; use std::fmt::{self, Debug, Display}; +/// A trait used to convert a `ScalarToken` into a certain scalar value type pub trait ParseScalarValue { + /// See the trait documentation fn from_str<'a>(value: ScalarToken<'a>) -> Result>; } +/// 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. +/// The only trait that needs to be implemented manually in this case is `serde::Deserialize`. +/// +/// # 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 std::fmt; +/// # +/// #[derive(Debug, Clone, PartialEq, ScalarValue)] +/// enum MyScalarValue { +/// Int(i32), +/// Long(i64), +/// Float(f64), +/// String(String), +/// Boolean(bool), +/// } +/// +/// impl<'de> Deserialize<'de> for MyScalarValue { +/// fn deserialize(deserializer: D) -> Result +/// where +/// D: Deserializer<'de>, +/// { +/// 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(self, value: bool) -> Result { +/// Ok(MyScalarValue::Boolean(value)) +/// } +/// +/// fn visit_i32(self, value: i32) -> Result +/// where +/// E: de::Error, +/// { +/// Ok(MyScalarValue::Int(value)) +/// } +/// +/// fn visit_i64(self, value: i64) -> Result +/// where +/// E: de::Error, +/// { +/// Ok(MyScalarValue::Long(value)) +/// } +/// +/// fn visit_u32(self, value: u32) -> Result +/// 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(self, value: u64) -> Result +/// 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(self, value: f64) -> Result { +/// Ok(MyScalarValue::Float(value)) +/// } +/// +/// fn visit_str(self, value: &str) -> Result +/// where +/// E: de::Error, +/// { +/// self.visit_string(value.into()) +/// } +/// +/// fn visit_string(self, value: String) -> Result { +/// Ok(MyScalarValue::String(value)) +/// } +/// } +/// +/// deserializer.deserialize_any(MyScalarValueVisitor) +/// } +/// } +/// # fn main() {} +/// ``` pub trait ScalarValue: Debug + Display @@ -22,14 +136,34 @@ pub trait ScalarValue: + Into> + Into> { + /// 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::(), true); + /// assert_eq!(value.is_type::(), false); + /// + /// ``` fn is_type<'a, T>(&'a self) -> bool where - &'a Self: Into>, + T: 'a, + &'a Self: Into>, { self.into().is_some() } } +/// 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> @@ -48,7 +182,11 @@ where + Into>, {} +/// 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), @@ -56,6 +194,12 @@ pub enum DefaultScalarValue { Boolean(bool), } +impl<'a> From<&'a str> for DefaultScalarValue { + fn from(s: &'a str) -> Self { + DefaultScalarValue::String(s.into()) + } +} + impl<'de> Deserialize<'de> for DefaultScalarValue { fn deserialize(deserializer: D) -> Result where From 3def2168087085d34ed71a9afd2e8c10f75c4d56 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 18 Sep 2018 17:21:37 +0200 Subject: [PATCH 06/20] Deprecated some methods on `InputValue` and `Value` Those methods are related to scalar values, end users should better use the generic variant of those methods. --- juniper/src/ast.rs | 30 ++- juniper/src/executor_tests/custom_scalar.rs | 5 +- juniper/src/executor_tests/directives.rs | 58 ++--- juniper/src/executor_tests/enums.rs | 12 +- juniper/src/executor_tests/executor.rs | 80 +++---- .../src/executor_tests/interfaces_unions.rs | 20 +- .../src/executor_tests/introspection/enums.rs | 74 +++--- .../introspection/input_object.rs | 72 +++--- .../src/executor_tests/introspection/mod.rs | 96 ++++---- juniper/src/executor_tests/variables.rs | 170 +++++++------- juniper/src/integrations/chrono.rs | 40 ++-- juniper/src/integrations/serde.rs | 19 +- juniper/src/integrations/url.rs | 8 +- juniper/src/integrations/uuid.rs | 8 +- juniper/src/macros/tests/args.rs | 218 +++++++++--------- juniper/src/macros/tests/field.rs | 52 ++--- juniper/src/macros/tests/interface.rs | 50 ++-- juniper/src/macros/tests/object.rs | 48 ++-- juniper/src/macros/tests/scalar.rs | 26 +-- juniper/src/macros/tests/union.rs | 38 +-- juniper/src/parser/lexer.rs | 4 + juniper/src/parser/tests/document.rs | 2 +- juniper/src/parser/tests/value.rs | 20 +- juniper/src/parser/value.rs | 4 +- juniper/src/tests/introspection_tests.rs | 20 +- juniper/src/tests/query_tests.rs | 112 ++++----- juniper/src/tests/type_info_tests.rs | 6 +- juniper/src/types/base.rs | 2 +- juniper/src/types/scalars.rs | 14 +- juniper/src/validation/test_harness.rs | 8 +- juniper/src/value/mod.rs | 31 ++- juniper_codegen/src/derive_enum.rs | 9 +- 32 files changed, 692 insertions(+), 664 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 758ae6100..58a75f047 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -215,21 +215,25 @@ where } /// Construct an integer value. + #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] pub fn int(i: i32) -> Self { Self::scalar(i) } /// Construct a floating point value. + #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] pub fn float(f: f64) -> Self { Self::scalar(f) } /// Construct a boolean value. + #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] pub fn boolean(b: bool) -> Self { Self::scalar(b) } /// Construct a string value. + #[deprecated(since = "0.11.0", note = "Use `InputValue::scalar` instead")] pub fn string>(s: T) -> Self { InputValue::scalar(s.as_ref().to_owned()) } @@ -342,6 +346,10 @@ where } /// View the underlying int value, if present. + #[deprecated( + since = "0.11.0", + note = "Use `InputValue::as_scalar_value` instead" + )] pub fn as_int_value<'a>(&'a self) -> Option where &'a S: Into>, @@ -350,6 +358,10 @@ where } /// View the underlying float value, if present. + #[deprecated( + since = "0.11.0", + note = "Use `InputValue::as_scalar_value` instead" + )] pub fn as_float_value<'a>(&'a self) -> Option where &'a S: Into>, @@ -358,6 +370,10 @@ where } /// 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>, @@ -534,16 +550,16 @@ mod tests { let value: TestValue = InputValue::null(); assert_eq!(format!("{}", value), "null"); - let value: TestValue = InputValue::int(123); + let value: TestValue = InputValue::scalar(123); assert_eq!(format!("{}", value), "123"); - let value: TestValue = InputValue::float(12.3); + let value: TestValue = InputValue::scalar(12.3); assert_eq!(format!("{}", value), "12.3"); - let value: TestValue = InputValue::string("FOO".to_owned()); + let value: TestValue = InputValue::scalar("FOO".to_owned()); assert_eq!(format!("{}", value), "\"FOO\""); - let value: TestValue = InputValue::boolean(true); + let value: TestValue = InputValue::scalar(true); assert_eq!(format!("{}", value), "true"); let value: TestValue = InputValue::enum_value("BAR".to_owned()); @@ -552,18 +568,18 @@ mod tests { let value: TestValue = InputValue::variable("baz".to_owned()); assert_eq!(format!("{}", value), "$baz"); - let list = vec![InputValue::int(1), InputValue::int(2)]; + let list = vec![InputValue::scalar(1), InputValue::scalar(2)]; let value: TestValue = 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: TestValue = InputValue::parsed_object(object); diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs index 14006251b..af5db6bdc 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -4,11 +4,10 @@ use parser::{ParseError, ScalarToken, Token}; use schema::meta::MetaType; use schema::model::RootNode; use serde::de::{self, Deserialize, Deserializer}; -use serde::ser::{Serialize, Serializer}; -use std::fmt::{self, Display}; +use std::fmt; use types::base::{Arguments, GraphQLType}; use types::scalars::EmptyMutation; -use value::{Object, ScalarRefValue, ScalarValue, Value}; +use value::{Object, ScalarRefValue, Value}; #[derive(Debug, Clone, PartialEq, ScalarValue)] enum MyScalarValue { diff --git a/juniper/src/executor_tests/directives.rs b/juniper/src/executor_tests/directives.rs index 00189c858..1aaaa2acf 100644 --- a/juniper/src/executor_tests/directives.rs +++ b/juniper/src/executor_tests/directives.rs @@ -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); }); } diff --git a/juniper/src/executor_tests/enums.rs b/juniper/src/executor_tests/enums.rs index 09223ce72..032ccccdb 100644 --- a/juniper/src/executor_tests/enums.rs +++ b/juniper/src/executor_tests/enums.rs @@ -54,7 +54,7 @@ fn accepts_enum_literal() { run_query("{ toString(color: RED) }", |result| { assert_eq!( result.get_field_value("toString"), - Some(&Value::string("Color::Red")) + Some(&Value::scalar("Color::Red")) ); }); } @@ -64,7 +64,7 @@ fn serializes_as_output() { run_query("{ aColor }", |result| { assert_eq!( result.get_field_value("aColor"), - Some(&Value::string("RED")) + Some(&Value::scalar("RED")) ); }); } @@ -92,13 +92,13 @@ 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")) + Some(&Value::scalar("Color::Red")) ); }, ); @@ -110,7 +110,7 @@ fn does_not_accept_incorrect_enum_name_in_variables() { 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(); @@ -131,7 +131,7 @@ fn does_not_accept_incorrect_type_in_variables() { 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(); diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index d295c744d..eb124dabf 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -62,7 +62,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(); @@ -76,25 +76,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"), ]), ), ( @@ -102,16 +102,16 @@ 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(), ), Value::null(), Value::object( vec![ - ("a", Value::string("Apple")), - ("b", Value::string("Banana")), + ("a", Value::scalar("Apple")), + ("b", Value::scalar("Banana")), ].into_iter() .collect(), ), @@ -169,29 +169,29 @@ 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(), ), ), - ("c", Value::string("Cherry")), + ("c", Value::scalar("Cherry")), ].into_iter() .collect(), ), ), - ("c", Value::string("Cherry")), + ("c", Value::scalar("Cherry")), ].into_iter() .collect() ) @@ -264,13 +264,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![ @@ -279,8 +279,8 @@ 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(), ), @@ -292,8 +292,8 @@ 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(), ), @@ -302,12 +302,12 @@ mod merge_parallel_inline_fragments { ), ]), ), - ("c", Value::string("Cherry")), + ("c", Value::scalar("Cherry")), ].into_iter() .collect(), ), ), - ("c", Value::string("Cherry")), + ("c", Value::scalar("Cherry")), ].into_iter() .collect() ) @@ -358,7 +358,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() ) @@ -460,7 +460,7 @@ mod dynamic_context_switching { ( "first", Value::object( - vec![("value", Value::string("First value"))] + vec![("value", Value::scalar("First value"))] .into_iter() .collect(), ), @@ -514,7 +514,7 @@ mod dynamic_context_switching { vec![( "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 { ( "first", Value::object( - vec![("value", Value::string("First value"))] + vec![("value", Value::scalar("First value"))] .into_iter() .collect(), ), @@ -673,7 +673,7 @@ mod dynamic_context_switching { vec![( "first", Value::object( - vec![("value", Value::string("First value"))] + vec![("value", Value::scalar("First value"))] .into_iter() .collect(), ), @@ -981,7 +981,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()) ); } @@ -999,7 +999,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()) ); } @@ -1018,7 +1018,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()) ); } diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs index 460803bc8..49dd474ce 100644 --- a/juniper/src/executor_tests/interfaces_unions.rs +++ b/juniper/src/executor_tests/interfaces_unions.rs @@ -121,15 +121,15 @@ 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(), ), Value::object( vec![ - ("name", Value::string("Garfield")), - ("meows", Value::boolean(false)), + ("name", Value::scalar("Garfield")), + ("meows", Value::scalar(false)), ].into_iter() .collect(), ), @@ -252,17 +252,17 @@ 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(), ), 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(), ), diff --git a/juniper/src/executor_tests/introspection/enums.rs b/juniper/src/executor_tests/introspection/enums.rs index e2c398135..0862d1106 100644 --- a/juniper/src/executor_tests/introspection/enums.rs +++ b/juniper/src/executor_tests/introspection/enums.rs @@ -116,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); @@ -124,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(), @@ -136,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(), @@ -165,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); @@ -173,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(), @@ -185,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(), @@ -216,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())); @@ -225,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(), @@ -237,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(), @@ -268,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); @@ -280,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(), @@ -292,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(), @@ -323,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())); @@ -332,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(), @@ -344,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(), @@ -375,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())); @@ -384,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(), @@ -399,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(), @@ -433,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())); diff --git a/juniper/src/executor_tests/introspection/input_object.rs b/juniper/src/executor_tests/introspection/input_object.rs index f29dbc55f..aa711e621 100644 --- a/juniper/src/executor_tests/introspection/input_object.rs +++ b/juniper/src/executor_tests/introspection/input_object.rs @@ -149,7 +149,7 @@ fn default_name_introspection() { run_type_info_query(doc, |type_info, fields| { assert_eq!( type_info.get_field_value("name"), - Some(&Value::string("DefaultName")) + Some(&Value::scalar("DefaultName")) ); assert_eq!( type_info.get_field_value("description"), @@ -161,7 +161,7 @@ fn default_name_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldOne")), + ("name", Value::scalar("fieldOne")), ("description", Value::null()), ( "type", @@ -169,7 +169,7 @@ fn default_name_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -186,7 +186,7 @@ fn default_name_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldTwo")), + ("name", Value::scalar("fieldTwo")), ("description", Value::null()), ( "type", @@ -194,7 +194,7 @@ fn default_name_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -214,8 +214,8 @@ fn default_name_introspection() { fn default_name_input_value() { let iv: InputValue = 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(), ); @@ -254,7 +254,7 @@ 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"), @@ -266,7 +266,7 @@ fn no_trailing_comma_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldOne")), + ("name", Value::scalar("fieldOne")), ("description", Value::null()), ( "type", @@ -274,7 +274,7 @@ fn no_trailing_comma_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -291,7 +291,7 @@ fn no_trailing_comma_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldTwo")), + ("name", Value::scalar("fieldTwo")), ("description", Value::null()), ( "type", @@ -299,7 +299,7 @@ fn no_trailing_comma_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -339,7 +339,7 @@ fn derive_introspection() { run_type_info_query(doc, |type_info, fields| { assert_eq!( type_info.get_field_value("name"), - Some(&Value::string("Derive")) + Some(&Value::scalar("Derive")) ); assert_eq!( type_info.get_field_value("description"), @@ -351,7 +351,7 @@ fn derive_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldOne")), + ("name", Value::scalar("fieldOne")), ("description", Value::null()), ( "type", @@ -359,7 +359,7 @@ fn derive_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -412,7 +412,7 @@ 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"), @@ -424,7 +424,7 @@ fn named_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldOne")), + ("name", Value::scalar("fieldOne")), ("description", Value::null()), ( "type", @@ -432,7 +432,7 @@ fn named_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -472,11 +472,11 @@ fn description_introspection() { run_type_info_query(doc, |type_info, fields| { assert_eq!( type_info.get_field_value("name"), - Some(&Value::string("Description")) + 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); @@ -484,7 +484,7 @@ fn description_introspection() { assert!( fields.contains(&Value::object( vec![ - ("name", Value::string("fieldOne")), + ("name", Value::scalar("fieldOne")), ("description", Value::null()), ( "type", @@ -492,7 +492,7 @@ fn description_introspection() { vec![( "ofType", Value::object( - vec![("name", Value::string("String"))] + vec![("name", Value::scalar("String"))] .into_iter() .collect(), ), @@ -532,7 +532,7 @@ 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"), @@ -544,15 +544,15 @@ fn field_description_introspection() { 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(), ), @@ -569,15 +569,15 @@ fn field_description_introspection() { 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(), ), @@ -613,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); @@ -621,12 +621,12 @@ 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(), )) @@ -635,12 +635,12 @@ fn field_with_defaults_introspection() { 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(), )) diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index 97ac490bc..14962a2eb 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -26,11 +26,11 @@ struct Root {} graphql_scalar!(Scalar as "SampleScalar" where Scalar = { resolve(&self) -> Value { - Value::int(self.0) + Value::scalar(self.0) } from_input_value(v: &InputValue) -> Option { - v.as_int_value().map(|i| Scalar(i)) + v.as_scalar_value().map(|i: &i32| Scalar(*i)) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -90,9 +90,9 @@ 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() ) @@ -140,11 +140,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"), @@ -175,9 +175,9 @@ 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(), @@ -187,9 +187,9 @@ fn enum_introspection() { 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(), @@ -252,15 +252,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"), @@ -285,7 +285,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 @@ -299,10 +299,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![])), ( @@ -310,13 +310,13 @@ 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(), ), @@ -325,7 +325,7 @@ fn interface_introspection() { .collect(), ), ), - ("isDeprecated", Value::boolean(false)), + ("isDeprecated", Value::scalar(false)), ("deprecationReason", Value::null()), ].into_iter() .collect(), @@ -399,20 +399,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(), )])) @@ -444,7 +444,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![])), ( @@ -452,13 +452,13 @@ 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(), ), @@ -467,7 +467,7 @@ fn object_introspection() { .collect(), ), ), - ("isDeprecated", Value::boolean(false)), + ("isDeprecated", Value::scalar(false)), ("deprecationReason", Value::null()), ].into_iter() .collect(), @@ -477,30 +477,30 @@ fn object_introspection() { 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(), @@ -516,20 +516,20 @@ fn object_introspection() { ), 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(), ), ), - ("defaultValue", Value::string("123")), + ("defaultValue", Value::scalar("123")), ].into_iter() .collect(), ), @@ -540,13 +540,13 @@ 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(), ), @@ -555,7 +555,7 @@ fn object_introspection() { .collect(), ), ), - ("isDeprecated", Value::boolean(false)), + ("isDeprecated", Value::scalar(false)), ("deprecationReason", Value::null()), ].into_iter() .collect(), @@ -600,8 +600,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()), diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 9bdc2fe5e..124cee39a 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -15,12 +15,12 @@ struct TestType; graphql_scalar!(TestComplexScalar where Scalar = { resolve(&self) -> Value { - Value::string("SerializedValue") + Value::scalar(String::from("SerializedValue")) } from_input_value(v: &InputValue) -> Option { - if let Some(s) = v.as_string_value() { - if s == "SerializedValue" { + if let Some(s) = v.as_scalar_value::() { + if *s == "SerializedValue" { return Some(TestComplexScalar); } } @@ -144,7 +144,7 @@ fn inline_complex_input() { |result: &Object| { 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 })"#))); }, ); } @@ -156,7 +156,7 @@ fn inline_parse_single_value_to_list() { |result: &Object| { 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 })"#))); }, ); } @@ -168,7 +168,7 @@ fn inline_runs_from_input_value_on_scalar() { |result: &Object| { 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) })"#))); }, ); } @@ -181,9 +181,9 @@ 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(), ), @@ -192,7 +192,7 @@ fn variable_complex_input() { |result: &Object| { 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 })"#))); }, ); } @@ -205,9 +205,9 @@ 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(), ), @@ -216,7 +216,7 @@ fn variable_parse_single_value_to_list() { |result: &Object| { 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 })"#))); }, ); } @@ -229,8 +229,8 @@ 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(), ), @@ -239,7 +239,7 @@ fn variable_runs_from_input_value_on_scalar() { |result: &Object| { 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) })"#))); }, ); } @@ -254,8 +254,8 @@ 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(), @@ -280,7 +280,7 @@ fn variable_error_on_incorrect_type() { 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(); @@ -304,8 +304,8 @@ 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(), ), @@ -335,7 +335,7 @@ 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(), ), @@ -366,10 +366,10 @@ 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(), ), @@ -394,7 +394,7 @@ fn allow_nullable_inputs_to_be_omitted() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -407,7 +407,7 @@ fn allow_nullable_inputs_to_be_omitted_in_variable() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -420,7 +420,7 @@ fn allow_nullable_inputs_to_be_explicitly_null() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -436,7 +436,7 @@ fn allow_nullable_inputs_to_be_set_to_null_in_variable() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -446,13 +446,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: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"Some("a")"#)) + Some(&Value::scalar(r#"Some("a")"#)) ); }, ); @@ -465,7 +465,7 @@ fn allow_nullable_inputs_to_be_set_to_value_directly() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNullableStringInput"), - Some(&Value::string(r#"Some("a")"#)) + Some(&Value::scalar(r#"Some("a")"#)) ); }, ); @@ -515,13 +515,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""#)) ); }, ); @@ -534,7 +534,7 @@ fn allow_non_nullable_inputs_to_be_set_to_value_directly() { |result: &Object| { assert_eq!( result.get_field_value("fieldWithNonNullableStringInput"), - Some(&Value::string(r#""a""#)) + Some(&Value::scalar(r#""a""#)) ); }, ); @@ -550,7 +550,7 @@ fn allow_lists_to_be_null() { |result: &Object| { assert_eq!( result.get_field_value("list"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -562,13 +562,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(), |result| { assert_eq!( result.get_field_value("list"), - Some(&Value::string(r#"Some([Some("A")])"#)) + Some(&Value::scalar(r#"Some([Some("A")])"#)) ); }, ); @@ -581,16 +581,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(), |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")])"#)) ); }, ); @@ -623,13 +623,13 @@ 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(), |result| { assert_eq!( result.get_field_value("nnList"), - Some(&Value::string(r#"[Some("A")]"#)) + Some(&Value::scalar(r#"[Some("A")]"#)) ); }, ); @@ -641,16 +641,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(), |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")]"#)) ); }, ); @@ -666,7 +666,7 @@ fn allow_lists_of_non_null_to_be_null() { |result| { assert_eq!( result.get_field_value("listNn"), - Some(&Value::string(r#"None"#)) + Some(&Value::scalar(r#"None"#)) ); }, ); @@ -678,13 +678,13 @@ 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(), |result| { assert_eq!( result.get_field_value("listNn"), - Some(&Value::string(r#"Some(["A"])"#)) + Some(&Value::scalar(r#"Some(["A"])"#)) ); }, ); @@ -699,9 +699,9 @@ 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(); @@ -725,9 +725,9 @@ 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(); @@ -769,13 +769,13 @@ 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(), |result| { assert_eq!( result.get_field_value("nnListNn"), - Some(&Value::string(r#"["A"]"#)) + Some(&Value::scalar(r#"["A"]"#)) ); }, ); @@ -789,7 +789,7 @@ 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(); @@ -811,7 +811,7 @@ 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(); @@ -830,7 +830,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""#)) ); }); } @@ -842,7 +842,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""#)) ); }, ); @@ -858,7 +858,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""#)) ); }, ); @@ -869,14 +869,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"#)) ); }); } @@ -885,13 +885,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"#)) ); }, ); @@ -904,7 +904,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"#)) ); }, ); @@ -915,7 +915,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"#)) ); }, ); @@ -1004,19 +1004,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"#)) ); }, ); @@ -1027,20 +1027,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"#)) ); }, ); @@ -1053,26 +1053,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"#)) ); }, ); @@ -1084,7 +1084,7 @@ mod integers { 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(); @@ -1105,7 +1105,7 @@ mod integers { 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(); @@ -1128,13 +1128,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"#)) ); }, ); @@ -1144,13 +1144,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"#)) ); }, ); @@ -1162,7 +1162,7 @@ mod floats { 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(); diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index e16fb2b95..4a2ffde5a 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -26,12 +26,12 @@ graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = Value { - Value::string(&self.to_rfc3339()) + Value::scalar(self.to_rfc3339()) } from_input_value(v: &InputValue) -> Option> { - v.as_string_value() - .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) + v.as_scalar_value() + .and_then(|s: &String| DateTime::parse_from_rfc3339(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -47,12 +47,12 @@ graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { description: "DateTime" resolve(&self) -> Value { - Value::string(&self.to_rfc3339()) + Value::scalar(self.to_rfc3339()) } from_input_value(v: &InputValue) -> Option> { - v.as_string_value() - .and_then(|s| (s.parse::>().ok())) + v.as_scalar_value() + .and_then(|s: &String| (s.parse::>().ok())) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -73,12 +73,12 @@ graphql_scalar!(NaiveDate where Scalar = { 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 { - v.as_string_value() - .and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) + v.as_scalar_value() + .and_then(|s: &String| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -96,12 +96,12 @@ graphql_scalar!(NaiveDateTime where Scalar = { description: "NaiveDateTime" resolve(&self) -> Value { - Value::float(self.timestamp() as f64) + Value::scalar(self.timestamp() as f64) } from_input_value(v: &InputValue) -> Option { - v.as_float_value() - .and_then(|f| NaiveDateTime::from_timestamp_opt(f as i64, 0)) + v.as_scalar_value() + .and_then(|f: &f64| NaiveDateTime::from_timestamp_opt(*f as i64, 0)) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -115,7 +115,7 @@ mod test { use value::DefaultScalarValue; fn datetime_fixedoffset_test(raw: &'static str) { - let input: ::InputValue = ::InputValue::string(raw.to_string()); + let input: ::InputValue = ::InputValue::scalar(raw.to_string()); let parsed: DateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = DateTime::parse_from_rfc3339(raw).unwrap(); @@ -139,7 +139,7 @@ mod test { } fn datetime_utc_test(raw: &'static str) { - let input: ::InputValue = ::InputValue::string(raw.to_string()); + let input: ::InputValue = ::InputValue::scalar(raw.to_string()); let parsed: DateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = DateTime::parse_from_rfc3339(raw) @@ -167,7 +167,7 @@ mod test { #[test] fn naivedate_from_input_value() { let input: ::InputValue = - ::InputValue::string("1996-12-19".to_string()); + ::InputValue::scalar("1996-12-19".to_string()); let y = 1996; let m = 12; let d = 19; @@ -185,7 +185,7 @@ mod test { #[test] fn naivedatetime_from_input_value() { let raw = 1_000_000_000_f64; - let input: ::InputValue = ::InputValue::float(raw); + let input: ::InputValue = ::InputValue::scalar(raw); let parsed: NaiveDateTime = ::FromInputValue::from_input_value(&input).unwrap(); let expected = NaiveDateTime::from_timestamp_opt(raw as i64, 0).unwrap(); @@ -244,15 +244,15 @@ 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() diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index 1c3144a2e..ab0ac962f 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -96,7 +96,7 @@ where } fn visit_bool(self, value: bool) -> Result, E> { - Ok(InputValue::boolean(value)) + Ok(InputValue::scalar(value)) } fn visit_i64(self, value: i64) -> Result, E> @@ -104,14 +104,14 @@ where E: de::Error, { if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) { - Ok(InputValue::int(value as i32)) + Ok(InputValue::scalar(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)) + Ok(InputValue::scalar(value as f64)) } } @@ -127,12 +127,12 @@ where // must parse large integers as floating point otherwise // we would error on transferring large floating point // numbers. - Ok(InputValue::float(value as f64)) + Ok(InputValue::scalar(value as f64)) } } fn visit_f64(self, value: f64) -> Result, E> { - Ok(InputValue::float(value)) + Ok(InputValue::scalar(value)) } fn visit_str(self, value: &str) -> Result, E> @@ -143,7 +143,7 @@ where } fn visit_string(self, value: String) -> Result, E> { - Ok(InputValue::string(value)) + Ok(InputValue::scalar(value)) } fn visit_none(self) -> Result, E> { @@ -313,6 +313,7 @@ where mod tests { use super::{ExecutionError, GraphQLError}; use ast::InputValue; + use executor::ExecutionError; use serde_json::from_str; use serde_json::to_string; use {FieldError, Value}; @@ -322,7 +323,7 @@ mod tests { fn int() { assert_eq!( from_str::>("1235").unwrap(), - InputValue::int(1235) + InputValue::scalar(1235) ); } @@ -330,12 +331,12 @@ mod tests { fn float() { assert_eq!( from_str::>("2.0").unwrap(), - InputValue::float(2.0) + InputValue::scalar(2.0) ); // large value without a decimal part is also float assert_eq!( from_str::>("123567890123").unwrap(), - InputValue::float(123567890123.0) + InputValue::scalar(123567890123.0) ); } diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 608e0fd16..344aebb7a 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -8,12 +8,12 @@ graphql_scalar!(Url where Scalar = { description: "Url" resolve(&self) -> Value { - Value::string(self.as_str()) + Value::scalar(self.as_str().to_owned()) } from_input_value(v: &InputValue) -> Option { - v.as_string_value() - .and_then(|s| Url::parse(s).ok()) + v.as_scalar_value() + .and_then(|s: &String| Url::parse(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -29,7 +29,7 @@ mod test { #[test] fn url_from_input_value() { let raw = "https://example.net/"; - let input: ::InputValue = ::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(); diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index ed0cf85a2..a59cc2e30 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -7,12 +7,12 @@ graphql_scalar!(Uuid where Scalar = { description: "Uuid" resolve(&self) -> Value { - Value::string(&self.to_string()) + Value::scalar(self.to_string()) } from_input_value(v: &InputValue) -> Option { - v.as_string_value() - .and_then(|s| Uuid::parse_str(s).ok()) + v.as_scalar_value() + .and_then(|s: &String| Uuid::parse_str(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -32,7 +32,7 @@ mod test { #[test] fn uuid_from_input_value() { let raw = "123e4567-e89b-12d3-a456-426655440000"; - let input: ::InputValue = ::InputValue::string(raw.to_string()); + let input: ::InputValue = ::InputValue::scalar(raw.to_string()); let parsed: Uuid = ::FromInputValue::from_input_value(&input).unwrap(); let id = Uuid::parse_str(raw).unwrap(); diff --git a/juniper/src/macros/tests/args.rs b/juniper/src/macros/tests/args.rs index 3c74ea751..09c65899c 100644 --- a/juniper/src/macros/tests/args.rs +++ b/juniper/src/macros/tests/args.rs @@ -1,7 +1,7 @@ use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::{Value, DefaultScalarValue}; +use value::{DefaultScalarValue, Value}; struct Root; @@ -129,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::() + .expect("name is not a string") + == field_name + }).next() .expect("Field not found") .as_object_value() .expect("Field is not an object"); @@ -172,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()), ( @@ -183,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(), )) ); }); @@ -205,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()), ( @@ -216,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(), )) ); }); @@ -238,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()), ( @@ -249,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()), ( @@ -275,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(), )) ); }); @@ -297,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()), ( @@ -308,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()), ( @@ -334,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(), )) ); }); @@ -356,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", @@ -367,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(), )) ); }); @@ -389,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", @@ -400,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", @@ -426,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(), )) ); }); @@ -448,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", @@ -459,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", @@ -485,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(), )) ); }); @@ -507,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(), )) ); }); @@ -533,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(), )) ); }); @@ -578,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(), )) ); }); @@ -623,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(), )) ); }); @@ -649,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(), )) ); }); @@ -694,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(), )) ); }); @@ -739,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(), )) ); }); diff --git a/juniper/src/macros/tests/field.rs b/juniper/src/macros/tests/field.rs index e3e82e9fe..4294a5f70 100644 --- a/juniper/src/macros/tests/field.rs +++ b/juniper/src/macros/tests/field.rs @@ -73,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(); @@ -104,7 +104,7 @@ where .expect("Field not an object") .get_field_value("name") .expect("name field missing from field") - .as_string_value() + .as_scalar_value::() .expect("name is not a string") == field_name }).next() @@ -122,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"), @@ -141,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"), @@ -160,15 +160,15 @@ 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::boolean(false)) + Some(&Value::scalar(false)) ); assert_eq!( field.get_field_value("deprecationReason"), @@ -182,15 +182,15 @@ fn introspect_interface_field_description() { run_field_info_query("Interface", "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::boolean(false)) + Some(&Value::scalar(false)) ); assert_eq!( field.get_field_value("deprecationReason"), @@ -204,16 +204,16 @@ fn introspect_object_field_deprecated() { run_field_info_query("Root", "deprecated", |field| { assert_eq!( field.get_field_value("name"), - Some(&Value::string("deprecated")) + 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)) + Some(&Value::scalar(true)) ); assert_eq!( field.get_field_value("deprecationReason"), - Some(&Value::string("Deprecation reason")) + Some(&Value::scalar("Deprecation reason")) ); }); } @@ -223,16 +223,16 @@ fn introspect_interface_field_deprecated() { run_field_info_query("Interface", "deprecated", |field| { assert_eq!( field.get_field_value("name"), - Some(&Value::string("deprecated")) + 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)) + Some(&Value::scalar(true)) ); assert_eq!( field.get_field_value("deprecationReason"), - Some(&Value::string("Deprecation reason")) + Some(&Value::scalar("Deprecation reason")) ); }); } @@ -242,19 +242,19 @@ fn introspect_object_field_deprecated_descr() { run_field_info_query("Root", "deprecatedDescr", |field| { assert_eq!( field.get_field_value("name"), - Some(&Value::string("deprecatedDescr")) + 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::boolean(true)) + Some(&Value::scalar(true)) ); assert_eq!( field.get_field_value("deprecationReason"), - Some(&Value::string("Deprecation reason")) + Some(&Value::scalar("Deprecation reason")) ); }); } @@ -264,19 +264,19 @@ fn introspect_interface_field_deprecated_descr() { run_field_info_query("Interface", "deprecatedDescr", |field| { assert_eq!( field.get_field_value("name"), - Some(&Value::string("deprecatedDescr")) + 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::boolean(true)) + Some(&Value::scalar(true)) ); assert_eq!( field.get_field_value("deprecationReason"), - Some(&Value::string("Deprecation reason")) + Some(&Value::scalar("Deprecation reason")) ); }); } diff --git a/juniper/src/macros/tests/interface.rs b/juniper/src/macros/tests/interface.rs index a45474268..162ed01d4 100644 --- a/juniper/src/macros/tests/interface.rs +++ b/juniper/src/macros/tests/interface.rs @@ -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(), )) diff --git a/juniper/src/macros/tests/object.rs b/juniper/src/macros/tests/object.rs index fe046f663..898592e45 100644 --- a/juniper/src/macros/tests/object.rs +++ b/juniper/src/macros/tests/object.rs @@ -169,7 +169,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(); @@ -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,18 +262,18 @@ 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(), )])) @@ -291,18 +291,18 @@ 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(), )])) @@ -320,18 +320,18 @@ 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(), )])) @@ -349,18 +349,18 @@ 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(), )])) @@ -378,18 +378,18 @@ 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(), )])) diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index a771130db..c0f90f17d 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -22,11 +22,11 @@ Syntax to validate: graphql_scalar!(DefaultName where Scalar = { resolve(&self) -> Value { - Value::int(self.0) + Value::scalar(self.0) } from_input_value(v: &InputValue) -> Option { - v.as_int_value().map(|i| DefaultName(i)) + v.as_scalar_value().map(|i: &i32| DefaultName(*i)) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -35,11 +35,11 @@ graphql_scalar!(DefaultName where Scalar = { }); graphql_scalar!(OtherOrder where Scalar = { resolve(&self) -> Value { - Value::int(self.0) + Value::scalar(self.0) } from_input_value(v: &InputValue) -> Option { - v.as_int_value().map(|i| OtherOrder(i)) + v.as_scalar_value().map(|i: &i32| OtherOrder(*i)) } @@ -49,11 +49,11 @@ graphql_scalar!(OtherOrder where Scalar = { }); graphql_scalar!(Named as "ANamedScalar" where Scalar = { resolve(&self) -> Value { - Value::int(self.0) + Value::scalar(self.0) } from_input_value(v: &InputValue) -> Option { - v.as_int_value().map(|i| Named(i)) + v.as_scalar_value().map(|i: &i32| Named(*i)) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -65,11 +65,11 @@ graphql_scalar!(ScalarDescription where Scalar = { 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 { - v.as_int_value().map(|i| ScalarDescription(i)) + v.as_scalar_value().map(|i: &i32| ScalarDescription(*i)) } from_str<'a>(value: ScalarToken<'a>) -> Result> { @@ -134,7 +134,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())); }); } @@ -151,7 +151,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())); }); } @@ -168,7 +168,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())); }); } @@ -187,11 +187,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")) ); }); } diff --git a/juniper/src/macros/tests/union.rs b/juniper/src/macros/tests/union.rs index aecccf0d5..4b0cd5703 100644 --- a/juniper/src/macros/tests/union.rs +++ b/juniper/src/macros/tests/union.rs @@ -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(), )) diff --git a/juniper/src/parser/lexer.rs b/juniper/src/parser/lexer.rs index e872be3c1..b045cbe1a 100644 --- a/juniper/src/parser/lexer.rs +++ b/juniper/src/parser/lexer.rs @@ -16,7 +16,11 @@ 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), diff --git a/juniper/src/parser/tests/document.rs b/juniper/src/parser/tests/document.rs index 8ac85c706..653becf1c 100644 --- a/juniper/src/parser/tests/document.rs +++ b/juniper/src/parser/tests/document.rs @@ -71,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), ), )], }, diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index 160842fdb..066c38ef5 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -76,7 +76,7 @@ fn input_value_literals() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(3, 0, 3), - InputValue::int(123) + InputValue::scalar(123) ) ); assert_eq!( @@ -84,7 +84,7 @@ fn input_value_literals() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(6, 0, 6), - InputValue::float(123.45) + InputValue::scalar(123.45) ) ); assert_eq!( @@ -92,7 +92,7 @@ fn input_value_literals() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(4, 0, 4), - InputValue::boolean(true) + InputValue::scalar(true) ) ); assert_eq!( @@ -100,7 +100,7 @@ fn input_value_literals() { Spanning::start_end( &SourcePosition::new(0, 0, 0), &SourcePosition::new(5, 0, 5), - InputValue::boolean(false) + InputValue::scalar(false) ) ); assert_eq!( @@ -108,7 +108,7 @@ fn input_value_literals() { 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")]; @@ -147,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), @@ -156,12 +156,12 @@ 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), ), ]), ), @@ -198,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), ), ), ( @@ -219,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"), ), )]), ), diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs index 1fe2f4998..27e60f682 100644 --- a/juniper/src/parser/value.rs +++ b/juniper/src/parser/value.rs @@ -91,14 +91,14 @@ where .. }, _, - ) => Ok(parser.next()?.map(|_| InputValue::boolean(true))), + ) => Ok(parser.next()?.map(|_| InputValue::scalar(true))), ( &Spanning { item: Token::Name("false"), .. }, _, - ) => Ok(parser.next()?.map(|_| InputValue::boolean(false))), + ) => Ok(parser.next()?.map(|_| InputValue::scalar(false))), ( &Spanning { item: Token::Name("null"), diff --git a/juniper/src/tests/introspection_tests.rs b/juniper/src/tests/introspection_tests.rs index b04bd281f..4b1652c19 100644 --- a/juniper/src/tests/introspection_tests.rs +++ b/juniper/src/tests/introspection_tests.rs @@ -30,7 +30,7 @@ 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(), @@ -61,7 +61,7 @@ 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() ), @@ -92,8 +92,8 @@ 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(), ), @@ -127,8 +127,8 @@ 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(), ), @@ -162,10 +162,10 @@ 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(), @@ -218,8 +218,8 @@ 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") + .as_scalar_value::() + .expect("'name' not a string") as &str }).collect::>(); assert_eq!(possible_types, vec!["Human", "Droid"].into_iter().collect()); diff --git a/juniper/src/tests/query_tests.rs b/juniper/src/tests/query_tests.rs index bf2d1a866..30ed7c1c5 100644 --- a/juniper/src/tests/query_tests.rs +++ b/juniper/src/tests/query_tests.rs @@ -23,7 +23,7 @@ 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() ), @@ -53,8 +53,8 @@ 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(), ), @@ -80,8 +80,8 @@ 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(), ), @@ -117,23 +117,23 @@ 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(), ), @@ -178,44 +178,44 @@ 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(), ), 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(), ), @@ -226,13 +226,13 @@ fn test_hero_name_and_friends_and_friends_of_friends() { ), 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"), ]), ), ( @@ -241,19 +241,19 @@ 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(), ), Value::object( vec![( "name", - Value::string("Leia Organa"), + Value::scalar("Leia Organa"), )].into_iter() .collect(), ), Value::object( - vec![("name", Value::string("R2-D2"))] + vec![("name", Value::scalar("R2-D2"))] .into_iter() .collect(), ), @@ -264,13 +264,13 @@ fn test_hero_name_and_friends_and_friends_of_friends() { ), 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"), ]), ), ( @@ -279,22 +279,22 @@ 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(), ), 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(), ), @@ -330,7 +330,7 @@ fn test_query_name() { vec![( "human", Value::object( - vec![("name", Value::string("Luke Skywalker"))] + vec![("name", Value::scalar("Luke Skywalker"))] .into_iter() .collect(), ), @@ -356,7 +356,7 @@ fn test_query_alias_single() { vec![( "luke", Value::object( - vec![("name", Value::string("Luke Skywalker"))] + vec![("name", Value::scalar("Luke Skywalker"))] .into_iter() .collect(), ), @@ -387,7 +387,7 @@ fn test_query_alias_multiple() { ( "luke", Value::object( - vec![("name", Value::string("Luke Skywalker"))] + vec![("name", Value::scalar("Luke Skywalker"))] .into_iter() .collect(), ), @@ -395,7 +395,7 @@ fn test_query_alias_multiple() { ( "leia", Value::object( - vec![("name", Value::string("Leia Organa"))] + vec![("name", Value::scalar("Leia Organa"))] .into_iter() .collect(), ), @@ -433,8 +433,8 @@ 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(), ), @@ -443,8 +443,8 @@ fn test_query_alias_multiple_with_fragment() { "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(), ), @@ -464,7 +464,7 @@ fn test_query_name_variable() { let schema: RootNode = RootNode::new(&database, EmptyMutation::::new()); - let vars = vec![("someId".to_owned(), InputValue::string("1000"))] + let vars = vec![("someId".to_owned(), InputValue::scalar("1000"))] .into_iter() .collect(); @@ -475,7 +475,7 @@ fn test_query_name_variable() { vec![( "human", Value::object( - vec![("name", Value::string("Luke Skywalker"))] + vec![("name", Value::scalar("Luke Skywalker"))] .into_iter() .collect(), ), @@ -494,7 +494,7 @@ fn test_query_name_invalid_variable() { let schema: RootNode = RootNode::new(&database, EmptyMutation::::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(); @@ -525,20 +525,20 @@ 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() @@ -578,9 +578,9 @@ 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(), ), @@ -614,8 +614,8 @@ 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(), ), @@ -646,7 +646,7 @@ fn test_object_typename() { vec![( "human", Value::object( - vec![("__typename", Value::string("Human"))] + vec![("__typename", Value::scalar("Human"))] .into_iter() .collect(), ), diff --git a/juniper/src/tests/type_info_tests.rs b/juniper/src/tests/type_info_tests.rs index 225d0a96b..11166453c 100644 --- a/juniper/src/tests/type_info_tests.rs +++ b/juniper/src/tests/type_info_tests.rs @@ -80,9 +80,9 @@ fn test_node() { 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() ), diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index 11dd53616..e247dd7ef 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -378,7 +378,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; } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 8ac2924a6..fb9e2d36e 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -33,7 +33,7 @@ impl Deref for ID { graphql_scalar!(ID as "ID" where Scalar = { resolve(&self) -> Value { - Value::string(&self.0) + Value::scalar(self.0.clone()) } from_input_value(v: &InputValue) -> Option { @@ -59,7 +59,7 @@ graphql_scalar!(ID as "ID" where Scalar = { graphql_scalar!(String as "String" where Scalar = { resolve(&self) -> Value { - Value::string(self) + Value::scalar(self.clone()) } from_input_value(v: &InputValue) -> Option { @@ -182,7 +182,7 @@ where _: Option<&[Selection]>, _: &Executor, ) -> Value { - Value::string(self) + Value::scalar(String::from(*self)) } } @@ -191,13 +191,13 @@ where S: ScalarValue, { fn to_input_value(&self) -> InputValue { - InputValue::string(self) + InputValue::scalar(String::from(*self)) } } graphql_scalar!(bool as "Boolean" where Scalar = { resolve(&self) -> Value { - Value::boolean(*self) + Value::scalar(*self) } from_input_value(v: &InputValue) -> Option { @@ -215,7 +215,7 @@ graphql_scalar!(bool as "Boolean" where Scalar = { graphql_scalar!(i32 as "Int" where Scalar = { resolve(&self) -> Value { - Value::int(*self) + Value::scalar(*self) } from_input_value(v: &InputValue) -> Option { @@ -238,7 +238,7 @@ graphql_scalar!(i32 as "Int" where Scalar = { graphql_scalar!(f64 as "Float" where Scalar = { resolve(&self) -> Value { - Value::float(*self) + Value::scalar(*self) } from_input_value(v: &InputValue) -> Option { diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index b4f8036cd..d769d8799 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -718,7 +718,7 @@ where let errs = validate(r, m, q, factory); if !errs.is_empty() { - print_errors(&errs, q); + print_errors(&errs); panic!("Expected rule to pass, but errors found"); } } @@ -753,16 +753,16 @@ pub fn expect_fails_rule_with_schema<'a, Q, M, V, F, S>( panic!("Expected rule to fail, but no errors were found"); } else if errs != expected_errors { println!("==> Expected errors:"); - print_errors(expected_errors, q); + print_errors(expected_errors); println!("\n==> Actual errors:"); - print_errors(&errs, q); + print_errors(&errs); panic!("Unexpected set of errors found"); } } -fn print_errors(errs: &[RuleError], query: &str) { +fn print_errors(errs: &[RuleError]) { for err in errs { for p in err.locations() { print!("[{:>3},{:>3},{:>3}] ", p.index(), p.line(), p.column()); diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index e7785580d..be27eb9ad 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -37,21 +37,25 @@ where } /// 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) } @@ -66,6 +70,7 @@ where Value::Object(o) } + /// Construct a scalar value pub fn scalar(s: T) -> Self where T: Into, @@ -95,6 +100,7 @@ where } /// 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 where for<'a> &'a S: ScalarRefValue<'a>, @@ -135,6 +141,7 @@ where } /// 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>, @@ -270,31 +277,31 @@ mod tests { #[test] fn value_macro_string() { let s: Value = graphql_value!("test"); - assert_eq!(s, Value::string("test")); + assert_eq!(s, Value::scalar("test")); } #[test] fn value_macro_int() { let s: Value = graphql_value!(123); - assert_eq!(s, Value::int(123)); + assert_eq!(s, Value::scalar(123)); } #[test] fn value_macro_float() { let s: Value = graphql_value!(123.5); - assert_eq!(s, Value::float(123.5)); + assert_eq!(s, Value::scalar(123.5)); } #[test] fn value_macro_boolean() { let s: Value = graphql_value!(false); - assert_eq!(s, Value::boolean(false)); + assert_eq!(s, Value::scalar(false)); } #[test] fn value_macro_option() { let s: Value = graphql_value!(Some("test")); - assert_eq!(s, Value::string("test")); + assert_eq!(s, Value::scalar("test")); let s: Value = graphql_value!(None); assert_eq!(s, Value::null()); } @@ -305,18 +312,18 @@ mod tests { assert_eq!( s, Value::list(vec![ - Value::int(123), - Value::string("Test"), - Value::boolean(false), + Value::scalar(123), + Value::scalar("Test"), + Value::scalar(false), ]) ); let s: Value = graphql_value!([123, [456], 789]); assert_eq!( s, Value::list(vec![ - Value::int(123), - Value::list(vec![Value::int(456)]), - Value::int(789), + Value::scalar(123), + Value::list(vec![Value::scalar(456)]), + Value::scalar(789), ]) ); } @@ -327,7 +334,7 @@ mod tests { assert_eq!( s, Value::object( - vec![("key", Value::int(123)), ("next", Value::boolean(true))] + vec![("key", Value::scalar(123)), ("next", Value::scalar(true))] .into_iter() .collect(), ) diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index d52db5082..468a941bf 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -162,11 +162,12 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { description: #descr, deprecation_reason: #depr, }, - }); + }; + values.push(value); // 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,7 +178,7 @@ 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()), }); } @@ -220,7 +221,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { 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_string_value()) { + match v.as_enum_value().or_else(|| v.as_scalar_value().map(|s: &String| s as &str)) { #(#from_inputs)* _ => None, } From 70a4bfcbe308f527d909bd8d70035b9d4c911804 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 19 Sep 2018 15:36:21 +0200 Subject: [PATCH 07/20] Rewrite all the macros to support custom scalar value representations This commit changes all provided macros to 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 defaulting to the `DefaultScalarValue` type provided by juniper if no scalar value representation is specified. This is done with usability and backwards compatibility in mind. To implement this feature more or less the whole macro code is rewritten. Now there is a clear separation between parsing the custom dsl and expanding the final impls. Also the new code tries to share much of the implementation between the different macros. --- juniper/src/executor/mod.rs | 4 +- juniper/src/executor_tests/executor.rs | 8 +- juniper/src/macros/args.rs | 147 ---- juniper/src/macros/common.rs | 627 ++++++++++++++++++ juniper/src/macros/field.rs | 103 --- juniper/src/macros/interface.rs | 390 +++++------ juniper/src/macros/mod.rs | 6 +- juniper/src/macros/object.rs | 430 ++++++------ juniper/src/macros/scalar.rs | 184 ++--- juniper/src/macros/tests/field.rs | 4 +- juniper/src/macros/tests/object.rs | 16 +- juniper/src/macros/union.rs | 242 +++---- juniper/src/parser/tests/value.rs | 2 +- juniper/src/schema/schema.rs | 596 +++++------------ .../rules/provided_non_null_arguments.rs | 34 +- 15 files changed, 1371 insertions(+), 1422 deletions(-) delete mode 100644 juniper/src/macros/args.rs create mode 100644 juniper/src/macros/common.rs delete mode 100644 juniper/src/macros/field.rs diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 789fab56c..c580115f1 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -22,7 +22,7 @@ use schema::model::{RootNode, SchemaType, TypeType}; use types::base::GraphQLType; use types::name::Name; -use value::{ParseScalarValue, ScalarRefValue, ScalarValue}; +use value::{ParseScalarValue, ScalarRefValue, ScalarValue, DefaultScalarValue}; mod look_ahead; @@ -207,7 +207,7 @@ impl FieldError { } /// The result of resolving the value of a field of type `T` -pub type FieldResult = Result>; +pub type FieldResult = Result>; /// The result of resolving an unspecified field pub type ExecutionResult = Result, FieldError>; diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index eb124dabf..b27140c24 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -395,14 +395,14 @@ mod dynamic_context_switching { executor.context().items.get(&key).map(|c| (c, ItemRef)) } - field item_res(&executor, key: i32) -> FieldResult<(&InnerContext, ItemRef), __S> { + field item_res(&executor, key: i32) -> FieldResult<(&InnerContext, ItemRef)> { let res = executor.context().items.get(&key) .ok_or(format!("Could not find key {}", key)) .map(|c| (c, ItemRef))?; Ok(res) } - field item_res_opt(&executor, key: i32) -> FieldResult, __S> { + field item_res_opt(&executor, key: i32) -> FieldResult> { if key > 100 { Err(format!("Key too large: {}", key))?; } @@ -723,8 +723,8 @@ mod propagates_errors_to_nullable_fields { graphql_object!(Inner: () |&self| { field nullable_field() -> Option { Some(Inner) } field non_nullable_field() -> Inner { Inner } - field nullable_error_field() -> FieldResult, __S> { Err("Error for nullableErrorField")? } - field non_nullable_error_field() -> FieldResult<&str, __S> { Err("Error for nonNullableErrorField")? } + field nullable_error_field() -> FieldResult> { Err("Error for nullableErrorField")? } + field non_nullable_error_field() -> FieldResult<&str> { Err("Error for nonNullableErrorField")? } field custom_error_field() -> Result<&str, CustomError> { Err(CustomError::NotFound) } }); diff --git a/juniper/src/macros/args.rs b/juniper/src/macros/args.rs deleted file mode 100644 index 60ce9051b..000000000 --- a/juniper/src/macros/args.rs +++ /dev/null @@ -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)* )) - }; -} diff --git a/juniper/src/macros/common.rs b/juniper/src/macros/common.rs new file mode 100644 index 000000000..b37da3fa7 --- /dev/null +++ b/juniper/src/macros/common.rs @@ -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 { + () => {$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 = {}, + }, + 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 = {}, + }, + 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 = {}, + }, + 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 = {}, + }, + 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))* + }; +} diff --git a/juniper/src/macros/field.rs b/juniper/src/macros/field.rs deleted file mode 100644 index eff0add6c..000000000 --- a/juniper/src/macros/field.rs +++ /dev/null @@ -1,103 +0,0 @@ -#[doc(hidden)] -#[macro_export(local_inner_macros)] -macro_rules! __graphql__build_field_matches { - // field deprecated (...) -> as { ... } - ( - $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 (...) -> { ... } - ( - $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 (...) -> as { ... } - ( - $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 (...) -> { ... } - ( - $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); - }; -} diff --git a/juniper/src/macros/interface.rs b/juniper/src/macros/interface.rs index 3b0bca8ef..bb6387f64 100644 --- a/juniper/src/macros/interface.rs +++ b/juniper/src/macros/interface.rs @@ -87,251 +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 (...) -> as { ... } ( - @ 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 )* + @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,)* + },)*], ) => { - $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 )*); - }; + __juniper_impl_trait!( + impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name { + type Context = $ctx; + type TypeInfo = (); - // field deprecated (...) -> { ... } - ( - @ 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 )*); - }; + fn name(_ : &Self::TypeInfo) -> Option<&str> { + Some($($outname)*) + } - // field (...) -> as { ... } - ( - @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 )*); - }; + 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(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() + } - // field (...) -> { ... } - ( - @ 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: - ( - @ gather_meta, - ($reg:expr, $acc:expr, $info:expr, $descr:expr), - description : $value:tt $( $rest:tt )* - ) => { - $descr = Some(graphql_interface!(@as_expr, $value)); + #[allow(unused_variables)] + fn resolve_field( + &$main_self, + info: &Self::TypeInfo, + field: &str, + args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>, + executor: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context> + ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { + $( + if field == &$crate::to_camel_case(stringify!($fn_name)) { + let result: $return_ty = (|| { + $( + let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(stringify!($arg_name))) + .expect(concat!( + "Argument ", + stringify!($arg_name), + " missing - validation must have failed" + )); + )* + $( + let $executor = &executor; + )* + $body + })(); + + 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()) + } + }); + } + )* + + panic!("Field {} not found on type {}", field, $($outname)*) + } - graphql_interface!(@gather_meta, ($reg, $acc, $info, $descr), $( $rest )*) - }; + #[allow(unused_variables)] + fn concrete_type_name(&$main_self, context: &Self::Context, _info: &Self::TypeInfo) -> String { + $(let $resolver_ctx = &context;)* - // instance_resolvers: | | [...] - ( - @ 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 )*) - }; + $( + if ($resolver_expr as ::std::option::Option<$resolver_src>).is_some() { + return + <$resolver_src as $crate::GraphQLType<_>>::name(&()).unwrap().to_owned(); + } + )* - // instance_resolvers: | | [...] - ( - @ concrete_type_name, - ($outname:tt, $ctxtarg:ident, $ctxttype:ty), - instance_resolvers : | $ctxtvar:pat - | { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )* - ) => { - let $ctxtvar = &$ctxtarg; + panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + } - $( - if ($resolver as Option<$srctype>).is_some() { - return (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap().to_owned(); + fn resolve_into_type( + &$main_self, + _info: &Self::TypeInfo, + type_name: &str, + _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>, + executor: &$crate::Executor<__juniper_insert_generic!($($scalar)*), Self::Context>, + ) -> $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); + } + )* + + panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + } } - )* - - __graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname); + ); }; - // instance_resolvers: | | ( - @ resolve_into_type, - ($outname:tt, $typenamearg:ident, $execarg:ident, $ctxttype:ty), - instance_resolvers : | $ctxtvar:pat - | { $( $srctype:ty => $resolver:expr ),* $(,)* } $( $rest:tt )* + @parse, + meta = {$($meta:tt)*}, + rest = $($rest:tt)* ) => { - let $ctxtvar = &$execarg.context(); - - $( - if $typenamearg == (<$srctype as $crate::GraphQLType<__S>>::name(&())).unwrap() { - return $execarg.resolve(&(), &$resolver); - } - )* - - __graphql__panic!("Concrete type not handled by instance resolvers on {}", $outname); + __juniper_parse_field_list!( + success_callback = graphql_interface, + additional_parser = { + callback = __juniper_parse_instance_resolver, + header = {}, + }, + meta = {$($meta)*}, + items = [], + rest = $($rest)* + ); }; - ( @ $mfn:ident, $args:tt, $first:tt $($rest:tt)* ) => { - graphql_interface!(@ $mfn, $args, $($rest)*); + (@$($stuff:tt)*) => { + compile_error!("Invalid syntax for `graphql_interface!`"); }; - ( @ $mfn:ident, $buildargs:tt, ) => {}; - ( - ( $($lifetime:tt),* ) $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { - $( $items:tt )* - } + $($rest:tt)* ) => { - graphql_interface!(@as_item, impl<$($lifetime, )* __S> $crate::GraphQLType<__S> for $name - where __S: $crate::ScalarValue, - for<'__b> &'__b __S: $crate::ScalarRefValue<'__b> - { - 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, __S> - ) -> $crate::meta::MetaType<'r, __S> - where __S: 'r - { - let mut fields = Vec::new(); - let mut description = None; - graphql_interface!( - @ gather_meta, (registry, fields, info, description), $($items)* - ); - let mut mt = registry.build_interface_type::<$name>(&(), &fields); - - if let Some(description) = description { - mt = mt.description(description); - } - - mt.into_meta() - } - - #[allow(unused_variables)] - #[allow(unused_mut)] - fn resolve_field( - &$mainself, - info: &(), - field: &str, - args: &$crate::Arguments<__S>, - mut executor: &$crate::Executor<__S, Self::Context> - ) -> $crate::ExecutionResult<__S> { - __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)*); - } - - fn resolve_into_type( - &$mainself, - _: &(), - type_name: &str, - _: Option<&[$crate::Selection<__S>]>, - executor: &$crate::Executor<__S, Self::Context>, - ) - -> $crate::ExecutionResult<__S> - { - graphql_interface!( - @ resolve_into_type, - ($outname, type_name, executor, $ctxt), - $($items)*); - } - }); - }; + __juniper_parse_object_header!( + callback = graphql_interface, + rest = $($rest)* + ); + } - ( - <$($lifetime:tt),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { - $( $items:tt )* - } - ) => { - graphql_interface!( - ($($lifetime),*) $name : $ctxt as $outname | &$mainself | { $( $items )* }); - }; - ( - $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { - $( $items:tt )* - } - ) => { - graphql_interface!(() $name : $ctxt as $outname | &$mainself | { $( $items )* }); - }; - - ( - $name:ty : $ctxt:ty | &$mainself:ident | { - $( $items:tt )* - } - ) => { - graphql_interface!(() $name : $ctxt as (__graphql__stringify!($name)) | &$mainself | { $( $items )* }); - }; } diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 5347fbac6..772e60bbe 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -18,6 +18,8 @@ macro_rules! __graphql__vec { ($($t:tt)*) => ( vec!($($t)*) ); } +#[macro_use] +mod common; #[macro_use] mod object; #[macro_use] @@ -25,10 +27,6 @@ mod interface; #[macro_use] mod scalar; #[macro_use] -mod args; -#[macro_use] -mod field; -#[macro_use] mod union; #[cfg(test)] diff --git a/juniper/src/macros/object.rs b/juniper/src/macros/object.rs index f77096944..9c89f3fda 100644 --- a/juniper/src/macros/object.rs +++ b/juniper/src/macros/object.rs @@ -120,8 +120,8 @@ even have to be backed by a trait! ## Emitting errors -`FieldResult` is a type alias for `Result>`, where -`FieldResult` is a tuple that contains an error message and optionally a +`FieldResult` is a type alias for `Result>`, 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 @@ -137,11 +137,11 @@ automatically via the `?` operator, or you can construct them yourself using struct User { id: String } graphql_object!(User: () |&self| { - field id() -> FieldResult<&String, __S> { + field id() -> FieldResult<&String> { Ok(&self.id) } - field name() -> FieldResult<&String, __S> { + field name() -> FieldResult<&String> { Err("Does not have a name".to_owned())? } }); @@ -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 = ` 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 = |&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... } - ExposedType: ContextType as "ExposedName" |&self| { items... } + ExposedType: ContextType as "ExposedName" where Scalar = |&self| { items... } + ExposedType: ContextType as "ExposedName" where Scalar = SpecificType |&self| { items... } ``` +The following parts are optional: +* ``, if not set no generics are defined +* `as "ExposedName"`, if not set `ExposedType` is used as name +* `where Scalar = ` / `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,225 +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 (...) -> as { ... } - ( - @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 )* - ) => { - $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_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*); - }; - - // field deprecated (...) -> { ... } ( - @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 )* + @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) - .deprecated($reason), - $info, - $args)); - - graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*); - }; + __juniper_impl_trait!( + impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name { + type Context = $ctx; + type TypeInfo = (); - // field (...) -> as { ... } - ( - @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 )*); - }; + fn name(_ : &Self::TypeInfo) -> Option<&str> { + Some($($outname)*) + } - // field (...) -> { ... } - ( - @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 )*); - }; + 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(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() + } - // 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)); + fn concrete_type_name(&self, _: &Self::Context, _: &Self::TypeInfo) -> String { + $($outname)*.to_owned() + } - graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*) + #[allow(unused_variables)] + fn resolve_field( + &$main_self, + info: &Self::TypeInfo, + field: &str, + args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>, + executor: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context> + ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { + $( + if field == &$crate::to_camel_case(stringify!($fn_name)) { + let result: $return_ty = (|| { + $( + let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(stringify!($arg_name))) + .expect(concat!( + "Argument ", + stringify!($arg_name), + " missing - validation must have failed" + )); + )* + $( + let $executor = &executor; + )* + $body + })(); + + 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()) + } + }); + } + )* + + panic!("Field {} not found on type {}", field, $($outname)*); + } + } + ); }; - // interfaces: [...] ( - @gather_object_meta, - $reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr, - interfaces : $value:tt $( $rest: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!(@assign_interfaces, $reg, $ifaces, $value); - - graphql_object!(@gather_object_meta, $reg, $acc, $info, $descr, $ifaces, $( $rest )*) + __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)* + ); }; - // eat commas ( - @gather_object_meta, - $reg:expr, $acc:expr, $info:expr, $descr:expr, $ifaces:expr, , $( $rest:tt )* + @parse_interfaces, + success_callback = $success_callback: ident, + additional_parser = {$($additional:tt)*}, + meta = { $($meta:tt)* }, + items = [$({$($items: tt)*},)*], + rest = interfaces: $($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>(&())),* - ]); + graphql_object!( + @parse_interfaces, + success_callback = $success_callback, + additional_parser = {$($additional)*}, + meta = { $($meta)* }, + items = [$({$($items)*},)*], + rest = $($rest)* + ); }; - ( @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)* + @parse, + meta = {$($meta:tt)*}, + rest = $($rest:tt)* ) => { - graphql_object!(@as_item, impl<$($lifetime,)* __S> $crate::GraphQLType<__S> for $name - where __S: $crate::ScalarValue, - for<'__b> &'__b __S: $crate::ScalarRefValue<'__b>, - { - 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, __S> - ) -> $crate::meta::MetaType<'r, __S> - where __S: 'r - { - let mut fields = Vec::new(); - let mut description = None; - let mut interfaces: Option> = None; - graphql_object!( - @gather_object_meta, - registry, fields, info, description, interfaces, $($items)* - ); - let mut mt = registry.build_object_type::<$name>(info, &fields); - - if let Some(description) = description { - mt = mt.description(description); - } - - if let Some(interfaces) = interfaces { - mt = mt.interfaces(&interfaces); - } - - mt.into_meta() - } - - fn concrete_type_name(&self, _: &Self::Context, _: &()) -> String { - $outname.to_owned() - } - - #[allow(unused_variables)] - #[allow(unused_mut)] - fn resolve_field( - &$mainself, - info: &(), - field: &str, - args: &$crate::Arguments<__S>, - executor: &$crate::Executor<__S, Self::Context> - ) - -> $crate::ExecutionResult<__S> - { - __graphql__build_field_matches!( - ($outname, $mainself, field, args, executor), - (), - $($items)*); - } - }); + __juniper_parse_field_list!( + success_callback = graphql_object, + additional_parser = { + callback = graphql_object, + header = {@parse_interfaces,}, + }, + meta = {$($meta)*}, + items = [], + rest = $($rest)* + ); }; - ( - <$( $lifetime:tt ),*> $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { - $( $items:tt )* - } - ) => { - graphql_object!( - ( $($lifetime),* ); $name; $ctxt; $outname; $mainself; $( $items )*); + (@$($stuff:tt)*) => { + compile_error!("Invalid syntax for `graphql_object!`"); }; ( - $name:ty : $ctxt:ty as $outname:tt | &$mainself:ident | { - $( $items:tt )* - } + $($rest:tt)* ) => { - graphql_object!( - ( ); $name; $ctxt; $outname; $mainself; $( $items )*); - }; + __juniper_parse_object_header!( + callback = graphql_object, + rest = $($rest)* + ); + } - ( - $name:ty : $ctxt:ty | &$mainself:ident | { - $( $items:tt )* - } - ) => { - graphql_object!( - ( ); $name; $ctxt; (__graphql__stringify!($name)); $mainself; $( $items )*); - }; } diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index f4c4f4836..da92184f1 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -44,49 +44,6 @@ usable as arguments and default values. #[macro_export(local_inner_macros)] macro_rules! graphql_scalar { ( @as_expr $e:expr) => { $e }; - ( - @insert_generic - <$generic:tt> - ) => { - $generic - }; - ( - @insert_generic - $scalar: ty - ) => { - $scalar - }; - - ( - @impl_trait - impl< <$generic:tt> > $impl_trait:tt for $name:ty { - $($body:tt)+ - } - ) => { - impl<$generic> $crate::$impl_trait<$generic> for $name - where $generic: $crate::ScalarValue, - for<'__b> &'__b $generic: $crate::ScalarRefValue<'__b> - { - $($body)+ - } - }; - - ( - @impl_trait - impl<$scalar:ty> $impl_trait:tt for $name:ty { - $($body:tt)+ - } - ) => { - impl $crate::$impl_trait<$scalar> for $name { - $($body)+ - } - }; - - - - - // Each of the @parse match arms accumulates data up to a call to @generate - // ( @generate, meta = { @@ -112,8 +69,7 @@ macro_rules! graphql_scalar { }, ) => { - graphql_scalar!( - @impl_trait + __juniper_impl_trait!( impl <$($scalar)+> GraphQLType for $name { type Context = (); type TypeInfo = (); @@ -124,10 +80,10 @@ macro_rules! graphql_scalar { fn meta<'r>( info: &Self::TypeInfo, - registry: &mut $crate::Registry<'r, graphql_scalar!(@insert_generic $($scalar)+)> - ) -> $crate::meta::MetaType<'r, graphql_scalar!(@insert_generic $($scalar)+)> - where for<'__b> &'__b graphql_scalar!(@insert_generic $($scalar)+): $crate::ScalarRefValue<'__b>, - graphql_scalar!(@insert_generic $($scalar)+): 'r + 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::(info); $( @@ -139,35 +95,32 @@ macro_rules! graphql_scalar { fn resolve( &$resolve_self_var, _: &(), - _: Option<&[$crate::Selection]>, - _: &$crate::Executor) -> $crate::Value { + _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)+)>]>, + _: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context>) -> $crate::Value<__juniper_insert_generic!($($scalar)+)> { $resolve_body } }); - graphql_scalar!( - @impl_trait + __juniper_impl_trait!( impl<$($scalar)+> ToInputValue for $name { - fn to_input_value(&$resolve_self_var) -> $crate::InputValue { + fn to_input_value(&$resolve_self_var) -> $crate::InputValue<__juniper_insert_generic!($($scalar)+)> { let v = $resolve_body; $crate::ToInputValue::to_input_value(&v) } } ); - graphql_scalar!( - @impl_trait + __juniper_impl_trait!( impl<$($scalar)+> FromInputValue for $name { fn from_input_value( - $from_input_value_arg: &$crate::InputValue + $from_input_value_arg: &$crate::InputValue<__juniper_insert_generic!($($scalar)+)> ) -> $from_input_value_result { $from_input_value_body } } ); - graphql_scalar!( - @impl_trait + __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 @@ -184,7 +137,7 @@ macro_rules! graphql_scalar { // No more items to parse ( - @parse, + @parse_functions, meta = { name = $name:ty, outname = {$($outname:tt)+}, @@ -211,7 +164,7 @@ macro_rules! graphql_scalar { }; ( - @parse, + @parse_functions, meta = { name = $name:ty, outname = {$($outname:tt)+}, @@ -226,7 +179,7 @@ macro_rules! graphql_scalar { }; ( - @parse, + @parse_functions, meta = { name = $name:ty, outname = {$($outname:tt)+}, @@ -241,7 +194,7 @@ macro_rules! graphql_scalar { }; ( - @parse, + @parse_functions, meta = { name = $name:ty, outname = {$($outname:tt)+}, @@ -258,7 +211,7 @@ macro_rules! graphql_scalar { // resolve(&self) -> Value { ... } ( - @parse, + @parse_functions, meta = {$($meta:tt)*}, $(resolve = {$($resolve_body:tt)+},)* $(from_input_value = {$($from_input_value_body:tt)+},)* @@ -266,7 +219,7 @@ macro_rules! graphql_scalar { rest = resolve(&$selfvar:ident) -> Value $body:block $($rest:tt)* ) => { graphql_scalar!( - @parse, + @parse_functions, meta = {$($meta)*}, resolve = { self_var = $selfvar, @@ -280,7 +233,7 @@ macro_rules! graphql_scalar { // from_input_value(arg: &InputValue) -> ... { ... } ( - @parse, + @parse_functions, meta = { $($meta:tt)* }, $(resolve = {$($resolve_body:tt)+})*, $(from_input_value = {$($from_input_value_body:tt)+},)* @@ -288,7 +241,7 @@ macro_rules! graphql_scalar { rest = from_input_value($arg:ident: &InputValue) -> $result:ty $body:block $($rest:tt)* ) => { graphql_scalar!( - @parse, + @parse_functions, meta = { $($meta)* }, $(resolve = {$($resolve_body)+},)* from_input_value = { @@ -303,7 +256,7 @@ macro_rules! graphql_scalar { // from_str(value: &str) -> Result ( - @parse, + @parse_functions, meta = { $($meta:tt)* }, $(resolve = {$($resolve_body:tt)+},)* $(from_input_value = {$($from_input_value_body:tt)+},)* @@ -311,7 +264,7 @@ macro_rules! graphql_scalar { rest = from_str<$from_str_lt: tt>($value_arg:ident: ScalarToken<$ignored_lt2: tt>) -> $result:ty $body:block $($rest:tt)* ) => { graphql_scalar!( - @parse, + @parse_functions, meta = { $($meta)* }, $(resolve = {$($resolve_body)+},)* $(from_input_value = {$($from_input_value_body)+},)* @@ -327,7 +280,7 @@ macro_rules! graphql_scalar { // description: ( - @parse, + @parse_functions, meta = { name = $name:ty, outname = {$($outname:tt)+}, @@ -339,7 +292,7 @@ macro_rules! graphql_scalar { rest = description: $descr:tt $($rest:tt)* ) => { graphql_scalar!( - @parse, + @parse_functions, meta = { name = $name, outname = {$($outname)+}, @@ -353,80 +306,35 @@ macro_rules! graphql_scalar { ); }; - // Entry point: - // RustName as "GrahpQLName" where scalar - ($name:ty as $outname:tt where Scalar = <$generic:tt> { $($items:tt)* }) => { - graphql_scalar!( - @parse, - meta = { - name = $name, - outname = {$outname}, - scalar = {<$generic>}, - }, - rest = $($items)* - ); - }; - - ($name:ty as $outname:tt where Scalar = $scalar: ty { $($items:tt)* }) => { - graphql_scalar!( - @parse, - meta = { - name = $name, - outname = {$outname}, - scalar = {$scalar}, - }, - rest = $($items)* - ); - }; - // Entry point: - // RustName as "GraphQLName" { ... } - ( $name:ty as $outname:tt { $( $items:tt )* }) => { - graphql_scalar!( - @parse, + ( + @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 = {<__S>}, + outname = {$($outname)*}, + scalar = {$($scalar)*}, }, - rest = $($items)* + rest = $($rest)* ); }; - - ($name:ty where Scalar = <$generic:tt> { $($items:tt)* }) => { - graphql_scalar!( - @parse, - meta = { - name = $name, - outname = {stringify!($name)}, - scalar = {<$generic>}, - }, - rest = $($items)* - ); + (@$($stuff:tt)*) => { + compile_error!("Invalid syntax for `graphql_scalar!`"); }; - ($name:ty where Scalar = $scalar: ty { $($items:tt)* }) => { - graphql_scalar!( - @parse, - meta = { - name = $name, - outname = {stringify!($name)}, - scalar = {$scalar}, - }, - rest = $($items)* - ) - }; - // Entry point - // RustName { ... } - ( $name:ty { $( $items:tt )* }) => { - graphql_scalar!( - @parse, - meta = { - name = $name, - outname = {stringify!($name)}, - scalar = {<__S>}, - }, - rest = $($items)* + ($($rest:tt)*) => { + __juniper_parse_object_header!( + callback = graphql_scalar, + rest = $($rest)* ); - }; + } } diff --git a/juniper/src/macros/tests/field.rs b/juniper/src/macros/tests/field.rs index 4294a5f70..862e2cb1e 100644 --- a/juniper/src/macros/tests/field.rs +++ b/juniper/src/macros/tests/field.rs @@ -31,11 +31,11 @@ graphql_object!(Root: () |&self| { field deprecated "Deprecation reason" deprecated_descr() -> i32 as "Field description" { 0 } - field with_field_result() -> FieldResult { Ok(0) } + field with_field_result() -> FieldResult { Ok(0) } field with_return() -> i32 { return 0; } - field with_return_field_result() -> FieldResult { return Ok(0); } + field with_return_field_result() -> FieldResult { return Ok(0); } interfaces: [Interface] }); diff --git a/juniper/src/macros/tests/object.rs b/juniper/src/macros/tests/object.rs index 898592e45..f7323f18b 100644 --- a/juniper/src/macros/tests/object.rs +++ b/juniper/src/macros/tests/object.rs @@ -4,7 +4,7 @@ use ast::InputValue; use executor::{Context, FieldResult}; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::{Object, Value, DefaultScalarValue}; +use value::{DefaultScalarValue, Object, Value}; /* @@ -116,11 +116,11 @@ graphql_object!(CtxSwitcher: InnerContext |&self| { Some((executor.context(), InnerType)) } - field ctx_switch_res(&executor) -> FieldResult<(&InnerContext, InnerType), __S> { + field ctx_switch_res(&executor) -> FieldResult<(&InnerContext, InnerType)> { Ok((executor.context(), InnerType)) } - field ctx_switch_res_opt(&executor) -> FieldResult, __S> { + field ctx_switch_res_opt(&executor) -> FieldResult> { Ok(Some((executor.context(), InnerType))) } }); @@ -275,7 +275,7 @@ fn introspect_description_first() { ("name", Value::scalar("Interface")), ("kind", Value::scalar("INTERFACE")), ].into_iter() - .collect(), + .collect(), )])) ); @@ -304,7 +304,7 @@ fn introspect_fields_first() { ("name", Value::scalar("Interface")), ("kind", Value::scalar("INTERFACE")), ].into_iter() - .collect(), + .collect(), )])) ); @@ -333,7 +333,7 @@ fn introspect_interfaces_first() { ("name", Value::scalar("Interface")), ("kind", Value::scalar("INTERFACE")), ].into_iter() - .collect(), + .collect(), )])) ); @@ -362,7 +362,7 @@ fn introspect_commas_with_trailing() { ("name", Value::scalar("Interface")), ("kind", Value::scalar("INTERFACE")), ].into_iter() - .collect(), + .collect(), )])) ); @@ -391,7 +391,7 @@ fn introspect_commas_on_meta() { ("name", Value::scalar("Interface")), ("kind", Value::scalar("INTERFACE")), ].into_iter() - .collect(), + .collect(), )])) ); diff --git a/juniper/src/macros/union.rs b/juniper/src/macros/union.rs index 130ffa4fa..f86b39276 100644 --- a/juniper/src/macros/union.rs +++ b/juniper/src/macros/union.rs @@ -19,169 +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: ( - @ gather_meta, - ($reg:expr, $acc:expr, $descr:expr), - description : $value:tt $( $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:tt,)* + additional = { + resolver = { + $(context = $resolver_ctx: ident,)* + items = [ + $({ + src = $resolver_src: ty, + resolver = $resolver_expr: expr, + },)* + ], + }, + }, + }, + items = [], ) => { - $descr = Some(graphql_interface!(@as_expr, $value)); + __juniper_impl_trait!( + impl<$($scalar)* $(, $lifetimes)* > GraphQLType for $name { + type Context = $ctx; + type TypeInfo = (); - graphql_union!(@ gather_meta, ($reg, $acc, $descr), $( $rest )*) - }; - - // Gathering meta for instance resolvers - // instance_resolvers: | | [...] - ( - @ 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: | | [...] - ( - @ 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<__S>>::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: | | [...] - ( - @ 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<__S>>::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 )* - } - ) => { - graphql_union!(@as_item, impl<$($lifetime, )* __S> $crate::GraphQLType<__S> for $name - where __S: $crate::value::ScalarValue, - for<'__b> &'__b __S: $crate::value::ScalarRefValue<'__b> - { - type Context = $ctxt; - type TypeInfo = (); - - fn name(_: &()) -> Option<&str> { - Some($outname) - } + fn name(_ : &Self::TypeInfo) -> Option<&str> { + Some($($outname)*) + } - #[allow(unused_assignments)] - #[allow(unused_mut)] - fn meta<'r>(_: &(), registry: &mut $crate::Registry<'r, __S>) -> $crate::meta::MetaType<'r, __S> - where __S: 'r + 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 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); + let types = &[ + $( + registry.get_type::<$resolver_src>(&()), + )* + ]; + registry.build_union_type::<$name>( + info, types + ) + $(.description($desciption))* + .into_meta() } - mt.into_meta() - } + #[allow(unused_variables)] + fn concrete_type_name(&$main_self, context: &Self::Context, _info: &Self::TypeInfo) -> String { + $(let $resolver_ctx = &context;)* - fn concrete_type_name(&$mainself, context: &Self::Context, _: &()) -> String { - graphql_union!( - @ concrete_type_name, - ($outname, context, $ctxt), - $($items)*); - } + $( + if ($resolver_expr as ::std::option::Option<$resolver_src>).is_some() { + return + <$resolver_src as $crate::GraphQLType<_>>::name(&()).unwrap().to_owned(); + } + )* + + panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + } - fn resolve_into_type( - &$mainself, - _: &(), - type_name: &str, - _: Option<&[$crate::Selection<__S>]>, - executor: &$crate::Executor<__S, Self::Context>, - ) - -> $crate::ExecutionResult<__S> - { - graphql_union!( - @ resolve_into_type, - ($outname, type_name, executor, $ctxt), - $($items)*); + fn resolve_into_type( + &$main_self, + _info: &Self::TypeInfo, + type_name: &str, + _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>, + executor: &$crate::Executor<__juniper_insert_generic!($($scalar)*), Self::Context>, + ) -> $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); + } + )* + + 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)*) => { + 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)* + ); }; } diff --git a/juniper/src/parser/tests/value.rs b/juniper/src/parser/tests/value.rs index 066c38ef5..88411f576 100644 --- a/juniper/src/parser/tests/value.rs +++ b/juniper/src/parser/tests/value.rs @@ -27,7 +27,7 @@ struct Foo { struct Query; -graphql_object!(Query: () |&self| { +graphql_object!(Query: () where Scalar = |&self| { field int_field() -> i32 { 42 } diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index a3d2ebb8e..266f04855 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -73,391 +73,190 @@ where } } -impl<'a, S> GraphQLType for SchemaType<'a, S> -where - S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b>, +graphql_object!(<'a> SchemaType<'a, S>: SchemaType<'a, S> as "__Schema" + where Scalar = |&self| { - type Context = Self; - type TypeInfo = (); + field types() -> Vec> { + self.type_list() + .into_iter() + .filter(|t| t.to_concrete().map(|t| t.name() != Some("_EmptyMutation")).unwrap_or(false)) + .collect() + } - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("__Schema") + field query_type() -> TypeType { + self.query_type() } - fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r, - for<'b> &'b S: ScalarRefValue<'b>, - { - let types = registry.field::>>("types", info); - let query_type = registry.field::>("queryType", info); - let mutation_type = registry.field::>>("mutationType", info); - let subscription_type = registry.field::>>("subscriptionType", info); - let directives = registry.field::>>("directives", info); + field mutation_type() -> Option> { + self.mutation_type() + } - let obj = registry.build_object_type::( - info, - &[ - types, - query_type, - mutation_type, - subscription_type, - directives, - ], - ); - obj.into_meta() + // Included for compatibility with the introspection query in GraphQL.js + field subscription_type() -> Option> { + None } - fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { - String::from("__Schema") + field directives() -> Vec<&DirectiveType> { + self.directive_list() } +}); - fn resolve_field( - &self, - info: &Self::TypeInfo, - field: &str, - _args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { - match field { - "types" => { - let r = self - .type_list() - .into_iter() - .filter(|t| { - t.to_concrete() - .map(|t| t.name() != Some("_EmptyMutation")) - .unwrap_or(false) - }).collect::>(); - executor.resolve(info, &r) - } - "queryType" => executor.resolve(info, &self.query_type()), - "mutationType" => executor.resolve(info, &self.mutation_type()), - "subscriptionType" => executor.resolve::>>(info, &None), - "directives" => executor.resolve(info, &self.directive_list()), - e => panic!("Field {} not found on type __Schema", e), +graphql_object!(<'a> TypeType<'a, S>: SchemaType<'a, S> as "__Type" + where Scalar = |&self| +{ + field name() -> Option<&str> { + match *self { + TypeType::Concrete(t) => t.name(), + _ => None, } } -} -impl<'a, S> GraphQLType for TypeType<'a, S> -where - S: ScalarValue + 'a, - for<'b> &'b S: ScalarRefValue<'b>, -{ - type Context = SchemaType<'a, S>; - type TypeInfo = (); + field description() -> Option<&String> { + match *self { + TypeType::Concrete(t) => t.description(), + _ => None, + } + } - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("__Type") + field kind() -> TypeKind { + match *self { + TypeType::Concrete(t) => t.type_kind(), + TypeType::List(_) => TypeKind::List, + TypeType::NonNull(_) => TypeKind::NonNull, + } } - fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r, - for<'b> &'b S: ScalarRefValue<'b>, - { - let name = registry.field::>("name", info); - let description = registry.field::>("description", info); - let kind = registry.field::("kind", info); - let fields = registry - .field::>>>("fields", info) - .argument(registry.arg_with_default("includeDeprecated", &false, info)); - let of_type = registry.field::>>>("ofType", info); - let input_fields = registry.field::>>>("inputFields", info); - let interfaces = registry.field::>>>("interfaces", info); - let possible_types = registry.field::>>>("possibleTypes", info); - let enum_values = registry - .field::>>("enumValues", info) - .argument(registry.arg_with_default("includeDeprecated", &false, info)); - - let obj = registry.build_object_type::( - info, - &[ - name, - description, - kind, - fields, - of_type, - input_fields, - interfaces, - possible_types, - enum_values, - ], - ); - obj.into_meta() - } - - fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { - String::from("__Type") + field fields(include_deprecated = false: bool) -> Option>> { + match *self { + TypeType::Concrete(&MetaType::Interface(InterfaceMeta { ref fields, .. })) | + TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => + Some(fields + .iter() + .filter(|f| include_deprecated || f.deprecation_reason.is_none()) + .filter(|f| !f.name.starts_with("__")) + .collect()), + _ => None, + } } - fn resolve_field( - &self, - info: &Self::TypeInfo, - field: &str, - args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { - match field { - "name" => { - let r = match *self { - TypeType::Concrete(t) => t.name(), - _ => None, - }; - executor.replaced_context(&()).resolve(info, &r) - } - "description" => { - let r = match *self { - TypeType::Concrete(t) => t.description(), - _ => None, - }; - executor.replaced_context(&()).resolve(info, &r) - } - "kind" => { - let r = match *self { - TypeType::Concrete(t) => t.type_kind(), - TypeType::List(_) => TypeKind::List, - TypeType::NonNull(_) => TypeKind::NonNull, - }; - executor.replaced_context(&()).resolve(info, &r) - } - "fields" => { - let include_deprecated = args.get("includeDeprecated").unwrap_or(false); - let r: Option> = match *self { - TypeType::Concrete(&MetaType::Interface(InterfaceMeta { - ref fields, .. - })) - | TypeType::Concrete(&MetaType::Object(ObjectMeta { ref fields, .. })) => Some( - fields - .iter() - .filter(|f| include_deprecated || f.deprecation_reason.is_none()) - .filter(|f| !f.name.starts_with("__")) - .collect(), - ), - _ => None, - }; - executor.resolve(info, &r) - } - "ofType" => { - let r = match *self { - TypeType::Concrete(_) => None, - TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l), - }; - executor.resolve(info, &r) - } - "inputFields" => { - let r = match *self { - TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { - ref input_fields, - .. - })) => Some(input_fields), - _ => None, - }; - executor.resolve(info, &r) - } - "interfaces" => { - let r: Option> = match *self { - TypeType::Concrete(&MetaType::Object(ObjectMeta { - ref interface_names, - .. - })) => { - let schema = executor.context(); - Some( - interface_names - .iter() - .filter_map(|n| schema.type_by_name(n)) - .collect(), - ) - } - _ => None, - }; - executor.resolve(info, &r) - } - "possibleTypes" => { + field of_type() -> Option<&Box>> { + match *self { + TypeType::Concrete(_) => None, + TypeType::List(ref l) | TypeType::NonNull(ref l) => Some(l), + } + } + + field input_fields() -> Option<&Vec>> { + match *self { + TypeType::Concrete(&MetaType::InputObject(InputObjectMeta { ref input_fields, .. })) => + Some(input_fields), + _ => None, + } + } + + field interfaces(&executor) -> Option>> { + match *self { + TypeType::Concrete(&MetaType::Object(ObjectMeta { ref interface_names, .. })) => { let schema = executor.context(); - let r: Option> = match *self { - TypeType::Concrete(&MetaType::Union(UnionMeta { - ref of_type_names, .. - })) => Some( - of_type_names - .iter() - .filter_map(|tn| schema.type_by_name(tn)) - .collect(), - ), - TypeType::Concrete(&MetaType::Interface(InterfaceMeta { - name: ref iface_name, - .. - })) => Some( - schema - .concrete_type_list() - .iter() - .filter_map(|&ct| { - if let MetaType::Object(ObjectMeta { - ref name, - ref interface_names, - .. - }) = *ct - { - if interface_names.contains(&iface_name.to_string()) { - schema.type_by_name(name) - } else { - None - } - } else { - None - } - }).collect(), - ), - _ => None, - }; - executor.resolve(info, &r) + Some(interface_names + .iter() + .filter_map(|n| schema.type_by_name(n)) + .collect()) } - "enumValues" => { - let include_deprecated = args.get("includeDeprecated").unwrap_or(false); - let r: Option> = match *self { - TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => Some( - values - .iter() - .filter(|f| include_deprecated || f.deprecation_reason.is_none()) - .collect(), - ), - _ => None, - }; - executor.replaced_context(&()).resolve(info, &r) + _ => None, + } + } + + field possible_types(&executor) -> Option>> { + let schema = executor.context(); + match *self { + TypeType::Concrete(&MetaType::Union(UnionMeta { ref of_type_names, .. })) => { + Some(of_type_names + .iter() + .filter_map(|tn| schema.type_by_name(tn)) + .collect()) } - e => panic!("Field {} not found on type __Type", e), + TypeType::Concrete(&MetaType::Interface(InterfaceMeta{name: ref iface_name, .. })) => { + Some(schema.concrete_type_list() + .iter() + .filter_map(|&ct| + if let MetaType::Object(ObjectMeta{ + ref name, + ref interface_names, + .. + }) = *ct { + if interface_names.contains(&iface_name.to_string()) { + schema.type_by_name(name) + } else { None } + } else { None } + ) + .collect()) + } + _ => None, } } -} -impl<'a, S> GraphQLType for Field<'a, S> -where - S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b>, + field enum_values(include_deprecated = false: bool) -> Option> { + match *self { + TypeType::Concrete(&MetaType::Enum(EnumMeta { ref values, .. })) => + Some(values + .iter() + .filter(|f| include_deprecated || f.deprecation_reason.is_none()) + .collect()), + _ => None, + } + } +}); + +graphql_object!(<'a> Field<'a, S>: SchemaType<'a, S> as "__Field" + where Scalar = |&self| { - type Context = SchemaType<'a, S>; - type TypeInfo = (); + field name() -> &String { + &self.name + } - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("__Field") + field description() -> &Option { + &self.description } - fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r, - for<'b> &'b S: ScalarRefValue<'b>, - { - let name = registry.field::>("name", info); - let description = registry.field::>("description", info); - let args = registry.field::>>("args", info); - let tpe = registry.field::>("type", info); - let is_deprecated = registry.field::("isDeprecated", info); - let deprecation_reason = registry.field::>("deprecationReason", info); - - let obj = registry.build_object_type::( - info, - &[ - name, - description, - args, - tpe, - is_deprecated, - deprecation_reason, - ], - ); - obj.into_meta() - } - - fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { - String::from("__Field") + field args() -> Vec<&Argument> { + self.arguments.as_ref().map_or_else(Vec::new, |v| v.iter().collect()) } - fn resolve_field( - &self, - info: &Self::TypeInfo, - field: &str, - _args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { - match field { - "name" => executor.replaced_context(&()).resolve(info, &self.name), - "description" => executor - .replaced_context(&()) - .resolve(&(), &self.description), - "args" => executor.resolve( - info, - &self - .arguments - .as_ref() - .map_or_else(Vec::new, |v| v.iter().collect()), - ), - "type" => executor.resolve(info, &executor.context().make_type(&self.field_type)), - "isDeprecated" => executor - .replaced_context(&()) - .resolve(info, &self.deprecation_reason.is_some()), - "deprecationReason" => executor - .replaced_context(&()) - .resolve(info, &&self.deprecation_reason), - e => panic!("Field {} not found on type __Type", e), - } + field type(&executor) -> TypeType { + executor.context().make_type(&self.field_type) } -} -impl<'a, S> GraphQLType for Argument<'a, S> -where - S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b>, -{ - type Context = SchemaType<'a, S>; - type TypeInfo = (); + field is_deprecated() -> bool { + self.deprecation_reason.is_some() + } - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("__InputValue") + field deprecation_reason() -> &Option { + &self.deprecation_reason } +}); - fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r, - for<'b> &'b S: ScalarRefValue<'b>, - { - let name = registry.field::>("name", info); - let description = registry.field::>("description", info); - let tpe = registry.field::>("type", info); - let default_value = registry.field::>("defaultValue", info); +graphql_object!(<'a> Argument<'a, S>: SchemaType<'a, S> as "__InputValue" + where Scalar = |&self| +{ + field name() -> &String { + &self.name + } - let obj = - registry.build_object_type::(info, &[name, description, tpe, default_value]); - obj.into_meta() + field description() -> &Option { + &self.description } - fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { - String::from("__InputValue") + field type(&executor) -> TypeType { + executor.context().make_type(&self.arg_type) } - fn resolve_field( - &self, - info: &Self::TypeInfo, - field: &str, - _args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { - match field { - "name" => executor.replaced_context(&()).resolve(info, &self.name), - "description" => executor - .replaced_context(&()) - .resolve(info, &self.description), - "type" => executor.resolve(info, &executor.context().make_type(&self.arg_type)), - "defaultValue" => executor - .replaced_context(&()) - .resolve(info, &self.default_value.as_ref().map(|v| format!("{}", v))), - e => panic!("Field {} not found on type __Type", e), - } + field default_value() -> Option { + self.default_value.as_ref().map(|v| format!("{}", v)) } -} +}); -graphql_object!(EnumValue: () as "__EnumValue" |&self| { +graphql_object!(EnumValue: () as "__EnumValue" where Scalar = |&self| { field name() -> &String { &self.name } @@ -475,87 +274,42 @@ graphql_object!(EnumValue: () as "__EnumValue" |&self| { } }); -impl<'a, S> GraphQLType for DirectiveType<'a, S> -where - S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b>, +graphql_object!(<'a> DirectiveType<'a, S>: SchemaType<'a, S> as "__Directive" + where Scalar = |&self| { - type Context = SchemaType<'a, S>; - type TypeInfo = (); + field name() -> &String { + &self.name + } - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("__Directive") + field description() -> &Option { + &self.description } - fn meta<'r>(info: &Self::TypeInfo, registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where - S: 'r, - { - let name = registry.field::>("name", info); - let description = registry.field::>("description", info); - let locations = registry.field::<&Vec>("locations", info); - let args = registry.field::>>("args", info); - - let on_operation = registry - .field::("onOperation", info) - .deprecated("Use the locations array instead"); - let on_fragment = registry - .field::("onFragment", info) - .deprecated("Use the locations array instead"); - let on_field = registry - .field::("onField", info) - .deprecated("Use the locations array instead"); - - let obj = registry.build_object_type::( - info, - &[ - name, - description, - locations, - args, - on_operation, - on_fragment, - on_field, - ], - ); - obj.into_meta() - } - - fn concrete_type_name(&self, _: &Self::Context, (): &Self::TypeInfo) -> String { - String::from("__Directive") + field locations() -> &Vec { + &self.locations } - fn resolve_field( - &self, - info: &Self::TypeInfo, - field: &str, - _args: &Arguments, - executor: &Executor, - ) -> ExecutionResult { - match field { - "name" => executor.replaced_context(&()).resolve(info, &self.name), - "description" => executor - .replaced_context(&()) - .resolve(info, &self.description), - "locations" => executor - .replaced_context(&()) - .resolve(info, &self.locations), - "args" => executor.resolve(info, &self.arguments), - "onOperation" => executor - .replaced_context(&()) - .resolve(info, &self.locations.contains(&DirectiveLocation::Query)), - "onFragment" => executor.replaced_context(&()).resolve( - info, - &(self - .locations - .contains(&DirectiveLocation::FragmentDefinition) - || self.locations.contains(&DirectiveLocation::InlineFragment) - || self.locations.contains(&DirectiveLocation::FragmentSpread)), - ), - "onField" => executor - .replaced_context(&()) - .resolve(info, &self.locations.contains(&DirectiveLocation::Field)), - e => panic!("Field {} not found on type __Type", e), - } + field args() -> &Vec> { + &self.arguments } -} + + // Included for compatibility with the introspection query in GraphQL.js + field deprecated "Use the locations array instead" + on_operation() -> bool { + self.locations.contains(&DirectiveLocation::Query) + } + + // Included for compatibility with the introspection query in GraphQL.js + field deprecated "Use the locations array instead" + on_fragment() -> bool { + self.locations.contains(&DirectiveLocation::FragmentDefinition) || + self.locations.contains(&DirectiveLocation::InlineFragment) || + self.locations.contains(&DirectiveLocation::FragmentSpread) + } + + // Included for compatibility with the introspection query in GraphQL.js + field deprecated "Use the locations array instead" + on_field() -> bool { + self.locations.contains(&DirectiveLocation::Field) + } +}); diff --git a/juniper/src/validation/rules/provided_non_null_arguments.rs b/juniper/src/validation/rules/provided_non_null_arguments.rs index 5151217fd..d11bc1acb 100644 --- a/juniper/src/validation/rules/provided_non_null_arguments.rs +++ b/juniper/src/validation/rules/provided_non_null_arguments.rs @@ -15,12 +15,7 @@ impl<'a, S> Visitor<'a, S> for ProvidedNonNullArguments where S: ScalarValue, { - - fn enter_field( - &mut self, - ctx: &mut ValidatorContext<'a, S>, - field: &'a Spanning>, - ) { + fn enter_field(&mut self, ctx: &mut ValidatorContext<'a, S>, field: &'a Spanning>) { let field_name = &field.item.name.item; if let Some(&FieldType { @@ -29,13 +24,13 @@ where }) = 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( @@ -63,13 +58,12 @@ where }) = 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( From bac6948bada1c017122802a0cb7b41d0b110c990 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 19 Sep 2018 21:33:12 +0200 Subject: [PATCH 08/20] Add `DefaultScalarValue` as default type for some generics In general this change should allow old to work without many explicit types everywhere. This also removes some unused trait bounds. --- juniper/src/ast.rs | 8 +- juniper/src/executor/mod.rs | 55 +++++-------- juniper/src/executor_tests/custom_scalar.rs | 6 +- juniper/src/executor_tests/enums.rs | 9 +-- juniper/src/executor_tests/executor.rs | 80 +++++++------------ .../src/executor_tests/interfaces_unions.rs | 8 +- .../src/executor_tests/introspection/mod.rs | 30 +++---- juniper/src/executor_tests/variables.rs | 67 ++++++---------- juniper/src/http/mod.rs | 2 +- juniper/src/integrations/chrono.rs | 17 ++-- juniper/src/integrations/url.rs | 5 +- juniper/src/integrations/uuid.rs | 3 +- juniper/src/lib.rs | 16 +++- juniper/src/macros/interface.rs | 4 +- juniper/src/macros/object.rs | 2 +- juniper/src/macros/scalar.rs | 19 +++-- juniper/src/macros/tests/scalar.rs | 25 +++--- juniper/src/macros/union.rs | 2 +- juniper/src/schema/meta.rs | 30 +++---- juniper/src/schema/model.rs | 26 +++--- juniper/src/schema/schema.rs | 6 +- juniper/src/tests/introspection_tests.rs | 20 ++--- juniper/src/tests/query_tests.rs | 44 ++++------ juniper/src/tests/type_info_tests.rs | 7 +- juniper/src/types/base.rs | 35 ++++---- juniper/src/types/containers.rs | 10 +-- juniper/src/types/pointers.rs | 18 ++--- juniper/src/types/scalars.rs | 17 ++-- juniper/src/value/mod.rs | 16 +++- juniper/src/value/scalar.rs | 7 +- juniper_codegen/src/derive_enum.rs | 2 +- 31 files changed, 267 insertions(+), 329 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index 58a75f047..eb6557c9a 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -8,7 +8,7 @@ use indexmap::IndexMap; use executor::Variables; use parser::Spanning; -use value::{ScalarRefValue, ScalarValue}; +use value::{ScalarRefValue, ScalarValue, DefaultScalarValue}; /// A type literal in the syntax tree /// @@ -38,7 +38,7 @@ pub enum Type<'a> { /// their position in the source file, if available. #[derive(Debug, Clone, PartialEq)] #[allow(missing_docs)] -pub enum InputValue { +pub enum InputValue { Null, Scalar(S), Enum(String), @@ -151,7 +151,7 @@ pub type Document<'a, S> = Vec>; /// 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: Sized { /// Performs the conversion. fn from_input_value(v: &InputValue) -> Option where @@ -159,7 +159,7 @@ pub trait FromInputValue: Sized { } /// Losslessly clones a Rust data type into an InputValue. -pub trait ToInputValue: Sized { +pub trait ToInputValue: Sized { /// Performs the conversion. fn to_input_value(&self) -> InputValue; } diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index c580115f1..2c66a4a38 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use std::cmp::Ordering; use std::collections::HashMap; -use std::fmt::{Debug, Display}; +use std::fmt::Display; use std::sync::RwLock; use fnv::FnvHashMap; @@ -22,7 +22,7 @@ use schema::model::{RootNode, SchemaType, TypeType}; use types::base::GraphQLType; use types::name::Name; -use value::{ParseScalarValue, ScalarRefValue, ScalarValue, DefaultScalarValue}; +use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue}; mod look_ahead; @@ -36,7 +36,7 @@ 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, S: Debug> { +pub struct Registry<'r, S = DefaultScalarValue> { /// Currently registered types pub types: FnvHashMap>, } @@ -51,10 +51,10 @@ 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, S, CtxT> +pub struct Executor<'a, CtxT, S = DefaultScalarValue> where CtxT: 'a, - S: Debug + 'a, + S: 'a, { fragments: &'a HashMap<&'a str, &'a Fragment<'a, S>>, variables: &'a Variables, @@ -127,15 +127,14 @@ where /// /// ```rust /// # use juniper::{FieldError, ScalarValue}; -/// fn get_string(data: Vec) -> Result> -/// where S: ScalarValue +/// fn get_string(data: Vec) -> Result /// { /// let s = String::from_utf8(data)?; /// Ok(s) /// } /// ``` #[derive(Debug, PartialEq)] -pub struct FieldError { +pub struct FieldError { message: String, extensions: Value, } @@ -210,16 +209,16 @@ impl FieldError { pub type FieldResult = Result>; /// The result of resolving an unspecified field -pub type ExecutionResult = Result, FieldError>; +pub type ExecutionResult = Result, FieldError>; /// The map of variables used for substitution during query execution -pub type Variables = HashMap>; +pub type Variables = HashMap>; /// 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 { #[doc(hidden)] fn into_field_error(self) -> FieldError; } @@ -348,33 +347,25 @@ where } } -impl<'a, CtxT, S> Executor<'a, S, 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>( - &self, - info: &T::TypeInfo, - value: &T, - ) -> ExecutionResult + pub fn resolve_with_ctx(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult where NewCtxT: FromContext, - for<'b> &'b S: ScalarRefValue<'b>, + T: GraphQLType, { self.replaced_context(>::from(self.context)) .resolve(info, value) } /// Resolve a single arbitrary value into an `ExecutionResult` - pub fn resolve>( - &self, - info: &T::TypeInfo, - value: &T, - ) -> ExecutionResult + pub fn resolve(&self, info: &T::TypeInfo, value: &T) -> ExecutionResult where - for<'b> &'b S: ScalarRefValue<'b>, + T: GraphQLType, { Ok(value.resolve(info, self.current_selection_set, self)) } @@ -382,13 +373,9 @@ where /// Resolve a single arbitrary value into a return value /// /// If the field fails to resolve, `null` will be returned. - pub fn resolve_into_value>( - &self, - info: &T::TypeInfo, - value: &T, - ) -> Value + pub fn resolve_into_value(&self, info: &T::TypeInfo, value: &T) -> Value where - for<'b> &'b S: ScalarRefValue<'b>, + T: GraphQLType, { match self.resolve(info, value) { Ok(v) => v, @@ -403,7 +390,7 @@ where /// /// 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, S, NewCtxT> { + pub fn replaced_context<'b, NewCtxT>(&'b self, ctx: &'b NewCtxT) -> Executor<'b, NewCtxT, S> { Executor { fragments: self.fragments, variables: self.variables, @@ -424,7 +411,7 @@ where field_name: &'a str, location: SourcePosition, selection_set: Option<&'a [Selection]>, - ) -> Executor { + ) -> Executor { Executor { fragments: self.fragments, variables: self.variables, @@ -450,7 +437,7 @@ where &self, type_name: Option<&'a str>, selection_set: Option<&'a [Selection]>, - ) -> Executor { + ) -> Executor { Executor { fragments: self.fragments, variables: self.variables, @@ -596,7 +583,7 @@ impl ExecutionError { pub fn execute_validated_query<'a, QueryT, MutationT, CtxT, S>( document: Document, operation_name: Option<&str>, - root_node: &RootNode, + root_node: &RootNode, variables: &Variables, context: &CtxT, ) -> Result<(Value, Vec>), GraphQLError<'a>> diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs index af5db6bdc..ddaf1c227 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -7,7 +7,7 @@ use serde::de::{self, Deserialize, Deserializer}; use std::fmt; use types::base::{Arguments, GraphQLType}; use types::scalars::EmptyMutation; -use value::{Object, ScalarRefValue, Value}; +use value::{Object, ParseScalarResult, ScalarRefValue, Value}; #[derive(Debug, Clone, PartialEq, ScalarValue)] enum MyScalarValue { @@ -109,7 +109,7 @@ graphql_scalar!(i64 as "Long" where Scalar = MyScalarValue { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, MyScalarValue> { if let ScalarToken::Int(v) = value { v.parse() .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) @@ -156,7 +156,7 @@ impl GraphQLType for TestType { _info: &Self::TypeInfo, field_name: &str, args: &Arguments, - _executor: &Executor, + _executor: &Executor, ) -> ExecutionResult { match field_name { "longField" => Ok(Value::Scalar(MyScalarValue::Long( diff --git a/juniper/src/executor_tests/enums.rs b/juniper/src/executor_tests/enums.rs index 032ccccdb..025af1af1 100644 --- a/juniper/src/executor_tests/enums.rs +++ b/juniper/src/executor_tests/enums.rs @@ -71,8 +71,7 @@ fn serializes_as_output() { #[test] fn does_not_accept_string_literals() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ toString(color: "RED") }"#; let vars = vec![].into_iter().collect(); @@ -106,8 +105,7 @@ fn accepts_strings_in_variables() { #[test] fn does_not_accept_incorrect_enum_name_in_variables() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![("color".to_owned(), InputValue::scalar("BLURPLE"))] @@ -127,8 +125,7 @@ fn does_not_accept_incorrect_enum_name_in_variables() { #[test] fn does_not_accept_incorrect_type_in_variables() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($color: Color!) { toString(color: $color) }"#; let vars = vec![("color".to_owned(), InputValue::scalar(123))] diff --git a/juniper/src/executor_tests/executor.rs b/juniper/src/executor_tests/executor.rs index b27140c24..e9fba95f6 100644 --- a/juniper/src/executor_tests/executor.rs +++ b/juniper/src/executor_tests/executor.rs @@ -2,7 +2,7 @@ mod field_execution { use ast::InputValue; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{DefaultScalarValue, Value}; + use value::Value; struct DataType; struct DeepDataType; @@ -34,8 +34,7 @@ mod field_execution { #[test] fn test() { - let schema: RootNode = - RootNode::new(DataType, EmptyMutation::<()>::new()); + let schema = RootNode::new(DataType, EmptyMutation::<()>::new()); let doc = r" query Example($size: Int) { a, @@ -131,7 +130,7 @@ mod field_execution { mod merge_parallel_fragments { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{DefaultScalarValue, Value}; + use value::Value; struct Type; @@ -144,8 +143,7 @@ mod merge_parallel_fragments { #[test] fn test() { - let schema: RootNode = - RootNode::new(Type, EmptyMutation::<()>::new()); + let schema = RootNode::new(Type, EmptyMutation::<()>::new()); let doc = r" { a, ...FragOne, ...FragTwo } fragment FragOne on Type { @@ -202,7 +200,7 @@ mod merge_parallel_fragments { mod merge_parallel_inline_fragments { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{Value, DefaultScalarValue}; + use value::Value; struct Type; struct Other; @@ -225,8 +223,7 @@ mod merge_parallel_inline_fragments { #[test] fn test() { - let schema: RootNode = - RootNode::new(Type, EmptyMutation::<()>::new()); + let schema = RootNode::new(Type, EmptyMutation::<()>::new()); let doc = r" { a, ...FragOne } fragment FragOne on Type { @@ -319,7 +316,7 @@ mod threads_context_correctly { use executor::Context; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{Value, DefaultScalarValue}; + use value::Value; struct Schema; @@ -335,8 +332,7 @@ mod threads_context_correctly { #[test] fn test() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -373,7 +369,7 @@ mod dynamic_context_switching { use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{Value, DefaultScalarValue}; + use value::Value; struct Schema; @@ -423,8 +419,7 @@ mod dynamic_context_switching { #[test] fn test_opt() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ first: itemOpt(key: 0) { value }, missing: itemOpt(key: 2) { value } }"; let vars = vec![].into_iter().collect(); @@ -474,8 +469,7 @@ mod dynamic_context_switching { #[test] fn test_res_success() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { first: itemRes(key: 0) { value } @@ -526,8 +520,7 @@ mod dynamic_context_switching { #[test] fn test_res_fail() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { missing: itemRes(key: 2) { value } @@ -572,8 +565,7 @@ mod dynamic_context_switching { #[test] fn test_res_opt() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r" { first: itemResOpt(key: 0) { value } @@ -637,8 +629,7 @@ mod dynamic_context_switching { #[test] fn test_always() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::::new()); + let schema = RootNode::new(Schema, EmptyMutation::::new()); let doc = r"{ first: itemAlways(key: 0) { value } }"; let vars = vec![].into_iter().collect(); @@ -689,7 +680,7 @@ mod propagates_errors_to_nullable_fields { use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{DefaultScalarValue, ScalarValue, Value}; + use value::{ScalarValue, Value}; struct Schema; struct Inner; @@ -730,8 +721,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn nullable_first_level() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -757,8 +747,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_nullable_first_level() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -781,8 +770,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn custom_error_first_level() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { customErrorField } }"; let vars = vec![].into_iter().collect(); @@ -805,8 +793,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn nullable_nested_level() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nullableField { nonNullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -832,8 +819,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_nullable_nested_level() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableField { nonNullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -856,8 +842,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn nullable_innermost() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inner { nonNullableField { nullableErrorField } } }"; let vars = vec![].into_iter().collect(); @@ -883,8 +868,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_null_list() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ inners { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -907,8 +891,7 @@ mod propagates_errors_to_nullable_fields { #[test] fn non_null_list_of_nullable() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ nullableInners { nonNullableErrorField } }"; let vars = vec![].into_iter().collect(); @@ -958,7 +941,7 @@ mod propagates_errors_to_nullable_fields { mod named_operations { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{Value, DefaultScalarValue}; + use value::Value; use GraphQLError; struct Schema; @@ -969,8 +952,7 @@ mod named_operations { #[test] fn uses_inline_operation_if_no_name_provided() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"{ a }"; let vars = vec![].into_iter().collect(); @@ -987,8 +969,7 @@ mod named_operations { #[test] fn uses_only_named_operation() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { a }"; let vars = vec![].into_iter().collect(); @@ -1005,8 +986,7 @@ mod named_operations { #[test] fn uses_named_operation_if_name_provided() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -1024,8 +1004,7 @@ mod named_operations { #[test] fn error_if_multiple_operations_provided_but_no_name() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); @@ -1037,8 +1016,7 @@ mod named_operations { #[test] fn error_if_unknown_operation_name_provided() { - let schema: RootNode = - RootNode::new(Schema, EmptyMutation::<()>::new()); + let schema = RootNode::new(Schema, EmptyMutation::<()>::new()); let doc = r"query Example { first: a } query OtherExample { second: a }"; let vars = vec![].into_iter().collect(); diff --git a/juniper/src/executor_tests/interfaces_unions.rs b/juniper/src/executor_tests/interfaces_unions.rs index 49dd474ce..e4890f5ad 100644 --- a/juniper/src/executor_tests/interfaces_unions.rs +++ b/juniper/src/executor_tests/interfaces_unions.rs @@ -1,7 +1,7 @@ mod interface { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{DefaultScalarValue, Value}; + use value::Value; trait Pet { fn name(&self) -> &str; @@ -77,7 +77,7 @@ mod interface { #[test] fn test() { - let schema: RootNode = RootNode::new( + let schema = RootNode::new( Schema { pets: vec![ Box::new(Dog { @@ -144,7 +144,7 @@ mod interface { mod union { use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{Value, DefaultScalarValue}; + use value::Value; trait Pet { fn as_dog(&self) -> Option<&Dog> { @@ -206,7 +206,7 @@ mod union { #[test] fn test() { - let schema: RootNode = RootNode::new( + let schema = RootNode::new( Schema { pets: vec![ Box::new(Dog { diff --git a/juniper/src/executor_tests/introspection/mod.rs b/juniper/src/executor_tests/introspection/mod.rs index 14962a2eb..c2a527eaf 100644 --- a/juniper/src/executor_tests/introspection/mod.rs +++ b/juniper/src/executor_tests/introspection/mod.rs @@ -8,8 +8,7 @@ use self::input_object::{NamedPublic, NamedPublicWithDescription}; use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use parser::ParseError; -use value::{DefaultScalarValue, Value, ParseScalarValue}; +use value::{ParseScalarResult, ParseScalarValue, Value}; #[derive(GraphQLEnum)] #[graphql(name = "SampleEnum")] @@ -20,11 +19,11 @@ enum Sample { struct Scalar(i32); -struct Interface {} +struct Interface; -struct Root {} +struct Root; -graphql_scalar!(Scalar as "SampleScalar" where Scalar = { +graphql_scalar!(Scalar as "SampleScalar" { resolve(&self) -> Value { Value::scalar(self.0) } @@ -33,8 +32,8 @@ graphql_scalar!(Scalar as "SampleScalar" where Scalar = { v.as_scalar_value().map(|i: &i32| Scalar(*i)) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { + ::from_str(value) } }); @@ -46,7 +45,7 @@ graphql_interface!(Interface: () as "SampleInterface" |&self| { } instance_resolvers: |&_| { - Root => Some(Root {}), + Root => Some(Root), } }); @@ -76,8 +75,7 @@ fn test_execution() { second: sampleScalar(first: 10 second: 20) } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema = RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -120,8 +118,7 @@ fn enum_introspection() { } } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema = RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -232,8 +229,7 @@ fn interface_introspection() { } } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema = RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -379,8 +375,7 @@ fn object_introspection() { } } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema = RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); @@ -580,8 +575,7 @@ fn scalar_introspection() { } } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema = RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 124cee39a..3a8b7187c 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -1,11 +1,10 @@ use ast::InputValue; use executor::Variables; -use parser::ParseError; use parser::SourcePosition; use schema::model::RootNode; use types::scalars::EmptyMutation; use validation::RuleError; -use value::{DefaultScalarValue, Object, ParseScalarValue, Value}; +use value::{DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, Value}; use GraphQLError::ValidationError; #[derive(Debug)] @@ -28,8 +27,8 @@ graphql_scalar!(TestComplexScalar where Scalar = { None } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { + >::from_str(value) } }); @@ -246,8 +245,7 @@ fn variable_runs_from_input_value_on_scalar() { #[test] fn variable_error_on_nested_non_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -276,8 +274,7 @@ fn variable_error_on_nested_non_null() { #[test] fn variable_error_on_incorrect_type() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![("input".to_owned(), InputValue::scalar("foo bar"))] @@ -296,8 +293,7 @@ fn variable_error_on_incorrect_type() { #[test] fn variable_error_on_omit_non_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -325,8 +321,7 @@ fn variable_error_on_omit_non_null() { #[test] fn variable_multiple_errors_with_nesting() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestNestedInputObject) { fieldWithNestedObjectInput(input: $input) }"#; @@ -358,8 +353,7 @@ fn variable_multiple_errors_with_nesting() { #[test] fn variable_error_on_additional_field() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestInputObject) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -473,8 +467,7 @@ fn allow_nullable_inputs_to_be_set_to_value_directly() { #[test] fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![].into_iter().collect(); @@ -492,8 +485,7 @@ fn does_not_allow_non_nullable_input_to_be_omitted_in_variable() { #[test] fn does_not_allow_non_nullable_input_to_be_set_to_null_in_variable() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($value: String!) { fieldWithNonNullableStringInput(input: $value) }"#; let vars = vec![("value".to_owned(), InputValue::null())] @@ -598,8 +590,7 @@ fn allow_lists_to_contain_null() { #[test] fn does_not_allow_non_null_lists_to_be_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String]!) { nnList(input: $input) }"#; let vars = vec![("input".to_owned(), InputValue::null())] @@ -692,8 +683,7 @@ fn allow_lists_of_non_null_to_contain_values() { #[test] fn does_not_allow_lists_of_non_null_to_contain_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]) { listNn(input: $input) }"#; let vars = vec![( @@ -718,8 +708,7 @@ fn does_not_allow_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![( @@ -744,8 +733,7 @@ fn does_not_allow_non_null_lists_of_non_null_to_contain_null() { #[test] fn does_not_allow_non_null_lists_of_non_null_to_be_null() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: [String!]!) { nnListNn(input: $input) }"#; let vars = vec![("value".to_owned(), InputValue::null())] @@ -783,8 +771,7 @@ fn allow_non_null_lists_of_non_null_to_contain_values() { #[test] fn does_not_allow_invalid_types_to_be_used_as_values() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: TestType!) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -805,8 +792,7 @@ fn does_not_allow_invalid_types_to_be_used_as_values() { #[test] fn does_not_allow_unknown_types_to_be_used_as_values() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($input: UnknownType!) { fieldWithObjectInput(input: $input) }"#; let vars = vec![( @@ -923,8 +909,7 @@ fn nullable_input_object_arguments_successful_with_variables() { #[test] fn does_not_allow_missing_required_field() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ exampleInput(arg: {a: "abc"}) }"#; let vars = vec![].into_iter().collect(); @@ -942,8 +927,7 @@ fn does_not_allow_missing_required_field() { #[test] fn does_not_allow_null_in_required_field() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"{ exampleInput(arg: {a: "abc", b: null}) }"#; let vars = vec![].into_iter().collect(); @@ -961,8 +945,7 @@ fn does_not_allow_null_in_required_field() { #[test] fn does_not_allow_missing_variable_for_required_field() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let vars = vec![].into_iter().collect(); @@ -980,8 +963,7 @@ fn does_not_allow_missing_variable_for_required_field() { #[test] fn does_not_allow_null_variable_for_required_field() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { exampleInput(arg: {b: $var}) }"#; let vars = vec![("var".to_owned(), InputValue::null())] @@ -1080,8 +1062,7 @@ mod integers { #[test] fn does_not_coerce_from_float() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let vars = vec![("var".to_owned(), InputValue::scalar(10.0))] @@ -1101,8 +1082,7 @@ mod integers { #[test] fn does_not_coerce_from_string() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Int!) { integerInput(value: $var) }"#; let vars = vec![("var".to_owned(), InputValue::scalar("10"))] @@ -1158,8 +1138,7 @@ mod floats { #[test] fn does_not_coerce_from_string() { - let schema: RootNode = - RootNode::new(TestType, EmptyMutation::<()>::new()); + let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); let query = r#"query q($var: Float!) { floatInput(value: $var) }"#; let vars = vec![("var".to_owned(), InputValue::scalar("10"))] diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index a70f8999d..b69448815 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -68,7 +68,7 @@ where /// top level of this crate. pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &'a RootNode, + root_node: &'a RootNode, context: &CtxT, ) -> GraphQLResponse<'a, S> where diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index 4a2ffde5a..2bcd5facd 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -16,7 +16,7 @@ use chrono::prelude::*; use parser::{ParseError, ScalarToken, Token}; -use value::ParseScalarValue; +use value::{ParseScalarResult, ParseScalarValue}; use Value; #[doc(hidden)] @@ -34,7 +34,7 @@ graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = (value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { if let ScalarToken::String(value) = value { Ok(S::from(value.to_owned())) } else { @@ -55,7 +55,7 @@ graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { .and_then(|s: &String| (s.parse::>().ok())) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { if let ScalarToken::String(value) = value { Ok(S::from(value.to_owned())) } else { @@ -81,7 +81,7 @@ graphql_scalar!(NaiveDate where Scalar = { .and_then(|s: &String| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { if let ScalarToken::String(value) = value { Ok(S::from(value.to_owned())) } else { @@ -104,7 +104,7 @@ graphql_scalar!(NaiveDateTime where Scalar = { .and_then(|f: &f64| NaiveDateTime::from_timestamp_opt(*f as i64, 0)) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { >::from_str(value) } }); @@ -203,11 +203,11 @@ mod integration_test { use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; - use value::{DefaultScalarValue, 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) @@ -232,8 +232,7 @@ mod integration_test { } "#; - let schema: RootNode = - RootNode::new(Root {}, EmptyMutation::<()>::new()); + let schema= RootNode::new(Root, EmptyMutation::<()>::new()); let (result, errs) = ::execute(doc, None, &schema, &Variables::new(), &()).expect("Execution failed"); diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 344aebb7a..01d8d820b 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -1,7 +1,6 @@ use url::Url; -use parser::ParseError; -use value::ParseScalarValue; +use value::{ParseScalarValue, ParseScalarResult}; use Value; graphql_scalar!(Url where Scalar = { @@ -16,7 +15,7 @@ graphql_scalar!(Url where Scalar = { .and_then(|s: &String| Url::parse(s).ok()) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { >::from_str(value) } }); diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index a59cc2e30..7b0d49627 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -1,6 +1,7 @@ use uuid::Uuid; use parser::{ParseError, ScalarToken, Token}; +use value::ParseScalarResult; use Value; graphql_scalar!(Uuid where Scalar = { @@ -15,7 +16,7 @@ graphql_scalar!(Uuid where Scalar = { .and_then(|s: &String| Uuid::parse_str(s).ok()) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { if let ScalarToken::String(value) = value { Ok(S::from(value.to_owned())) } else { diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index f5ebb27e8..ee13e6e12 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -112,13 +112,20 @@ 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 { @@ -173,7 +180,10 @@ 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::{DefaultScalarValue, Object, ParseScalarValue, ScalarRefValue, ScalarValue, Value}; +pub use value::{ + DefaultScalarValue, Object, ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue, + Value, +}; /// An error that prevented query execution #[derive(Debug, PartialEq)] @@ -190,7 +200,7 @@ pub enum GraphQLError<'a> { pub fn execute<'a, S, CtxT, QueryT, MutationT>( document_source: &'a str, operation_name: Option<&str>, - root_node: &'a RootNode, + root_node: &'a RootNode, variables: &Variables, context: &CtxT, ) -> Result<(Value, Vec>), GraphQLError<'a>> diff --git a/juniper/src/macros/interface.rs b/juniper/src/macros/interface.rs index bb6387f64..020128b9b 100644 --- a/juniper/src/macros/interface.rs +++ b/juniper/src/macros/interface.rs @@ -177,7 +177,7 @@ macro_rules! graphql_interface { info: &Self::TypeInfo, field: &str, args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>, - executor: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context> + executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( if field == &$crate::to_camel_case(stringify!($fn_name)) { @@ -231,7 +231,7 @@ macro_rules! graphql_interface { _info: &Self::TypeInfo, type_name: &str, _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>, - executor: &$crate::Executor<__juniper_insert_generic!($($scalar)*), Self::Context>, + executor: &$crate::Executor, ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)*)> { $(let $resolver_ctx = &executor.context();)* diff --git a/juniper/src/macros/object.rs b/juniper/src/macros/object.rs index 9c89f3fda..ec2e640e7 100644 --- a/juniper/src/macros/object.rs +++ b/juniper/src/macros/object.rs @@ -357,7 +357,7 @@ macro_rules! graphql_object { info: &Self::TypeInfo, field: &str, args: &$crate::Arguments<__juniper_insert_generic!($($scalar)+)>, - executor: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context> + executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( if field == &$crate::to_camel_case(stringify!($fn_name)) { diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index da92184f1..ec744d75f 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -11,13 +11,17 @@ 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, ParseScalarValue}; -# use juniper::parser::ParseError; +# use juniper::{Value, FieldResult, ParseScalarValue, ParseScalarResult}; struct UserID(String); -graphql_scalar!(UserID where Scalar = { +graphql_scalar!(UserID { description: "An opaque identifier, represented as a string" resolve(&self) -> Value { @@ -28,8 +32,8 @@ graphql_scalar!(UserID where Scalar = { v.as_string_value().map(|s| UserID(s.to_owned())) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { + ::from_str(value) } }); @@ -96,7 +100,10 @@ macro_rules! graphql_scalar { &$resolve_self_var, _: &(), _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)+)>]>, - _: &$crate::Executor<__juniper_insert_generic!($($scalar)+), Self::Context>) -> $crate::Value<__juniper_insert_generic!($($scalar)+)> { + _: &$crate::Executor< + Self::Context, + __juniper_insert_generic!($($scalar)+) + >) -> $crate::Value<__juniper_insert_generic!($($scalar)+)> { $resolve_body } }); diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index c0f90f17d..29addd605 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -1,8 +1,7 @@ use executor::Variables; use schema::model::RootNode; use types::scalars::EmptyMutation; -use value::{Value, Object, DefaultScalarValue, ParseScalarValue}; -use parser::ParseError; +use value::{Value, Object, DefaultScalarValue, ParseScalarValue, ParseScalarResult}; struct DefaultName(i32); struct OtherOrder(i32); @@ -29,11 +28,12 @@ graphql_scalar!(DefaultName where Scalar = { v.as_scalar_value().map(|i: &i32| DefaultName(*i)) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { >::from_str(value) } }); -graphql_scalar!(OtherOrder where Scalar = { + +graphql_scalar!(OtherOrder { resolve(&self) -> Value { Value::scalar(self.0) } @@ -43,11 +43,12 @@ graphql_scalar!(OtherOrder where Scalar = { } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { + ::from_str(value) } }); -graphql_scalar!(Named as "ANamedScalar" where Scalar = { + +graphql_scalar!(Named as "ANamedScalar" where Scalar = DefaultScalarValue { resolve(&self) -> Value { Value::scalar(self.0) } @@ -56,12 +57,12 @@ graphql_scalar!(Named as "ANamedScalar" where Scalar = { v.as_scalar_value().map(|i: &i32| Named(*i)) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { + ::from_str(value) } }); -graphql_scalar!(ScalarDescription where Scalar = { +graphql_scalar!(ScalarDescription { description: "A sample scalar, represented as an integer" resolve(&self) -> Value { @@ -72,8 +73,8 @@ graphql_scalar!(ScalarDescription where Scalar = { v.as_scalar_value().map(|i: &i32| ScalarDescription(*i)) } - from_str<'a>(value: ScalarToken<'a>) -> Result> { - >::from_str(value) + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { + ::from_str(value) } }); diff --git a/juniper/src/macros/union.rs b/juniper/src/macros/union.rs index f86b39276..abe2a3ebc 100644 --- a/juniper/src/macros/union.rs +++ b/juniper/src/macros/union.rs @@ -91,7 +91,7 @@ macro_rules! graphql_union { _info: &Self::TypeInfo, type_name: &str, _: Option<&[$crate::Selection<__juniper_insert_generic!($($scalar)*)>]>, - executor: &$crate::Executor<__juniper_insert_generic!($($scalar)*), Self::Context>, + executor: &$crate::Executor, ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)*)> { $(let $resolver_ctx = &executor.context();)* diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index d2474bafd..6a572e6b4 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -7,10 +7,10 @@ use ast::{FromInputValue, InputValue, Type}; use parser::{ParseError, ScalarToken}; use schema::model::SchemaType; use types::base::TypeKind; -use value::{ParseScalarValue, ScalarRefValue, ScalarValue}; +use value::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue}; /// Scalar type metadata -pub struct ScalarMeta<'a, S: fmt::Debug> { +pub struct ScalarMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -35,7 +35,7 @@ pub struct NullableMeta<'a> { /// Object type metadata #[derive(Debug)] -pub struct ObjectMeta<'a, S: fmt::Debug> { +pub struct ObjectMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -47,7 +47,7 @@ pub struct ObjectMeta<'a, S: fmt::Debug> { } /// Enum type metadata -pub struct EnumMeta<'a, S: fmt::Debug> { +pub struct EnumMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -59,7 +59,7 @@ pub struct EnumMeta<'a, S: fmt::Debug> { /// Interface type metadata #[derive(Debug)] -pub struct InterfaceMeta<'a, S: fmt::Debug> { +pub struct InterfaceMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -80,7 +80,7 @@ pub struct UnionMeta<'a> { } /// Input object metadata -pub struct InputObjectMeta<'a, S: fmt::Debug> { +pub struct InputObjectMeta<'a, S> { #[doc(hidden)] pub name: Cow<'a, str>, #[doc(hidden)] @@ -102,7 +102,7 @@ pub struct PlaceholderMeta<'a> { /// Generic type metadata #[derive(Debug)] -pub enum MetaType<'a, S: fmt::Debug> { +pub enum MetaType<'a, S = DefaultScalarValue> { #[doc(hidden)] Scalar(ScalarMeta<'a, S>), #[doc(hidden)] @@ -125,7 +125,7 @@ pub enum MetaType<'a, S: fmt::Debug> { /// Metadata for a field #[derive(Debug, Clone)] -pub struct Field<'a, S: fmt::Debug> { +pub struct Field<'a, S> { #[doc(hidden)] pub name: String, #[doc(hidden)] @@ -140,7 +140,7 @@ pub struct Field<'a, S: fmt::Debug> { /// Metadata for an argument to a field #[derive(Debug, Clone)] -pub struct Argument<'a, S: fmt::Debug> { +pub struct Argument<'a, S> { #[doc(hidden)] pub name: String, #[doc(hidden)] @@ -169,7 +169,7 @@ pub struct EnumValue { pub deprecation_reason: Option, } -impl<'a, S: fmt::Debug> MetaType<'a, S> { +impl<'a, S> MetaType<'a, S> { /// Access the name of the type, if applicable /// /// Lists, non-null wrappers, and placeholders don't have names. @@ -395,7 +395,7 @@ impl<'a> ListMeta<'a> { } /// Wrap the list in a generic meta type - pub fn into_meta(self) -> MetaType<'a, S> { + pub fn into_meta(self) -> MetaType<'a, S> { MetaType::List(self) } } @@ -407,7 +407,7 @@ impl<'a> NullableMeta<'a> { } /// Wrap the nullable type in a generic meta type - pub fn into_meta(self) -> MetaType<'a, S> { + pub fn into_meta(self) -> MetaType<'a, S> { MetaType::Nullable(self) } } @@ -533,7 +533,7 @@ impl<'a> UnionMeta<'a> { } /// Wrap this union type in a generic meta type - pub fn into_meta(self) -> MetaType<'a, S> { + pub fn into_meta(self) -> MetaType<'a, S> { MetaType::Union(self) } } @@ -569,7 +569,7 @@ where } } -impl<'a, S: fmt::Debug> Field<'a, S> { +impl<'a, S> Field<'a, S> { /// Set the description of the field /// /// This overwrites the description if any was previously set. @@ -603,7 +603,7 @@ impl<'a, S: fmt::Debug> Field<'a, S> { } } -impl<'a, S: fmt::Debug> Argument<'a, S> { +impl<'a, S> Argument<'a, S> { #[doc(hidden)] pub fn new(name: &str, arg_type: Type<'a>) -> Self { Argument { diff --git a/juniper/src/schema/model.rs b/juniper/src/schema/model.rs index 96e53bc12..3b18736ff 100644 --- a/juniper/src/schema/model.rs +++ b/juniper/src/schema/model.rs @@ -7,14 +7,14 @@ use executor::{Context, Registry}; use schema::meta::{Argument, InterfaceMeta, MetaType, ObjectMeta, PlaceholderMeta, UnionMeta}; use types::base::GraphQLType; use types::name::Name; -use value::{ScalarRefValue, ScalarValue}; +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. #[derive(Debug)] -pub struct RootNode<'a, S, QueryT: GraphQLType, MutationT: GraphQLType> +pub struct RootNode<'a, QueryT: GraphQLType, MutationT: GraphQLType, S = DefaultScalarValue> where S: ScalarValue, for<'b> &'b S: ScalarRefValue<'b>, @@ -33,24 +33,24 @@ where /// Metadata for a schema #[derive(Debug)] -pub struct SchemaType<'a, S: fmt::Debug> { +pub struct SchemaType<'a, S> { pub(crate) types: FnvHashMap>, query_type_name: String, mutation_type_name: Option, directives: FnvHashMap>, } -impl<'a, S: fmt::Debug> Context for SchemaType<'a, S> {} +impl<'a, S> Context for SchemaType<'a, S> {} #[derive(Clone)] -pub enum TypeType<'a, S: fmt::Debug + 'a> { +pub enum TypeType<'a, S: 'a> { Concrete(&'a MetaType<'a, S>), NonNull(Box>), List(Box>), } #[derive(Debug)] -pub struct DirectiveType<'a, S: fmt::Debug> { +pub struct DirectiveType<'a, S> { pub name: String, pub description: Option, pub locations: Vec, @@ -71,7 +71,7 @@ pub enum DirectiveLocation { InlineFragment, } -impl<'a, QueryT, MutationT, S> RootNode<'a, S, QueryT, MutationT> +impl<'a, QueryT, MutationT, S> RootNode<'a, QueryT, MutationT, S> where S: ScalarValue + 'a, QueryT: GraphQLType, @@ -82,7 +82,7 @@ where /// /// If the schema should not support mutations, use the /// `new` constructor instead. - pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> RootNode<'a, S, QueryT, MutationT> + pub fn new(query_obj: QueryT, mutation_obj: MutationT) -> Self where for<'b> &'b S: ScalarRefValue<'b>, { @@ -90,7 +90,7 @@ where } } -impl<'a, S, QueryT, MutationT> RootNode<'a, S, QueryT, MutationT> +impl<'a, S, QueryT, MutationT> RootNode<'a, QueryT, MutationT, S> where QueryT: GraphQLType, MutationT: GraphQLType, @@ -105,7 +105,7 @@ where mutation_obj: MutationT, query_info: QueryT::TypeInfo, mutation_info: MutationT::TypeInfo, - ) -> RootNode<'a, S, QueryT, MutationT> + ) -> Self where for<'b> &'b S: ScalarRefValue<'b>, { @@ -119,7 +119,7 @@ where } } -impl<'a, S: fmt::Debug> SchemaType<'a, S> { +impl<'a, S> SchemaType<'a, S> { pub fn new( query_info: &QueryT::TypeInfo, mutation_info: &MutationT::TypeInfo, @@ -353,7 +353,7 @@ impl<'a, S: fmt::Debug> SchemaType<'a, S> { } } -impl<'a, S: fmt::Debug> TypeType<'a, S> { +impl<'a, S> TypeType<'a, S> { #[inline] pub fn to_concrete(&self) -> Option<&'a MetaType> { match *self { @@ -456,7 +456,7 @@ impl fmt::Display for DirectiveLocation { } } -impl<'a, S: fmt::Debug> fmt::Display for TypeType<'a, S> { +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()), diff --git a/juniper/src/schema/schema.rs b/juniper/src/schema/schema.rs index 266f04855..f5d34001d 100644 --- a/juniper/src/schema/schema.rs +++ b/juniper/src/schema/schema.rs @@ -9,7 +9,7 @@ use schema::meta::{ }; use schema::model::{DirectiveLocation, DirectiveType, RootNode, SchemaType, TypeType}; -impl<'a, CtxT, S, QueryT, MutationT> GraphQLType for RootNode<'a, S, QueryT, MutationT> +impl<'a, CtxT, S, QueryT, MutationT> GraphQLType for RootNode<'a, QueryT, MutationT, S> where S: ScalarValue, QueryT: GraphQLType, @@ -36,7 +36,7 @@ where info: &QueryT::TypeInfo, field: &str, args: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { match field { "__schema" => executor @@ -56,7 +56,7 @@ where &self, info: &Self::TypeInfo, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { use types::base::resolve_selection_set_into; use value::Object; diff --git a/juniper/src/tests/introspection_tests.rs b/juniper/src/tests/introspection_tests.rs index 4b1652c19..70384aa14 100644 --- a/juniper/src/tests/introspection_tests.rs +++ b/juniper/src/tests/introspection_tests.rs @@ -4,7 +4,7 @@ use executor::Variables; use schema::model::RootNode; use tests::model::Database; use types::scalars::EmptyMutation; -use value::{DefaultScalarValue, Value}; +use value::Value; #[test] fn test_query_type_name() { @@ -17,8 +17,7 @@ fn test_query_type_name() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -52,8 +51,7 @@ fn test_specific_type_name() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -81,8 +79,7 @@ fn test_specific_object_type_name_and_kind() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -116,8 +113,7 @@ fn test_specific_interface_type_name_and_kind() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -151,8 +147,7 @@ fn test_documentation() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -190,8 +185,7 @@ fn test_possible_types() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); let result = ::execute(doc, None, &schema, &Variables::new(), &database); diff --git a/juniper/src/tests/query_tests.rs b/juniper/src/tests/query_tests.rs index 30ed7c1c5..5ad264744 100644 --- a/juniper/src/tests/query_tests.rs +++ b/juniper/src/tests/query_tests.rs @@ -3,7 +3,7 @@ use executor::Variables; use schema::model::RootNode; use tests::model::Database; use types::scalars::EmptyMutation; -use value::{DefaultScalarValue, Value}; +use value::Value; #[test] fn test_hero_name() { @@ -14,8 +14,7 @@ fn test_hero_name() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -35,8 +34,7 @@ fn test_hero_name() { #[test] fn test_hero_field_order() { let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); let doc = r#" { @@ -106,8 +104,7 @@ fn test_hero_name_and_friends() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -167,8 +164,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -320,8 +316,7 @@ fn test_hero_name_and_friends_and_friends_of_friends() { fn test_query_name() { let doc = r#"{ human(id: "1000") { name } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -346,8 +341,7 @@ fn test_query_name() { fn test_query_alias_single() { let doc = r#"{ luke: human(id: "1000") { name } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -376,8 +370,7 @@ fn test_query_alias_multiple() { leia: human(id: "1003") { name } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -421,8 +414,7 @@ fn test_query_alias_multiple_with_fragment() { homePlanet }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -461,8 +453,7 @@ fn test_query_alias_multiple_with_fragment() { fn test_query_name_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); let vars = vec![("someId".to_owned(), InputValue::scalar("1000"))] .into_iter() @@ -491,8 +482,7 @@ fn test_query_name_variable() { fn test_query_name_invalid_variable() { let doc = r#"query FetchSomeIDQuery($someId: String!) { human(id: $someId) { name } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); let vars = vec![("someId".to_owned(), InputValue::scalar("some invalid id"))] .into_iter() @@ -511,8 +501,7 @@ fn test_query_name_invalid_variable() { fn test_query_friends_names() { let doc = r#"{ human(id: "1000") { friends { name } } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -567,8 +556,7 @@ fn test_query_inline_fragments_droid() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -603,8 +591,7 @@ fn test_query_inline_fragments_human() { } "#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), @@ -636,8 +623,7 @@ fn test_object_typename() { } }"#; let database = Database::new(); - let schema: RootNode = - RootNode::new(&database, EmptyMutation::::new()); + let schema = RootNode::new(&database, EmptyMutation::::new()); assert_eq!( ::execute(doc, None, &schema, &Variables::new(), &database), diff --git a/juniper/src/tests/type_info_tests.rs b/juniper/src/tests/type_info_tests.rs index 11166453c..63b777ef6 100644 --- a/juniper/src/tests/type_info_tests.rs +++ b/juniper/src/tests/type_info_tests.rs @@ -5,7 +5,7 @@ use schema::meta::MetaType; use schema::model::RootNode; use types::base::{Arguments, GraphQLType}; use types::scalars::EmptyMutation; -use value::{DefaultScalarValue, ScalarRefValue, ScalarValue, Value}; +use value::{ScalarRefValue, ScalarValue, Value}; pub struct NodeTypeInfo { name: String, @@ -48,7 +48,7 @@ where _: &Self::TypeInfo, field_name: &str, _: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { executor.resolve(&(), &self.attributes.get(field_name).unwrap()) } @@ -72,8 +72,7 @@ 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 = - 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(), &()), diff --git a/juniper/src/types/base.rs b/juniper/src/types/base.rs index e247dd7ef..f62216fe0 100644 --- a/juniper/src/types/base.rs +++ b/juniper/src/types/base.rs @@ -2,19 +2,17 @@ use indexmap::IndexMap; use ast::{Directive, FromInputValue, InputValue, Selection}; use executor::Variables; -use value::{Object, ScalarRefValue, ScalarValue, Value}; +use value::{DefaultScalarValue, Object, ScalarRefValue, ScalarValue, Value}; use executor::{ExecutionResult, Executor, Registry}; use parser::Spanning; use schema::meta::{Argument, MetaType}; -use std::fmt::Debug; /// GraphQL type kind /// /// The GraphQL specification defines a number of type kinds - the meta type /// of a type. #[derive(Clone, Eq, PartialEq, Debug, GraphQLEnum)] -// Note: _internal flag needed to make derive work in juniper crate itself. #[graphql(name = "__TypeKind")] pub enum TypeKind { /// ## Scalar types @@ -64,14 +62,13 @@ pub enum TypeKind { /// /// In GraphQL, nullable types are the default. By putting a `!` after a /// type, it becomes non-nullable. - #[graphql(name = "NON_NULL")] + #[graphql(name = "NON_NULL")] NonNull, } - /// Field argument container #[derive(Debug)] -pub struct Arguments<'a, S: Debug> { +pub struct Arguments<'a, S = DefaultScalarValue> { args: Option>>, } @@ -152,7 +149,7 @@ root: ```rust use juniper::{GraphQLType, Registry, FieldResult, Context, Arguments, Executor, ExecutionResult, - ScalarValue, ScalarRefValue}; + DefaultScalarValue}; use juniper::meta::MetaType; # use std::collections::HashMap; @@ -163,9 +160,7 @@ struct Database { users: HashMap } impl Context for Database {} -impl GraphQLType for User -where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> +impl GraphQLType for User { type Context = Database; type TypeInfo = (); @@ -174,8 +169,8 @@ where S: ScalarValue, Some("User") } - fn meta<'r>(_: &(), registry: &mut Registry<'r, S>) -> MetaType<'r, S> - where S: '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. // @@ -194,10 +189,10 @@ where S: ScalarValue, &self, info: &(), field_name: &str, - args: &Arguments, - executor: &Executor + args: &Arguments, + executor: &Executor ) - -> ExecutionResult + -> ExecutionResult { // Next, we need to match the queried field name. All arms of this // match statement return `ExecutionResult`, which makes it hard to @@ -234,7 +229,7 @@ where S: ScalarValue, ``` */ -pub trait GraphQLType: Sized +pub trait GraphQLType: Sized where S: ScalarValue, for<'b> &'b S: ScalarRefValue<'b>, @@ -279,7 +274,7 @@ where info: &Self::TypeInfo, field_name: &str, arguments: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { panic!("resolve_field must be implemented by object types"); } @@ -296,7 +291,7 @@ where info: &Self::TypeInfo, type_name: &str, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { if Self::name(info).unwrap() == type_name { Ok(self.resolve(info, selection_set, executor)) @@ -327,7 +322,7 @@ where &self, info: &Self::TypeInfo, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { if let Some(selection_set) = selection_set { let mut result = Object::with_capacity(selection_set.len()); @@ -346,7 +341,7 @@ pub(crate) fn resolve_selection_set_into( instance: &T, info: &T::TypeInfo, selection_set: &[Selection], - executor: &Executor, + executor: &Executor, result: &mut Object, ) -> bool where diff --git a/juniper/src/types/containers.rs b/juniper/src/types/containers.rs index b04cde7b4..1cfd5fc04 100644 --- a/juniper/src/types/containers.rs +++ b/juniper/src/types/containers.rs @@ -30,7 +30,7 @@ where &self, info: &T::TypeInfo, _: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { match *self { Some(ref obj) => executor.resolve_into_value(info, obj), @@ -96,7 +96,7 @@ where &self, info: &T::TypeInfo, _: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { resolve_into_list(executor, info, self.iter()) } @@ -109,7 +109,7 @@ where { fn from_input_value<'a>(v: &'a InputValue) -> Option> where - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { match *v { InputValue::List(ref ls) => { @@ -165,7 +165,7 @@ where &self, info: &T::TypeInfo, _: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { resolve_into_list(executor, info, self.iter()) } @@ -182,7 +182,7 @@ where } fn resolve_into_list( - executor: &Executor, + executor: &Executor, info: &T::TypeInfo, iter: I, ) -> Value diff --git a/juniper/src/types/pointers.rs b/juniper/src/types/pointers.rs index c39041916..4cb4b2d42 100644 --- a/juniper/src/types/pointers.rs +++ b/juniper/src/types/pointers.rs @@ -33,7 +33,7 @@ where info: &T::TypeInfo, name: &str, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -43,7 +43,7 @@ where info: &T::TypeInfo, field: &str, args: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } @@ -52,7 +52,7 @@ where &self, info: &T::TypeInfo, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { (**self).resolve(info, selection_set, executor) } @@ -110,7 +110,7 @@ where info: &T::TypeInfo, name: &str, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -120,7 +120,7 @@ where info: &T::TypeInfo, field: &str, args: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } @@ -129,7 +129,7 @@ where &self, info: &T::TypeInfo, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { (**self).resolve(info, selection_set, executor) } @@ -171,7 +171,7 @@ where info: &T::TypeInfo, name: &str, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_into_type(info, name, selection_set, executor) } @@ -181,7 +181,7 @@ where info: &T::TypeInfo, field: &str, args: &Arguments, - executor: &Executor, + executor: &Executor, ) -> ExecutionResult { (**self).resolve_field(info, field, args, executor) } @@ -190,7 +190,7 @@ where &self, info: &T::TypeInfo, selection_set: Option<&[Selection]>, - executor: &Executor, + executor: &Executor, ) -> Value { (**self).resolve(info, selection_set, executor) } diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index fb9e2d36e..55ce11556 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -9,7 +9,7 @@ use executor::{Executor, Registry}; use parser::{LexerError, ParseError, ScalarToken, Token}; use schema::meta::MetaType; use types::base::GraphQLType; -use value::{ParseScalarValue, ScalarRefValue, ScalarValue, Value}; +use value::{ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue, Value}; /// An ID as defined by the GraphQL specification /// @@ -47,7 +47,7 @@ graphql_scalar!(ID as "ID" where Scalar = { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { match value { ScalarToken::String(value) | ScalarToken::Int(value) => { Ok(S::from(value.to_owned())) @@ -69,7 +69,7 @@ graphql_scalar!(String as "String" where Scalar = { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + 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(); @@ -180,7 +180,7 @@ where &self, _: &(), _: Option<&[Selection]>, - _: &Executor, + _: &Executor, ) -> Value { Value::scalar(String::from(*self)) } @@ -207,7 +207,8 @@ graphql_scalar!(bool as "Boolean" where Scalar = { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + + 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))) } @@ -225,7 +226,7 @@ graphql_scalar!(i32 as "Int" where Scalar = { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { if let ScalarToken::Int(v) = value { v.parse() .map_err(|_| ParseError::UnexpectedToken(Token::Scalar(value))) @@ -251,7 +252,7 @@ graphql_scalar!(f64 as "Float" where Scalar = { } } - from_str<'a>(value: ScalarToken<'a>) -> Result> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { match value { ScalarToken::Int(v) | ScalarToken::Float(v) => { v.parse() @@ -290,7 +291,7 @@ impl ParseScalarValue for () where S: ScalarValue, { - fn from_str<'a>(_value: ScalarToken<'a>) -> Result> { + fn from_str<'a>(_value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { Ok(S::from(0)) } } diff --git a/juniper/src/value/mod.rs b/juniper/src/value/mod.rs index be27eb9ad..95e72c237 100644 --- a/juniper/src/value/mod.rs +++ b/juniper/src/value/mod.rs @@ -5,7 +5,9 @@ mod scalar; pub use self::object::Object; -pub use self::scalar::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, ScalarValue}; +pub use self::scalar::{ + DefaultScalarValue, ParseScalarResult, ParseScalarValue, ScalarRefValue, ScalarValue, +}; /// Serializable value returned from query and field execution. /// @@ -18,7 +20,7 @@ pub use self::scalar::{DefaultScalarValue, ParseScalarValue, ScalarRefValue, Sca /// than parsing a source query. #[derive(Debug, PartialEq, Clone)] #[allow(missing_docs)] -pub enum Value { +pub enum Value { Null, Scalar(S), List(Vec>), @@ -100,7 +102,10 @@ where } /// View the underlying float value, if present. - #[deprecated(since = "0.11", note = "Use `Value::as_scalar_value` instead")] + #[deprecated( + since = "0.11", + note = "Use `Value::as_scalar_value` instead" + )] pub fn as_float_value(&self) -> Option where for<'a> &'a S: ScalarRefValue<'a>, @@ -141,7 +146,10 @@ where } /// View the underlying string value, if present. - #[deprecated(since = "0.11", note = "Use `Value::as_scalar_value` instead")] + #[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>, diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 25d275a4e..c875d2f66 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -3,10 +3,13 @@ use serde::de::{self, Deserialize, Deserializer}; 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>; + /// A trait used to convert a `ScalarToken` into a certain scalar value type -pub trait ParseScalarValue { +pub trait ParseScalarValue { /// See the trait documentation - fn from_str<'a>(value: ScalarToken<'a>) -> Result>; + fn from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S>; } /// A trait marking a type that could be used as internal representation of diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 468a941bf..004f1634a 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -209,7 +209,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { &self, _: &(), _: Option<&[_juniper::Selection<__S>]>, - _: &_juniper::Executor<__S, Self::Context> + _: &_juniper::Executor ) -> _juniper::Value<__S> { match self { #(#resolves)* From 324311676ab28949668562f39b0050437be2273a Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 19 Sep 2018 22:50:40 +0200 Subject: [PATCH 09/20] Improve custom scalar type test case * There was an issue while serializing values from json * use `graphql_object!` for implementing `GraphQLType` --- juniper/src/executor_tests/custom_scalar.rs | 215 +++++++-------- juniper/src/integrations/serde.rs | 149 ++++++++--- juniper/src/value/scalar.rs | 246 +++++++++--------- .../src/derive_juniper_scalar_value.rs | 15 +- juniper_codegen/src/lib.rs | 2 +- juniper_codegen/src/util.rs | 10 +- 6 files changed, 351 insertions(+), 286 deletions(-) diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper/src/executor_tests/custom_scalar.rs index ddaf1c227..0ca9763b6 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper/src/executor_tests/custom_scalar.rs @@ -1,15 +1,16 @@ +extern crate serde_json; + use ast::InputValue; -use executor::{ExecutionResult, Executor, Registry, Variables}; -use parser::{ParseError, ScalarToken, Token}; -use schema::meta::MetaType; +use executor::Variables; +use parser::{ParseError, ScalarToken, Spanning, Token}; use schema::model::RootNode; -use serde::de::{self, Deserialize, Deserializer}; +use serde::de; use std::fmt; -use types::base::{Arguments, GraphQLType}; use types::scalars::EmptyMutation; -use value::{Object, ParseScalarResult, ScalarRefValue, Value}; +use value::{Object, ParseScalarResult, Value}; #[derive(Debug, Clone, PartialEq, ScalarValue)] +#[juniper(visitor = "MyScalarValueVisitor")] enum MyScalarValue { Int(i32), Long(i64), @@ -18,82 +19,74 @@ enum MyScalarValue { Boolean(bool), } -impl<'de> Deserialize<'de> for MyScalarValue { - fn deserialize(deserializer: D) -> Result +#[derive(Default, Debug)] +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(self, value: bool) -> Result { + Ok(MyScalarValue::Boolean(value)) + } + + fn visit_i32(self, value: i32) -> Result + where + E: de::Error, + { + Ok(MyScalarValue::Int(value)) + } + + fn visit_i64(self, value: i64) -> Result where - D: Deserializer<'de>, + E: de::Error, { - 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(self, value: bool) -> Result { - Ok(MyScalarValue::Boolean(value)) - } - - fn visit_i32(self, value: i32) -> Result - where - E: de::Error, - { - Ok(MyScalarValue::Int(value)) - } - - fn visit_i64(self, value: i64) -> Result - where - E: de::Error, - { - Ok(MyScalarValue::Long(value)) - } - - fn visit_u32(self, value: u32) -> Result - 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(self, value: u64) -> Result - 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(self, value: f64) -> Result { - Ok(MyScalarValue::Float(value)) - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - self.visit_string(value.into()) - } - - fn visit_string(self, value: String) -> Result { - Ok(MyScalarValue::String(value)) - } + Ok(MyScalarValue::Long(value)) + } + + fn visit_u32(self, value: u32) -> Result + 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(self, value: u64) -> Result + 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(self, value: f64) -> Result { + Ok(MyScalarValue::Float(value)) + } - deserializer.deserialize_any(MyScalarValueVisitor) + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.into()) + } + + fn visit_string(self, value: String) -> Result { + Ok(MyScalarValue::String(value)) } } @@ -122,53 +115,15 @@ graphql_scalar!(i64 as "Long" where Scalar = MyScalarValue { struct TestType; -impl GraphQLType for TestType { - type Context = (); - type TypeInfo = (); - - fn name((): &Self::TypeInfo) -> Option<&str> { - Some("TestType") - } - - fn meta<'r>( - info: &Self::TypeInfo, - registry: &mut Registry<'r, MyScalarValue>, - ) -> MetaType<'r, MyScalarValue> - where - MyScalarValue: 'r, - for<'b> &'b MyScalarValue: ScalarRefValue<'b>, - { - let long_field = registry.field::("longField", info); - - let long_arg = registry.arg::("longArg", info); - - let long_field_with_arg = registry - .field::("longWithArg", info) - .argument(long_arg); - - registry - .build_object_type::(info, &[long_field, long_field_with_arg]) - .into_meta() +graphql_object!(TestType: () where Scalar = MyScalarValue |&self| { + field long_field() -> i64 { + (::std::i32::MAX as i64) + 1 } - fn resolve_field( - &self, - _info: &Self::TypeInfo, - field_name: &str, - args: &Arguments, - _executor: &Executor, - ) -> ExecutionResult { - match field_name { - "longField" => Ok(Value::Scalar(MyScalarValue::Long( - (::std::i32::MAX as i64) + 1, - ))), - "longWithArg" => Ok(Value::Scalar(MyScalarValue::Long( - args.get::("longArg").unwrap(), - ))), - _ => unreachable!(), - } + field long_with_arg(long_arg: i64) -> i64 { + long_arg } -} +}); fn run_variable_query(query: &str, vars: Variables, f: F) where @@ -237,3 +192,19 @@ fn querying_long_variable() { }, ); } + +#[test] +fn deserialize_variable() { + let json = format!("{{\"field\": {}}}", (::std::i32::MAX as i64) + 42); + + let input_value: InputValue = self::serde_json::from_str(&json).unwrap(); + assert_eq!( + input_value, + InputValue::Object(vec![( + Spanning::unlocated("field".into()), + Spanning::unlocated(InputValue::Scalar(MyScalarValue::Long( + (::std::i32::MAX as i64) + 42 + ))) + )]) + ); +} diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index ab0ac962f..159f18223 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -71,23 +71,23 @@ impl<'a> ser::Serialize for GraphQLError<'a> { impl<'de, S> de::Deserialize<'de> for InputValue where - S: de::Deserialize<'de> + ScalarValue, + S: ScalarValue, { fn deserialize(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de>, { - struct InputValueVisitor(::std::marker::PhantomData); + struct InputValueVisitor(S::Visitor); - impl Default for InputValueVisitor { + impl Default for InputValueVisitor { fn default() -> Self { - InputValueVisitor(Default::default()) + InputValueVisitor(S::Visitor::default()) } } impl<'de, S> de::Visitor<'de> for InputValueVisitor where - S: ScalarValue + de::Deserialize<'de>, + S: ScalarValue, { type Value = InputValue; @@ -95,62 +95,143 @@ where formatter.write_str("a valid input value") } - fn visit_bool(self, value: bool) -> Result, E> { - Ok(InputValue::scalar(value)) + fn visit_bool(self, value: bool) -> Result, E> + where + E: de::Error, + { + self.0.visit_bool(value).map(InputValue::Scalar) + } + + fn visit_i8(self, value: i8) -> Result, E> + where + E: de::Error, + { + self.0.visit_i8(value).map(InputValue::Scalar) + } + + fn visit_i16(self, value: i16) -> Result, E> + where + E: de::Error, + { + self.0.visit_i16(value).map(InputValue::Scalar) + } + + fn visit_i32(self, value: i32) -> Result, E> + where + E: de::Error, + { + self.0.visit_i32(value).map(InputValue::Scalar) } fn visit_i64(self, value: i64) -> Result, E> where E: de::Error, { - if value >= i64::from(i32::min_value()) && value <= i64::from(i32::max_value()) { - Ok(InputValue::scalar(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::scalar(value as f64)) - } + self.0.visit_i64(value).map(InputValue::Scalar) + } + + fn visit_i128(self, value: i128) -> Result, E> + where + E: de::Error, + { + self.0.visit_i128(value).map(InputValue::Scalar) + } + + fn visit_u8(self, value: u8) -> Result, E> + where + E: de::Error, + { + self.0.visit_u8(value).map(InputValue::Scalar) + } + + fn visit_u16(self, value: u16) -> Result, E> + where + E: de::Error, + { + self.0.visit_u16(value).map(InputValue::Scalar) + } + + fn visit_u32(self, value: u32) -> Result, E> + where + E: de::Error, + { + self.0.visit_u32(value).map(InputValue::Scalar) } fn visit_u64(self, value: u64) -> Result, 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::scalar(value as f64)) - } + self.0.visit_u64(value).map(InputValue::Scalar) + } + + fn visit_u128(self, value: u128) -> Result, E> + where + E: de::Error, + { + self.0.visit_u128(value).map(InputValue::Scalar) } - fn visit_f64(self, value: f64) -> Result, E> { - Ok(InputValue::scalar(value)) + fn visit_f32(self, value: f32) -> Result, E> + where + E: de::Error, + { + self.0.visit_f32(value).map(InputValue::Scalar) + } + + fn visit_f64(self, value: f64) -> Result, E> + where + E: de::Error, + { + self.0.visit_f64(value).map(InputValue::Scalar) + } + + fn visit_char(self, value: char) -> Result, E> + where + E: de::Error, + { + self.0.visit_char(value).map(InputValue::Scalar) } fn visit_str(self, value: &str) -> Result, E> where E: de::Error, { - self.visit_string(value.into()) + self.0.visit_str(value).map(InputValue::Scalar) } - fn visit_string(self, value: String) -> Result, E> { - Ok(InputValue::scalar(value)) + fn visit_string(self, value: String) -> Result, E> + where + E: de::Error, + { + self.0.visit_string(value).map(InputValue::Scalar) + } + + fn visit_bytes(self, bytes: &[u8]) -> Result, E> + where + E: de::Error, + { + self.0.visit_bytes(bytes).map(InputValue::Scalar) } - fn visit_none(self) -> Result, E> { + fn visit_byte_buf(self, bytes: Vec) -> Result, E> + where + E: de::Error, + { + self.0.visit_byte_buf(bytes).map(InputValue::Scalar) + } + + fn visit_none(self) -> Result, E> + where + E: de::Error, + { Ok(InputValue::null()) } - fn visit_unit(self) -> Result, E> { + fn visit_unit(self) -> Result, E> + where + E: de::Error, + { Ok(InputValue::null()) } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index c875d2f66..88b25154a 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -1,5 +1,5 @@ use parser::{ParseError, ScalarToken}; -use serde::de::{self, Deserialize, Deserializer}; +use serde::de; use serde::ser::Serialize; use std::fmt::{self, Debug, Display}; @@ -20,7 +20,9 @@ pub trait ParseScalarValue { /// needs. /// There is a custom derive (`#[derive(ScalarValue)]`) available that implements /// most of the required traits automatically for a enum representing a scalar value. -/// The only trait that needs to be implemented manually in this case is `serde::Deserialize`. +/// 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 @@ -36,6 +38,7 @@ pub trait ParseScalarValue { /// # use std::fmt; /// # /// #[derive(Debug, Clone, PartialEq, ScalarValue)] +/// #[juniper(visitor = "MyScalarValueVisitor")] /// enum MyScalarValue { /// Int(i32), /// Long(i64), @@ -44,84 +47,77 @@ pub trait ParseScalarValue { /// Boolean(bool), /// } /// -/// impl<'de> Deserialize<'de> for MyScalarValue { -/// fn deserialize(deserializer: D) -> Result -/// where -/// D: Deserializer<'de>, -/// { -/// struct MyScalarValueVisitor; -/// -/// impl<'de> de::Visitor<'de> for MyScalarValueVisitor { -/// type Value = MyScalarValue; +/// #[derive(Default)] +/// struct MyScalarValueVisitor; /// -/// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { -/// formatter.write_str("a valid input value") -/// } +/// impl<'de> de::Visitor<'de> for MyScalarValueVisitor { +/// type Value = MyScalarValue; /// -/// fn visit_bool(self, value: bool) -> Result { -/// Ok(MyScalarValue::Boolean(value)) -/// } +/// fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { +/// formatter.write_str("a valid input value") +/// } /// -/// fn visit_i32(self, value: i32) -> Result -/// where -/// E: de::Error, -/// { -/// Ok(MyScalarValue::Int(value)) -/// } +/// fn visit_bool(self, value: bool) -> Result { +/// Ok(MyScalarValue::Boolean(value)) +/// } /// -/// fn visit_i64(self, value: i64) -> Result -/// where -/// E: de::Error, -/// { -/// Ok(MyScalarValue::Long(value)) -/// } +/// fn visit_i32(self, value: i32) -> Result +/// where +/// E: de::Error, +/// { +/// Ok(MyScalarValue::Int(value)) +/// } /// -/// fn visit_u32(self, value: u32) -> Result -/// 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_i64(self, value: i64) -> Result +/// where +/// E: de::Error, +/// { +/// Ok(MyScalarValue::Long(value)) +/// } /// -/// fn visit_u64(self, value: u64) -> Result -/// 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_u32(self, value: u32) -> Result +/// 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_f64(self, value: f64) -> Result { -/// Ok(MyScalarValue::Float(value)) -/// } +/// fn visit_u64(self, value: u64) -> Result +/// 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_str(self, value: &str) -> Result -/// where -/// E: de::Error, -/// { -/// self.visit_string(value.into()) -/// } +/// fn visit_f64(self, value: f64) -> Result { +/// Ok(MyScalarValue::Float(value)) +/// } /// -/// fn visit_string(self, value: String) -> Result { -/// Ok(MyScalarValue::String(value)) -/// } -/// } +/// fn visit_str(self, value: &str) -> Result +/// where +/// E: de::Error, +/// { +/// self.visit_string(value.into()) +/// } /// -/// deserializer.deserialize_any(MyScalarValueVisitor) +/// fn visit_string(self, value: String) -> Result { +/// Ok(MyScalarValue::String(value)) /// } /// } +/// /// # fn main() {} /// ``` pub trait ScalarValue: @@ -139,6 +135,9 @@ pub trait ScalarValue: + Into> + Into> { + /// 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 /// /// ``` @@ -190,6 +189,7 @@ where /// This types closely follows the graphql specification. #[derive(Debug, PartialEq, Clone, ScalarValue)] #[allow(missing_docs)] +#[juniper(visitor = "DefaultScalarValueVisitor")] pub enum DefaultScalarValue { Int(i32), Float(f64), @@ -203,72 +203,64 @@ impl<'a> From<&'a str> for DefaultScalarValue { } } -impl<'de> Deserialize<'de> for DefaultScalarValue { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct DefaultScalarValueVisitor; +#[derive(Default, Clone, Copy, Debug)] +pub struct DefaultScalarValueVisitor; - impl<'de> de::Visitor<'de> for DefaultScalarValueVisitor { - type Value = DefaultScalarValue; +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(self, value: bool) -> Result { - Ok(DefaultScalarValue::Boolean(value)) - } + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid input value") + } - fn visit_i64(self, value: i64) -> Result - 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_bool(self, value: bool) -> Result { + Ok(DefaultScalarValue::Boolean(value)) + } - fn visit_u64(self, value: u64) -> Result - 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_i64(self, value: i64) -> Result + 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_f64(self, value: f64) -> Result { - Ok(DefaultScalarValue::Float(value)) - } + fn visit_u64(self, value: u64) -> Result + 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_str(self, value: &str) -> Result - where - E: de::Error, - { - self.visit_string(value.into()) - } + fn visit_f64(self, value: f64) -> Result { + Ok(DefaultScalarValue::Float(value)) + } - fn visit_string(self, value: String) -> Result { - Ok(DefaultScalarValue::String(value)) - } - } + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + self.visit_string(value.into()) + } - deserializer.deserialize_any(DefaultScalarValueVisitor) + fn visit_string(self, value: String) -> Result { + Ok(DefaultScalarValue::String(value)) } } diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs index 3b03f9f5d..cc467117d 100644 --- a/juniper_codegen/src/derive_juniper_scalar_value.rs +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -1,8 +1,19 @@ use quote::Tokens; use syn::{self, Data, Fields, Ident, Variant}; +use util::{get_juniper_attr, keyed_item_value}; pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { let ident = &ast.ident; + let visitor = if let Some(items) = get_juniper_attr(&ast.attrs) { + items.into_iter() + .filter_map(|i| keyed_item_value(&i, "visitor", true)) + .next() + .map(|t| Ident::from(&t as &str)) + .expect("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`") + } else { + panic!("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`"); + }; + let variants = match ast.data { Data::Enum(ref enum_data) => &enum_data.variants, _ => { @@ -34,7 +45,9 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { #serialize #display - impl juniper::ScalarValue for #ident {} + impl juniper::ScalarValue for #ident { + type Visitor = #visitor; + } }; } diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index 712f22bc6..5d3abd192 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -44,7 +44,7 @@ pub fn derive_object(input: TokenStream) -> TokenStream { gen.into() } -#[proc_macro_derive(ScalarValue)] +#[proc_macro_derive(ScalarValue, attributes(juniper))] pub fn derive_juniper_scalar_value(input: TokenStream) -> TokenStream { let ast = syn::parse::(input).unwrap(); let gen = derive_juniper_scalar_value::impl_scalar_value(&ast); diff --git a/juniper_codegen/src/util.rs b/juniper_codegen/src/util.rs index 676a98cb4..c20c7cf5e 100644 --- a/juniper_codegen/src/util.rs +++ b/juniper_codegen/src/util.rs @@ -71,9 +71,17 @@ fn get_doc_attr(attrs: &Vec) -> Option> { // Get the nested items of a a #[graphql(...)] attribute. pub fn get_graphql_attr(attrs: &Vec) -> Option> { + get_named_attr(attrs, "graphql") +} + +pub fn get_juniper_attr(attrs: &Vec) -> Option> { + get_named_attr(attrs, "juniper") +} + +pub fn get_named_attr(attrs: &Vec, name: &str) -> Option> { for attr in attrs { match attr.interpret_meta() { - Some(Meta::List(ref list)) if list.ident == "graphql" => { + Some(Meta::List(ref list)) if list.ident == name => { return Some(list.nested.iter().map(|x| x.clone()).collect()); } _ => {} From 955d69855c3cf35be3fc00f77fb7981ce5fa8286 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 19 Sep 2018 23:12:25 +0200 Subject: [PATCH 10/20] Extend derives to support custom scalar value representations Allow to specify the used scalar value representations via a attribute (`#[graphql(scalar = "Type")]`). As default a generic implementation is provided because that will work in most cases. --- juniper/src/executor_tests/variables.rs | 6 ++- juniper_codegen/src/derive_enum.rs | 14 +----- juniper_codegen/src/derive_input_object.rs | 57 ++++++++++++---------- juniper_codegen/src/derive_object.rs | 47 +++++++++++++++--- 4 files changed, 75 insertions(+), 49 deletions(-) diff --git a/juniper/src/executor_tests/variables.rs b/juniper/src/executor_tests/variables.rs index 3a8b7187c..d450a8a01 100644 --- a/juniper/src/executor_tests/variables.rs +++ b/juniper/src/executor_tests/variables.rs @@ -12,7 +12,7 @@ struct TestComplexScalar; struct TestType; -graphql_scalar!(TestComplexScalar where Scalar = { +graphql_scalar!(TestComplexScalar { resolve(&self) -> Value { Value::scalar(String::from("SerializedValue")) } @@ -27,12 +27,13 @@ graphql_scalar!(TestComplexScalar where Scalar = { None } - from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { >::from_str(value) } }); #[derive(GraphQLInputObject, Debug)] +#[graphql(scalar = "DefaultScalarValue")] struct TestInputObject { a: Option, b: Option>>, @@ -41,6 +42,7 @@ struct TestInputObject { } #[derive(GraphQLInputObject, Debug)] +#[graphql(scalar = "DefaultScalarValue")] struct TestNestedInputObject { na: TestInputObject, nb: String, diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 004f1634a..769afc6be 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -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, description: Option, - 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. @@ -42,15 +39,6 @@ impl EnumAttrs { 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 diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs index e1a7fb74c..74a241ade 100644 --- a/juniper_codegen/src/derive_input_object.rs +++ b/juniper_codegen/src/derive_input_object.rs @@ -10,7 +10,7 @@ use util::*; struct ObjAttrs { name: Option, description: Option, - internal: bool, + scalar: Option, } impl ObjAttrs { @@ -38,14 +38,9 @@ impl ObjAttrs { res.description = Some(val); continue; } - match item { - NestedMeta::Meta(Meta::Word(ref ident)) => { - if ident == "_internal" { - res.internal = true; - continue; - } - } - _ => {} + if let Some(scalar) = keyed_item_value(&item, "scalar", true) { + res.scalar = Some(Ident::from(&scalar as &str)); + continue; } panic!(format!( "Unknown attribute for #[derive(GraphQLInputObject)]: {:?}", @@ -94,6 +89,7 @@ impl ObjFieldAttrs { res.default_expr = Some(val); continue; } + match item { NestedMeta::Meta(Meta::Word(ref ident)) => { if ident == "default" { @@ -240,10 +236,23 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { }); } + let (where_clause, define_scalar) = if attrs.scalar.is_none() { + ( + Some(quote!{ + where __S: _juniper::ScalarValue, + for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b> + }), + Some(quote!(<__S>)), + ) + } else { + (None, None) + }; + + let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); + let body = quote! { - impl _juniper::GraphQLType for #ident - where S: _juniper::ScalarValue, - for<'b> &'b S: _juniper::ScalarRefValue<'b> + impl#define_scalar _juniper::GraphQLType<#scalar> for #ident + #where_clause { type Context = (); type TypeInfo = (); @@ -254,9 +263,9 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { fn meta<'r>( _: &(), - registry: &mut _juniper::Registry<'r, S> - ) -> _juniper::meta::MetaType<'r, S> - where S: 'r + registry: &mut _juniper::Registry<'r, #scalar> + ) -> _juniper::meta::MetaType<'r, #scalar> + where #scalar: 'r { let fields = &[ #(#meta_fields)* @@ -267,15 +276,12 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { } } - impl _juniper::FromInputValue for #ident - where S: _juniper::ScalarValue, - for<'__b> &'__b S: _juniper::ScalarRefValue<'__b> + impl#define_scalar _juniper::FromInputValue<#scalar> for #ident + #where_clause { - fn from_input_value(value: &_juniper::InputValue) -> Option<#ident> + fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option<#ident> where - for<'b> &'b S: _juniper::ScalarRefValue<'b> - // S: 'a, - // &'a S: _juniper::ScalarRefValue<'a>, + for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b> { if let Some(obj) = value.to_object_value() { let item = #ident { @@ -289,11 +295,10 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { } } - impl _juniper::ToInputValue for #ident - where S: _juniper::ScalarValue, - for<'__b> &'__b S: _juniper::ScalarRefValue<'__b> + impl#define_scalar _juniper::ToInputValue<#scalar> for #ident + #where_clause { - fn to_input_value(&self) -> _juniper::InputValue { + fn to_input_value(&self) -> _juniper::InputValue<#scalar> { _juniper::InputValue::object(vec![ #(#to_inputs)* ].into_iter().collect()) diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs index 8d2173c47..9b7a74ea8 100644 --- a/juniper_codegen/src/derive_object.rs +++ b/juniper_codegen/src/derive_object.rs @@ -1,6 +1,6 @@ use proc_macro2::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, description: Option, + scalar: Option, } impl ObjAttrs { @@ -35,6 +36,10 @@ impl ObjAttrs { res.description = Some(val); continue; } + if let Some(AttributeValue::String(scalar)) = keyed_item_value(&item, "scalar", true) { + res.scalar = Some(Ident::from(&scalar as &str)); + continue; + } panic!(format!( "Unknown object attribute for #[derive(GraphQLObject)]: {:?}", item @@ -170,8 +175,25 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { }); } + let (where_clause, define_scalar) = if attrs.scalar.is_none() { + ( + Some(quote!{ + where __S: juniper::ScalarValue, + for<'__b> &'__b: juniper::ScalarRefValue<'__b> + }), + Some(quote!(<__S>)), + ) + } else { + (None, None) + }; + + let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); + let dummy_const = Ident::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + let toks = quote! { - impl #generics ::juniper::GraphQLType for #ident #generics { + impl#define_scalar juniper::GraphQLType<#scalar> for #ident + #where_clause + { type Context = (); type TypeInfo = (); @@ -185,8 +207,8 @@ 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> { let fields = &[ #(#meta_fields)* ]; @@ -199,9 +221,9 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { &self, _: &(), field_name: &str, - _: &::juniper::Arguments, - executor: &::juniper::Executor - ) -> ::juniper::ExecutionResult + _: &juniper::Arguments<#scalar>, + executor: &juniper::Executor + ) -> juniper::ExecutionResult<#scalar> { match field_name { @@ -212,6 +234,15 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { } } }; + quote!{ + const #dummy_const: () = { + mod juniper { + __juniper_use_everything!(); + } + + extern crate std; - toks + #toks + }; + } } From 435e85e9e8280f0fdea22155937a83b7987399fd Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 19 Sep 2018 23:29:36 +0200 Subject: [PATCH 11/20] Fix juniper_iron --- juniper/src/http/mod.rs | 8 ++-- juniper_iron/src/lib.rs | 91 ++++++++++++++++++++++++++--------------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/juniper/src/http/mod.rs b/juniper/src/http/mod.rs index b69448815..dab4852dc 100644 --- a/juniper/src/http/mod.rs +++ b/juniper/src/http/mod.rs @@ -7,7 +7,7 @@ use serde::ser::{self, Serialize, SerializeMap}; use ast::InputValue; use executor::ExecutionError; -use value::{ScalarRefValue, ScalarValue}; +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. @@ -18,7 +18,7 @@ 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 where S: ScalarValue, { @@ -92,7 +92,9 @@ where /// 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, S>(Result<(Value, Vec>), GraphQLError<'a>>); +pub struct GraphQLResponse<'a, S = DefaultScalarValue>( + Result<(Value, Vec>), GraphQLError<'a>>, +); impl<'a, S> GraphQLResponse<'a, S> where diff --git a/juniper_iron/src/lib.rs b/juniper_iron/src/lib.rs index 79ed2d974..3b0e185b9 100644 --- a/juniper_iron/src/lib.rs +++ b/juniper_iron/src/lib.rs @@ -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), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLBatchRequest +where + S: ScalarValue, +{ + Single(http::GraphQLRequest), + Batch(Vec>), } #[derive(Serialize)] #[serde(untagged)] -enum GraphQLBatchResponse<'a> { - Single(http::GraphQLResponse<'a>), - Batch(Vec>), +enum GraphQLBatchResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + Single(http::GraphQLResponse<'a, S>), + Batch(Vec>), } -impl GraphQLBatchRequest { +impl GraphQLBatchRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &RootNode, + root_node: &'a RootNode, context: &CtxT, - ) -> GraphQLBatchResponse<'a> + ) -> GraphQLBatchResponse<'a, S> where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLType, + MutationT: GraphQLType, { 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 + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType + Send + Sync + 'static, - Mutation: GraphQLType + Send + Sync + 'static, + Query: GraphQLType + Send + Sync + 'static, + Mutation: GraphQLType + 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>) -> IronResult> { } } -fn parse_variable_param(params: Option>) -> IronResult> { +fn parse_variable_param(params: Option>) -> IronResult>> +where + S: ScalarValue, +{ if let Some(values) = params { Ok( - serde_json::from_str::(get_single_value(values)?.as_ref()) + serde_json::from_str::>(get_single_value(values)?.as_ref()) .map(Some) .map_err(GraphQLIronError::Serde)?, ) @@ -232,12 +252,15 @@ fn parse_variable_param(params: Option>) -> IronResult 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 + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType + Send + Sync + 'static, - Mutation: GraphQLType + Send + Sync + 'static, + Query: GraphQLType + Send + Sync + 'static, + Mutation: GraphQLType + Send + Sync + 'static, { /// Build a new GraphQL handler /// @@ -252,8 +275,9 @@ where } } - fn handle_get(&self, req: &mut Request) -> IronResult { - let url_query_string = req.get_mut::() + fn handle_get(&self, req: &mut Request) -> IronResult> { + let url_query_string = req + .get_mut::() .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 { + fn handle_post(&self, req: &mut Request) -> IronResult> { let mut request_payload = String::new(); itry!(req.body.read_to_string(&mut request_payload)); Ok( - serde_json::from_str::(request_payload.as_str()) + serde_json::from_str::>(request_payload.as_str()) .map_err(GraphQLIronError::Serde)?, ) } - fn execute(&self, context: &CtxT, request: GraphQLBatchRequest) -> IronResult { + fn execute(&self, context: &CtxT, request: GraphQLBatchRequest) -> IronResult { let response = request.execute(&self.root_node, context); let content_type = "application/json".parse::().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 + Send + Sync + 'static, CtxT: 'static, - Query: GraphQLType + Send + Sync + 'static, - Mutation: GraphQLType + Send + Sync + 'static, + Query: GraphQLType + Send + Sync + 'static, + Mutation: GraphQLType + Send + Sync + 'static, 'a: 'static, { fn handle(&self, mut req: &mut Request) -> IronResult { @@ -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::>() From ace8714ee9755d99e80fa3fbf0212986f42fd38d Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 20 Sep 2018 00:24:31 +0200 Subject: [PATCH 12/20] Fix juniper_tests * Fix issues with the GraphQLObject derive * Move custom_scalar test here because this is something that should be possible outside of juniper. In that way we ensure that this is tested. --- juniper/src/executor_tests/mod.rs | 1 - juniper_codegen/src/derive_object.rs | 6 +- juniper_tests/src/codegen/derive_enum.rs | 30 ++++---- .../src/codegen/derive_input_object.rs | 14 ++-- juniper_tests/src/codegen/derive_object.rs | 70 ++++++++++++------- .../src}/custom_scalar.rs | 18 ++--- juniper_tests/src/lib.rs | 1 + 7 files changed, 83 insertions(+), 57 deletions(-) rename {juniper/src/executor_tests => juniper_tests/src}/custom_scalar.rs (93%) diff --git a/juniper/src/executor_tests/mod.rs b/juniper/src/executor_tests/mod.rs index 53d32e175..010976183 100644 --- a/juniper/src/executor_tests/mod.rs +++ b/juniper/src/executor_tests/mod.rs @@ -4,4 +4,3 @@ mod executor; mod interfaces_unions; mod introspection; mod variables; -mod custom_scalar; diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs index 9b7a74ea8..e0218b580 100644 --- a/juniper_codegen/src/derive_object.rs +++ b/juniper_codegen/src/derive_object.rs @@ -179,7 +179,7 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { ( Some(quote!{ where __S: juniper::ScalarValue, - for<'__b> &'__b: juniper::ScalarRefValue<'__b> + for<'__b> &'__b __S: juniper::ScalarRefValue<'__b> }), Some(quote!(<__S>)), ) @@ -208,7 +208,9 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { fn meta<'r>( _: &(), registry: &mut juniper::Registry<'r, #scalar> - ) -> juniper::meta::MetaType<'r, #scalar> { + ) -> juniper::meta::MetaType<'r, #scalar> + where #scalar: 'r + { let fields = &[ #(#meta_fields)* ]; diff --git a/juniper_tests/src/codegen/derive_enum.rs b/juniper_tests/src/codegen/derive_enum.rs index 84329a63d..755ec67b2 100644 --- a/juniper_tests/src/codegen/derive_enum.rs +++ b/juniper_tests/src/codegen/derive_enum.rs @@ -2,13 +2,17 @@ use fnv::FnvHashMap; #[cfg(test)] -use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue}; +use juniper::{self, DefaultScalarValue, FromInputValue, GraphQLType, InputValue, ToInputValue}; #[derive(GraphQLEnum, Debug, PartialEq)] #[graphql(name = "Some", description = "enum descr")] enum SomeEnum { Regular, - #[graphql(name = "FULL", description = "field descr", deprecated = "depr")] + #[graphql( + name = "FULL", + description = "field descr", + deprecated = "depr" + )] Full, } @@ -42,10 +46,10 @@ enum OverrideDocEnum { #[test] fn test_derived_enum() { // Ensure that rename works. - assert_eq!(SomeEnum::name(&()), Some("Some")); + assert_eq!(::name(&()), Some("Some")); // Ensure validity of meta info. - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = SomeEnum::meta(&(), &mut registry); assert_eq!(meta.name(), Some("Some")); @@ -53,35 +57,35 @@ fn test_derived_enum() { // Test Regular variant. assert_eq!( - SomeEnum::Regular.to_input_value(), - InputValue::String("REGULAR".into()) + <_ as ToInputValue>::to_input_value(&SomeEnum::Regular), + InputValue::scalar("REGULAR") ); assert_eq!( - FromInputValue::from_input_value(&InputValue::String("REGULAR".into())), + FromInputValue::::from_input_value(&InputValue::scalar("REGULAR")), Some(SomeEnum::Regular) ); // Test FULL variant. assert_eq!( - SomeEnum::Full.to_input_value(), - InputValue::String("FULL".into()) + <_ as ToInputValue>::to_input_value(&SomeEnum::Full), + InputValue::scalar("FULL") ); assert_eq!( - FromInputValue::from_input_value(&InputValue::String("FULL".into())), + FromInputValue::::from_input_value(&InputValue::scalar("FULL")), Some(SomeEnum::Full) ); } #[test] fn test_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = DocEnum::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Enum doc.".to_string())); } #[test] fn test_multi_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = MultiDocEnum::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -91,7 +95,7 @@ fn test_multi_doc_comment() { #[test] fn test_doc_comment_override() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = OverrideDocEnum::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"enum override".to_string())); } diff --git a/juniper_tests/src/codegen/derive_input_object.rs b/juniper_tests/src/codegen/derive_input_object.rs index 977529b93..191d6c94b 100644 --- a/juniper_tests/src/codegen/derive_input_object.rs +++ b/juniper_tests/src/codegen/derive_input_object.rs @@ -3,8 +3,10 @@ use fnv::FnvHashMap; use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue}; +use juniper::DefaultScalarValue; + #[derive(GraphQLInputObject, Debug, PartialEq)] -#[graphql(name = "MyInput", description = "input descr")] +#[graphql(name = "MyInput", description = "input descr", scalar = "DefaultScalarValue")] struct Input { regular_field: String, #[graphql(name = "haha", default = "33", description = "haha descr")] @@ -83,10 +85,10 @@ struct WithLifetime<'a> { #[test] fn test_derived_input_object() { - assert_eq!(Input::name(&()), Some("MyInput")); + assert_eq!(::name(&()), Some("MyInput")); // Validate meta info. - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = Input::meta(&(), &mut registry); assert_eq!(meta.name(), Some("MyInput")); assert_eq!(meta.description(), Some(&"input descr".to_string())); @@ -128,14 +130,14 @@ fn test_derived_input_object() { #[test] fn test_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = DocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Object comment.".to_string())); } #[test] fn test_multi_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = MultiDocComment::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -145,7 +147,7 @@ fn test_multi_doc_comment() { #[test] fn test_doc_comment_override() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = OverrideDocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"obj override".to_string())); } diff --git a/juniper_tests/src/codegen/derive_object.rs b/juniper_tests/src/codegen/derive_object.rs index 3060a3bec..26acf9a15 100644 --- a/juniper_tests/src/codegen/derive_object.rs +++ b/juniper_tests/src/codegen/derive_object.rs @@ -1,5 +1,6 @@ #[cfg(test)] use fnv::FnvHashMap; +use juniper::DefaultScalarValue; #[cfg(test)] use juniper::Object; @@ -7,14 +8,23 @@ use juniper::Object; use juniper::{self, execute, EmptyMutation, GraphQLType, RootNode, Value, Variables}; #[derive(GraphQLObject, Debug, PartialEq)] -#[graphql(name = "MyObj", description = "obj descr")] +#[graphql( + name = "MyObj", + description = "obj descr", + scalar = "DefaultScalarValue" +)] struct Obj { regular_field: bool, - #[graphql(name = "renamedField", description = "descr", deprecation = "field descr")] + #[graphql( + name = "renamedField", + description = "descr", + deprecation = "field descr" + )] c: i32, } #[derive(GraphQLObject, Debug, PartialEq)] +#[graphql(scalar = "DefaultScalarValue")] struct Nested { obj: Obj, } @@ -105,21 +115,21 @@ graphql_object!(Query: () |&self| { #[test] fn test_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = DocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"Object comment.".to_string())); check_descriptions( "DocComment", - &Value::string("Object comment."), + &Value::scalar("Object comment."), "regularField", - &Value::string("Field comment."), + &Value::scalar("Field comment."), ); } #[test] fn test_multi_doc_comment() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = MultiDocComment::meta(&(), &mut registry); assert_eq!( meta.description(), @@ -128,32 +138,32 @@ fn test_multi_doc_comment() { check_descriptions( "MultiDocComment", - &Value::string("Doc 1. Doc 2.\nDoc 4."), + &Value::scalar("Doc 1. Doc 2.\nDoc 4."), "regularField", - &Value::string("Field 1. Field 2."), + &Value::scalar("Field 1. Field 2."), ); } #[test] fn test_doc_comment_override() { - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = OverrideDocComment::meta(&(), &mut registry); assert_eq!(meta.description(), Some(&"obj override".to_string())); check_descriptions( "OverrideDocComment", - &Value::string("obj override"), + &Value::scalar("obj override"), "regularField", - &Value::string("field override"), + &Value::scalar("field override"), ); } #[test] fn test_derived_object() { - assert_eq!(Obj::name(&()), Some("MyObj")); + assert_eq!(::name(&()), Some("MyObj")); // Verify meta info. - let mut registry = juniper::Registry::new(FnvHashMap::default()); + let mut registry: juniper::Registry = juniper::Registry::new(FnvHashMap::default()); let meta = Obj::meta(&(), &mut registry); assert_eq!(meta.name(), Some("MyObj")); @@ -177,13 +187,13 @@ fn test_derived_object() { "obj", Value::object( vec![ - ("regularField", Value::boolean(true)), - ("renamedField", Value::int(22)), + ("regularField", Value::scalar(true)), + ("renamedField", Value::scalar(22)), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -240,16 +250,16 @@ fn test_derived_object_nested() { "obj", Value::object( vec![ - ("regularField", Value::boolean(false)), - ("renamedField", Value::int(333)), + ("regularField", Value::scalar(false)), + ("renamedField", Value::scalar(333)), ].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect(), + .collect(), ), )].into_iter() - .collect() + .collect() ), vec![] )) @@ -279,15 +289,21 @@ fn check_descriptions( object_name ); run_type_info_query(&doc, |(type_info, values)| { - assert_eq!(type_info.get_field_value("name"), Some(&Value::string(object_name))); - assert_eq!(type_info.get_field_value("description"), Some(object_description)); + assert_eq!( + type_info.get_field_value("name"), + Some(&Value::scalar(object_name)) + ); + assert_eq!( + type_info.get_field_value("description"), + Some(object_description) + ); assert!( values.contains(&Value::object( vec![ - ("name", Value::string(field_name)), + ("name", Value::scalar(field_name)), ("description", field_value.clone()), ].into_iter() - .collect(), + .collect(), )) ); }); @@ -296,7 +312,7 @@ fn check_descriptions( #[cfg(test)] fn run_type_info_query(doc: &str, f: F) where - F: Fn((&Object, &Vec)) -> (), + F: Fn((&Object, &Vec)) -> (), { let schema = RootNode::new(Query, EmptyMutation::<()>::new()); diff --git a/juniper/src/executor_tests/custom_scalar.rs b/juniper_tests/src/custom_scalar.rs similarity index 93% rename from juniper/src/executor_tests/custom_scalar.rs rename to juniper_tests/src/custom_scalar.rs index 0ca9763b6..a493b8757 100644 --- a/juniper/src/executor_tests/custom_scalar.rs +++ b/juniper_tests/src/custom_scalar.rs @@ -1,13 +1,13 @@ extern crate serde_json; -use ast::InputValue; -use executor::Variables; -use parser::{ParseError, ScalarToken, Spanning, Token}; -use schema::model::RootNode; -use serde::de; +#[cfg(test)] +use juniper::parser::Spanning; +use juniper::parser::{ParseError, ScalarToken, Token}; +use juniper::serde::de; +#[cfg(test)] +use juniper::{execute, EmptyMutation, Object, RootNode, Variables}; +use juniper::{InputValue, ParseScalarResult, Value}; use std::fmt; -use types::scalars::EmptyMutation; -use value::{Object, ParseScalarResult, Value}; #[derive(Debug, Clone, PartialEq, ScalarValue)] #[juniper(visitor = "MyScalarValueVisitor")] @@ -125,13 +125,14 @@ graphql_object!(TestType: () where Scalar = MyScalarValue |&self| { } }); +#[cfg(test)] fn run_variable_query(query: &str, vars: Variables, f: F) where F: Fn(&Object) -> (), { let schema = RootNode::new(TestType, EmptyMutation::<()>::new()); - let (result, errs) = ::execute(query, None, &schema, &vars, &()).expect("Execution failed"); + let (result, errs) = execute(query, None, &schema, &vars, &()).expect("Execution failed"); assert_eq!(errs, []); @@ -142,6 +143,7 @@ where f(obj); } +#[cfg(test)] fn run_query(query: &str, f: F) where F: Fn(&Object) -> (), diff --git a/juniper_tests/src/lib.rs b/juniper_tests/src/lib.rs index a392cfca1..dd47c596d 100644 --- a/juniper_tests/src/lib.rs +++ b/juniper_tests/src/lib.rs @@ -10,3 +10,4 @@ extern crate fnv; extern crate indexmap; mod codegen; +mod custom_scalar; From 69f2a0cdeed2bba6bc2ac6e709393dd6a2d1e5b1 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 20 Sep 2018 00:34:24 +0200 Subject: [PATCH 13/20] Fix juniper_rocket --- juniper_rocket/src/lib.rs | 81 +++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 25 deletions(-) diff --git a/juniper_rocket/src/lib.rs b/juniper_rocket/src/lib.rs index 6e7bae58d..a5aa9f906 100644 --- a/juniper_rocket/src/lib.rs +++ b/juniper_rocket/src/lib.rs @@ -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), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLBatchRequest +where + S: ScalarValue, +{ + Single(http::GraphQLRequest), + Batch(Vec>), } #[derive(Serialize)] #[serde(untagged)] -enum GraphQLBatchResponse<'a> { - Single(http::GraphQLResponse<'a>), - Batch(Vec>), +enum GraphQLBatchResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + Single(http::GraphQLResponse<'a, S>), + Batch(Vec>), } -impl GraphQLBatchRequest { +impl GraphQLBatchRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &RootNode, + root_node: &'a RootNode, context: &CtxT, - ) -> GraphQLBatchResponse<'a> + ) -> GraphQLBatchResponse<'a, S> where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLType, + MutationT: GraphQLType, { 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(GraphQLBatchRequest) +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 { content::Html(juniper::graphiql::graphiql_source(graphql_endpoint_url)) } -impl GraphQLRequest { +impl GraphQLRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ /// Execute an incoming GraphQL query pub fn execute( &self, - root_node: &RootNode, + root_node: &RootNode, context: &CtxT, ) -> GraphQLResponse where - QueryT: GraphQLType, - MutationT: GraphQLType, + QueryT: GraphQLType, + MutationT: GraphQLType, { 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 +where + S: ScalarValue, +{ type Error = String; fn from_form(form_items: &mut FormItems<'f>, strict: bool) -> Result { @@ -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::(&decoded) - .map_err(|err| err.description().to_owned())?); + variables = Some( + serde_json::from_str::>(&decoded) + .map_err(|err| err.description().to_owned())?, + ); } } _ => { @@ -270,7 +299,10 @@ impl<'f> FromForm<'f> for GraphQLRequest { } } -impl FromData for GraphQLRequest { +impl FromData for GraphQLRequest +where + S: ScalarValue, +{ type Error = String; fn from_data(request: &Request, data: Data) -> FromDataOutcome { @@ -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::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::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::::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 { From b545273a994f71e132372f59194cdce9907d2c04 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 20 Sep 2018 16:51:32 +0200 Subject: [PATCH 14/20] Fix errors indroduced by the rebase --- Cargo.toml | 2 + juniper/Cargo.toml | 2 +- juniper/src/integrations/serde.rs | 7 +- juniper/src/lib.rs | 1 - juniper/src/macros/interface.rs | 16 +- juniper/src/macros/mod.rs | 10 +- juniper/src/macros/object.rs | 14 +- juniper/src/macros/scalar.rs | 18 +-- juniper/src/macros/tests/scalar.rs | 9 +- juniper/src/macros/union.rs | 6 +- juniper/src/validation/input_value.rs | 37 +++-- juniper_codegen/src/derive_enum.rs | 23 ++- juniper_codegen/src/derive_input_object.rs | 146 ++++++++++-------- .../src/derive_juniper_scalar_value.rs | 26 ++-- juniper_codegen/src/derive_object.rs | 70 ++++++--- juniper_codegen/src/lib.rs | 1 + juniper_hyper/src/lib.rs | 51 +++--- .../src/codegen/derive_input_object.rs | 14 +- juniper_warp/Cargo.toml | 3 - juniper_warp/src/lib.rs | 52 +++++-- 20 files changed, 314 insertions(+), 194 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 08120b82d..48508f849 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,4 +6,6 @@ members = [ "juniper_hyper", "juniper_iron", "juniper_rocket", + "juniper_warp", + "juniper_warp/examples/warp_server/", ] diff --git a/juniper/Cargo.toml b/juniper/Cargo.toml index da3dc242e..7a111b23a 100644 --- a/juniper/Cargo.toml +++ b/juniper/Cargo.toml @@ -27,7 +27,7 @@ expose-test-schema = [] default = [ "chrono", "url", - "uuid" + "uuid", ] [dependencies] diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index 159f18223..d7f4ded9a 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -394,11 +394,10 @@ where mod tests { use super::{ExecutionError, GraphQLError}; use ast::InputValue; - use executor::ExecutionError; use serde_json::from_str; use serde_json::to_string; - use {FieldError, Value}; use value::{DefaultScalarValue, Object}; + use {FieldError, Value}; #[test] fn int() { @@ -431,8 +430,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 = Object::with_capacity(1); + obj.add_field("foo".to_string(), Value::scalar("bar")); assert_eq!( to_string(&ExecutionError::at_origin(FieldError::new( "foo error", diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index ee13e6e12..4900fc9b9 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -211,7 +211,6 @@ where MutationT: GraphQLType, { let document = parse_document_source(document_source, &root_node.schema)?; - { let errors = validate_input_values(variables, &document, &root_node.schema); diff --git a/juniper/src/macros/interface.rs b/juniper/src/macros/interface.rs index 020128b9b..7f79944ce 100644 --- a/juniper/src/macros/interface.rs +++ b/juniper/src/macros/interface.rs @@ -147,7 +147,7 @@ macro_rules! graphql_interface { )* let fields = &[$( registry.field_convert::<$return_ty, _, Self::Context>( - &$crate::to_camel_case(stringify!($fn_name)), + &$crate::to_camel_case(__graphql__stringify!($fn_name)), info ) $(.description($fn_description))* @@ -180,13 +180,13 @@ macro_rules! graphql_interface { executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( - if field == &$crate::to_camel_case(stringify!($fn_name)) { + 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(concat!( + .expect(__graphql__concat!( "Argument ", - stringify!($arg_name), + __graphql__stringify!($arg_name), " missing - validation must have failed" )); )* @@ -209,7 +209,7 @@ macro_rules! graphql_interface { } )* - panic!("Field {} not found on type {}", field, $($outname)*) + __graphql__panic!("Field {} not found on type {}", field, $($outname)*) } #[allow(unused_variables)] @@ -223,7 +223,7 @@ macro_rules! graphql_interface { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } fn resolve_into_type( @@ -241,7 +241,7 @@ macro_rules! graphql_interface { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } } ); @@ -265,7 +265,7 @@ macro_rules! graphql_interface { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_interface!`"); + __graphql__compile_error!("Invalid syntax for `graphql_interface!`"); }; ( diff --git a/juniper/src/macros/mod.rs b/juniper/src/macros/mod.rs index 772e60bbe..4526fa1f8 100644 --- a/juniper/src/macros/mod.rs +++ b/juniper/src/macros/mod.rs @@ -14,8 +14,14 @@ 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] diff --git a/juniper/src/macros/object.rs b/juniper/src/macros/object.rs index ec2e640e7..5261b15a5 100644 --- a/juniper/src/macros/object.rs +++ b/juniper/src/macros/object.rs @@ -321,7 +321,7 @@ macro_rules! graphql_object { { let fields = &[$( registry.field_convert::<$return_ty, _, Self::Context>( - &$crate::to_camel_case(stringify!($fn_name)), + &$crate::to_camel_case(__graphql__stringify!($fn_name)), info ) $(.description($fn_description))* @@ -360,13 +360,13 @@ macro_rules! graphql_object { executor: &$crate::Executor ) -> $crate::ExecutionResult<__juniper_insert_generic!($($scalar)+)> { $( - if field == &$crate::to_camel_case(stringify!($fn_name)) { + 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(concat!( + let $arg_name: $arg_ty = args.get(&$crate::to_camel_case(__graphql__stringify!($arg_name))) + .expect(__graphql__concat!( "Argument ", - stringify!($arg_name), + __graphql__stringify!($arg_name), " missing - validation must have failed" )); )* @@ -389,7 +389,7 @@ macro_rules! graphql_object { } )* - panic!("Field {} not found on type {}", field, $($outname)*); + __graphql__panic!("Field {} not found on type {}", field, $($outname)*); } } ); @@ -472,7 +472,7 @@ macro_rules! graphql_object { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_object!`"); + __graphql__compile_error!("Invalid syntax for `graphql_object!`"); }; ( diff --git a/juniper/src/macros/scalar.rs b/juniper/src/macros/scalar.rs index ec744d75f..967857cca 100644 --- a/juniper/src/macros/scalar.rs +++ b/juniper/src/macros/scalar.rs @@ -59,6 +59,7 @@ macro_rules! graphql_scalar { 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, @@ -134,12 +135,6 @@ macro_rules! graphql_scalar { } } ); - - impl $crate::FromInputValue for $name { - fn from_input_value($fiv_arg: &$crate::InputValue) -> $fiv_result { - $fiv_body - } - } }; // No more items to parse @@ -182,7 +177,7 @@ macro_rules! graphql_scalar { $(from_str = {$($from_str_body:tt)+})*, rest = ) => { - compile_error!("Missing resolve function"); + __graphql__compile_error!("Missing resolve function"); }; ( @@ -197,7 +192,7 @@ macro_rules! graphql_scalar { $(from_str = {$($from_str_body:tt)+})*, rest = ) => { - compile_error!("Missing from_input_value function"); + __graphql__compile_error!("Missing from_input_value function"); }; ( @@ -212,7 +207,7 @@ macro_rules! graphql_scalar { from_input_value = {$($from_input_value_body:tt)+}, rest = ) =>{ - compile_error!("Missing from_str function"); + __graphql__compile_error!("Missing from_str function"); }; @@ -223,7 +218,7 @@ macro_rules! graphql_scalar { $(resolve = {$($resolve_body:tt)+},)* $(from_input_value = {$($from_input_value_body:tt)+},)* $(from_str = {$($from_str_body:tt)+},)* - rest = resolve(&$selfvar:ident) -> Value $body:block $($rest:tt)* + rest = resolve(&$selfvar:ident) -> $return_ty:ty $body:block $($rest:tt)* ) => { graphql_scalar!( @parse_functions, @@ -231,6 +226,7 @@ macro_rules! graphql_scalar { resolve = { self_var = $selfvar, body = $body, + return_type = $return_ty, }, $(from_input_value = {$($from_input_value_body)+},)* $(from_str = {$($from_str_body)+},)* @@ -335,7 +331,7 @@ macro_rules! graphql_scalar { }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_scalar!`"); + __graphql__compile_error!("Invalid syntax for `graphql_scalar!`"); }; ($($rest:tt)*) => { diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index 29addd605..068936e20 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -112,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 { - v.as_int_value().map(|i| ResolvePath(i)) + v.as_scalar_value().map(|i: &i32| ResolvePath(*i)) + } + + from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { + ::from_str(value) } }); } diff --git a/juniper/src/macros/union.rs b/juniper/src/macros/union.rs index abe2a3ebc..60fe627b4 100644 --- a/juniper/src/macros/union.rs +++ b/juniper/src/macros/union.rs @@ -83,7 +83,7 @@ macro_rules! graphql_union { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } fn resolve_into_type( @@ -101,7 +101,7 @@ macro_rules! graphql_union { } )* - panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); + __graphql__panic!("Concrete type not handled by instance resolvers on {}", $($outname)*); } } ); @@ -125,7 +125,7 @@ macro_rules! graphql_union { ); }; (@$($stuff:tt)*) => { - compile_error!("Invalid syntax for `graphql_union!`"); + __graphql__compile_error!("Invalid syntax for `graphql_union!`"); }; ($($rest: tt)*) => { diff --git a/juniper/src/validation/input_value.rs b/juniper/src/validation/input_value.rs index 9236e9635..219fb5738 100644 --- a/juniper/src/validation/input_value.rs +++ b/juniper/src/validation/input_value.rs @@ -7,7 +7,7 @@ use parser::SourcePosition; use schema::meta::{EnumMeta, InputObjectMeta, MetaType, ScalarMeta}; use schema::model::{SchemaType, TypeType}; use validation::RuleError; -use value::{ScalarValue, ScalarRefValue}; +use value::{ScalarRefValue, ScalarValue}; #[derive(Debug)] enum Path<'a> { @@ -23,7 +23,7 @@ pub fn validate_input_values( ) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errs = vec![]; @@ -46,7 +46,7 @@ fn validate_var_defs( errors: &mut Vec, ) where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + 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(); @@ -85,10 +85,10 @@ fn unify_value<'a, S>( meta_type: &TypeType<'a, S>, schema: &SchemaType, path: Path<'a>, -) -> Vec - where +) -> Vec +where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; @@ -173,7 +173,7 @@ fn unify_scalar<'a, S>( value: &InputValue, meta: &ScalarMeta, path: &Path<'a>, -) -> Vec +) -> Vec where S: fmt::Debug, { @@ -212,15 +212,26 @@ fn unify_enum<'a, S>( value: &InputValue, meta: &EnumMeta, path: &Path<'a>, -) -> Vec +) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; - match *value { - InputValue::String(ref name) | InputValue::Enum(ref name) => { + InputValue::Scalar(ref scalar) if scalar.is_type::() => { + if let Some(ref name) = <&S as Into>>::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, @@ -247,10 +258,10 @@ fn unify_input_object<'a, S>( meta: &InputObjectMeta, schema: &SchemaType, path: &Path<'a>, -) -> Vec +) -> Vec where S: ScalarValue, - for<'b> &'b S: ScalarRefValue<'b> + for<'b> &'b S: ScalarRefValue<'b>, { let mut errors: Vec = vec![]; diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 769afc6be..31f1dc10f 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -24,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; @@ -35,7 +37,9 @@ 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; } @@ -66,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; @@ -77,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; } @@ -150,8 +160,7 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { description: #descr, deprecation_reason: #depr, }, - }; - values.push(value); + }); // Build resolve match clause. resolves.extend(quote!{ diff --git a/juniper_codegen/src/derive_input_object.rs b/juniper_codegen/src/derive_input_object.rs index 74a241ade..25a3c7137 100644 --- a/juniper_codegen/src/derive_input_object.rs +++ b/juniper_codegen/src/derive_input_object.rs @@ -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,12 +36,16 @@ 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(scalar) = keyed_item_value(&item, "scalar", true) { - res.scalar = Some(Ident::from(&scalar as &str)); + 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!( @@ -70,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; @@ -81,11 +89,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, "default", AttributeValidation::Any) { + if let Some(AttributeValue::String(val)) = + keyed_item_value(&item, "default", AttributeValidation::Any) + { res.default_expr = Some(val); continue; } @@ -236,75 +248,83 @@ pub fn impl_input_object(ast: &syn::DeriveInput) -> TokenStream { }); } - let (where_clause, define_scalar) = if attrs.scalar.is_none() { - ( - Some(quote!{ - where __S: _juniper::ScalarValue, - for<'__b> &'__b __S: _juniper::ScalarRefValue<'__b> - }), - Some(quote!(<__S>)), - ) + let (_, ty_generics, _) = generics.split_for_impl(); + + let mut generics = generics.clone(); + + let scalar = if let Some(scalar) = attrs.scalar { + scalar } else { - (None, 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>)); + } + Ident::new("__S", Span::call_site()) }; - let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); + let (impl_generics, _, where_clause) = generics.split_for_impl(); let body = quote! { - impl#define_scalar _juniper::GraphQLType<#scalar> for #ident - #where_clause - { - type Context = (); - type TypeInfo = (); - - fn name(_: &()) -> Option<&'static str> { - Some(#name) - } - - fn meta<'r>( - _: &(), - registry: &mut _juniper::Registry<'r, #scalar> - ) -> _juniper::meta::MetaType<'r, #scalar> - where #scalar: 'r - { - let fields = &[ - #(#meta_fields)* - ]; - let meta = registry.build_input_object_type::<#ident>(&(), fields); - #meta_description - meta.into_meta() - } + impl#impl_generics _juniper::GraphQLType<#scalar> for #ident #ty_generics + #where_clause + { + type Context = (); + type TypeInfo = (); + + fn name(_: &()) -> Option<&'static str> { + Some(#name) } - impl#define_scalar _juniper::FromInputValue<#scalar> for #ident - #where_clause + fn meta<'r>( + _: &(), + registry: &mut _juniper::Registry<'r, #scalar> + ) -> _juniper::meta::MetaType<'r, #scalar> + where #scalar: 'r { - fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option<#ident> - where - for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b> - { - if let Some(obj) = value.to_object_value() { - let item = #ident { - #(#from_inputs)* - }; - Some(item) - } - else { - None - } - } + let fields = &[ + #(#meta_fields)* + ]; + let meta = registry.build_input_object_type::<#ident>(&(), fields); + #meta_description + meta.into_meta() } + } - impl#define_scalar _juniper::ToInputValue<#scalar> for #ident - #where_clause + impl#impl_generics _juniper::FromInputValue<#scalar> for #ident #ty_generics + #where_clause + { + fn from_input_value(value: &_juniper::InputValue<#scalar>) -> Option + where + for<'__b> &'__b #scalar: _juniper::ScalarRefValue<'__b> { - fn to_input_value(&self) -> _juniper::InputValue<#scalar> { - _juniper::InputValue::object(vec![ - #(#to_inputs)* - ].into_iter().collect()) + if let Some(obj) = value.to_object_value() { + let item = #ident { + #(#from_inputs)* + }; + Some(item) + } + else { + None } } - }; + } + + 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()) + } + } + }; let dummy_const = Ident::new( &format!("_IMPL_GRAPHQLINPUTOBJECT_FOR_{}", ident), diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs index cc467117d..8ff198362 100644 --- a/juniper_codegen/src/derive_juniper_scalar_value.rs +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -1,14 +1,19 @@ -use quote::Tokens; +use proc_macro2::{Span, TokenStream}; + use syn::{self, Data, Fields, Ident, Variant}; -use util::{get_juniper_attr, keyed_item_value}; +use util::{get_juniper_attr, keyed_item_value, AttributeValidation, AttributeValue}; -pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { +pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream { let ident = &ast.ident; let visitor = if let Some(items) = get_juniper_attr(&ast.attrs) { items.into_iter() - .filter_map(|i| keyed_item_value(&i, "visitor", true)) + .filter_map(|i| keyed_item_value(&i, "visitor", AttributeValidation::String)) .next() - .map(|t| Ident::from(&t as &str)) + .and_then(|t| if let AttributeValue::String(s) = t { + Some(Ident::new(&s, Span::call_site())) + } else { + None + }) .expect("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`") } else { panic!("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`"); @@ -30,7 +35,10 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { let serialize = derive_serialize(variants.iter(), ident); let display = derive_display(variants.iter(), ident); - let dummy_const = Ident::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + let dummy_const = Ident::new( + format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(), + Span::call_site(), + ); quote!{ const #dummy_const: () = { @@ -53,7 +61,7 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> Tokens { } } -fn derive_display<'a, I>(variants: I, ident: &Ident) -> Tokens +fn derive_display<'a, I>(variants: I, ident: &Ident) -> TokenStream where I: Iterator, { @@ -73,7 +81,7 @@ where } } -fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> Tokens +fn derive_serialize<'a, I>(variants: I, ident: &Ident) -> TokenStream where I: Iterator, { @@ -95,7 +103,7 @@ where } } -fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result { +fn derive_from_variant(variant: &Variant, ident: &Ident) -> Result { let ty = match variant.fields { Fields::Unnamed(ref u) if u.unnamed.len() == 1 => &u.unnamed.first().unwrap().value().ty, diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs index e0218b580..6a43edc35 100644 --- a/juniper_codegen/src/derive_object.rs +++ b/juniper_codegen/src/derive_object.rs @@ -1,4 +1,4 @@ -use proc_macro2::TokenStream; +use proc_macro2::{Span, TokenStream}; use syn; use syn::{Data, DeriveInput, Field, Fields, Ident}; @@ -21,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; @@ -32,12 +34,16 @@ 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", true) { - res.scalar = Some(Ident::from(&scalar as &str)); + 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!( @@ -68,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; @@ -79,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; } @@ -175,23 +187,31 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { }); } - let (where_clause, define_scalar) = if attrs.scalar.is_none() { - ( - Some(quote!{ - where __S: juniper::ScalarValue, - for<'__b> &'__b __S: juniper::ScalarRefValue<'__b> - }), - Some(quote!(<__S>)), - ) - } else { - (None, None) - }; + let (_, ty_generics, _) = generics.split_for_impl(); + + let mut generics = generics.clone(); - let scalar = attrs.scalar.unwrap_or_else(|| Ident::from("__S")); - let dummy_const = Ident::from(format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str()); + 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#define_scalar juniper::GraphQLType<#scalar> for #ident + impl#impl_generics juniper::GraphQLType<#scalar> for #ident #ty_generics #where_clause { type Context = (); @@ -236,6 +256,12 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { } } }; + + let dummy_const = Ident::new( + format!("_IMPL_JUNIPER_SCALAR_VALUE_FOR_{}", ident).as_str(), + Span::call_site(), + ); + quote!{ const #dummy_const: () = { mod juniper { diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index 5d3abd192..d15c97435 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -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; diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs index 01b6deb75..8c5eb2596 100644 --- a/juniper_hyper/src/lib.rs +++ b/juniper_hyper/src/lib.rs @@ -25,15 +25,17 @@ use std::sync::Arc; use tokio::prelude::*; use url::form_urlencoded; -pub fn graphql( - root_node: Arc>, +pub fn graphql( + root_node: Arc>, context: Arc, request: Request, ) -> impl Future, Error = hyper::Error> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, CtxT: Send + Sync + 'static, - QueryT: GraphQLType + Send + Sync + 'static, - MutationT: GraphQLType + Send + Sync + 'static, + QueryT: GraphQLType + Send + Sync + 'static, + MutationT: GraphQLType + Send + Sync + 'static, QueryT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync, { @@ -93,15 +95,17 @@ fn render_error(err: GraphQLRequestError) -> Response { resp } -fn execute_request( - root_node: Arc>, +fn execute_request( + root_node: Arc>, context: Arc, - request: GraphQLRequest, + request: GraphQLRequest, ) -> impl Future, Error = tokio_threadpool::BlockingError> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, CtxT: Send + Sync + 'static, - QueryT: GraphQLType + Send + Sync + 'static, - MutationT: GraphQLType + Send + Sync + 'static, + QueryT: GraphQLType + Send + Sync + 'static, + MutationT: GraphQLType + Send + Sync + 'static, QueryT::TypeInfo: Send + Sync, MutationT::TypeInfo: Send + Sync, { @@ -121,7 +125,10 @@ where }) } -fn gql_request_from_get(input: &str) -> Result { +fn gql_request_from_get(input: &str) -> Result, GraphQLRequestError> +where + S: ScalarValue, +{ let mut query = None; let operation_name = None; let mut variables = None; @@ -142,7 +149,7 @@ fn gql_request_from_get(input: &str) -> Result(&value) + match serde_json::from_str::>(&value) .map_err(GraphQLRequestError::Variables) { Ok(parsed_variables) => variables = Some(parsed_variables), @@ -184,20 +191,28 @@ fn new_html_response(code: StatusCode) -> Response { #[derive(Deserialize)] #[serde(untagged)] -enum GraphQLRequest { - Single(JuniperGraphQLRequest), - Batch(Vec), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLRequest +where + S: ScalarValue, +{ + Single(JuniperGraphQLRequest), + Batch(Vec>), } -impl GraphQLRequest { +impl GraphQLRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ fn execute<'a, CtxT: 'a, QueryT, MutationT>( self, - root_node: Arc>, + root_node: Arc>, context: Arc, ) -> impl Future + 'a where - QueryT: GraphQLType + 'a, - MutationT: GraphQLType + 'a, + QueryT: GraphQLType + 'a, + MutationT: GraphQLType + 'a, { match self { GraphQLRequest::Single(request) => Either::A(future::poll_fn(move || { diff --git a/juniper_tests/src/codegen/derive_input_object.rs b/juniper_tests/src/codegen/derive_input_object.rs index 191d6c94b..068068fa9 100644 --- a/juniper_tests/src/codegen/derive_input_object.rs +++ b/juniper_tests/src/codegen/derive_input_object.rs @@ -6,7 +6,11 @@ use juniper::{self, FromInputValue, GraphQLType, InputValue, ToInputValue}; use juniper::DefaultScalarValue; #[derive(GraphQLInputObject, Debug, PartialEq)] -#[graphql(name = "MyInput", description = "input descr", scalar = "DefaultScalarValue")] +#[graphql( + name = "MyInput", + description = "input descr", + scalar = "DefaultScalarValue" +)] struct Input { regular_field: String, #[graphql(name = "haha", default = "33", description = "haha descr")] @@ -54,7 +58,7 @@ impl<'a> FromInputValue for &'a Fake { impl<'a> ToInputValue for &'a Fake { fn to_input_value(&self) -> InputValue { - InputValue::string("this is fake".to_string()) + InputValue::scalar("this is fake") } } @@ -65,7 +69,10 @@ impl<'a> GraphQLType for &'a Fake { fn name(_: &()) -> Option<&'static str> { None } - fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> { + fn meta<'r>(_: &(), registry: &mut juniper::Registry<'r>) -> juniper::meta::MetaType<'r> + where + DefaultScalarValue: 'r, + { let meta = registry.build_enum_type::<&'a Fake>( &(), &[juniper::meta::EnumValue { @@ -79,6 +86,7 @@ impl<'a> GraphQLType for &'a Fake { } #[derive(GraphQLInputObject, Debug, PartialEq)] +#[graphql(scalar = "DefaultScalarValue")] struct WithLifetime<'a> { regular_field: &'a Fake, } diff --git a/juniper_warp/Cargo.toml b/juniper_warp/Cargo.toml index fa338dd91..b8405a0ab 100644 --- a/juniper_warp/Cargo.toml +++ b/juniper_warp/Cargo.toml @@ -20,6 +20,3 @@ serde = "1.0.75" [dev-dependencies] juniper = { path = "../juniper", version = "0.10.0", features = ["expose-test-schema", "serde_json"] } percent-encoding = "1.0" - -[workspace] -members = [".", "examples/warp_server"] diff --git a/juniper_warp/src/lib.rs b/juniper_warp/src/lib.rs index 952674c92..d844be66f 100644 --- a/juniper_warp/src/lib.rs +++ b/juniper_warp/src/lib.rs @@ -55,25 +55,35 @@ extern crate percent_encoding; use futures::Future; use futures_cpupool::CpuPool; +use juniper::{DefaultScalarValue, ScalarRefValue, ScalarValue, InputValue}; +use serde::Deserialize; use std::sync::Arc; use warp::{filters::BoxedFilter, Filter}; #[derive(Debug, Deserialize, PartialEq)] #[serde(untagged)] -enum GraphQLBatchRequest { - Single(juniper::http::GraphQLRequest), - Batch(Vec), +#[serde(bound = "InputValue: Deserialize<'de>")] +enum GraphQLBatchRequest +where + S: ScalarValue, +{ + Single(juniper::http::GraphQLRequest), + Batch(Vec>), } -impl GraphQLBatchRequest { +impl GraphQLBatchRequest +where + S: ScalarValue, + for<'b> &'b S: ScalarRefValue<'b>, +{ pub fn execute<'a, CtxT, QueryT, MutationT>( &'a self, - root_node: &juniper::RootNode, + root_node: &'a juniper::RootNode, context: &CtxT, - ) -> GraphQLBatchResponse<'a> + ) -> GraphQLBatchResponse<'a, S> where - QueryT: juniper::GraphQLType, - MutationT: juniper::GraphQLType, + QueryT: juniper::GraphQLType, + MutationT: juniper::GraphQLType, { match self { &GraphQLBatchRequest::Single(ref request) => { @@ -91,12 +101,18 @@ impl GraphQLBatchRequest { #[derive(Serialize)] #[serde(untagged)] -enum GraphQLBatchResponse<'a> { - Single(juniper::http::GraphQLResponse<'a>), - Batch(Vec>), +enum GraphQLBatchResponse<'a, S = DefaultScalarValue> +where + S: ScalarValue, +{ + Single(juniper::http::GraphQLResponse<'a, S>), + Batch(Vec>), } -impl<'a> GraphQLBatchResponse<'a> { +impl<'a, S> GraphQLBatchResponse<'a, S> +where + S: ScalarValue, +{ fn is_ok(&self) -> bool { match self { GraphQLBatchResponse::Single(res) => res.is_ok(), @@ -182,22 +198,24 @@ type Response = Box>, Error = warp::reject::Rejection> + Send>; /// Same as [make_graphql_filter](./fn.make_graphql_filter.html), but use the provided [CpuPool](../futures_cpupool/struct.CpuPool.html) instead. -pub fn make_graphql_filter_with_thread_pool( - schema: juniper::RootNode<'static, Query, Mutation>, +pub fn make_graphql_filter_with_thread_pool( + schema: juniper::RootNode<'static, Query, Mutation, S>, context_extractor: BoxedFilter<(Context,)>, thread_pool: futures_cpupool::CpuPool, ) -> BoxedFilter<(warp::http::Response>,)> where + S: ScalarValue + Send + Sync + 'static, + for<'b> &'b S: ScalarRefValue<'b>, Context: Send + 'static, - Query: juniper::GraphQLType + Send + Sync + 'static, - Mutation: juniper::GraphQLType + Send + Sync + 'static, + Query: juniper::GraphQLType + Send + Sync + 'static, + Mutation: juniper::GraphQLType + Send + Sync + 'static, { let schema = Arc::new(schema); let post_schema = schema.clone(); let pool_filter = warp::any().map(move || thread_pool.clone()); let handle_post_request = - move |context: Context, request: GraphQLBatchRequest, pool: CpuPool| -> Response { + move |context: Context, request: GraphQLBatchRequest, pool: CpuPool| -> Response { let schema = post_schema.clone(); Box::new( pool.spawn_fn(move || { From a115ca1e2cac13a8864af72f524187fe03e1327d Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 21 Sep 2018 21:08:49 +0200 Subject: [PATCH 15/20] Fix compile on rust 1.22 --- juniper/src/executor/mod.rs | 4 +-- juniper/src/integrations/chrono.rs | 16 +++++------ juniper/src/integrations/serde.rs | 24 +++++++++------- juniper/src/integrations/url.rs | 4 +-- juniper/src/integrations/uuid.rs | 4 +-- juniper/src/lib.rs | 1 + juniper/src/macros/tests/scalar.rs | 10 +++---- juniper/src/parser/lexer.rs | 4 +-- juniper/src/parser/value.rs | 6 ++-- juniper/src/schema/meta.rs | 28 ++++++++++--------- juniper/src/validation/test_harness.rs | 2 +- juniper_codegen/src/derive_enum.rs | 4 ++- .../src/derive_juniper_scalar_value.rs | 2 ++ juniper_codegen/src/derive_object.rs | 2 ++ 14 files changed, 62 insertions(+), 49 deletions(-) diff --git a/juniper/src/executor/mod.rs b/juniper/src/executor/mod.rs index 2c66a4a38..5dda3136d 100644 --- a/juniper/src/executor/mod.rs +++ b/juniper/src/executor/mod.rs @@ -259,7 +259,7 @@ where for<'b> &'b S: ScalarRefValue<'b>, { fn into(self, ctx: &'a C) -> FieldResult, S> { - self.map(|v| Some((FromContext::from(ctx), v))) + self.map(|v: T| Some((>::from(ctx), v))) .map_err(|e| e.into_field_error()) } } @@ -604,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); diff --git a/juniper/src/integrations/chrono.rs b/juniper/src/integrations/chrono.rs index 2bcd5facd..5c25d9875 100644 --- a/juniper/src/integrations/chrono.rs +++ b/juniper/src/integrations/chrono.rs @@ -30,8 +30,8 @@ graphql_scalar!(DateTime as "DateTimeFixedOffset" where Scalar = Option> { - v.as_scalar_value() - .and_then(|s: &String| DateTime::parse_from_rfc3339(s).ok()) + v.as_scalar_value::() + .and_then(|s| DateTime::parse_from_rfc3339(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -51,8 +51,8 @@ graphql_scalar!(DateTime as "DateTimeUtc" where Scalar = { } from_input_value(v: &InputValue) -> Option> { - v.as_scalar_value() - .and_then(|s: &String| (s.parse::>().ok())) + v.as_scalar_value::() + .and_then(|s| (s.parse::>().ok())) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -77,8 +77,8 @@ graphql_scalar!(NaiveDate where Scalar = { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|s: &String| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) + v.as_scalar_value::() + .and_then(|s| NaiveDate::parse_from_str(s, "%Y-%m-%d").ok()) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -100,8 +100,8 @@ graphql_scalar!(NaiveDateTime where Scalar = { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|f: &f64| NaiveDateTime::from_timestamp_opt(*f as i64, 0)) + v.as_scalar_value::() + .and_then(|f| NaiveDateTime::from_timestamp_opt(*f as i64, 0)) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/serde.rs b/juniper/src/integrations/serde.rs index d7f4ded9a..d09fd5ce1 100644 --- a/juniper/src/integrations/serde.rs +++ b/juniper/src/integrations/serde.rs @@ -130,11 +130,13 @@ where self.0.visit_i64(value).map(InputValue::Scalar) } - fn visit_i128(self, value: i128) -> Result, E> - where - E: de::Error, - { - self.0.visit_i128(value).map(InputValue::Scalar) + serde_if_integer128! { + fn visit_i128(self, value: i128) -> Result, E> + where + E: de::Error, + { + self.0.visit_i128(value).map(InputValue::Scalar) + } } fn visit_u8(self, value: u8) -> Result, E> @@ -165,11 +167,13 @@ where self.0.visit_u64(value).map(InputValue::Scalar) } - fn visit_u128(self, value: u128) -> Result, E> - where - E: de::Error, - { - self.0.visit_u128(value).map(InputValue::Scalar) + serde_if_integer128!{ + fn visit_u128(self, value: u128) -> Result, E> + where + E: de::Error, + { + self.0.visit_u128(value).map(InputValue::Scalar) + } } fn visit_f32(self, value: f32) -> Result, E> diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 01d8d820b..6b717c01c 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -11,8 +11,8 @@ graphql_scalar!(Url where Scalar = { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|s: &String| Url::parse(s).ok()) + v.as_scalar_value::() + .and_then(|s| Url::parse(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/integrations/uuid.rs b/juniper/src/integrations/uuid.rs index 7b0d49627..52cf78c27 100644 --- a/juniper/src/integrations/uuid.rs +++ b/juniper/src/integrations/uuid.rs @@ -12,8 +12,8 @@ graphql_scalar!(Uuid where Scalar = { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value() - .and_then(|s: &String| Uuid::parse_str(s).ok()) + v.as_scalar_value::() + .and_then(|s| Uuid::parse_str(s).ok()) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { diff --git a/juniper/src/lib.rs b/juniper/src/lib.rs index 4900fc9b9..c32cf3c7e 100644 --- a/juniper/src/lib.rs +++ b/juniper/src/lib.rs @@ -91,6 +91,7 @@ Juniper has not reached 1.0 yet, thus some API instability should be expected. #![warn(missing_docs)] #[doc(hidden)] +#[macro_use] pub extern crate serde; #[macro_use] extern crate serde_derive; diff --git a/juniper/src/macros/tests/scalar.rs b/juniper/src/macros/tests/scalar.rs index 068936e20..3c50318df 100644 --- a/juniper/src/macros/tests/scalar.rs +++ b/juniper/src/macros/tests/scalar.rs @@ -25,7 +25,7 @@ graphql_scalar!(DefaultName where Scalar = { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| DefaultName(*i)) + v.as_scalar_value::().map(|i| DefaultName(*i)) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, S> { @@ -39,7 +39,7 @@ graphql_scalar!(OtherOrder { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| OtherOrder(*i)) + v.as_scalar_value::().map(|i| OtherOrder(*i)) } @@ -54,7 +54,7 @@ graphql_scalar!(Named as "ANamedScalar" where Scalar = DefaultScalarValue { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| Named(*i)) + v.as_scalar_value::().map(|i| Named(*i)) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a, DefaultScalarValue> { @@ -70,7 +70,7 @@ graphql_scalar!(ScalarDescription { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| ScalarDescription(*i)) + v.as_scalar_value::().map(|i| ScalarDescription(*i)) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { @@ -119,7 +119,7 @@ fn path_in_resolve_return_type() { } from_input_value(v: &InputValue) -> Option { - v.as_scalar_value().map(|i: &i32| ResolvePath(*i)) + v.as_scalar_value::().map(|i| ResolvePath(*i)) } from_str<'a>(value: ScalarToken<'a>) -> ParseScalarResult<'a> { diff --git a/juniper/src/parser/lexer.rs b/juniper/src/parser/lexer.rs index b045cbe1a..6602a815a 100644 --- a/juniper/src/parser/lexer.rs +++ b/juniper/src/parser/lexer.rs @@ -210,7 +210,7 @@ impl<'a> Lexer<'a> { Ok(Spanning::start_end( &start_pos, &self.position, - Token::Name(&self.source[start_idx..=end_idx]), + Token::Name(&self.source[start_idx..end_idx + 1]), )) } @@ -300,7 +300,7 @@ impl<'a> Lexer<'a> { len += 1; } - let escape = &self.source[start_idx..=end_idx]; + let escape = &self.source[start_idx..end_idx + 1]; if len != 4 { return Err(Spanning::zero_width( diff --git a/juniper/src/parser/value.rs b/juniper/src/parser/value.rs index 27e60f682..297fa7c05 100644 --- a/juniper/src/parser/value.rs +++ b/juniper/src/parser/value.rs @@ -210,7 +210,7 @@ where { match token { ScalarToken::String(_) => { - if let Some(MetaType::Scalar(s)) = schema.concrete_type_by_name("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)) @@ -219,7 +219,7 @@ where } } ScalarToken::Int(_) => { - if let Some(MetaType::Scalar(s)) = schema.concrete_type_by_name("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)) @@ -228,7 +228,7 @@ where } } ScalarToken::Float(_) => { - if let Some(MetaType::Scalar(s)) = schema.concrete_type_by_name("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)) diff --git a/juniper/src/schema/meta.rs b/juniper/src/schema/meta.rs index 6a572e6b4..f437f888b 100644 --- a/juniper/src/schema/meta.rs +++ b/juniper/src/schema/meta.rs @@ -340,19 +340,21 @@ impl<'a, S> MetaType<'a, S> { } pub(crate) fn fields<'b>(&self, schema: &'b SchemaType) -> Option>> { - match schema.lookup_type(&self.as_type()) { - Some(MetaType::Interface(ref i)) => Some(i.fields.iter().collect()), - Some(MetaType::Object(ref o)) => Some(o.fields.iter().collect()), - Some(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)) - .flatten() - .collect(), - ), - _ => None, - } + 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, + } + }) } } diff --git a/juniper/src/validation/test_harness.rs b/juniper/src/validation/test_harness.rs index d769d8799..987d91bdf 100644 --- a/juniper/src/validation/test_harness.rs +++ b/juniper/src/validation/test_harness.rs @@ -621,7 +621,7 @@ where type Context = (); type TypeInfo = (); - fn name((): &()) -> Option<&str> { + fn name(_: &()) -> Option<&str> { Some("MutationRoot") } diff --git a/juniper_codegen/src/derive_enum.rs b/juniper_codegen/src/derive_enum.rs index 31f1dc10f..e5d82b343 100644 --- a/juniper_codegen/src/derive_enum.rs +++ b/juniper_codegen/src/derive_enum.rs @@ -218,7 +218,9 @@ pub fn impl_enum(ast: &syn::DeriveInput) -> TokenStream { 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().map(|s: &String| s as &str)) { + match v.as_enum_value().or_else(|| { + v.as_scalar_value::().map(|s| s as &str) + }) { #(#from_inputs)* _ => None, } diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs index 8ff198362..3a318b7fa 100644 --- a/juniper_codegen/src/derive_juniper_scalar_value.rs +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -41,6 +41,8 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream { ); quote!{ + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + #[doc(hidden)] const #dummy_const: () = { mod juniper { __juniper_use_everything!(); diff --git a/juniper_codegen/src/derive_object.rs b/juniper_codegen/src/derive_object.rs index 6a43edc35..76be84982 100644 --- a/juniper_codegen/src/derive_object.rs +++ b/juniper_codegen/src/derive_object.rs @@ -263,6 +263,8 @@ pub fn impl_object(ast: &syn::DeriveInput) -> TokenStream { ); quote!{ + #[allow(non_upper_case_globals, unused_attributes, unused_qualifications)] + #[doc(hidden)] const #dummy_const: () = { mod juniper { __juniper_use_everything!(); From 996d5d9e55c4a4e2b06d45747d433a3d477825ac Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Tue, 25 Sep 2018 10:49:19 +0200 Subject: [PATCH 16/20] Try fixing the ci --- juniper_rocket/Makefile.toml | 3 +-- juniper_warp/examples/warp_server/Makefile.toml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 juniper_warp/examples/warp_server/Makefile.toml diff --git a/juniper_rocket/Makefile.toml b/juniper_rocket/Makefile.toml index fd86490c8..8695d6a67 100644 --- a/juniper_rocket/Makefile.toml +++ b/juniper_rocket/Makefile.toml @@ -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 \ No newline at end of file diff --git a/juniper_warp/examples/warp_server/Makefile.toml b/juniper_warp/examples/warp_server/Makefile.toml new file mode 100644 index 000000000..94c3ecad4 --- /dev/null +++ b/juniper_warp/examples/warp_server/Makefile.toml @@ -0,0 +1,17 @@ +[tasks.build-verbose] +condition = { rust_version = { min = "1.29.0" } } + +[tasks.build-verbose.windows] +condition = { rust_version = { min = "1.29.0" }, env = { "TARGET" = "x86_64-pc-windows-msvc" } } + +[tasks.test-verbose] +condition = { rust_version = { min = "1.29.0" } } + +[tasks.test-verbose.windows] +condition = { rust_version = { min = "1.29.0" }, env = { "TARGET" = "x86_64-pc-windows-msvc" } } + +[tasks.ci-coverage-flow] +condition = { rust_version = { min = "1.29.0" } } + +[tasks.ci-coverage-flow.windows] +disabled = true \ No newline at end of file From 0e2156f4eb58cb0a7fc69af63ce30722d417a9d1 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 26 Sep 2018 11:45:53 +0200 Subject: [PATCH 17/20] Some minor tweaks --- juniper/src/ast.rs | 23 ++++++++++------------- juniper/src/executor/look_ahead.rs | 2 +- juniper/src/integrations/url.rs | 5 ++--- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/juniper/src/ast.rs b/juniper/src/ast.rs index eb6557c9a..df325957b 100644 --- a/juniper/src/ast.rs +++ b/juniper/src/ast.rs @@ -102,7 +102,7 @@ pub struct InlineFragment<'a, S> { /// ``` #[derive(Clone, PartialEq, Debug)] #[allow(missing_docs)] -pub enum Selection<'a, S> { +pub enum Selection<'a, S = DefaultScalarValue> { Field(Spanning>), FragmentSpread(Spanning>), InlineFragment(Spanning>), @@ -541,35 +541,32 @@ impl<'a, S> VariableDefinitions<'a, S> { mod tests { use super::InputValue; use parser::Spanning; - use value::DefaultScalarValue; - - type TestValue = InputValue; #[test] fn test_input_value_fmt() { - let value: TestValue = InputValue::null(); + let value: InputValue = InputValue::null(); assert_eq!(format!("{}", value), "null"); - let value: TestValue = InputValue::scalar(123); + let value: InputValue = InputValue::scalar(123); assert_eq!(format!("{}", value), "123"); - let value: TestValue = InputValue::scalar(12.3); + let value: InputValue = InputValue::scalar(12.3); assert_eq!(format!("{}", value), "12.3"); - let value: TestValue = InputValue::scalar("FOO".to_owned()); + let value: InputValue = InputValue::scalar("FOO".to_owned()); assert_eq!(format!("{}", value), "\"FOO\""); - let value: TestValue = InputValue::scalar(true); + let value: InputValue = InputValue::scalar(true); assert_eq!(format!("{}", value), "true"); - let value: TestValue = InputValue::enum_value("BAR".to_owned()); + let value: InputValue = InputValue::enum_value("BAR".to_owned()); assert_eq!(format!("{}", value), "BAR"); - let value: TestValue = InputValue::variable("baz".to_owned()); + let value: InputValue = InputValue::variable("baz".to_owned()); assert_eq!(format!("{}", value), "$baz"); let list = vec![InputValue::scalar(1), InputValue::scalar(2)]; - let value: TestValue = InputValue::list(list); + let value: InputValue = InputValue::list(list); assert_eq!(format!("{}", value), "[1, 2]"); let object = vec![ @@ -582,7 +579,7 @@ mod tests { Spanning::unlocated(InputValue::scalar(2)), ), ]; - let value: TestValue = InputValue::parsed_object(object); + let value: InputValue = InputValue::parsed_object(object); assert_eq!(format!("{}", value), "{foo: 1, bar: 2}"); } } diff --git a/juniper/src/executor/look_ahead.rs b/juniper/src/executor/look_ahead.rs index a1245f727..37e725ba7 100644 --- a/juniper/src/executor/look_ahead.rs +++ b/juniper/src/executor/look_ahead.rs @@ -867,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) { diff --git a/juniper/src/integrations/url.rs b/juniper/src/integrations/url.rs index 6b717c01c..c1c00f522 100644 --- a/juniper/src/integrations/url.rs +++ b/juniper/src/integrations/url.rs @@ -1,6 +1,6 @@ use url::Url; -use value::{ParseScalarValue, ParseScalarResult}; +use value::{ParseScalarResult, ParseScalarValue}; use Value; graphql_scalar!(Url where Scalar = { @@ -23,12 +23,11 @@ graphql_scalar!(Url where Scalar = { #[cfg(test)] mod test { use url::Url; - use value::DefaultScalarValue; #[test] fn url_from_input_value() { let raw = "https://example.net/"; - let input: ::InputValue = ::InputValue::scalar(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(); From 60a197cb99dd0bf9d47704521321d8abbf38f3bf Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 28 Sep 2018 11:59:04 +0200 Subject: [PATCH 18/20] Fix a bug regarding to input value casting in custom scalars This fix introduces a new way to convert a scalar value into one of the 4 standardized scalar values (Int, Float, String, Bool). Using this abstraction it is possible to do certain needed conversations in custom scalar value code, for example that one that converts a integer value to a floating point value. --- juniper/src/types/scalars.rs | 16 ++- juniper/src/value/scalar.rs | 102 +++++++++++++++++- .../src/derive_juniper_scalar_value.rs | 19 ---- juniper_codegen/src/lib.rs | 2 +- juniper_codegen/src/util.rs | 10 +- juniper_tests/src/custom_scalar.rs | 42 +++++++- 6 files changed, 146 insertions(+), 45 deletions(-) diff --git a/juniper/src/types/scalars.rs b/juniper/src/types/scalars.rs index 55ce11556..7e7472611 100644 --- a/juniper/src/types/scalars.rs +++ b/juniper/src/types/scalars.rs @@ -39,9 +39,8 @@ graphql_scalar!(ID as "ID" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { InputValue::Scalar(ref s) => { - <_ as Into>>::into(s.clone()).or_else(||{ - <_ as Into>>::into(s.clone()).map(|i| i.to_string()) - }).map(ID) + s.as_string().or_else(|| s.as_int().map(|i| i.to_string())) + .map(ID) } _ => None } @@ -64,7 +63,7 @@ graphql_scalar!(String as "String" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref s) => <_ as Into>>::into(s.clone()), + InputValue::Scalar(ref s) => s.as_string(), _ => None, } } @@ -202,7 +201,7 @@ graphql_scalar!(bool as "Boolean" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref b) => <_ as Into>>::into(b).map(|b| *b), + InputValue::Scalar(ref b) => b.as_boolean(), _ => None, } } @@ -221,7 +220,7 @@ graphql_scalar!(i32 as "Int" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref i) => <_ as Into>>::into(i).map(|i| *i), + InputValue::Scalar(ref i) => i.as_int(), _ => None, } } @@ -244,10 +243,7 @@ graphql_scalar!(f64 as "Float" where Scalar = { from_input_value(v: &InputValue) -> Option { match *v { - InputValue::Scalar(ref s) => { - <_ as Into>>::into(s).map(|i| *i) - .or_else(|| <_ as Into>>::into(s).map(|i| *i as f64)) - } + InputValue::Scalar(ref s) => s.as_float(), _ => None, } } diff --git a/juniper/src/value/scalar.rs b/juniper/src/value/scalar.rs index 88b25154a..5c110ead2 100644 --- a/juniper/src/value/scalar.rs +++ b/juniper/src/value/scalar.rs @@ -35,10 +35,10 @@ pub trait ParseScalarValue { /// # extern crate juniper; /// # extern crate serde; /// # use serde::{de, Deserialize, Deserializer}; +/// # use juniper::ScalarValue; /// # use std::fmt; /// # /// #[derive(Debug, Clone, PartialEq, ScalarValue)] -/// #[juniper(visitor = "MyScalarValueVisitor")] /// enum MyScalarValue { /// Int(i32), /// Long(i64), @@ -47,6 +47,39 @@ pub trait ParseScalarValue { /// Boolean(bool), /// } /// +/// impl ScalarValue for MyScalarValue { +/// type Visitor = MyScalarValueVisitor; +/// +/// fn as_int(&self) -> Option { +/// match *self { +/// MyScalarValue::Int(ref i) => Some(*i), +/// _ => None, +/// } +/// } +/// +/// fn as_string(&self) -> Option { +/// match *self { +/// MyScalarValue::String(ref s) => Some(s.clone()), +/// _ => None, +/// } +/// } +/// +/// fn as_float(&self) -> Option { +/// match *self { +/// MyScalarValue::Int(ref i) => Some(*i as f64), +/// MyScalarValue::Float(ref f) => Some(*f), +/// _ => None, +/// } +/// } +/// +/// fn as_boolean(&self) -> Option { +/// match *self { +/// MyScalarValue::Boolean(ref b) => Some(*b), +/// _ => None, +/// } +/// } +/// } +/// /// #[derive(Default)] /// struct MyScalarValueVisitor; /// @@ -72,7 +105,11 @@ pub trait ParseScalarValue { /// where /// E: de::Error, /// { -/// Ok(MyScalarValue::Long(value)) +/// if value <= i32::max_value() as i64 { +/// self.visit_i32(value as i32) +/// } else { +/// Ok(MyScalarValue::Long(value)) +/// } /// } /// /// fn visit_u32(self, value: u32) -> Result @@ -156,6 +193,33 @@ pub trait ScalarValue: { 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; + + /// 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; + + /// 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; + + /// 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; } /// A marker trait extending the [`ScalarValue`](../trait.ScalarValue.html) trait @@ -189,7 +253,6 @@ where /// This types closely follows the graphql specification. #[derive(Debug, PartialEq, Clone, ScalarValue)] #[allow(missing_docs)] -#[juniper(visitor = "DefaultScalarValueVisitor")] pub enum DefaultScalarValue { Int(i32), Float(f64), @@ -197,6 +260,39 @@ pub enum DefaultScalarValue { Boolean(bool), } +impl ScalarValue for DefaultScalarValue { + type Visitor = DefaultScalarValueVisitor; + + fn as_int(&self) -> Option { + match *self { + DefaultScalarValue::Int(ref i) => Some(*i), + _ => None, + } + } + + fn as_string(&self) -> Option { + match *self { + DefaultScalarValue::String(ref s) => Some(s.clone()), + _ => None, + } + } + + fn as_float(&self) -> Option { + match *self { + DefaultScalarValue::Int(ref i) => Some(*i as f64), + DefaultScalarValue::Float(ref f) => Some(*f), + _ => None, + } + } + + fn as_boolean(&self) -> Option { + 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()) diff --git a/juniper_codegen/src/derive_juniper_scalar_value.rs b/juniper_codegen/src/derive_juniper_scalar_value.rs index 3a318b7fa..7f6fb1c0b 100644 --- a/juniper_codegen/src/derive_juniper_scalar_value.rs +++ b/juniper_codegen/src/derive_juniper_scalar_value.rs @@ -1,23 +1,9 @@ use proc_macro2::{Span, TokenStream}; use syn::{self, Data, Fields, Ident, Variant}; -use util::{get_juniper_attr, keyed_item_value, AttributeValidation, AttributeValue}; pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream { let ident = &ast.ident; - let visitor = if let Some(items) = get_juniper_attr(&ast.attrs) { - items.into_iter() - .filter_map(|i| keyed_item_value(&i, "visitor", AttributeValidation::String)) - .next() - .and_then(|t| if let AttributeValue::String(s) = t { - Some(Ident::new(&s, Span::call_site())) - } else { - None - }) - .expect("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`") - } else { - panic!("`#[derive(ScalarValue)]` needs a annotation of the form `#[juniper(visitor = \"VisitorType\")]`"); - }; let variants = match ast.data { Data::Enum(ref enum_data) => &enum_data.variants, @@ -54,11 +40,6 @@ pub fn impl_scalar_value(ast: &syn::DeriveInput) -> TokenStream { #serialize #display - - impl juniper::ScalarValue for #ident { - type Visitor = #visitor; - } - }; } } diff --git a/juniper_codegen/src/lib.rs b/juniper_codegen/src/lib.rs index d15c97435..03be1b608 100644 --- a/juniper_codegen/src/lib.rs +++ b/juniper_codegen/src/lib.rs @@ -45,7 +45,7 @@ pub fn derive_object(input: TokenStream) -> TokenStream { gen.into() } -#[proc_macro_derive(ScalarValue, attributes(juniper))] +#[proc_macro_derive(ScalarValue)] pub fn derive_juniper_scalar_value(input: TokenStream) -> TokenStream { let ast = syn::parse::(input).unwrap(); let gen = derive_juniper_scalar_value::impl_scalar_value(&ast); diff --git a/juniper_codegen/src/util.rs b/juniper_codegen/src/util.rs index c20c7cf5e..676a98cb4 100644 --- a/juniper_codegen/src/util.rs +++ b/juniper_codegen/src/util.rs @@ -71,17 +71,9 @@ fn get_doc_attr(attrs: &Vec) -> Option> { // Get the nested items of a a #[graphql(...)] attribute. pub fn get_graphql_attr(attrs: &Vec) -> Option> { - get_named_attr(attrs, "graphql") -} - -pub fn get_juniper_attr(attrs: &Vec) -> Option> { - get_named_attr(attrs, "juniper") -} - -pub fn get_named_attr(attrs: &Vec, name: &str) -> Option> { for attr in attrs { match attr.interpret_meta() { - Some(Meta::List(ref list)) if list.ident == name => { + Some(Meta::List(ref list)) if list.ident == "graphql" => { return Some(list.nested.iter().map(|x| x.clone()).collect()); } _ => {} diff --git a/juniper_tests/src/custom_scalar.rs b/juniper_tests/src/custom_scalar.rs index a493b8757..d54e3fa7d 100644 --- a/juniper_tests/src/custom_scalar.rs +++ b/juniper_tests/src/custom_scalar.rs @@ -6,11 +6,10 @@ use juniper::parser::{ParseError, ScalarToken, Token}; use juniper::serde::de; #[cfg(test)] use juniper::{execute, EmptyMutation, Object, RootNode, Variables}; -use juniper::{InputValue, ParseScalarResult, Value}; +use juniper::{InputValue, ParseScalarResult, ScalarValue, Value}; use std::fmt; #[derive(Debug, Clone, PartialEq, ScalarValue)] -#[juniper(visitor = "MyScalarValueVisitor")] enum MyScalarValue { Int(i32), Long(i64), @@ -19,6 +18,39 @@ enum MyScalarValue { Boolean(bool), } +impl ScalarValue for MyScalarValue { + type Visitor = MyScalarValueVisitor; + + fn as_int(&self) -> Option { + match *self { + MyScalarValue::Int(ref i) => Some(*i), + _ => None, + } + } + + fn as_string(&self) -> Option { + match *self { + MyScalarValue::String(ref s) => Some(s.clone()), + _ => None, + } + } + + fn as_float(&self) -> Option { + match *self { + MyScalarValue::Int(ref i) => Some(*i as f64), + MyScalarValue::Float(ref f) => Some(*f), + _ => None, + } + } + + fn as_boolean(&self) -> Option { + match *self { + MyScalarValue::Boolean(ref b) => Some(*b), + _ => None, + } + } +} + #[derive(Default, Debug)] struct MyScalarValueVisitor; @@ -44,7 +76,11 @@ impl<'de> de::Visitor<'de> for MyScalarValueVisitor { where E: de::Error, { - Ok(MyScalarValue::Long(value)) + if value <= i32::max_value() as i64 { + self.visit_i32(value as i32) + } else { + Ok(MyScalarValue::Long(value)) + } } fn visit_u32(self, value: u32) -> Result From 16d02861b586b2584a2487e2c20b122dbe1e02ca Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 19 Oct 2018 13:56:50 +0200 Subject: [PATCH 19/20] Add a changelog entry --- changelog/master.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/changelog/master.md b/changelog/master.md index cacd21410..ef5b81ba8 100644 --- a/changelog/master.md +++ b/changelog/master.md @@ -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) From b9aa072998df6a07f0b0b44d0cc91dfd535c4e06 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Fri, 19 Oct 2018 14:54:04 +0200 Subject: [PATCH 20/20] Fix rebase errors --- juniper_hyper/src/lib.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/juniper_hyper/src/lib.rs b/juniper_hyper/src/lib.rs index 8c5eb2596..03fd16d04 100644 --- a/juniper_hyper/src/lib.rs +++ b/juniper_hyper/src/lib.rs @@ -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; @@ -65,7 +66,7 @@ where String::from_utf8(chunk.iter().cloned().collect::>()) .map_err(GraphQLRequestError::BodyUtf8) .and_then(|input| { - serde_json::from_str::(&input) + serde_json::from_str::>(&input) .map_err(GraphQLRequestError::BodyJSONError) }) }) @@ -95,7 +96,7 @@ fn render_error(err: GraphQLRequestError) -> Response { resp } -fn execute_request( +fn execute_request( root_node: Arc>, context: Arc, request: GraphQLRequest, @@ -200,7 +201,7 @@ where Batch(Vec>), } -impl GraphQLRequest +impl GraphQLRequest where S: ScalarValue, for<'b> &'b S: ScalarRefValue<'b>, @@ -211,6 +212,7 @@ where context: Arc, ) -> impl Future + 'a where + S: 'a, QueryT: GraphQLType + 'a, MutationT: GraphQLType + 'a, {