From 3c351ab9ef0d22eeb350fbbe9591716cf869303c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=20Houl=C3=A9?= Date: Tue, 19 Jun 2018 21:44:22 +0200 Subject: [PATCH] Convert usages of graphql_parser SelectionSet to internal Selection --- graphql_query_derive/src/fragments.rs | 4 +- graphql_query_derive/src/interfaces.rs | 4 +- graphql_query_derive/src/objects.rs | 22 +++++----- graphql_query_derive/src/query.rs | 10 ++--- graphql_query_derive/src/schema.rs | 20 +++++---- graphql_query_derive/src/selection.rs | 12 +++--- graphql_query_derive/src/unions.rs | 56 +++++++++++++++++++++++--- 7 files changed, 89 insertions(+), 39 deletions(-) diff --git a/graphql_query_derive/src/fragments.rs b/graphql_query_derive/src/fragments.rs index 61064062a..f49a3d44e 100644 --- a/graphql_query_derive/src/fragments.rs +++ b/graphql_query_derive/src/fragments.rs @@ -1,4 +1,4 @@ -use graphql_parser::query::SelectionSet; +use selection::Selection; use proc_macro2::{Ident, Span, TokenStream}; use query::QueryContext; @@ -6,7 +6,7 @@ use query::QueryContext; pub struct GqlFragment { pub name: String, pub on: String, - pub selection: SelectionSet, + pub selection: Selection, } impl GqlFragment { diff --git a/graphql_query_derive/src/interfaces.rs b/graphql_query_derive/src/interfaces.rs index fe91e84ae..cb5a558d4 100644 --- a/graphql_query_derive/src/interfaces.rs +++ b/graphql_query_derive/src/interfaces.rs @@ -1,4 +1,4 @@ -use graphql_parser::query; +use selection::Selection; use objects::GqlObjectField; use proc_macro2::{Ident, Span, TokenStream}; use query::QueryContext; @@ -14,7 +14,7 @@ impl GqlInterface { pub fn response_for_selection( &self, _query_context: &QueryContext, - _selection: &query::SelectionSet, + _selection: &Selection, prefix: &str, ) -> TokenStream { let name = Ident::new(&prefix, Span::call_site()); diff --git a/graphql_query_derive/src/objects.rs b/graphql_query_derive/src/objects.rs index 10531f3c0..7dbffe7ff 100644 --- a/graphql_query_derive/src/objects.rs +++ b/graphql_query_derive/src/objects.rs @@ -1,10 +1,10 @@ use failure; use field_type::FieldType; -use graphql_parser::query; use heck::{CamelCase, SnakeCase}; use proc_macro2::{Ident, Span, TokenStream}; use query::QueryContext; use shared::render_object_field; +use selection::*; #[derive(Debug, PartialEq)] pub struct GqlObject { @@ -22,7 +22,7 @@ impl GqlObject { pub fn response_for_selection( &self, query_context: &QueryContext, - selection: &query::SelectionSet, + selection: &Selection, prefix: &str, ) -> Result { let name = Ident::new(prefix, Span::call_site()); @@ -41,14 +41,14 @@ impl GqlObject { pub fn field_impls_for_selection( &self, query_context: &QueryContext, - selection: &query::SelectionSet, + selection: &Selection, prefix: &str, ) -> Result, failure::Error> { selection - .items + .0 .iter() .map(|selected| { - if let query::Selection::Field(selected) = selected { + if let SelectionItem::Field(selected) = selected { let ty = self .fields .iter() @@ -61,7 +61,7 @@ impl GqlObject { prefix.to_camel_case(), selected.name.to_camel_case() ); - query_context.maybe_expand_field(&selected, &ty, &prefix) + query_context.maybe_expand_field(&ty, &selected.fields, &prefix) } else { Ok(quote!()) } @@ -72,14 +72,14 @@ impl GqlObject { pub fn response_fields_for_selection( &self, query_context: &QueryContext, - selection: &query::SelectionSet, + selection: &Selection, prefix: &str, ) -> Vec { let mut fields = Vec::new(); - for item in selection.items.iter() { + for item in selection.0.iter() { match item { - query::Selection::Field(f) => { + SelectionItem::Field(f) => { let name = &f.name; let ty = &self .fields @@ -93,7 +93,7 @@ impl GqlObject { ); fields.push(render_object_field(name, ty)); } - query::Selection::FragmentSpread(fragment) => { + SelectionItem::FragmentSpread(fragment) => { let field_name = Ident::new(&fragment.fragment_name.to_snake_case(), Span::call_site()); let type_name = Ident::new(&fragment.fragment_name, Span::call_site()); @@ -102,7 +102,7 @@ impl GqlObject { #field_name: #type_name }) } - query::Selection::InlineFragment(_) => { + SelectionItem::InlineFragment(_) => { unreachable!("inline fragment on object field") } } diff --git a/graphql_query_derive/src/query.rs b/graphql_query_derive/src/query.rs index 8f4226020..754e2d7e1 100644 --- a/graphql_query_derive/src/query.rs +++ b/graphql_query_derive/src/query.rs @@ -1,10 +1,10 @@ use failure; use field_type::FieldType; use fragments::GqlFragment; -use graphql_parser::query; use proc_macro2::TokenStream; use schema::Schema; use std::collections::BTreeMap; +use selection::Selection; pub struct QueryContext { pub _subscription_root: Option>, @@ -42,18 +42,18 @@ impl QueryContext { pub fn maybe_expand_field( &self, - field: &query::Field, ty: &str, + selection: &Selection, prefix: &str, ) -> Result { if let Some(_enm) = self.schema.enums.get(ty) { Ok(quote!()) // we already expand enums separately } else if let Some(obj) = self.schema.objects.get(ty) { - obj.response_for_selection(self, &field.selection_set, prefix) + obj.response_for_selection(self, &selection, prefix) } else if let Some(iface) = self.schema.interfaces.get(ty) { - Ok(iface.response_for_selection(self, &field.selection_set, prefix)) + Ok(iface.response_for_selection(self, &selection, prefix)) } else if let Some(unn) = self.schema.unions.get(ty) { - Ok(unn.response_for_selection(self, &field.selection_set, prefix)) + Ok(unn.response_for_selection(self, &selection, prefix)) } else { Ok(quote!()) } diff --git a/graphql_query_derive/src/schema.rs b/graphql_query_derive/src/schema.rs index 84ad6f8ed..7d9bfba33 100644 --- a/graphql_query_derive/src/schema.rs +++ b/graphql_query_derive/src/schema.rs @@ -9,6 +9,7 @@ use objects::{GqlObject, GqlObjectField}; use proc_macro2::TokenStream; use query::QueryContext; use std::collections::{BTreeMap, BTreeSet}; +use selection::Selection; use unions::GqlUnion; pub const DEFAULT_SCALARS: &[&'static str] = &["ID", "String", "Int", "Float", "Boolean"]; @@ -57,14 +58,16 @@ impl Schema { .expect("query type is defined"); let prefix = &q.name.expect("unnamed operation"); let prefix = format!("RUST_{}", prefix); + let selection = Selection::from(&q.selection_set); + definitions.extend(definition.field_impls_for_selection( &context, - &q.selection_set, + &selection, &prefix, )?); Some(definition.response_fields_for_selection( &context, - &q.selection_set, + &selection, &prefix, )) }; @@ -79,15 +82,16 @@ impl Schema { .expect("mutation type is defined"); let prefix = &q.name.expect("unnamed operation"); let prefix = format!("RUST_{}", prefix); + let selection = Selection::from(&q.selection_set); definitions.extend(definition.field_impls_for_selection( &context, - &q.selection_set, + &selection, &prefix, )?); Some(definition.response_fields_for_selection( &context, - &q.selection_set, + &selection, &prefix, )) }; @@ -104,14 +108,16 @@ impl Schema { .expect("subscription type is defined"); let prefix = &q.name.expect("unnamed operation"); let prefix = format!("RUST_{}", prefix); + let selection = Selection::from(&q.selection_set); + definitions.extend(definition.field_impls_for_selection( &context, - &q.selection_set, + &selection, &prefix, )?); Some(definition.response_fields_for_selection( &context, - &q.selection_set, + &selection, &prefix, )) }; @@ -125,7 +131,7 @@ impl Schema { fragment.name.clone(), GqlFragment { name: fragment.name, - selection: fragment.selection_set, + selection: Selection::from(&fragment.selection_set), on, }, ); diff --git a/graphql_query_derive/src/selection.rs b/graphql_query_derive/src/selection.rs index af91ff553..a6a84be6e 100644 --- a/graphql_query_derive/src/selection.rs +++ b/graphql_query_derive/src/selection.rs @@ -2,19 +2,19 @@ use graphql_parser::query::SelectionSet; #[derive(Debug, PartialEq)] pub struct SelectionField { - name: String, - fields: Selection, + pub name: String, + pub fields: Selection, } #[derive(Debug, PartialEq)] pub struct SelectionFragmentSpread { - fragment_name: String, + pub fragment_name: String, } #[derive(Debug, PartialEq)] pub struct SelectionInlineFragment { - on: String, - fields: Selection, + pub on: String, + pub fields: Selection, } #[derive(Debug, PartialEq)] @@ -25,7 +25,7 @@ pub enum SelectionItem { } #[derive(Debug, PartialEq)] -pub struct Selection(Vec); +pub struct Selection(pub Vec); impl<'a> ::std::convert::From<&'a SelectionSet> for Selection { fn from(selection_set: &SelectionSet) -> Selection { diff --git a/graphql_query_derive/src/unions.rs b/graphql_query_derive/src/unions.rs index bb4aa0ff1..8cad030ec 100644 --- a/graphql_query_derive/src/unions.rs +++ b/graphql_query_derive/src/unions.rs @@ -1,7 +1,8 @@ -use graphql_parser::query; -use proc_macro2::TokenStream; +use proc_macro2::{Ident, Span, TokenStream}; use query::QueryContext; use std::collections::BTreeSet; +use selection::{Selection, SelectionItem}; +use heck::SnakeCase; #[derive(Debug, PartialEq)] pub struct GqlUnion(pub BTreeSet); @@ -9,11 +10,54 @@ pub struct GqlUnion(pub BTreeSet); impl GqlUnion { pub fn response_for_selection( &self, - _query_context: &QueryContext, - _selection: &query::SelectionSet, - _prefix: &str, + query_context: &QueryContext, + selection: &Selection, + prefix: &str, ) -> TokenStream { - unimplemented!("union generation") + let struct_name = Ident::new(prefix, Span::call_site()); + let mut children_definitions = Vec::new(); + let fields = selection.0.iter().map(|item| { + match item { + SelectionItem::Field(_) => unreachable!("field selection on union"), + SelectionItem::FragmentSpread(_) => unreachable!("fragment spread on union"), + SelectionItem::InlineFragment(frag) => { + let field_name = Ident::new( + &format!("on_{}", frag.on).to_snake_case(), + Span::call_site(), + ); + + let field_type = Ident::new(&format!("{}On{}", prefix, frag.on), Span::call_site()); + + let new_prefix = format!("{}On{}", prefix, frag.on); + + let field_object_type = query_context.schema.objects.get(&frag.on) + .map(|f| query_context.maybe_expand_field(&frag.on, &frag.fields, &new_prefix)); + let field_interface = query_context.schema.interfaces.get(&frag.on) + .map(|f| query_context.maybe_expand_field(&frag.on, &frag.fields, &new_prefix)); + // nested unions, is that even a thing? + let field_union_type = query_context.schema.unions.get(&frag.on) + .map(|f| query_context.maybe_expand_field(&frag.on, &frag.fields, &new_prefix)); + + if let Some(tokens) = field_object_type.or(field_interface).or(field_union_type) { + children_definitions.push(tokens) + } + + // query_context.maybe_expand_field( + + // ); + quote! { + #field_name: #field_type + } + } + } + }); + + quote!{ + #[derive(Deserialize)] + pub struct #struct_name { + #(#fields),* + } + } } }