Performance improvements (#202)

Performance improvements

* Replace the IndexMap in the serialized object with a plain
  `Vec<(String, Value)>` because linear search is faster for few
  elements

* Some general tweaks to skip some allocations
This commit is contained in:
Georg Semmler 2018-08-13 13:47:18 +00:00 committed by Christian Legnitto
parent 56f71e934b
commit 90b89f00ee
22 changed files with 614 additions and 353 deletions

View file

@ -1,9 +1,7 @@
use indexmap::IndexMap;
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
struct TestType;
@ -19,7 +17,7 @@ graphql_object!(TestType: () |&self| {
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -36,7 +34,7 @@ where
fn run_query<F>(query: &str, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
run_variable_query(query, Variables::new(), f);
}
@ -44,32 +42,32 @@ where
#[test]
fn scalar_include_true() {
run_query("{ a, b @include(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_include_false() {
run_query("{ a, b @include(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn scalar_skip_false() {
run_query("{ a, b @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_skip_true() {
run_query("{ a, b @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
@ -78,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("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
},
);
}
@ -89,8 +87,8 @@ fn fragment_spread_include_false() {
run_query(
"{ a, ...Frag @include(if: false) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
},
);
}
@ -100,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("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
},
);
}
@ -111,8 +109,8 @@ fn fragment_spread_skip_true() {
run_query(
"{ a, ...Frag @skip(if: true) } fragment Frag on TestType { b }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
},
);
}
@ -122,8 +120,8 @@ fn inline_fragment_include_true() {
run_query(
"{ a, ... on TestType @include(if: true) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
},
);
}
@ -133,8 +131,8 @@ fn inline_fragment_include_false() {
run_query(
"{ a, ... on TestType @include(if: false) { b } }",
|result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
},
);
}
@ -142,79 +140,79 @@ 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("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn inline_fragment_skip_true() {
run_query("{ a, ... on TestType @skip(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn anonymous_inline_fragment_include_true() {
run_query("{ a, ... @include(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn anonymous_inline_fragment_include_false() {
run_query("{ a, ... @include(if: false) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn anonymous_inline_fragment_skip_false() {
run_query("{ a, ... @skip(if: false) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn anonymous_inline_fragment_skip_true() {
run_query("{ a, ... @skip(if: true) { b } }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn scalar_include_true_skip_true() {
run_query("{ a, b @include(if: true) @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn scalar_include_true_skip_false() {
run_query("{ a, b @include(if: true) @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), Some(&Value::string("b")));
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), Some(&Value::string("b")));
});
}
#[test]
fn scalar_include_false_skip_true() {
run_query("{ a, b @include(if: false) @skip(if: true) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}
#[test]
fn scalar_include_false_skip_false() {
run_query("{ a, b @include(if: false) @skip(if: false) }", |result| {
assert_eq!(result.get("a"), Some(&Value::string("a")));
assert_eq!(result.get("b"), None);
assert_eq!(result.get_field_value("a"), Some(&Value::string("a")));
assert_eq!(result.get_field_value("b"), None);
});
}

View file

@ -1,12 +1,10 @@
use indexmap::IndexMap;
use ast::InputValue;
use executor::Variables;
use parser::SourcePosition;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use validation::RuleError;
use value::Value;
use value::{Value, Object};
use GraphQLError::ValidationError;
#[derive(GraphQLEnum, Debug)]
@ -30,7 +28,7 @@ graphql_object!(TestType: () |&self| {
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -47,7 +45,7 @@ where
fn run_query<F>(query: &str, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
run_variable_query(query, Variables::new(), f);
}
@ -55,14 +53,14 @@ where
#[test]
fn accepts_enum_literal() {
run_query("{ toString(color: RED) }", |result| {
assert_eq!(result.get("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("aColor"), Some(&Value::string("RED")));
assert_eq!(result.get_field_value("aColor"), Some(&Value::string("RED")));
});
}
@ -92,7 +90,7 @@ fn accepts_strings_in_variables() {
.into_iter()
.collect(),
|result| {
assert_eq!(result.get("toString"), Some(&Value::string("Color::Red")));
assert_eq!(result.get_field_value("toString"), Some(&Value::string("Color::Red")));
},
);
}

View file

@ -1,9 +1,7 @@
use indexmap::IndexMap;
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
/*
@ -76,7 +74,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F)
where
F: Fn((&IndexMap<String, Value>, &Vec<Value>)) -> (),
F: Fn((&Object, &Vec<Value>)) -> (),
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -90,13 +88,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let values = type_info
.get("enumValues")
.get_field_value("enumValues")
.expect("enumValues field missing")
.as_list_value()
.expect("enumValues not a list");
@ -122,8 +120,8 @@ fn default_name_introspection() {
"#;
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(type_info.get("name"), Some(&Value::string("DefaultName")));
assert_eq!(type_info.get("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!(values.len(), 2);
@ -171,8 +169,8 @@ fn named_introspection() {
"#;
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(type_info.get("name"), Some(&Value::string("ANamedEnum")));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("ANamedEnum")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2);
@ -221,10 +219,10 @@ fn no_trailing_comma_introspection() {
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("NoTrailingComma"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2);
@ -273,11 +271,11 @@ fn enum_description_introspection() {
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("EnumDescription"))
);
assert_eq!(
type_info.get("description"),
type_info.get_field_value("description"),
Some(&Value::string("A description of the enum itself"))
);
@ -328,10 +326,10 @@ fn enum_value_description_introspection() {
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("EnumValueDescription"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2);
@ -380,10 +378,10 @@ fn enum_deprecation_introspection() {
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("EnumDeprecation"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 2);
@ -438,10 +436,10 @@ fn enum_deprecation_no_values_introspection() {
run_type_info_query(doc, |(type_info, values)| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("EnumDeprecation"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(values.len(), 0);
});

View file

@ -1,10 +1,8 @@
use indexmap::IndexMap;
use ast::{FromInputValue, InputValue};
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
struct Root;
@ -106,7 +104,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F)
where
F: Fn(&IndexMap<String, Value>, &Vec<Value>) -> (),
F: Fn(&Object, &Vec<Value>) -> (),
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -120,13 +118,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("inputFields")
.get_field_value("inputFields")
.expect("inputFields field missing")
.as_list_value()
.expect("inputFields not a list");
@ -156,8 +154,8 @@ fn default_name_introspection() {
"#;
run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get("name"), Some(&Value::string("DefaultName")));
assert_eq!(type_info.get("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);
@ -256,10 +254,10 @@ fn no_trailing_comma_introspection() {
run_type_info_query(doc, |type_info, fields| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("NoTrailingComma"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 2);
@ -337,8 +335,8 @@ fn derive_introspection() {
"#;
run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get("name"), Some(&Value::string("Derive")));
assert_eq!(type_info.get("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);
@ -405,10 +403,10 @@ fn named_introspection() {
run_type_info_query(doc, |type_info, fields| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("ANamedInputObject"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 1);
@ -461,9 +459,9 @@ fn description_introspection() {
"#;
run_type_info_query(doc, |type_info, fields| {
assert_eq!(type_info.get("name"), Some(&Value::string("Description")));
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("Description")));
assert_eq!(
type_info.get("description"),
type_info.get_field_value("description"),
Some(&Value::string("Description for the input object"))
);
@ -519,10 +517,10 @@ fn field_description_introspection() {
run_type_info_query(doc, |type_info, fields| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("FieldDescription"))
);
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
assert_eq!(fields.len(), 2);
@ -597,7 +595,7 @@ fn field_with_defaults_introspection() {
run_type_info_query(doc, |type_info, fields| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("FieldWithDefaults"))
);

View file

@ -126,21 +126,39 @@ fn enum_introspection() {
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(type_info.get("name"), Some(&Value::string("SampleEnum")));
assert_eq!(type_info.get("kind"), Some(&Value::string("ENUM")));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get("interfaces"), Some(&Value::null()));
assert_eq!(type_info.get("possibleTypes"), Some(&Value::null()));
assert_eq!(type_info.get("inputFields"), Some(&Value::null()));
assert_eq!(type_info.get("ofType"), Some(&Value::null()));
assert_eq!(
type_info.get_field_value("name"),
Some(&Value::string("SampleEnum"))
);
assert_eq!(
type_info.get_field_value("kind"),
Some(&Value::string("ENUM"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("interfaces"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("possibleTypes"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("inputFields"),
Some(&Value::null())
);
assert_eq!(type_info.get_field_value("ofType"), Some(&Value::null()));
let values = type_info
.get("enumValues")
.get_field_value("enumValues")
.expect("enumValues field missing")
.as_list_value()
.expect("enumValues not a list");
@ -219,27 +237,39 @@ fn interface_introspection() {
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("SampleInterface"))
);
assert_eq!(type_info.get("kind"), Some(&Value::string("INTERFACE")));
assert_eq!(
type_info.get("description"),
type_info.get_field_value("kind"),
Some(&Value::string("INTERFACE"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::string("A sample interface"))
);
assert_eq!(type_info.get("interfaces"), Some(&Value::null()));
assert_eq!(type_info.get("enumValues"), Some(&Value::null()));
assert_eq!(type_info.get("inputFields"), Some(&Value::null()));
assert_eq!(type_info.get("ofType"), Some(&Value::null()));
assert_eq!(
type_info.get_field_value("interfaces"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("enumValues"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("inputFields"),
Some(&Value::null())
);
assert_eq!(type_info.get_field_value("ofType"), Some(&Value::null()));
let possible_types = type_info
.get("possibleTypes")
.get_field_value("possibleTypes")
.expect("possibleTypes field missing")
.as_list_value()
.expect("possibleTypes not a list");
@ -251,7 +281,7 @@ fn interface_introspection() {
)));
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields field not an object value");
@ -353,32 +383,47 @@ fn object_introspection() {
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
assert_eq!(type_info.get("name"), Some(&Value::string("Root")));
assert_eq!(type_info.get("kind"), Some(&Value::string("OBJECT")));
assert_eq!(
type_info.get("description"),
type_info.get_field_value("name"),
Some(&Value::string("Root"))
);
assert_eq!(
type_info.get_field_value("kind"),
Some(&Value::string("OBJECT"))
);
assert_eq!(
type_info.get_field_value("description"),
Some(&Value::string("The root query object in the schema"))
);
assert_eq!(
type_info.get("interfaces"),
type_info.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![("name", Value::string("SampleInterface"))]
.into_iter()
.collect(),
)]))
);
assert_eq!(type_info.get("enumValues"), Some(&Value::null()));
assert_eq!(type_info.get("inputFields"), Some(&Value::null()));
assert_eq!(type_info.get("ofType"), Some(&Value::null()));
assert_eq!(type_info.get("possibleTypes"), Some(&Value::null()));
assert_eq!(
type_info.get_field_value("enumValues"),
Some(&Value::null())
);
assert_eq!(
type_info.get_field_value("inputFields"),
Some(&Value::null())
);
assert_eq!(type_info.get_field_value("ofType"), Some(&Value::null()));
assert_eq!(
type_info.get_field_value("possibleTypes"),
Some(&Value::null())
);
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields field not an object value");
@ -538,7 +583,7 @@ fn scalar_introspection() {
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing");
assert_eq!(

View file

@ -1,12 +1,10 @@
use indexmap::IndexMap;
use ast::InputValue;
use executor::Variables;
use parser::SourcePosition;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use validation::RuleError;
use value::Value;
use value::{Value, Object};
use GraphQLError::ValidationError;
#[derive(Debug)]
@ -116,7 +114,7 @@ graphql_object!(TestType: () |&self| {
fn run_variable_query<F>(query: &str, vars: Variables, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
let schema = RootNode::new(TestType, EmptyMutation::<()>::new());
@ -133,7 +131,7 @@ where
fn run_query<F>(query: &str, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
run_variable_query(query, Variables::new(), f);
}
@ -144,7 +142,7 @@ fn inline_complex_input() {
r#"{ fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"}) }"#,
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
},
);
@ -156,7 +154,7 @@ fn inline_parse_single_value_to_list() {
r#"{ fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"}) }"#,
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
},
);
@ -168,7 +166,7 @@ fn inline_runs_from_input_value_on_scalar() {
r#"{ fieldWithObjectInput(input: {c: "baz", d: "SerializedValue"}) }"#,
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#)));
},
);
@ -192,7 +190,7 @@ fn variable_complex_input() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
},
);
@ -216,7 +214,7 @@ fn variable_parse_single_value_to_list() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: Some("foo"), b: Some([Some("bar")]), c: "baz", d: None })"#)));
},
);
@ -239,7 +237,7 @@ fn variable_runs_from_input_value_on_scalar() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithObjectInput"),
result.get_field_value("fieldWithObjectInput"),
Some(&Value::string(r#"Some(TestInputObject { a: None, b: None, c: "baz", d: Some(TestComplexScalar) })"#)));
},
);
@ -387,7 +385,7 @@ fn variable_error_on_additional_field() {
fn allow_nullable_inputs_to_be_omitted() {
run_query(r#"{ fieldWithNullableStringInput }"#, |result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))
);
});
@ -399,7 +397,7 @@ fn allow_nullable_inputs_to_be_omitted_in_variable() {
r#"query q($value: String) { fieldWithNullableStringInput(input: $value) }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))
);
},
@ -412,7 +410,7 @@ fn allow_nullable_inputs_to_be_explicitly_null() {
r#"{ fieldWithNullableStringInput(input: null) }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))
);
},
@ -428,7 +426,7 @@ fn allow_nullable_inputs_to_be_set_to_null_in_variable() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"None"#))
);
},
@ -444,7 +442,7 @@ fn allow_nullable_inputs_to_be_set_to_value_in_variable() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"Some("a")"#))
);
},
@ -457,7 +455,7 @@ fn allow_nullable_inputs_to_be_set_to_value_directly() {
r#"{ fieldWithNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
result.get("fieldWithNullableStringInput"),
result.get_field_value("fieldWithNullableStringInput"),
Some(&Value::string(r#"Some("a")"#))
);
},
@ -511,7 +509,7 @@ fn allow_non_nullable_inputs_to_be_set_to_value_in_variable() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithNonNullableStringInput"),
result.get_field_value("fieldWithNonNullableStringInput"),
Some(&Value::string(r#""a""#))
);
},
@ -524,7 +522,7 @@ fn allow_non_nullable_inputs_to_be_set_to_value_directly() {
r#"{ fieldWithNonNullableStringInput(input: "a") }"#,
|result| {
assert_eq!(
result.get("fieldWithNonNullableStringInput"),
result.get_field_value("fieldWithNonNullableStringInput"),
Some(&Value::string(r#""a""#))
);
},
@ -539,7 +537,7 @@ fn allow_lists_to_be_null() {
.into_iter()
.collect(),
|result| {
assert_eq!(result.get("list"), Some(&Value::string(r#"None"#)));
assert_eq!(result.get_field_value("list"), Some(&Value::string(r#"None"#)));
},
);
}
@ -555,7 +553,7 @@ fn allow_lists_to_contain_values() {
.collect(),
|result| {
assert_eq!(
result.get("list"),
result.get_field_value("list"),
Some(&Value::string(r#"Some([Some("A")])"#))
);
},
@ -577,7 +575,7 @@ fn allow_lists_to_contain_null() {
.collect(),
|result| {
assert_eq!(
result.get("list"),
result.get_field_value("list"),
Some(&Value::string(r#"Some([Some("A"), None, Some("B")])"#))
);
},
@ -614,7 +612,7 @@ fn allow_non_null_lists_to_contain_values() {
)].into_iter()
.collect(),
|result| {
assert_eq!(result.get("nnList"), Some(&Value::string(r#"[Some("A")]"#)));
assert_eq!(result.get_field_value("nnList"), Some(&Value::string(r#"[Some("A")]"#)));
},
);
}
@ -633,7 +631,7 @@ fn allow_non_null_lists_to_contain_null() {
.collect(),
|result| {
assert_eq!(
result.get("nnList"),
result.get_field_value("nnList"),
Some(&Value::string(r#"[Some("A"), None, Some("B")]"#))
);
},
@ -648,7 +646,7 @@ fn allow_lists_of_non_null_to_be_null() {
.into_iter()
.collect(),
|result| {
assert_eq!(result.get("listNn"), Some(&Value::string(r#"None"#)));
assert_eq!(result.get_field_value("listNn"), Some(&Value::string(r#"None"#)));
},
);
}
@ -663,7 +661,7 @@ fn allow_lists_of_non_null_to_contain_values() {
)].into_iter()
.collect(),
|result| {
assert_eq!(result.get("listNn"), Some(&Value::string(r#"Some(["A"])"#)));
assert_eq!(result.get_field_value("listNn"), Some(&Value::string(r#"Some(["A"])"#)));
},
);
}
@ -748,7 +746,7 @@ fn allow_non_null_lists_of_non_null_to_contain_values() {
)].into_iter()
.collect(),
|result| {
assert_eq!(result.get("nnListNn"), Some(&Value::string(r#"["A"]"#)));
assert_eq!(result.get_field_value("nnListNn"), Some(&Value::string(r#"["A"]"#)));
},
);
}
@ -799,7 +797,7 @@ fn does_not_allow_unknown_types_to_be_used_as_values() {
fn default_argument_when_not_provided() {
run_query(r#"{ fieldWithDefaultArgumentValue }"#, |result| {
assert_eq!(
result.get("fieldWithDefaultArgumentValue"),
result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#))
);
});
@ -811,7 +809,7 @@ fn default_argument_when_nullable_variable_not_provided() {
r#"query q($input: String) { fieldWithDefaultArgumentValue(input: $input) }"#,
|result| {
assert_eq!(
result.get("fieldWithDefaultArgumentValue"),
result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#))
);
},
@ -827,7 +825,7 @@ fn default_argument_when_nullable_variable_set_to_null() {
.collect(),
|result| {
assert_eq!(
result.get("fieldWithDefaultArgumentValue"),
result.get_field_value("fieldWithDefaultArgumentValue"),
Some(&Value::string(r#""Hello World""#))
);
},
@ -838,14 +836,14 @@ fn default_argument_when_nullable_variable_set_to_null() {
fn nullable_input_object_arguments_successful_without_variables() {
run_query(r#"{ exampleInput(arg: {a: "abc", b: 123}) }"#, |result| {
assert_eq!(
result.get("exampleInput"),
result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: Some("abc"), b: 123"#))
);
});
run_query(r#"{ exampleInput(arg: {a: null, b: 1}) }"#, |result| {
assert_eq!(
result.get("exampleInput"),
result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#))
);
});
@ -860,7 +858,7 @@ fn nullable_input_object_arguments_successful_with_variables() {
.collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 123"#))
);
},
@ -873,7 +871,7 @@ fn nullable_input_object_arguments_successful_with_variables() {
.collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#))
);
},
@ -884,7 +882,7 @@ fn nullable_input_object_arguments_successful_with_variables() {
vec![].into_iter().collect(),
|result| {
assert_eq!(
result.get("exampleInput"),
result.get_field_value("exampleInput"),
Some(&Value::string(r#"a: None, b: 1"#))
);
},
@ -969,7 +967,7 @@ fn does_not_allow_null_variable_for_required_field() {
fn input_object_with_default_values() {
run_query(r#"{ inputWithDefaults(arg: {a: 1}) }"#, |result| {
assert_eq!(
result.get("inputWithDefaults"),
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))
);
});
@ -981,7 +979,7 @@ fn input_object_with_default_values() {
.collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))
);
},
@ -992,7 +990,7 @@ fn input_object_with_default_values() {
vec![].into_iter().collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 1"#))
);
},
@ -1005,7 +1003,7 @@ fn input_object_with_default_values() {
.collect(),
|result| {
assert_eq!(
result.get("inputWithDefaults"),
result.get_field_value("inputWithDefaults"),
Some(&Value::string(r#"a: 2"#))
);
},
@ -1024,7 +1022,7 @@ mod integers {
.collect(),
|result| {
assert_eq!(
result.get("integerInput"),
result.get_field_value("integerInput"),
Some(&Value::string(r#"value: 1"#))
);
},
@ -1037,7 +1035,7 @@ mod integers {
.collect(),
|result| {
assert_eq!(
result.get("integerInput"),
result.get_field_value("integerInput"),
Some(&Value::string(r#"value: -1"#))
);
},
@ -1097,7 +1095,7 @@ mod floats {
.collect(),
|result| {
assert_eq!(
result.get("floatInput"),
result.get_field_value("floatInput"),
Some(&Value::string(r#"value: 10"#))
);
},
@ -1113,7 +1111,7 @@ mod floats {
.collect(),
|result| {
assert_eq!(
result.get("floatInput"),
result.get_field_value("floatInput"),
Some(&Value::string(r#"value: -1"#))
);
},

View file

@ -8,7 +8,7 @@ use ast::InputValue;
use executor::ExecutionError;
use parser::{ParseError, SourcePosition, Spanning};
use validation::RuleError;
use {GraphQLError, Value};
use {GraphQLError, Object, Value};
#[derive(Serialize)]
struct SerializeHelper {
@ -250,6 +250,22 @@ impl<'a> ser::Serialize for Spanning<ParseError<'a>> {
}
}
impl ser::Serialize for Object {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let mut map = serializer.serialize_map(Some(self.field_count()))?;
for &(ref f, ref v) in self.iter() {
map.serialize_key(f)?;
map.serialize_value(v)?;
}
map.end()
}
}
impl ser::Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -162,7 +162,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::Value;
pub use value::{Value, Object};
pub use schema::meta;

View file

@ -112,13 +112,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields not a list");
@ -128,7 +128,7 @@ where
.filter(|f| {
f.as_object_value()
.expect("Field not an object")
.get("name")
.get_field_value("name")
.expect("name field missing from field")
.as_string_value()
.expect("name is not a string") == field_name
@ -141,7 +141,7 @@ where
println!("Field: {:?}", field);
let args = field
.get("args")
.get_field_value("args")
.expect("args missing from field")
.as_list_value()
.expect("args is not a list");

View file

@ -1,10 +1,8 @@
use indexmap::IndexMap;
use ast::InputValue;
use executor::FieldResult;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Object, Value};
struct Interface;
struct Root;
@ -59,7 +57,7 @@ graphql_interface!(Interface: () |&self| {
fn run_field_info_query<F>(type_name: &str, field_name: &str, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -87,13 +85,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields not a list");
@ -103,7 +101,7 @@ where
.filter(|f| {
f.as_object_value()
.expect("Field not an object")
.get("name")
.get_field_value("name")
.expect("name field missing from field")
.as_string_value()
.expect("name is not a string") == field_name
@ -121,57 +119,78 @@ where
#[test]
fn introspect_object_field_simple() {
run_field_info_query("Root", "simple", |field| {
assert_eq!(field.get("name"), Some(&Value::string("simple")));
assert_eq!(field.get("description"), Some(&Value::null()));
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get("deprecationReason"), Some(&Value::null()));
assert_eq!(
field.get_field_value("name"),
Some(&Value::string("simple"))
);
assert_eq!(field.get_field_value("description"), 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_simple() {
run_field_info_query("Interface", "simple", |field| {
assert_eq!(field.get("name"), Some(&Value::string("simple")));
assert_eq!(field.get("description"), Some(&Value::null()));
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get("deprecationReason"), Some(&Value::null()));
assert_eq!(
field.get_field_value("name"),
Some(&Value::string("simple"))
);
assert_eq!(field.get_field_value("description"), 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_description() {
run_field_info_query("Root", "description", |field| {
assert_eq!(field.get("name"), Some(&Value::string("description")));
assert_eq!(
field.get("description"),
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("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get("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("name"), Some(&Value::string("description")));
assert_eq!(field.get_field_value("name"), Some(&Value::string("description")));
assert_eq!(
field.get("description"),
field.get_field_value("description"),
Some(&Value::string("Field description"))
);
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(false)));
assert_eq!(field.get("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("name"), Some(&Value::string("deprecated")));
assert_eq!(field.get("description"), Some(&Value::null()));
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true)));
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("deprecationReason"),
field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason"))
);
});
@ -180,11 +199,11 @@ fn introspect_object_field_deprecated() {
#[test]
fn introspect_interface_field_deprecated() {
run_field_info_query("Interface", "deprecated", |field| {
assert_eq!(field.get("name"), Some(&Value::string("deprecated")));
assert_eq!(field.get("description"), Some(&Value::null()));
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true)));
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("deprecationReason"),
field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason"))
);
});
@ -193,14 +212,14 @@ fn introspect_interface_field_deprecated() {
#[test]
fn introspect_object_field_deprecated_descr() {
run_field_info_query("Root", "deprecatedDescr", |field| {
assert_eq!(field.get("name"), Some(&Value::string("deprecatedDescr")));
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecatedDescr")));
assert_eq!(
field.get("description"),
field.get_field_value("description"),
Some(&Value::string("Field description"))
);
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(
field.get("deprecationReason"),
field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason"))
);
});
@ -209,14 +228,14 @@ fn introspect_object_field_deprecated_descr() {
#[test]
fn introspect_interface_field_deprecated_descr() {
run_field_info_query("Interface", "deprecatedDescr", |field| {
assert_eq!(field.get("name"), Some(&Value::string("deprecatedDescr")));
assert_eq!(field.get_field_value("name"), Some(&Value::string("deprecatedDescr")));
assert_eq!(
field.get("description"),
field.get_field_value("description"),
Some(&Value::string("Field description"))
);
assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(field.get_field_value("isDeprecated"), Some(&Value::boolean(true)));
assert_eq!(
field.get("deprecationReason"),
field.get_field_value("deprecationReason"),
Some(&Value::string("Deprecation reason"))
);
});

View file

@ -1,10 +1,9 @@
use indexmap::IndexMap;
use std::marker::PhantomData;
use ast::InputValue;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
/*
@ -130,7 +129,7 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F)
where
F: Fn(&IndexMap<String, Value>, &Vec<Value>) -> (),
F: Fn(&Object, &Vec<Value>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -157,13 +156,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields field not a list value");
@ -175,10 +174,10 @@ where
fn introspect_custom_name() {
run_type_info_query("ACustomNamedInterface", |object, fields| {
assert_eq!(
object.get("name"),
object.get_field_value("name"),
Some(&Value::string("ACustomNamedInterface"))
);
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!(
fields.contains(&Value::object(
@ -193,8 +192,8 @@ fn introspect_custom_name() {
#[test]
fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("WithLifetime")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get_field_value("name"), Some(&Value::string("WithLifetime")));
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!(
fields.contains(&Value::object(
@ -209,8 +208,8 @@ fn introspect_with_lifetime() {
#[test]
fn introspect_with_generics() {
run_type_info_query("WithGenerics", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("WithGenerics")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get_field_value("name"), Some(&Value::string("WithGenerics")));
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert!(
fields.contains(&Value::object(
@ -225,9 +224,9 @@ fn introspect_with_generics() {
#[test]
fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("DescriptionFirst")));
assert_eq!(object.get_field_value("name"), Some(&Value::string("DescriptionFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -244,9 +243,9 @@ fn introspect_description_first() {
#[test]
fn introspect_fields_first() {
run_type_info_query("FieldsFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("FieldsFirst")));
assert_eq!(object.get_field_value("name"), Some(&Value::string("FieldsFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -263,9 +262,9 @@ fn introspect_fields_first() {
#[test]
fn introspect_interfaces_first() {
run_type_info_query("InterfacesFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("InterfacesFirst")));
assert_eq!(object.get_field_value("name"), Some(&Value::string("InterfacesFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -283,11 +282,11 @@ fn introspect_interfaces_first() {
fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!(
object.get("name"),
object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
);
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -304,9 +303,9 @@ fn introspect_commas_with_trailing() {
#[test]
fn introspect_commas_on_meta() {
run_type_info_query("CommasOnMeta", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("CommasOnMeta")));
assert_eq!(object.get_field_value("name"), Some(&Value::string("CommasOnMeta")));
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -324,11 +323,11 @@ fn introspect_commas_on_meta() {
fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |object, fields| {
assert_eq!(
object.get("name"),
object.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma"))
);
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);

View file

@ -1,11 +1,10 @@
use indexmap::IndexMap;
use std::marker::PhantomData;
use ast::InputValue;
use executor::{Context, FieldResult};
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Object, Value};
/*
@ -144,7 +143,7 @@ graphql_object!(<'a> Root: InnerContext as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F)
where
F: Fn(&IndexMap<String, Value>, &Vec<Value>) -> (),
F: Fn(&Object, &Vec<Value>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -184,13 +183,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields field not a list value");
@ -201,12 +200,18 @@ where
#[test]
fn introspect_custom_name() {
run_type_info_query("ACustomNamedType", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("ACustomNamedType")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get("interfaces"), Some(&Value::list(vec![])));
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("ACustomNamedType"))
);
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![]))
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -215,12 +220,18 @@ fn introspect_custom_name() {
#[test]
fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("WithLifetime")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get("interfaces"), Some(&Value::list(vec![])));
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("WithLifetime"))
);
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![]))
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -229,12 +240,18 @@ fn introspect_with_lifetime() {
#[test]
fn introspect_with_generics() {
run_type_info_query("WithGenerics", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("WithGenerics")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert_eq!(object.get("interfaces"), Some(&Value::list(vec![])));
assert_eq!(
object.get_field_value("name"),
Some(&Value::string("WithGenerics"))
);
assert_eq!(object.get_field_value("description"), Some(&Value::null()));
assert_eq!(
object.get_field_value("interfaces"),
Some(&Value::list(vec![]))
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -243,13 +260,16 @@ fn introspect_with_generics() {
#[test]
fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("DescriptionFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("name"),
Some(&Value::string("DescriptionFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
);
assert_eq!(
object.get("interfaces"),
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
@ -260,7 +280,7 @@ fn introspect_description_first() {
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -269,13 +289,16 @@ fn introspect_description_first() {
#[test]
fn introspect_fields_first() {
run_type_info_query("FieldsFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("FieldsFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("name"),
Some(&Value::string("FieldsFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
);
assert_eq!(
object.get("interfaces"),
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
@ -286,7 +309,7 @@ fn introspect_fields_first() {
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -295,13 +318,16 @@ fn introspect_fields_first() {
#[test]
fn introspect_interfaces_first() {
run_type_info_query("InterfacesFirst", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("InterfacesFirst")));
assert_eq!(
object.get("description"),
object.get_field_value("name"),
Some(&Value::string("InterfacesFirst"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
);
assert_eq!(
object.get("interfaces"),
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
@ -312,7 +338,7 @@ fn introspect_interfaces_first() {
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -322,15 +348,15 @@ fn introspect_interfaces_first() {
fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!(
object.get("name"),
object.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
);
assert_eq!(
object.get("description"),
object.get_field_value("description"),
Some(&Value::string("A description"))
);
assert_eq!(
object.get("interfaces"),
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
@ -341,7 +367,7 @@ fn introspect_commas_with_trailing() {
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});
@ -350,13 +376,16 @@ fn introspect_commas_with_trailing() {
#[test]
fn introspect_commas_on_meta() {
run_type_info_query("CommasOnMeta", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("CommasOnMeta")));
assert_eq!(
object.get("description"),
object.get_field_value("name"),
Some(&Value::string("CommasOnMeta"))
);
assert_eq!(
object.get_field_value("description"),
Some(&Value::string("A description"))
);
assert_eq!(
object.get("interfaces"),
object.get_field_value("interfaces"),
Some(&Value::list(vec![Value::object(
vec![
("name", Value::string("Interface")),
@ -367,7 +396,7 @@ fn introspect_commas_on_meta() {
);
assert!(fields.contains(&graphql_value!({
"name": "simple",
"name": "simple",
"type": { "kind": "NON_NULL", "name": None, "ofType": { "kind": "SCALAR", "name": "Int" } }
})));
});

View file

@ -1,9 +1,7 @@
use indexmap::IndexMap;
use executor::Variables;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
struct DefaultName(i32);
struct OtherOrder(i32);
@ -72,7 +70,7 @@ graphql_object!(Root: () |&self| {
fn run_type_info_query<F>(doc: &str, f: F)
where
F: Fn(&IndexMap<String, Value>) -> (),
F: Fn(&Object) -> (),
{
let schema = RootNode::new(Root {}, EmptyMutation::<()>::new());
@ -86,7 +84,7 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
@ -106,8 +104,8 @@ fn default_name_introspection() {
"#;
run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get("name"), Some(&Value::string("DefaultName")));
assert_eq!(type_info.get("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()));
});
}
@ -123,8 +121,8 @@ fn other_order_introspection() {
"#;
run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get("name"), Some(&Value::string("OtherOrder")));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("OtherOrder")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
});
}
@ -140,8 +138,8 @@ fn named_introspection() {
"#;
run_type_info_query(doc, |type_info| {
assert_eq!(type_info.get("name"), Some(&Value::string("ANamedScalar")));
assert_eq!(type_info.get("description"), Some(&Value::null()));
assert_eq!(type_info.get_field_value("name"), Some(&Value::string("ANamedScalar")));
assert_eq!(type_info.get_field_value("description"), Some(&Value::null()));
});
}
@ -158,11 +156,11 @@ fn scalar_description_introspection() {
run_type_info_query(doc, |type_info| {
assert_eq!(
type_info.get("name"),
type_info.get_field_value("name"),
Some(&Value::string("ScalarDescription"))
);
assert_eq!(
type_info.get("description"),
type_info.get_field_value("description"),
Some(&Value::string("A sample scalar, represented as an integer"))
);
});

View file

@ -1,10 +1,9 @@
use indexmap::IndexMap;
use std::marker::PhantomData;
use ast::InputValue;
use schema::model::RootNode;
use types::scalars::EmptyMutation;
use value::Value;
use value::{Value, Object};
/*
@ -111,7 +110,7 @@ graphql_object!(<'a> Root: () as "Root" |&self| {
fn run_type_info_query<F>(type_name: &str, f: F)
where
F: Fn(&IndexMap<String, Value>, &Vec<Value>) -> (),
F: Fn(&Object, &Vec<Value>) -> (),
{
let doc = r#"
query ($typeName: String!) {
@ -138,13 +137,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let possible_types = type_info
.get("possibleTypes")
.get_field_value("possibleTypes")
.expect("possibleTypes field missing")
.as_list_value()
.expect("possibleTypes field not a list value");
@ -155,8 +154,8 @@ where
#[test]
fn introspect_custom_name() {
run_type_info_query("ACustomNamedUnion", |union, possible_types| {
assert_eq!(union.get("name"), Some(&Value::string("ACustomNamedUnion")));
assert_eq!(union.get("description"), Some(&Value::null()));
assert_eq!(union.get_field_value("name"), Some(&Value::string("ACustomNamedUnion")));
assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!(
possible_types.contains(&Value::object(
@ -171,8 +170,8 @@ fn introspect_custom_name() {
#[test]
fn introspect_with_lifetime() {
run_type_info_query("WithLifetime", |union, possible_types| {
assert_eq!(union.get("name"), Some(&Value::string("WithLifetime")));
assert_eq!(union.get("description"), Some(&Value::null()));
assert_eq!(union.get_field_value("name"), Some(&Value::string("WithLifetime")));
assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!(
possible_types.contains(&Value::object(
@ -187,8 +186,8 @@ fn introspect_with_lifetime() {
#[test]
fn introspect_with_generics() {
run_type_info_query("WithGenerics", |union, possible_types| {
assert_eq!(union.get("name"), Some(&Value::string("WithGenerics")));
assert_eq!(union.get("description"), Some(&Value::null()));
assert_eq!(union.get_field_value("name"), Some(&Value::string("WithGenerics")));
assert_eq!(union.get_field_value("description"), Some(&Value::null()));
assert!(
possible_types.contains(&Value::object(
@ -203,9 +202,9 @@ fn introspect_with_generics() {
#[test]
fn introspect_description_first() {
run_type_info_query("DescriptionFirst", |union, possible_types| {
assert_eq!(union.get("name"), Some(&Value::string("DescriptionFirst")));
assert_eq!(union.get_field_value("name"), Some(&Value::string("DescriptionFirst")));
assert_eq!(
union.get("description"),
union.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -222,9 +221,9 @@ fn introspect_description_first() {
#[test]
fn introspect_resolvers_first() {
run_type_info_query("ResolversFirst", |union, possible_types| {
assert_eq!(union.get("name"), Some(&Value::string("ResolversFirst")));
assert_eq!(union.get_field_value("name"), Some(&Value::string("ResolversFirst")));
assert_eq!(
union.get("description"),
union.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -242,11 +241,11 @@ fn introspect_resolvers_first() {
fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |union, possible_types| {
assert_eq!(
union.get("name"),
union.get_field_value("name"),
Some(&Value::string("CommasWithTrailing"))
);
assert_eq!(
union.get("description"),
union.get_field_value("description"),
Some(&Value::string("A description"))
);
@ -264,11 +263,11 @@ fn introspect_commas_with_trailing() {
fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |union, possible_types| {
assert_eq!(
union.get("name"),
union.get_field_value("name"),
Some(&Value::string("ResolversWithTrailingComma"))
);
assert_eq!(
union.get("description"),
union.get_field_value("description"),
Some(&Value::string("A description"))
);

View file

@ -2,7 +2,7 @@ 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)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
pub struct SourcePosition {
index: usize,
line: usize,

View file

@ -319,6 +319,7 @@ impl<'a> SchemaType<'a> {
}
impl<'a> TypeType<'a> {
#[inline]
pub fn to_concrete(&self) -> Option<&'a MetaType> {
match *self {
TypeType::Concrete(t) => Some(t),
@ -326,6 +327,7 @@ impl<'a> TypeType<'a> {
}
}
#[inline]
pub fn innermost_concrete(&self) -> &'a MetaType {
match *self {
TypeType::Concrete(t) => t,
@ -333,6 +335,7 @@ impl<'a> TypeType<'a> {
}
}
#[inline]
pub fn list_contents(&self) -> Option<&TypeType<'a>> {
match *self {
TypeType::List(ref n) => Some(n),
@ -341,6 +344,7 @@ impl<'a> TypeType<'a> {
}
}
#[inline]
pub fn is_non_null(&self) -> bool {
match *self {
TypeType::NonNull(_) => true,

View file

@ -1,5 +1,7 @@
use executor::{ExecutionResult, Executor, Registry};
use types::base::{Arguments, GraphQLType, TypeKind};
use value::Value;
use ast::Selection;
use schema::meta::{
Argument, EnumMeta, EnumValue, Field, InputObjectMeta, InterfaceMeta, MetaType, ObjectMeta,
@ -43,6 +45,26 @@ where
_ => self.query_type.resolve_field(info, field, args, executor),
}
}
fn resolve(
&self,
info: &Self::TypeInfo,
selection_set: Option<&[Selection]>,
executor: &Executor<Self::Context>,
) -> Value {
use value::Object;
use types::base::resolve_selection_set_into;
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) {
Value::Object(result)
} else {
Value::null()
}
} else {
panic!("resolve() must be implemented by non-object output types");
}
}
}
graphql_object!(<'a> SchemaType<'a>: SchemaType<'a> as "__Schema" |&self| {

View file

@ -198,11 +198,11 @@ fn test_possible_types() {
let possible_types = result
.as_object_value()
.expect("execution result not an object")
.get("__type")
.get_field_value("__type")
.expect("'__type' not present in result")
.as_object_value()
.expect("'__type' not an object")
.get("possibleTypes")
.get_field_value("possibleTypes")
.expect("'possibleTypes' not present in '__type'")
.as_list_value()
.expect("'possibleTypes' not a list")
@ -210,7 +210,7 @@ fn test_possible_types() {
.map(|t| {
t.as_object_value()
.expect("possible type not an object")
.get("name")
.get_field_value("name")
.expect("'name' not present in type")
.as_string_value()
.expect("'name' not a string")

View file

@ -1,9 +1,8 @@
use indexmap::map::Entry;
use indexmap::IndexMap;
use ast::{Directive, FromInputValue, InputValue, Selection};
use executor::Variables;
use value::Value;
use value::{Object, Value};
use executor::{ExecutionResult, Executor, Registry};
use parser::Spanning;
@ -310,9 +309,9 @@ pub trait GraphQLType: Sized {
executor: &Executor<Self::Context>,
) -> Value {
if let Some(selection_set) = selection_set {
let mut result = IndexMap::new();
let mut result = Object::with_capacity(selection_set.len());
if resolve_selection_set_into(self, info, selection_set, executor, &mut result) {
Value::object(result)
Value::Object(result)
} else {
Value::null()
}
@ -322,12 +321,12 @@ pub trait GraphQLType: Sized {
}
}
fn resolve_selection_set_into<T, CtxT>(
pub(crate) fn resolve_selection_set_into<T, CtxT>(
instance: &T,
info: &T::TypeInfo,
selection_set: &[Selection],
executor: &Executor<CtxT>,
result: &mut IndexMap<String, Value>,
result: &mut Object,
) -> bool
where
T: GraphQLType<Context = CtxT>,
@ -352,11 +351,11 @@ where
continue;
}
let response_name = &f.alias.as_ref().unwrap_or(&f.name).item;
let response_name = f.alias.as_ref().unwrap_or(&f.name).item;
if f.name.item == "__typename" {
result.insert(
(*response_name).to_owned(),
result.add_field(
response_name,
Value::string(instance.concrete_type_name(executor.context(), info)),
);
continue;
@ -406,7 +405,7 @@ where
return false;
}
result.insert((*response_name).to_owned(), Value::null());
result.add_field(response_name, Value::null());
}
}
}
@ -453,8 +452,8 @@ where
&sub_exec,
);
if let Ok(Value::Object(mut hash_map)) = sub_result {
for (k, v) in hash_map.drain(..) {
if let Ok(Value::Object(object)) = sub_result {
for (k, v) in object {
merge_key_into(result, &k, v);
}
} else if let Err(e) = sub_result {
@ -503,15 +502,16 @@ fn is_excluded(directives: &Option<Vec<Spanning<Directive>>>, vars: &Variables)
false
}
fn merge_key_into(result: &mut IndexMap<String, Value>, response_name: &str, value: Value) {
match result.entry(response_name.to_owned()) {
Entry::Occupied(mut e) => match e.get_mut() {
&mut Value::Object(ref mut dest_obj) => {
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) => {
if let Value::Object(src_obj) = value {
merge_maps(dest_obj, src_obj);
}
}
&mut Value::List(ref mut dest_list) => {
Value::List(ref mut dest_list) => {
if let Value::List(src_list) = value {
dest_list
.iter_mut()
@ -527,19 +527,18 @@ fn merge_key_into(result: &mut IndexMap<String, Value>, response_name: &str, val
}
}
_ => {}
},
Entry::Vacant(e) => {
e.insert(value);
}
return;
}
result.add_field(response_name, value);
}
fn merge_maps(dest: &mut IndexMap<String, Value>, src: IndexMap<String, Value>) {
fn merge_maps(dest: &mut Object, src: Object) {
for (key, value) in src {
if dest.contains_key(&key) {
if dest.contains_field(&key) {
merge_key_into(dest, &key, value);
} else {
dest.insert(key, value);
dest.add_field(key, value);
}
}
}

View file

@ -152,18 +152,18 @@ where
}
}
fn resolve_into_list<T: GraphQLType, I: Iterator<Item = T>>(
executor: &Executor<T::Context>,
info: &T::TypeInfo,
iter: I,
) -> Value {
fn resolve_into_list<T, I>(executor: &Executor<T::Context>, info: &T::TypeInfo, iter: I) -> Value
where
I: Iterator<Item = T> + ExactSizeIterator,
T: GraphQLType,
{
let stop_on_null = executor
.current_type()
.list_contents()
.expect("Current type is not a list type")
.is_non_null();
let mut result = Vec::new();
let mut result = Vec::with_capacity(iter.len());
for o in iter {
let value = executor.resolve_into_value(info, &o);

View file

@ -1,8 +1,7 @@
use indexmap::IndexMap;
use std::hash::Hash;
use ast::{InputValue, ToInputValue};
use parser::Spanning;
use std::iter::FromIterator;
use std::vec::IntoIter;
/// Serializable value returned from query and field execution.
///
@ -22,7 +21,152 @@ pub enum Value {
String(String),
Boolean(bool),
List(Vec<Value>),
Object(IndexMap<String, Value>),
Object(Object),
}
/// A Object value
#[derive(Debug, PartialEq, Clone)]
pub struct Object {
key_value_list: Vec<(String, Value)>,
}
impl Object {
/// Create a new Object value with a fixed number of
/// preallocated slots for field-value pairs
pub fn with_capacity(size: usize) -> Self {
Object {
key_value_list: Vec::with_capacity(size),
}
}
/// Add a new field with a value
///
/// If there is already a field with the same name the old value
/// is returned
pub fn add_field<K>(&mut self, k: K, value: Value) -> Option<Value>
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
if let Some(item) = self
.key_value_list
.iter_mut()
.find(|&&mut (ref key, _)| (key as &str) == k)
{
return Some(::std::mem::replace(&mut item.1, value));
}
self.key_value_list.push((k.into(), value));
None
}
/// Check if the object already contains a field with the given name
pub fn contains_field<K>(&self, f: K) -> bool
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.any(|&(ref key, _)| (key as &str) == f)
}
/// Get a iterator over all field value pairs
///
/// This method returns a iterator over `&'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &(String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter(&self) -> FieldIter {
FieldIter {
inner: self.key_value_list.iter(),
}
}
/// Get a iterator over all mutable field value pairs
///
/// This method returns a iterator over `&mut 'a (String, Value)`
// TODO: change this to `-> impl Iterator<Item = &mut (String, Value)>`
// as soon as juniper bumps the minimal supported rust verion to 1.26
pub fn iter_mut(&mut self) -> FieldIterMut {
FieldIterMut {
inner: self.key_value_list.iter_mut(),
}
}
/// Get the current number of fields
pub fn field_count(&self) -> usize {
self.key_value_list.len()
}
/// Get the value for a given field
pub fn get_field_value<K>(&self, key: K) -> Option<&Value>
where
for<'a> &'a str: PartialEq<K>,
{
self.key_value_list
.iter()
.find(|&&(ref k, _)| (k as &str) == key)
.map(|&(_, ref value)| value)
}
}
impl IntoIterator for Object {
type Item = (String, Value);
type IntoIter = IntoIter<Self::Item>;
fn into_iter(self) -> Self::IntoIter {
self.key_value_list.into_iter()
}
}
impl From<Object> for Value {
fn from(o: Object) -> Self {
Value::Object(o)
}
}
impl<K> FromIterator<(K, Value)> for Object
where
K: Into<String>,
for<'a> &'a str: PartialEq<K>,
{
fn from_iter<I>(iter: I) -> Self
where
I: IntoIterator<Item = (K, Value)>,
{
let iter = iter.into_iter();
let mut ret = Self {
key_value_list: Vec::with_capacity(iter.size_hint().0),
};
for (k, v) in iter {
ret.add_field(k, v);
}
ret
}
}
#[doc(hidden)]
pub struct FieldIter<'a> {
inner: ::std::slice::Iter<'a, (String, Value)>,
}
impl<'a> Iterator for FieldIter<'a> {
type Item = &'a (String, Value);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
#[doc(hidden)]
pub struct FieldIterMut<'a> {
inner: ::std::slice::IterMut<'a, (String, Value)>,
}
impl<'a> Iterator for FieldIterMut<'a> {
type Item = &'a mut (String, Value);
fn next(&mut self) -> Option<Self::Item> {
self.inner.next()
}
}
impl Value {
@ -59,11 +203,8 @@ impl Value {
}
/// Construct an object value.
pub fn object<K>(o: IndexMap<K, Value>) -> Value
where
K: Into<String> + Eq + Hash,
{
Value::Object(o.into_iter().map(|(k, v)| (k.into(), v)).collect())
pub fn object(o: Object) -> Value {
Value::Object(o)
}
// DISCRIMINATORS
@ -85,7 +226,7 @@ impl Value {
}
/// View the underlying object value, if present.
pub fn as_object_value(&self) -> Option<&IndexMap<String, Value>> {
pub fn as_object_value(&self) -> Option<&Object> {
match *self {
Value::Object(ref o) => Some(o),
_ => None,
@ -93,7 +234,7 @@ impl Value {
}
/// Mutable view into the underlying object value, if present.
pub fn as_mut_object_value(&mut self) -> Option<&mut IndexMap<String, Value>> {
pub fn as_mut_object_value(&mut self) -> Option<&mut Object> {
match *self {
Value::Object(ref mut o) => Some(o),
_ => None,
@ -132,7 +273,7 @@ impl ToInputValue for Value {
),
Value::Object(ref o) => InputValue::Object(
o.iter()
.map(|(k, v)| {
.map(|&(ref k, ref v)| {
(
Spanning::unlocated(k.clone()),
Spanning::unlocated(v.to_input_value()),

View file

@ -1,7 +1,7 @@
#[cfg(test)]
use fnv::FnvHashMap;
#[cfg(test)]
use indexmap::IndexMap;
use juniper::Object;
#[cfg(test)]
use juniper::{self, execute, EmptyMutation, GraphQLType, RootNode, Value, Variables};
@ -235,8 +235,8 @@ fn check_descriptions(
object_name
);
run_type_info_query(&doc, |(type_info, values)| {
assert_eq!(type_info.get("name"), Some(&Value::string(object_name)));
assert_eq!(type_info.get("description"), Some(object_description));
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!(
values.contains(&Value::object(
vec![
@ -252,7 +252,7 @@ fn check_descriptions(
#[cfg(test)]
fn run_type_info_query<F>(doc: &str, f: F)
where
F: Fn((&IndexMap<String, Value>, &Vec<Value>)) -> (),
F: Fn((&Object, &Vec<Value>)) -> (),
{
let schema = RootNode::new(Query, EmptyMutation::<()>::new());
@ -266,13 +266,13 @@ where
let type_info = result
.as_object_value()
.expect("Result is not an object")
.get("__type")
.get_field_value("__type")
.expect("__type field missing")
.as_object_value()
.expect("__type field not an object value");
let fields = type_info
.get("fields")
.get_field_value("fields")
.expect("fields field missing")
.as_list_value()
.expect("fields not a list");