Exchange graphql_input_object! macro to custom derive

* Refactor all internal use to derive
* Remove macro
* Move tests to executor_tests
This commit is contained in:
theduke 2017-12-02 15:29:18 +01:00
parent 643875838d
commit 0e3c9940c6
7 changed files with 105 additions and 392 deletions

View file

@ -8,82 +8,81 @@ use types::scalars::EmptyMutation;
struct Root; struct Root;
graphql_input_object!( #[derive(GraphQLInputObject)]
struct DefaultName { #[graphql(_internal)]
struct DefaultName {
field_one: String, field_one: String,
field_two: String, field_two: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject)]
struct NoTrailingComma { #[graphql(_internal)]
struct NoTrailingComma {
field_one: String, field_one: String,
field_two: String field_two: String
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
#[derive(Debug)] #[graphql(_internal)]
struct Derive { struct Derive {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
struct Named as "ANamedInputObject" { #[graphql(name = "ANamedInputObject", _internal)]
struct Named {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
description: "Description for the input object" #[graphql(description = "Description for the input object", _internal)]
struct Description {
struct Description {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
pub struct Public { #[graphql(_internal)]
pub struct Public {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
description: "Description for the input object" #[graphql(description = "Description for the input object", _internal)]
pub struct PublicWithDescription {
pub struct PublicWithDescription {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
description: "Description for the input object" #[graphql(name = "APublicNamedInputObjectWithDescription",
description = "Description for the input object",
pub struct NamedPublicWithDescription as "APublicNamedInputObjectWithDescription" { _internal)]
pub struct NamedPublicWithDescription {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
pub struct NamedPublic as "APublicNamedInputObject" { #[graphql(name = "APublicNamedInputObject", _internal)]
pub struct NamedPublic {
field_one: String, field_one: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
struct FieldDescription { #[graphql(_internal)]
field_one: String as "The first field", struct FieldDescription {
field_two: String as "The second field", #[graphql(description = "The first field")]
} field_one: String,
); #[graphql(description = "The second field")]
field_two: String,
}
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
struct FieldWithDefaults { #[graphql(_internal)]
field_one = 123: i32, struct FieldWithDefaults {
field_two = 456: i32 as "The second field", #[graphql(default = "123")]
} field_one: i32,
); #[graphql(default = "456", description = "The second field")]
field_two: i32,
}
graphql_object!(Root: () |&self| { graphql_object!(Root: () |&self| {
field test_field( field test_field(

View file

@ -1,8 +1,16 @@
mod input_object;
// This asserts that the input objects defined public actually became public
#[allow(unused_imports)]
use self::input_object::{NamedPublic, NamedPublicWithDescription};
use executor::Variables; use executor::Variables;
use value::Value; use value::Value;
use schema::model::RootNode; use schema::model::RootNode;
use types::scalars::EmptyMutation; use types::scalars::EmptyMutation;
#[derive(GraphQLEnum)] #[derive(GraphQLEnum)]
#[graphql(name = "SampleEnum", _internal)] #[graphql(name = "SampleEnum", _internal)]
enum Sample { enum Sample {

View file

@ -30,39 +30,35 @@ graphql_scalar!(TestComplexScalar {
} }
}); });
#[derive(GraphQLInputObject, Debug)]
graphql_input_object!( #[graphql(_internal)]
#[derive(Debug)] struct TestInputObject {
struct TestInputObject {
a: Option<String>, a: Option<String>,
b: Option<Vec<Option<String>>>, b: Option<Vec<Option<String>>>,
c: String, c: String,
d: Option<TestComplexScalar>, d: Option<TestComplexScalar>,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
#[derive(Debug)] #[graphql(_internal)]
struct TestNestedInputObject { struct TestNestedInputObject {
na: TestInputObject, na: TestInputObject,
nb: String, nb: String,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
#[derive(Debug)] #[graphql(_internal)]
struct ExampleInputObject { struct ExampleInputObject {
a: Option<String>, a: Option<String>,
b: i32, b: i32,
} }
);
graphql_input_object!( #[derive(GraphQLInputObject, Debug)]
#[derive(Debug)] #[graphql(_internal)]
struct InputWithDefaults { struct InputWithDefaults {
a = 123: i32, #[graphql(default = "123")]
} a: i32,
); }
graphql_object!(TestType: () |&self| { graphql_object!(TestType: () |&self| {
field field_with_object_input(input: Option<TestInputObject>) -> String { field field_with_object_input(input: Option<TestInputObject>) -> String {

View file

@ -1,283 +0,0 @@
/**
Create an input object
Input objects are used as data carriers for complex input values to
fields and mutations. Unlike the other helper macros,
`graphql_input_object!` actually *creates* the struct you define. It
does not add anything to the struct definition itself - what you type
is what will be generated:
```rust
# #[macro_use] extern crate juniper;
#
graphql_input_object!(
description: "Coordinates for the user"
struct Coordinates {
longitude: f64 as "The X coordinate, from -180 to +180",
latitude: f64 as "The Y coordinate, from -90 to +90",
}
);
# fn main() { }
```
This macro creates the struct as specified and implements
`FromInputValue` to automatically parse values provided from variables
and arguments.
If you want to expose the struct under a different name than the Rust
type, you can write `struct Coordinates as "MyCoordinates" { ...`.
You can specify *default values* for input object fields; the syntax
is similar to argument default values:
```rust
# #[macro_use] extern crate juniper;
#
graphql_input_object!(
struct SampleObject {
foo = 123: i32 as "A sample field, defaults to 123 if omitted"
}
);
# fn main() { }
```
*/
#[macro_export]
macro_rules! graphql_input_object {
// Calls $val.$func($arg) if $arg is not None
( @maybe_apply, None, $func:ident, $val:expr ) => { $val };
( @maybe_apply, $arg:tt, $func:ident, $val:expr ) => { $val.$func($arg) };
// Calls $val.description($descr) when $descr is not empty
( @apply_description, , $val:expr ) => { $val };
( @apply_description, $descr:tt , $val:expr ) => { $val.description($descr) };
// Generate the FromInputValue::from method body, provided a
// HashMap<&str, &InputValue> in $var
(
@generate_from_input_value,
$name:tt, $var:tt,
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
Some($name {
$( $field_name: {
let n = $crate::to_camel_case(stringify!($field_name));
let v: Option<&&$crate::InputValue> = $var.get(&n[..]);
match v {
$( Some(&&$crate::InputValue::Null) | None if true => $default, )*
Some(v) => $crate::FromInputValue::from_input_value(v).unwrap(),
_ => $crate::FromInputValue::from_input_value(&$crate::InputValue::null()).unwrap()
}
} ),*
})
};
// Generate the ToInputValue::To method body, provided self in $self
(
@generate_to_input_value,
$name:tt, $selfvar:tt,
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
$crate::InputValue::object(vec![
$(
($crate::to_camel_case(stringify!($field_name)), $selfvar.$field_name.to_input_value())
),*
].into_iter().collect())
};
// Generate the struct declaration, including (Rust) meta attributes
(
@generate_struct_fields,
( $($meta:tt)* ), ( $($pubmod:tt)* ), $name:tt,
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
$($meta)* $($pubmod)* struct $name {
$( $field_name: $field_type, )*
}
};
// Generate single field meta for field with default value
(
@generate_single_meta_field,
$reg:tt,
( $field_name:ident = $default:tt : $field_type:ty $(as $descr:tt)* )
) => {
graphql_input_object!(
@apply_description,
$($descr)*,
$reg.arg_with_default::<$field_type>(
&$crate::to_camel_case(stringify!($field_name)),
&$default, &()))
};
// Generate single field meta for field without default value
(
@generate_single_meta_field,
$reg:tt,
( $field_name:ident : $field_type:ty $(as $descr:tt)* )
) => {
graphql_input_object!(
@apply_description,
$($descr)*,
$reg.arg::<$field_type>(
&$crate::to_camel_case(stringify!($field_name)), &()))
};
// Generate the input field meta list, i.e. &[Argument] for
(
@generate_meta_fields,
$reg:tt,
( $($field_name:ident $(= $default:tt)* : $field_type:ty $(as $descr:tt)* $(,)* ),* )
) => {
&[
$(
graphql_input_object!(
@generate_single_meta_field,
$reg,
( $field_name $(= $default)* : $field_type $(as $descr)* )
)
),*
]
};
// #[...] struct $name { ... }
// struct $name { ... }
(
@parse,
( $_ignore1:tt, $_ignore2:tt, $_ignore3:tt, $_ignore4:tt, $_ignore5:tt, $descr:tt ),
$(#[$meta:meta])* struct $name:ident { $($fields:tt)* } $($rest:tt)*
) => {
graphql_input_object!(
@parse,
( ( $(#[$meta])* ), ( ), $name, (stringify!($name)), ($($fields)*), $descr ),
$($rest)*
);
};
// #[...] pub struct $name { ... }
// pub struct $name { ... }
(
@parse,
( $_ignore1:tt, $_ignore2:tt, $_ignore3:tt, $_ignore4:tt, $_ignore5:tt, $descr:tt ),
$(#[$meta:meta])* pub struct $name:ident { $($fields:tt)* } $($rest:tt)*
) => {
graphql_input_object!(
@parse,
( ( $(#[$meta])* ), ( pub ), $name, (stringify!($name)), ($($fields)*), $descr ),
$($rest)*
);
};
// #[...] struct $name as "GraphQLName" { ... }
// struct $name as "GraphQLName" { ... }
(
@parse,
( $_ignore1:tt, $_ignore2:tt, $_ignore3:tt, $_ignore4:tt, $_ignore5:tt, $descr:tt ),
$(#[$meta:meta])* struct $name:ident as $outname:tt { $($fields:tt)* } $($rest:tt)*
) => {
graphql_input_object!(
@parse,
( ( $($meta)* ), ( ), $name, $outname, ($($fields)*), $descr ),
$($rest)*
);
};
// #[...] pub struct $name as "GraphQLName" { ... }
// pub struct $name as "GraphQLName" { ... }
(
@parse,
( $_ignore1:tt, $_ignore2:tt, $_ignore3:tt, $_ignore4:tt, $_ignore5:tt, $descr:tt ),
$(#[$meta:meta])* pub struct $name:ident as $outname:tt { $($fields:tt)* } $($rest:tt)*
) => {
graphql_input_object!(
@parse,
( ( $($meta)* ), ( pub ), $name, $outname, ($($fields)*), $descr ),
$($rest)*
);
};
// description: <description>
(
@parse,
( $meta:tt, $pubmod:tt, $name:tt, $outname:tt, $fields:tt, $_ignore:tt ),
description: $descr:tt $($rest:tt)*
) => {
graphql_input_object!(
@parse,
( $meta, $pubmod, $name, $outname, $fields, $descr ),
$($rest)*
);
};
// No more data to parse, generate the struct and impls
(
@parse,
( $meta:tt, $pubmod:tt, $name:tt, $outname:tt, $fields:tt, $descr:tt ),
) => {
graphql_input_object!(@generate_struct_fields, $meta, $pubmod, $name, $fields);
impl $crate::FromInputValue for $name {
fn from_input_value(value: &$crate::InputValue) -> Option<$name> {
if let Some(obj) = value.to_object_value() {
graphql_input_object!(@generate_from_input_value, $name, obj, $fields)
}
else {
None
}
}
}
impl $crate::ToInputValue for $name {
fn to_input_value(&self) -> $crate::InputValue {
graphql_input_object!(@generate_to_input_value, $name, self, $fields)
}
}
impl $crate::GraphQLType for $name {
type Context = ();
type TypeInfo = ();
fn name(_: &()) -> Option<&str> {
Some($outname)
}
fn meta<'r>(_: &(), registry: &mut $crate::Registry<'r>) -> $crate::meta::MetaType<'r> {
let fields = graphql_input_object!(@generate_meta_fields, registry, $fields);
graphql_input_object!(
@maybe_apply, $descr, description,
registry.build_input_object_type::<$name>(&(), fields)).into_meta()
}
}
};
// Entry point: parse calls starting with a struct declaration
( $(#[$meta:meta])* struct $($items:tt)* ) => {
graphql_input_object!(
@parse,
( ( ), ( ), None, None, None, None ),
$(#[$meta])* struct $($items)*
);
};
// Entry point: parse calls starting with a public struct declaration
( $(#[$meta:meta])* pub struct $($items:tt)* ) => {
graphql_input_object!(
@parse,
( ( ), ( ), None, None, None, None ),
$(#[$meta])* pub struct $($items)*
);
};
// Entry point: parse calls starting with the description
( description: $($items:tt)* ) => {
graphql_input_object!(
@parse,
( ( ), ( ), None, None, None, None ),
description: $($items)*
);
};
}

View file

@ -9,8 +9,6 @@ mod args;
#[macro_use] #[macro_use]
mod field; mod field;
#[macro_use] #[macro_use]
mod input_object;
#[macro_use]
mod union; mod union;
#[cfg(test)] #[cfg(test)]

View file

@ -19,12 +19,11 @@ Syntax to validate:
*/ */
graphql_input_object!( #[derive(GraphQLInputObject)]
#[derive(Debug)] #[graphql(_internal)]
struct Point { struct Point {
x: i32, x: i32,
} }
);
graphql_object!(Root: () |&self| { graphql_object!(Root: () |&self| {
field simple() -> i32 { 0 } field simple() -> i32 { 0 }

View file

@ -1,6 +1,4 @@
mod scalar; mod scalar;
#[allow(dead_code)]
mod input_object;
mod args; mod args;
mod field; mod field;
mod object; mod object;
@ -9,6 +7,4 @@ mod union;
mod enums; mod enums;
// This asserts that the input objects defined public actually became public
#[allow(unused_imports)]
use self::input_object::{NamedPublic, NamedPublicWithDescription};