Fix interface macro syntax issues, add tests

This commit is contained in:
Magnus Hallin 2016-10-16 22:04:50 +02:00
parent 5caea1f908
commit 8647d06e2e
3 changed files with 298 additions and 3 deletions

View file

@ -175,7 +175,12 @@ macro_rules! graphql_interface {
graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*)
};
( @gather_meta, $reg:expr, $acc:expr, $descr:expr, $(,)* ) => {};
( @gather_meta, $reg:expr, $acc:expr, $descr:expr, , $( $rest:tt )* ) => {
graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*)
};
( @gather_meta, $reg:expr, $acc:expr, $descr:expr, ) => {
};
// field deprecated <reason> <name>(...) -> <type> as <description> { ... }
(
@ -269,7 +274,7 @@ macro_rules! graphql_interface {
(
@concrete_type_name,
($outname:tt, $ctxtarg:ident, $ctxttype:ty),
instance_resolvers : | $ctxtvar:pat | [ $( $resolver:expr , )* ] $( $rest:tt )*
instance_resolvers : | $ctxtvar:pat | [ $( $resolver:expr ),* $(,)* ] $( $rest:tt )*
) => {
let $ctxtvar = &$ctxtarg;
@ -294,7 +299,7 @@ macro_rules! graphql_interface {
(
@resolve_into_type,
($outname:tt, $typenamearg:ident, $execarg:ident, $ctxttype:ty),
instance_resolvers : | $ctxtvar:pat | [ $( $resolver:expr , )* ] $( $rest:tt )*
instance_resolvers : | $ctxtvar:pat | [ $( $resolver:expr ),* $(,)* ] $( $rest:tt )*
) => {
let $ctxtvar = &$execarg.context();
@ -362,6 +367,12 @@ macro_rules! graphql_interface {
$($items)*);
}
});
impl<$($lifetime)*> $crate::IntoFieldResult<$name> for $name {
fn into(self) -> $crate::FieldResult<$name> {
Ok(self)
}
}
};
(
@ -380,4 +391,12 @@ macro_rules! graphql_interface {
) => {
graphql_interface!(() $name : $ctxt as $outname | &$mainself | { $( $items )* });
};
(
$name:ty : $ctxt:ty | &$mainself:ident | {
$( $items:tt )*
}
) => {
graphql_interface!(() $name : $ctxt as (stringify!($name)) | &$mainself | { $( $items )* });
};
}

View file

@ -0,0 +1,275 @@
use std::collections::HashMap;
use std::marker::PhantomData;
use ast::InputValue;
use value::Value;
use schema::model::RootNode;
/*
Syntax to validate:
* Order of items: fields, description, instance resolvers
* Optional Generics/lifetimes
* Custom name vs. default name
* Optional commas between items
* Optional trailing commas on instance resolvers
*/
struct Concrete;
struct DefaultName;
#[allow(dead_code)]
struct WithLifetime<'a> { data: PhantomData<&'a i64> }
#[allow(dead_code)]
struct WithGenerics<T> { data: T }
struct DescriptionFirst;
struct FieldsFirst;
struct InterfacesFirst;
struct CommasWithTrailing;
struct CommasOnMeta;
struct ResolversWithTrailingComma;
struct Root;
graphql_object!(Concrete: () |&self| {
field simple() -> i64 { 0 }
});
graphql_interface!(DefaultName: () |&self| {
field simple() -> i64 { 0 }
instance_resolvers: |_| [ Some(Concrete) ]
});
graphql_interface!(<'a> WithLifetime<'a>: () as "WithLifetime" |&self| {
field simple() -> i64 { 0 }
instance_resolvers: |_| [ Some(Concrete) ]
});
graphql_interface!(<T> WithGenerics<T>: () as "WithGenerics" |&self| {
field simple() -> i64 { 0 }
instance_resolvers: |_| [ Some(Concrete) ]
});
graphql_interface!(DescriptionFirst: () as "DescriptionFirst" |&self| {
description: "A description"
field simple() -> i64 { 0 }
instance_resolvers: |_| [ Some(Concrete) ]
});
graphql_interface!(FieldsFirst: () as "FieldsFirst" |&self| {
field simple() -> i64 { 0 }
description: "A description"
instance_resolvers: |_| [ Some(Concrete) ]
});
graphql_interface!(InterfacesFirst: () as "InterfacesFirst" |&self| {
instance_resolvers: |_| [ Some(Concrete) ]
field simple() -> i64 { 0 }
description: "A description"
});
graphql_interface!(CommasWithTrailing: () as "CommasWithTrailing" |&self| {
instance_resolvers: |_| [ Some(Concrete) ],
field simple() -> i64 { 0 },
description: "A description",
});
graphql_interface!(CommasOnMeta: () as "CommasOnMeta" |&self| {
instance_resolvers: |_| [ Some(Concrete) ]
description: "A description",
field simple() -> i64 { 0 }
});
graphql_interface!(ResolversWithTrailingComma: () as "ResolversWithTrailingComma" |&self| {
instance_resolvers: |_| [ Some(Concrete), ]
description: "A description",
field simple() -> i64 { 0 }
});
graphql_object!(<'a> Root: () as "Root" |&self| {
field default_name() -> DefaultName { DefaultName {} }
field with_lifetime() -> WithLifetime<'a> { WithLifetime { data: PhantomData } }
field with_generics() -> WithGenerics<i64> { WithGenerics { data: 123 } }
field description_first() -> DescriptionFirst { DescriptionFirst {} }
field fields_first() -> FieldsFirst { FieldsFirst {} }
field interfaces_first() -> InterfacesFirst { InterfacesFirst {} }
field commas_with_trailing() -> CommasWithTrailing { CommasWithTrailing {} }
field commas_on_meta() -> CommasOnMeta { CommasOnMeta {} }
field resolvers_with_trailing_comma() -> ResolversWithTrailingComma {
ResolversWithTrailingComma {}
}
});
fn run_type_info_query<F>(type_name: &str, f: F)
where F: Fn(&HashMap<String, Value>, &Vec<Value>) -> ()
{
let doc = r#"
query ($typeName: String!) {
__type(name: $typeName) {
name
description
fields(includeDeprecated: true) {
name
}
}
}
"#;
let schema = RootNode::new(Root {}, ());
let vars = vec![
("typeName".to_owned(), InputValue::string(type_name)),
].into_iter().collect();
let (result, errs) = ::execute(doc, None, &schema, &vars, &())
.expect("Execution failed");
assert_eq!(errs, []);
println!("Result: {:?}", result);
let type_info = result
.as_object_value().expect("Result is not an object")
.get("__type").expect("__type field missing")
.as_object_value().expect("__type field not an object value");
let fields = type_info
.get("fields").expect("fields field missing")
.as_list_value().expect("fields field not a list value");
f(type_info, fields);
}
#[test]
fn introspect_default_name() {
run_type_info_query("DefaultName", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("DefaultName")));
assert_eq!(object.get("description"), Some(&Value::null()));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[test]
fn introspect_commas_with_trailing() {
run_type_info_query("CommasWithTrailing", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("CommasWithTrailing")));
assert_eq!(object.get("description"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[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"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}
#[test]
fn introspect_resolvers_with_trailing_comma() {
run_type_info_query("ResolversWithTrailingComma", |object, fields| {
assert_eq!(object.get("name"), Some(&Value::string("ResolversWithTrailingComma")));
assert_eq!(object.get("description"), Some(&Value::string("A description")));
assert!(fields.contains(&Value::object(vec![
("name", Value::string("simple")),
].into_iter().collect())));
});
}

View file

@ -4,3 +4,4 @@ mod scalar;
mod args;
mod field;
mod object;
mod interface;