@@ -7,7 +7,7 @@ use std::cell::Cell;
77use std:: collections:: BTreeSet ;
88
99#[ derive( Debug , Clone , PartialEq ) ]
10- pub struct GqlUnion {
10+ pub ( crate ) struct GqlUnion {
1111 pub description : Option < String > ,
1212 pub variants : BTreeSet < String > ,
1313 pub is_required : Cell < bool > ,
@@ -22,90 +22,104 @@ enum UnionError {
2222 MissingTypename { union_name : String } ,
2323}
2424
25- type UnionVariantResult = Result < ( Vec < TokenStream > , Vec < TokenStream > , Vec < String > ) , failure:: Error > ;
26-
27- pub ( crate ) fn union_variants (
28- selection : & Selection ,
29- query_context : & QueryContext ,
25+ type UnionVariantResult < ' selection > =
26+ Result < ( Vec < TokenStream > , Vec < TokenStream > , Vec < & ' selection str > ) , failure:: Error > ;
27+
28+ /// Returns a triple.
29+ ///
30+ /// - The first element is the union variants to be inserted directly into the `enum` declaration.
31+ /// - The second is the structs for each variant's sub-selection
32+ /// - The last one contains which fields have been selected on the union, so we can make the enum exhaustive by complementing with those missing.
33+ pub ( crate ) fn union_variants < ' selection > (
34+ selection : & ' selection Selection ,
35+ context : & QueryContext ,
3036 prefix : & str ,
31- ) -> UnionVariantResult {
32- let mut children_definitions = Vec :: new ( ) ;
33- let mut used_variants = Vec :: with_capacity ( selection. 0 . len ( ) ) ;
34-
35- let variants: Result < Vec < TokenStream > , failure:: Error > = selection
36- . 0
37- . iter ( )
38- // ignore __typename
39- . filter ( |item| {
40- if let SelectionItem :: Field ( f) = item {
41- f. name != TYPENAME_FIELD
42- } else {
43- true
44- }
45- } )
46- . map ( |item| {
47- let ( on, fields) = match item {
48- SelectionItem :: Field ( _) => Err ( format_err ! ( "field selection on union" ) ) ?,
49- SelectionItem :: FragmentSpread ( SelectionFragmentSpread { fragment_name } ) => {
50- let fragment = query_context
51- . fragments
52- . get ( fragment_name)
53- . ok_or_else ( || format_err ! ( "Unknown fragment: {}" , & fragment_name) ) ?;
54-
55- ( & fragment. on , & fragment. selection )
56- }
57- SelectionItem :: InlineFragment ( frag) => ( & frag. on , & frag. fields ) ,
58- } ;
59- let variant_name = Ident :: new ( & on, Span :: call_site ( ) ) ;
60- used_variants. push ( on. to_string ( ) ) ;
61-
62- let new_prefix = format ! ( "{}On{}" , prefix, on) ;
63-
64- let variant_type = Ident :: new ( & new_prefix, Span :: call_site ( ) ) ;
65-
66- let field_object_type = query_context
67- . schema
68- . objects
69- . get ( on)
70- . map ( |_f| query_context. maybe_expand_field ( & on, & fields, & new_prefix) ) ;
71- let field_interface = query_context
72- . schema
73- . interfaces
74- . get ( on)
75- . map ( |_f| query_context. maybe_expand_field ( & on, & fields, & new_prefix) ) ;
76- let field_union_type = query_context
77- . schema
78- . unions
79- . get ( on)
80- . map ( |_f| query_context. maybe_expand_field ( & on, & fields, & new_prefix) ) ;
81-
82- match field_object_type. or ( field_interface) . or ( field_union_type) {
83- Some ( tokens) => children_definitions. push ( tokens?) ,
84- None => Err ( UnionError :: UnknownType { ty : on. to_string ( ) } ) ?,
85- } ;
86-
87- Ok ( quote ! {
88- #variant_name( #variant_type)
89- } )
90- } )
91- . collect ( ) ;
37+ ) -> UnionVariantResult < ' selection > {
38+ let selection = selection. selected_variants_on_union ( context) ?;
39+ let used_variants = selection. keys ( ) . collect ( ) ;
40+ let mut children_definitions = Vec :: with_capacity ( selection. size ( ) ) ;
41+ let mut variants = Vec :: with_capacity ( selection. size ( ) ) ;
42+
43+ for ( on, fields) in selection. iter ( ) {
44+ let variant_name = Ident :: new ( & on, Span :: call_site ( ) ) ;
45+ used_variants. push ( on. to_string ( ) ) ;
46+
47+ let new_prefix = format ! ( "{}On{}" , prefix, on) ;
48+
49+ let variant_type = Ident :: new ( & new_prefix, Span :: call_site ( ) ) ;
50+
51+ let field_object_type = context
52+ . schema
53+ . objects
54+ . get ( on)
55+ . map ( |_f| context. maybe_expand_field ( & on, fields, & new_prefix) ) ;
56+ let field_interface = context
57+ . schema
58+ . interfaces
59+ . get ( on)
60+ . map ( |_f| context. maybe_expand_field ( & on, fields, & new_prefix) ) ;
61+ let field_union_type = context
62+ . schema
63+ . unions
64+ . get ( on)
65+ . map ( |_f| context. maybe_expand_field ( & on, fields, & new_prefix) ) ;
66+
67+ match field_object_type. or ( field_interface) . or ( field_union_type) {
68+ Some ( tokens) => children_definitions. push ( tokens?) ,
69+ None => Err ( UnionError :: UnknownType { ty : on. to_string ( ) } ) ?,
70+ } ;
9271
93- let variants = variants?;
72+ variants. push ( quote ! {
73+ #variant_name( #variant_type)
74+ } )
75+ }
9476
9577 Ok ( ( variants, children_definitions, used_variants) )
78+
79+ // let variants: Result<Vec<TokenStream>, failure::Error> = selection
80+ // .0
81+ // .iter()
82+ // // ignore __typename
83+ // .filter(|item| {
84+ // if let SelectionItem::Field(f) = item {
85+ // f.name != TYPENAME_FIELD
86+ // } else {
87+ // true
88+ // }
89+ // })
90+ // // .flat_map(
91+ // // |item| -> impl ::std::iter::Iterator<Item = Result<(_, _), failure::Error>> {
92+ // // match item {
93+ // // SelectionItem::Field(_) => Err(format_err!("field selection on union"))?,
94+ // // SelectionItem::FragmentSpread(SelectionFragmentSpread { fragment_name }) => {
95+ // // let fragment = query_context
96+ // // .fragments
97+ // // .get(fragment_name)
98+ // // .ok_or_else(|| format_err!("Unknown fragment: {}", &fragment_name))?;
99+ // // // found the bug! `on` doesn't mean the same here as in the inline fragments
100+ // // std::iter::once(Ok((&fragment.on, &fragment.selection)))
101+ // // }
102+ // // SelectionItem::InlineFragment(frag) => {
103+ // // std::iter::once(Ok((&frag.on, &frag.fields)))
104+ // // }
105+ // // }
106+ // // },
107+ // // )
108+ // // // .collect::<Result<_, _>>()?
109+ // .map(|result: Result<(_, _), failure::Error>| -> Result<_, _> {
110+ // let Ok((on, fields)) = result?;
111+
112+ // let variants = variants?;
96113}
97114
98115impl GqlUnion {
116+ /// Returns the code to deserialize this union in the response given the query selection.
99117 pub ( crate ) fn response_for_selection (
100118 & self ,
101119 query_context : & QueryContext ,
102120 selection : & Selection ,
103121 prefix : & str ,
104122 ) -> Result < TokenStream , failure:: Error > {
105- let struct_name = Ident :: new ( prefix, Span :: call_site ( ) ) ;
106- let derives = query_context. response_derives ( ) ;
107-
108- // TODO: do this inside fragments
109123 let typename_field = selection. extract_typename ( query_context) ;
110124
111125 if typename_field. is_none ( ) {
@@ -114,6 +128,9 @@ impl GqlUnion {
114128 } ) ?;
115129 }
116130
131+ let struct_name = Ident :: new ( prefix, Span :: call_site ( ) ) ;
132+ let derives = query_context. response_derives ( ) ;
133+
117134 let ( mut variants, children_definitions, used_variants) =
118135 union_variants ( selection, query_context, prefix) ?;
119136
0 commit comments