diff --git a/CHANGELOG.md b/CHANGELOG.md index aa08fd3dd..661099a33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - (breaking) Control over which types custom scalars deserialize to is given to the user: you now have to provide type aliases for the custom scalars in the scope of the struct under derive. - (breaking) Support for multi-operations documents. You can select a particular operation by naming the struct under derive after it. In case there is no match, we revert to the current behaviour: select the first operation. +- Support arbitrary derives on the generated response types via the `response_derives` option on the `graphql` attribute. ## [0.3.0] - 2018-07-24 diff --git a/README.md b/README.md index 5899ab84d..58e5cf330 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,9 @@ A typed GraphQL client library for Rust. - Works in the browser (WebAssembly) - Subscriptions support (serialization-deserialization only at the moment) - Copies documentation from the GraphQL schema to the generated Rust code +- Arbitrary derives on the generated responses +- Arbitrary custom scalars +- Supports multiple operations per query document ## Getting started @@ -74,6 +77,31 @@ A typed GraphQL client library for Rust. [A complete example using the GitHub GraphQL API is available](https://github.com/tomhoule/graphql-client/tree/master/examples/github), as well as sample [rustdoc output](https://www.tomhoule.com/docs/example_module/). +## Deriving specific traits on the response + +The generated response types always derive `serde::Deserialize` but you may want to print them (`Debug`), compare them (`PartialEq`) or derive any other trait on it. You can achieve this with the `response_derives` option of the `graphql` attribute. Example: + +```rust +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "src/search_schema.graphql", + query_path = "src/search_query.graphql" + query_path = "src/search_query.graphql", + response_derives = "Serialize,PartialEq", +)] +struct SearchQuery; +``` + +## Custom scalars + +The generated code will reference the scalar types as defined in the server schema. This means you have to provide matching rust types in the scope of the struct under derive. It can be as simple as declarations like `type Email = String;`. This gives you complete freedom on how to treat custom scalars, as long as they can be deserialized. + +## Query documents with multiple operations + +You can write multiple operations in one query document (one `.graphql` file). You can then select one by naming the struct you `#[derive(GraphQLQuery)]` on with the same name as one of the operations. This is neat, as it allows sharing fragments between operations. + +There is an example [in the tests](./tests/operation_selection). + ## Examples See the examples directory in this repository. diff --git a/examples/example_module/src/lib.rs b/examples/example_module/src/lib.rs index 1844e65f0..f5e102b24 100644 --- a/examples/example_module/src/lib.rs +++ b/examples/example_module/src/lib.rs @@ -11,6 +11,7 @@ use custom_scalars::*; #[derive(GraphQLQuery)] #[graphql( schema_path = "../github/src/schema.graphql", - query_path = "../github/src/query_1.graphql" + query_path = "../github/src/query_1.graphql", + response_derives = "Debug", )] pub struct ExampleModule; diff --git a/examples/github/src/main.rs b/examples/github/src/main.rs index 2f9ccda17..06c5fef8f 100644 --- a/examples/github/src/main.rs +++ b/examples/github/src/main.rs @@ -32,7 +32,8 @@ type DateTime = String; #[derive(GraphQLQuery)] #[graphql( schema_path = "src/schema.graphql", - query_path = "src/query_1.graphql" + query_path = "src/query_1.graphql", + response_derives = "Debug", )] struct Query1; diff --git a/graphql_client_cli/src/main.rs b/graphql_client_cli/src/main.rs index 5e5a3870c..4901d8938 100644 --- a/graphql_client_cli/src/main.rs +++ b/graphql_client_cli/src/main.rs @@ -15,7 +15,8 @@ use structopt::StructOpt; #[derive(GraphQLQuery)] #[graphql( schema_path = "src/introspection_schema.graphql", - query_path = "src/introspection_query.graphql" + query_path = "src/introspection_query.graphql", + response_derives = "Serialize", )] struct IntrospectionQuery; diff --git a/graphql_query_derive/src/attributes.rs b/graphql_query_derive/src/attributes.rs new file mode 100644 index 000000000..f15a21f15 --- /dev/null +++ b/graphql_query_derive/src/attributes.rs @@ -0,0 +1,29 @@ +use failure; +use syn; + +pub(crate) fn extract_attr(ast: &syn::DeriveInput, attr: &str) -> Result { + let attributes = &ast.attrs; + let attribute = attributes + .iter() + .find(|attr| { + let path = &attr.path; + quote!(#path).to_string() == "graphql" + }).ok_or_else(|| format_err!("The graphql attribute is missing"))?; + if let syn::Meta::List(items) = &attribute + .interpret_meta() + .expect("Attribute is well formatted") + { + for item in items.nested.iter() { + if let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) = item { + let syn::MetaNameValue { ident, lit, .. } = name_value; + if ident == attr { + if let syn::Lit::Str(lit) = lit { + return Ok(lit.value()); + } + } + } + } + } + + Err(format_err!("attribute not found"))? +} diff --git a/graphql_query_derive/src/codegen.rs b/graphql_query_derive/src/codegen.rs index f9501caec..821469b27 100644 --- a/graphql_query_derive/src/codegen.rs +++ b/graphql_query_derive/src/codegen.rs @@ -11,8 +11,14 @@ pub(crate) fn response_for_query( schema: schema::Schema, query: query::Document, selected_operation: String, + additional_derives: Option, ) -> Result { let mut context = QueryContext::new(schema); + + if let Some(derives) = additional_derives { + context.ingest_additional_derives(&derives).unwrap(); + } + let mut definitions = Vec::new(); let mut operations: Vec = Vec::new(); @@ -77,7 +83,11 @@ pub(crate) fn response_for_query( .unwrap() }; - let enum_definitions = context.schema.enums.values().map(|enm| enm.to_rust()); + let enum_definitions = context + .schema + .enums + .values() + .map(|enm| enm.to_rust(&context)); let fragment_definitions: Result, _> = context .fragments .values() @@ -101,6 +111,8 @@ pub(crate) fn response_for_query( .map(|s| s.to_rust()) .collect(); + let response_derives = context.response_derives(); + Ok(quote! { type Boolean = bool; type Float = f64; @@ -119,7 +131,7 @@ pub(crate) fn response_for_query( #variables_struct - #[derive(Debug, Serialize, Deserialize)] + #response_derives #[serde(rename_all = "camelCase")] pub struct ResponseData { #(#response_data_fields,)* diff --git a/graphql_query_derive/src/enums.rs b/graphql_query_derive/src/enums.rs index aeabba7b1..f32a0a773 100644 --- a/graphql_query_derive/src/enums.rs +++ b/graphql_query_derive/src/enums.rs @@ -16,7 +16,8 @@ pub struct GqlEnum { } impl GqlEnum { - pub fn to_rust(&self) -> TokenStream { + pub(crate) fn to_rust(&self, query_context: &::query::QueryContext) -> TokenStream { + let derives = query_context.response_enum_derives(); let variant_names: Vec = self .variants .iter() @@ -42,7 +43,7 @@ impl GqlEnum { let name = name_ident.clone(); quote! { - #[derive(Debug)] + #derives pub enum #name { #(#variant_names,)* Other(String), diff --git a/graphql_query_derive/src/fragments.rs b/graphql_query_derive/src/fragments.rs index 9387af156..f4eb05896 100644 --- a/graphql_query_derive/src/fragments.rs +++ b/graphql_query_derive/src/fragments.rs @@ -11,13 +11,14 @@ pub struct GqlFragment { impl GqlFragment { pub(crate) fn to_rust(&self, context: &QueryContext) -> Result { + let derives = context.response_derives(); let name_ident = Ident::new(&self.name, Span::call_site()); let object = context.schema.objects.get(&self.on).expect("oh, noes"); let field_impls = object.field_impls_for_selection(context, &self.selection, &self.name)?; let fields = object.response_fields_for_selection(context, &self.selection, &self.name)?; Ok(quote!{ - #[derive(Debug, Deserialize, Serialize)] + #derives pub struct #name_ident { #(#fields,)* } diff --git a/graphql_query_derive/src/interfaces.rs b/graphql_query_derive/src/interfaces.rs index c3d2698ef..ac58d09ca 100644 --- a/graphql_query_derive/src/interfaces.rs +++ b/graphql_query_derive/src/interfaces.rs @@ -33,6 +33,7 @@ impl GqlInterface { prefix: &str, ) -> Result { let name = Ident::new(&prefix, Span::call_site()); + let derives = query_context.response_derives(); selection .extract_typename() @@ -78,13 +79,13 @@ impl GqlInterface { let attached_enum_name = Ident::new(&format!("{}On", name), Span::call_site()); let (attached_enum, last_object_field) = if !union_variants.is_empty() { let attached_enum = quote! { - #[derive(Deserialize, Debug, Serialize)] + #derives #[serde(tag = "__typename")] pub enum #attached_enum_name { #(#union_variants,)* } }; - let last_object_field = quote!(#[serde(flatten)] on: #attached_enum_name,); + let last_object_field = quote!(#[serde(flatten)] pub on: #attached_enum_name,); (attached_enum, last_object_field) } else { (quote!(), quote!()) @@ -98,7 +99,7 @@ impl GqlInterface { #attached_enum - #[derive(Debug, Serialize, Deserialize)] + #derives pub struct #name { #(#object_fields,)* #last_object_field diff --git a/graphql_query_derive/src/lib.rs b/graphql_query_derive/src/lib.rs index 15f78c79e..70ef08589 100644 --- a/graphql_query_derive/src/lib.rs +++ b/graphql_query_derive/src/lib.rs @@ -1,4 +1,4 @@ -#![recursion_limit = "128"] +#![recursion_limit = "512"] #[macro_use] extern crate failure; @@ -16,6 +16,7 @@ extern crate quote; use proc_macro2::TokenStream; +mod attributes; mod codegen; mod constants; mod enums; @@ -77,8 +78,9 @@ fn impl_gql_query(input: &syn::DeriveInput) -> Result Result Result Result { - let attributes = &ast.attrs; - let attribute = attributes - .iter() - .find(|attr| { - let path = &attr.path; - quote!(#path).to_string() == "graphql" - }).ok_or_else(|| format_err!("The graphql attribute is missing"))?; - if let syn::Meta::List(items) = &attribute - .interpret_meta() - .expect("Attribute is well formatted") - { - for item in items.nested.iter() { - if let syn::NestedMeta::Meta(syn::Meta::NameValue(name_value)) = item { - let syn::MetaNameValue { ident, lit, .. } = name_value; - if ident == attr { - if let syn::Lit::Str(lit) = lit { - return Ok(lit.value()); - } - } - } - } - } - - Err(format_err!("attribute not found"))? -} diff --git a/graphql_query_derive/src/objects.rs b/graphql_query_derive/src/objects.rs index 00dbfb897..3864fe791 100644 --- a/graphql_query_derive/src/objects.rs +++ b/graphql_query_derive/src/objects.rs @@ -67,6 +67,7 @@ impl GqlObject { selection: &Selection, prefix: &str, ) -> Result { + let derives = query_context.response_derives(); let name = Ident::new(prefix, Span::call_site()); let fields = self.response_fields_for_selection(query_context, selection, prefix)?; let field_impls = self.field_impls_for_selection(query_context, selection, &prefix)?; @@ -74,7 +75,7 @@ impl GqlObject { Ok(quote! { #(#field_impls)* - #[derive(Debug, Serialize, Deserialize)] + #derives #[serde(rename_all = "camelCase")] #description pub struct #name { diff --git a/graphql_query_derive/src/query.rs b/graphql_query_derive/src/query.rs index 880df6fe3..701c881a5 100644 --- a/graphql_query_derive/src/query.rs +++ b/graphql_query_derive/src/query.rs @@ -1,16 +1,19 @@ use failure; use fragments::GqlFragment; use operations::Operation; +use proc_macro2::Span; use proc_macro2::TokenStream; use schema::Schema; use selection::Selection; use std::collections::BTreeMap; +use syn::Ident; /// This holds all the information we need during the code generation phase. pub(crate) struct QueryContext { pub fragments: BTreeMap, pub schema: Schema, pub selected_operation: Option, + response_derives: Vec, } impl QueryContext { @@ -20,6 +23,7 @@ impl QueryContext { fragments: BTreeMap::new(), schema, selected_operation: None, + response_derives: vec![Ident::new("Deserialize", Span::call_site())], } } @@ -30,6 +34,7 @@ impl QueryContext { fragments: BTreeMap::new(), schema: Schema::new(), selected_operation: None, + response_derives: vec![Ident::new("Deserialize", Span::call_site())], } } @@ -51,4 +56,100 @@ impl QueryContext { Ok(quote!()) } } + + pub(crate) fn ingest_additional_derives( + &mut self, + attribute_value: &str, + ) -> Result<(), failure::Error> { + if self.response_derives.len() > 1 { + return Err(format_err!( + "ingest_additional_derives should only be called once" + )); + } + + self.response_derives.extend( + attribute_value + .split(',') + .map(|s| s.trim()) + .map(|s| Ident::new(s, Span::call_site())), + ); + Ok(()) + } + + pub(crate) fn response_derives(&self) -> TokenStream { + let derives = &self.response_derives; + + quote! { + #[derive( #(#derives),* )] + } + } + + pub(crate) fn response_enum_derives(&self) -> TokenStream { + let enum_derives: Vec<_> = self + .response_derives + .iter() + .filter(|derive| { + !derive.to_string().contains("erialize") + && !derive.to_string().contains("Deserialize") + }).collect(); + + if enum_derives.len() > 0 { + quote! { + #[derive( #(#enum_derives),* )] + } + } else { + quote!() + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn response_derives_ingestion_works() { + let mut context = QueryContext::new_empty(); + + context + .ingest_additional_derives("PartialEq, PartialOrd, Serialize") + .unwrap(); + + assert_eq!( + context.response_derives().to_string(), + "# [ derive ( Deserialize , PartialEq , PartialOrd , Serialize ) ]" + ); + } + + #[test] + fn response_enum_derives_does_not_produce_empty_list() { + let context = QueryContext::new_empty(); + assert_eq!(context.response_enum_derives().to_string(), ""); + } + + #[test] + fn response_enum_derives_works() { + let mut context = QueryContext::new_empty(); + + context + .ingest_additional_derives("PartialEq, PartialOrd, Serialize") + .unwrap(); + + assert_eq!( + context.response_enum_derives().to_string(), + "# [ derive ( PartialEq , PartialOrd ) ]" + ); + } + + #[test] + fn response_derives_fails_when_called_twice() { + let mut context = QueryContext::new_empty(); + + assert!( + context + .ingest_additional_derives("PartialEq, PartialOrd") + .is_ok() + ); + assert!(context.ingest_additional_derives("Serialize").is_err()); + } } diff --git a/graphql_query_derive/src/unions.rs b/graphql_query_derive/src/unions.rs index b19ad164c..79bca0b23 100644 --- a/graphql_query_derive/src/unions.rs +++ b/graphql_query_derive/src/unions.rs @@ -102,6 +102,7 @@ impl GqlUnion { prefix: &str, ) -> Result { let struct_name = Ident::new(prefix, Span::call_site()); + let derives = query_context.response_derives(); let typename_field = selection.extract_typename(); @@ -127,7 +128,7 @@ impl GqlUnion { Ok(quote!{ #(#children_definitions)* - #[derive(Debug, Serialize, Deserialize)] + #derives #[serde(tag = "__typename")] pub enum #struct_name { #(#variants),* @@ -322,13 +323,13 @@ mod tests { assert_eq!( result.unwrap().to_string(), vec![ - "# [ derive ( Debug , Serialize , Deserialize ) ] ", + "# [ derive ( Deserialize ) ] ", "# [ serde ( rename_all = \"camelCase\" ) ] ", "pub struct MeowOnUser { pub first_name : String , } ", - "# [ derive ( Debug , Serialize , Deserialize ) ] ", + "# [ derive ( Deserialize ) ] ", "# [ serde ( rename_all = \"camelCase\" ) ] ", "pub struct MeowOnOrganization { pub title : String , } ", - "# [ derive ( Debug , Serialize , Deserialize ) ] ", + "# [ derive ( Deserialize ) ] ", "# [ serde ( tag = \"__typename\" ) ] ", "pub enum Meow { User ( MeowOnUser ) , Organization ( MeowOnOrganization ) }", ].into_iter() diff --git a/src/lib.rs b/src/lib.rs index d68a17893..a439fd5f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,15 +2,15 @@ //! //! The main interface to this library is the custom derive that generates modules from a GraphQL query and schema. See the docs for the [`GraphQLQuery`] trait for a full example. +#![deny(warnings)] #![deny(missing_docs)] extern crate serde; #[macro_use] extern crate serde_derive; -#[macro_use] -extern crate graphql_query_derive; +pub extern crate graphql_query_derive; -#[macro_use] +#[cfg_attr(test, macro_use)] extern crate serde_json; #[doc(hidden)] diff --git a/tests/input_object_variables.rs b/tests/input_object_variables.rs index a7f00578f..3613608a9 100644 --- a/tests/input_object_variables.rs +++ b/tests/input_object_variables.rs @@ -8,7 +8,8 @@ extern crate serde_json; #[derive(GraphQLQuery)] #[graphql( query_path = "tests/input_object_variables/input_object_variables_query.graphql", - schema_path = "tests/input_object_variables/input_object_variables_schema.graphql" + schema_path = "tests/input_object_variables/input_object_variables_schema.graphql", + response_derives = "Debug", )] #[allow(dead_code)] struct InputObjectVariablesQuery; @@ -33,7 +34,8 @@ type Email = String; #[derive(GraphQLQuery)] #[graphql( query_path = "tests/input_object_variables/input_object_variables_query_defaults.graphql", - schema_path = "tests/input_object_variables/input_object_variables_schema.graphql" + schema_path = "tests/input_object_variables/input_object_variables_schema.graphql", + response_derives = "Debug", )] #[allow(dead_code)] struct DefaultInputObjectVariablesQuery; diff --git a/tests/interfaces.rs b/tests/interfaces.rs index ebca3793f..57aacfc71 100644 --- a/tests/interfaces.rs +++ b/tests/interfaces.rs @@ -10,21 +10,47 @@ const RESPONSE: &'static str = include_str!("interfaces/interface_response.json" #[derive(GraphQLQuery)] #[graphql( query_path = "tests/interfaces/interface_query.graphql", - schema_path = "tests/interfaces/interface_schema.graphql" + schema_path = "tests/interfaces/interface_schema.graphql", + response_derives = "Debug, PartialEq", )] #[allow(dead_code)] struct InterfaceQuery; #[test] fn interface_deserialization() { + use interface_query::*; + println!("{:?}", RESPONSE); let response_data: interface_query::ResponseData = serde_json::from_str(RESPONSE).unwrap(); println!("{:?}", response_data); - let expected = r##"ResponseData { everything: Some([RustMyQueryEverything { name: "Audrey Lorde", on: Person(RustMyQueryEverythingOnPerson { birthday: Some("1934-02-18") }) }, RustMyQueryEverything { name: "Laïka", on: Dog(RustMyQueryEverythingOnDog { is_good_dog: true }) }, RustMyQueryEverything { name: "Mozilla", on: Organization(RustMyQueryEverythingOnOrganization { industry: OTHER }) }, RustMyQueryEverything { name: "Norbert", on: Dog(RustMyQueryEverythingOnDog { is_good_dog: true }) }]) }"##; + let expected = ResponseData { + everything: Some(vec![ + RustMyQueryEverything { + name: "Audrey Lorde".to_string(), + on: RustMyQueryEverythingOn::Person(RustMyQueryEverythingOnPerson { + birthday: Some("1934-02-18".to_string()), + }), + }, + RustMyQueryEverything { + name: "Laïka".to_string(), + on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog { is_good_dog: true }), + }, + RustMyQueryEverything { + name: "Mozilla".to_string(), + on: RustMyQueryEverythingOn::Organization(RustMyQueryEverythingOnOrganization { + industry: Industry::OTHER, + }), + }, + RustMyQueryEverything { + name: "Norbert".to_string(), + on: RustMyQueryEverythingOn::Dog(RustMyQueryEverythingOnDog { is_good_dog: true }), + }, + ]), + }; - assert_eq!(format!("{:?}", response_data), expected); + assert_eq!(response_data, expected); assert_eq!(response_data.everything.map(|names| names.len()), Some(4)); } @@ -32,7 +58,8 @@ fn interface_deserialization() { #[derive(GraphQLQuery)] #[graphql( query_path = "tests/interfaces/interface_not_on_everything_query.graphql", - schema_path = "tests/interfaces/interface_schema.graphql" + schema_path = "tests/interfaces/interface_schema.graphql", + response_derives = "Debug", )] #[allow(dead_code)] struct InterfaceNotOnEverythingQuery; diff --git a/tests/introspection.rs b/tests/introspection.rs index c4935f100..b065314f5 100644 --- a/tests/introspection.rs +++ b/tests/introspection.rs @@ -7,7 +7,7 @@ extern crate serde; #[derive(GraphQLQuery)] #[graphql( query_path = "tests/introspection/introspection_query.graphql", - schema_path = "tests/introspection/introspection_schema.graphql" + schema_path = "tests/introspection/introspection_schema.graphql", )] #[allow(dead_code)] struct IntrospectionQuery; diff --git a/tests/more_derives.rs b/tests/more_derives.rs new file mode 100644 index 000000000..f41b86707 --- /dev/null +++ b/tests/more_derives.rs @@ -0,0 +1,33 @@ +#[macro_use] +extern crate graphql_client; +extern crate serde; +#[macro_use] +extern crate serde_derive; + +#[derive(GraphQLQuery)] +#[graphql( + schema_path = "tests/more_derives/schema.graphql", + query_path = "tests/more_derives/query.graphql", + response_derives = "Debug, PartialEq, PartialOrd", +)] +struct MoreDerives; + +#[test] +fn response_derives_can_be_added() { + let response_data = more_derives::ResponseData { + current_user: Some(more_derives::RustTestCurrentUser { + id: Some("abcd".to_owned()), + name: Some("Angela Merkel".to_owned()), + }), + }; + + let response_data_2 = more_derives::ResponseData { + current_user: Some(more_derives::RustTestCurrentUser { + id: Some("ffff".to_owned()), + name: Some("Winnie the Pooh".to_owned()), + }), + }; + + assert_ne!(response_data, response_data_2); + assert!(response_data < response_data_2); +} diff --git a/tests/more_derives/query.graphql b/tests/more_derives/query.graphql new file mode 100644 index 000000000..373939d0a --- /dev/null +++ b/tests/more_derives/query.graphql @@ -0,0 +1,6 @@ +query Test { + currentUser { + name + id + } +} diff --git a/tests/more_derives/schema.graphql b/tests/more_derives/schema.graphql new file mode 100644 index 000000000..21c707e94 --- /dev/null +++ b/tests/more_derives/schema.graphql @@ -0,0 +1,12 @@ +schema { + query: TestQuery +} + +type TestQuery { + currentUser: TestUser +} + +type TestUser { + name: String + id: ID +} diff --git a/tests/operation_selection.rs b/tests/operation_selection.rs index 5b2c5c0e2..dba8222fb 100644 --- a/tests/operation_selection.rs +++ b/tests/operation_selection.rs @@ -8,7 +8,8 @@ extern crate serde_json; #[derive(GraphQLQuery)] #[graphql( query_path = "tests/operation_selection/queries.graphql", - schema_path = "tests/operation_selection/schema.graphql" + schema_path = "tests/operation_selection/schema.graphql", + response_derives = "Debug", )] #[allow(dead_code)] struct Heights; @@ -16,7 +17,8 @@ struct Heights; #[derive(GraphQLQuery)] #[graphql( query_path = "tests/operation_selection/queries.graphql", - schema_path = "tests/operation_selection/schema.graphql" + schema_path = "tests/operation_selection/schema.graphql", + response_derives = "Debug", )] #[allow(dead_code)] struct Echo; diff --git a/tests/subscriptions.rs b/tests/subscriptions.rs index 9fef180df..29074c8d7 100644 --- a/tests/subscriptions.rs +++ b/tests/subscriptions.rs @@ -20,6 +20,7 @@ const RESPONSE: &str = include_str!("subscription/subscription_query_response.js #[graphql( schema_path = "tests/subscription/subscription_schema.graphql", query_path = "tests/subscription/subscription_query.graphql", + response_derives = "Debug, PartialEq" )] #[allow(dead_code)] struct SubscriptionQuery; @@ -28,9 +29,24 @@ struct SubscriptionQuery; fn subscriptions_work() { let response_data: subscription_query::ResponseData = serde_json::from_str(RESPONSE).unwrap(); - let expected = r##"ResponseData { dog_birthdays: Some([RustBirthdaysDogBirthdays { name: Some("Maya") }, RustBirthdaysDogBirthdays { name: Some("Norbert") }, RustBirthdaysDogBirthdays { name: Some("Strelka") }, RustBirthdaysDogBirthdays { name: Some("Belka") }]) }"##; + let expected = subscription_query::ResponseData { + dog_birthdays: Some(vec![ + subscription_query::RustBirthdaysDogBirthdays { + name: Some("Maya".to_string()), + }, + subscription_query::RustBirthdaysDogBirthdays { + name: Some("Norbert".to_string()), + }, + subscription_query::RustBirthdaysDogBirthdays { + name: Some("Strelka".to_string()), + }, + subscription_query::RustBirthdaysDogBirthdays { + name: Some("Belka".to_string()), + }, + ]), + }; - assert_eq!(format!("{:?}", response_data), expected); + assert_eq!(response_data, expected); assert_eq!( response_data.dog_birthdays.map(|birthdays| birthdays.len()), diff --git a/tests/union_query.rs b/tests/union_query.rs index b7964da6f..01432ed35 100644 --- a/tests/union_query.rs +++ b/tests/union_query.rs @@ -10,7 +10,8 @@ const RESPONSE: &'static str = include_str!("unions/union_query_response.json"); #[derive(GraphQLQuery)] #[graphql( query_path = "tests/unions/union_query.graphql", - schema_path = "tests/unions/union_schema.graphql" + schema_path = "tests/unions/union_schema.graphql", + response_derives = "PartialEq, Debug", )] #[allow(dead_code)] struct UnionQuery; @@ -19,11 +20,27 @@ struct UnionQuery; fn union_query_deserialization() { let response_data: union_query::ResponseData = serde_json::from_str(RESPONSE).unwrap(); - println!("{:?}", response_data); + let expected = union_query::ResponseData { + names: Some(vec![ + union_query::RustMyQueryNames::Person(union_query::RustMyQueryNamesOnPerson { + first_name: "Audrey".to_string(), + last_name: Some("Lorde".to_string()), + }), + union_query::RustMyQueryNames::Dog(union_query::RustMyQueryNamesOnDog { + name: "Laïka".to_string(), + }), + union_query::RustMyQueryNames::Organization( + union_query::RustMyQueryNamesOnOrganization { + title: "Mozilla".to_string(), + }, + ), + union_query::RustMyQueryNames::Dog(union_query::RustMyQueryNamesOnDog { + name: "Norbert".to_string(), + }), + ]), + }; - let expected = r##"ResponseData { names: Some([Person(RustMyQueryNamesOnPerson { first_name: "Audrey", last_name: Some("Lorde") }), Dog(RustMyQueryNamesOnDog { name: "Laïka" }), Organization(RustMyQueryNamesOnOrganization { title: "Mozilla" }), Dog(RustMyQueryNamesOnDog { name: "Norbert" })]) }"##; - - assert_eq!(format!("{:?}", response_data), expected); + assert_eq!(response_data, expected); assert_eq!(response_data.names.map(|names| names.len()), Some(4)); }