diff --git a/src/macros/field.rs b/src/macros/field.rs index 1774c0d3..2f1d04ed 100644 --- a/src/macros/field.rs +++ b/src/macros/field.rs @@ -1,6 +1,31 @@ #[doc(hidden)] #[macro_export] 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 )* @@ -11,6 +36,7 @@ macro_rules! __graphql__build_field_matches { $( $rest )*); }; + // field (...) -> { ... } ( $resolveargs:tt, ( $( $acc:tt )* ), field $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )* diff --git a/src/macros/interface.rs b/src/macros/interface.rs index f9c64ef3..74f619aa 100644 --- a/src/macros/interface.rs +++ b/src/macros/interface.rs @@ -88,6 +88,44 @@ macro_rules! graphql_interface { ( @as_item, $i:item) => { $i }; ( @as_expr, $e:expr) => { $e }; + // field deprecated (...) -> as { ... } + ( + @gather_meta, + $reg:expr, $acc:expr, $descr:expr, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty as $desc:tt $body:block $( $rest:tt )* + ) => { + $acc.push(__graphql__args!( + @apply_args, + $reg, + $reg.field_inside_result( + &$crate::to_snake_case(stringify!($name)), + Err("dummy".to_owned()) as $t) + .description($desc) + .deprecated($reason), + $args)); + + graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*); + }; + + // field deprecated (...) -> { ... } + ( + @gather_meta, + $reg:expr, $acc: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_inside_result( + &$crate::to_snake_case(stringify!($name)), + Err("dummy".to_owned()) as $t) + .deprecated($reason), + $args)); + + graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*); + }; + + // field (...) -> as { ... } ( @gather_meta, $reg:expr, $acc:expr, $descr:expr, @@ -105,6 +143,7 @@ macro_rules! graphql_interface { graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*); }; + // field (...) -> { ... } ( @gather_meta, $reg:expr, $acc:expr, $descr:expr, @@ -121,6 +160,7 @@ macro_rules! graphql_interface { graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*); }; + // description: ( @gather_meta, $reg:expr, $acc:expr, $descr:expr, @@ -131,16 +171,36 @@ macro_rules! graphql_interface { graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*) }; + // instance_resolvers: | | [...] ( @gather_meta, $reg:expr, $acc:expr, $descr:expr, - instance_resolvers: | $execvar:pat | $resolvers:tt $( $rest:tt )* + instance_resolvers: | $ctxtvar:pat | $resolvers:tt $( $rest:tt )* ) => { graphql_interface!(@gather_meta, $reg, $acc, $descr, $( $rest )*) }; ( @gather_meta, $reg:expr, $acc:expr, $descr:expr, $(,)* ) => {}; + // field deprecated (...) -> as { ... } + ( + @resolve_into_type, + $buildargs:tt, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty as $descr:tt $body:block $( $rest:tt )* + ) => { + graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) + }; + + // field deprecated (...) -> { ... } + ( + @resolve_into_type, + $buildargs:tt, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )* + ) => { + graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) + }; + + // field (...) -> as { ... } ( @resolve_into_type, $buildargs:tt, @@ -149,6 +209,7 @@ macro_rules! graphql_interface { graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) }; + // field (...) -> { ... } ( @resolve_into_type, $buildargs:tt, @@ -157,6 +218,7 @@ macro_rules! graphql_interface { graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) }; + // description: ( @resolve_into_type, $buildargs:tt, description : $value:tt $( $rest:tt )* @@ -164,13 +226,25 @@ macro_rules! graphql_interface { graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) }; + // field deprecated (...) -> as { ... } ( - @resolve_into_type, - $buildargs:tt, interfaces : $value:tt $( $rest:tt )* + @concrete_type_name, + $buildargs:tt, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty as $descr:tt $body:block $( $rest:tt )* ) => { - graphql_interface!(@resolve_into_type, $buildargs, $( $rest )*) + graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) }; + // field deprecated (...) -> { ... } + ( + @concrete_type_name, + $buildargs:tt, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )* + ) => { + graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) + }; + + // field (...) -> as { ... } ( @concrete_type_name, $buildargs:tt, @@ -179,6 +253,7 @@ macro_rules! graphql_interface { graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) }; + // field (...) -> { ... } ( @concrete_type_name, $buildargs:tt, @@ -187,6 +262,7 @@ macro_rules! graphql_interface { graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) }; + // description: ( @concrete_type_name, $buildargs:tt, description : $value:tt $( $rest:tt )* @@ -194,13 +270,7 @@ macro_rules! graphql_interface { graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) }; - ( - @concrete_type_name, - $buildargs:tt, interfaces : $value:tt $( $rest:tt )* - ) => { - graphql_interface!(@concrete_type_name, $buildargs, $( $rest )*) - }; - + // instance_resolvers: | | [...] ( @concrete_type_name, ($outname:tt, $ctxtarg:ident, $ctxttype:ty), @@ -225,6 +295,7 @@ macro_rules! graphql_interface { () }; + // instance_resolvers: | | ( @resolve_into_type, ($outname:tt, $typenamearg:ident, $execarg:ident, $ctxttype:ty), diff --git a/src/macros/object.rs b/src/macros/object.rs index bd68495d..7cf1e725 100644 --- a/src/macros/object.rs +++ b/src/macros/object.rs @@ -239,6 +239,44 @@ macro_rules! graphql_object { ( @as_item, $i:item) => { $i }; ( @as_expr, $e:expr) => { $e }; + // field deprecated (...) -> as { ... } + ( + @gather_object_meta, + $reg:expr, $acc: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_inside_result( + &$crate::to_snake_case(stringify!($name)), + Err("dummy".to_owned()) as $t) + .description($desc) + .deprecated($reason), + $args)); + + graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*); + }; + + // field deprecated (...) -> { ... } + ( + @gather_object_meta, + $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, + field deprecated $reason:tt $name:ident $args:tt -> $t:ty $body:block $( $rest:tt )* + ) => { + $acc.push(__graphql__args!( + @apply_args, + $reg, + $reg.field_inside_result( + &$crate::to_snake_case(stringify!($name)), + Err("dummy".to_owned()) as $t) + .deprecated($reason), + $args)); + + graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*); + }; + + // field (...) -> as { ... } ( @gather_object_meta, $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, @@ -256,6 +294,7 @@ macro_rules! graphql_object { graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*); }; + // field (...) -> { ... } ( @gather_object_meta, $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, @@ -272,6 +311,7 @@ macro_rules! graphql_object { graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*); }; + // description: ( @gather_object_meta, $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, @@ -282,6 +322,7 @@ macro_rules! graphql_object { graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*) }; + // interfaces: [...] ( @gather_object_meta, $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, @@ -292,6 +333,7 @@ macro_rules! graphql_object { graphql_object!(@gather_object_meta, $reg, $acc, $descr, $ifaces, $( $rest )*) }; + // base case ( @gather_object_meta, $reg:expr, $acc:expr, $descr:expr, $ifaces:expr, $(,)* diff --git a/src/macros/tests/field.rs b/src/macros/tests/field.rs new file mode 100644 index 00000000..72575bb2 --- /dev/null +++ b/src/macros/tests/field.rs @@ -0,0 +1,179 @@ +use std::collections::HashMap; + +use executor::FieldResult; +use value::Value; +use ast::InputValue; +use schema::model::RootNode; + +struct Interface; +struct Root; + +/* + +Syntax to validate: + +* Object vs. interface +* Description vs. no description +* Deprecated vs. not deprecated + +*/ + +graphql_object!(Root: () as "Root" |&self| { + field simple() -> FieldResult { Ok(0) } + + field description() -> FieldResult as "Field description" { Ok(0) } + + field deprecated "Deprecation reason" + deprecated() -> FieldResult { Ok(0) } + + field deprecated "Deprecation reason" + deprecated_descr() -> FieldResult as "Field description" { Ok(0) } + + interfaces: [Interface] +}); + +graphql_interface!(Interface: () as "Interface" |&self| { + field simple() -> FieldResult { Ok(0) } + + field description() -> FieldResult as "Field description" { Ok(0) } + + field deprecated "Deprecation reason" + deprecated() -> FieldResult { Ok(0) } + + field deprecated "Deprecation reason" + deprecated_descr() -> FieldResult as "Field description" { Ok(0) } + + instance_resolvers: |&_| [ + Some(Root {}), + ] +}); + +fn run_field_info_query(type_name: &str, field_name: &str, f: F) + where F: Fn(&HashMap) -> () +{ + let doc = r#" + query ($typeName: String!) { + __type(name: $typeName) { + fields(includeDeprecated: true) { + name + description + isDeprecated + deprecationReason + } + } + } + "#; + 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 not a list"); + + let field = fields + .into_iter().filter( + |f| f.as_object_value().expect("Field not an object") + .get("name").expect("name field missing from field") + .as_string_value().expect("name is not a string") + == field_name) + .next().expect("Field not found") + .as_object_value().expect("Field is not an object"); + + println!("Field: {:?}", field); + + f(field); +} + +#[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())); + }); +} + +#[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())); + }); +} + +#[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"), Some(&Value::string("Field description"))); + assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(false))); + assert_eq!(field.get("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("description"), Some(&Value::string("Field description"))); + assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(false))); + assert_eq!(field.get("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("deprecationReason"), Some(&Value::string("Deprecation reason"))); + }); +} + +#[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("deprecationReason"), Some(&Value::string("Deprecation reason"))); + }); +} + +#[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("description"), Some(&Value::string("Field description"))); + assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true))); + assert_eq!(field.get("deprecationReason"), Some(&Value::string("Deprecation reason"))); + }); +} + +#[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("description"), Some(&Value::string("Field description"))); + assert_eq!(field.get("isDeprecated"), Some(&Value::boolean(true))); + assert_eq!(field.get("deprecationReason"), Some(&Value::string("Deprecation reason"))); + }); +} diff --git a/src/macros/tests/mod.rs b/src/macros/tests/mod.rs index c22c674f..f0b9274e 100644 --- a/src/macros/tests/mod.rs +++ b/src/macros/tests/mod.rs @@ -2,3 +2,4 @@ mod enums; mod scalar; #[allow(dead_code)] mod input_object; mod args; +mod field;