@@ -7,7 +7,7 @@ use std::cell::Cell;
7
7
use std:: collections:: BTreeSet ;
8
8
9
9
#[ derive( Debug , Clone , PartialEq ) ]
10
- pub struct GqlUnion {
10
+ pub ( crate ) struct GqlUnion {
11
11
pub description : Option < String > ,
12
12
pub variants : BTreeSet < String > ,
13
13
pub is_required : Cell < bool > ,
@@ -22,90 +22,104 @@ enum UnionError {
22
22
MissingTypename { union_name : String } ,
23
23
}
24
24
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 ,
30
36
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
+ } ;
92
71
93
- let variants = variants?;
72
+ variants. push ( quote ! {
73
+ #variant_name( #variant_type)
74
+ } )
75
+ }
94
76
95
77
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?;
96
113
}
97
114
98
115
impl GqlUnion {
116
+ /// Returns the code to deserialize this union in the response given the query selection.
99
117
pub ( crate ) fn response_for_selection (
100
118
& self ,
101
119
query_context : & QueryContext ,
102
120
selection : & Selection ,
103
121
prefix : & str ,
104
122
) -> 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
109
123
let typename_field = selection. extract_typename ( query_context) ;
110
124
111
125
if typename_field. is_none ( ) {
@@ -114,6 +128,9 @@ impl GqlUnion {
114
128
} ) ?;
115
129
}
116
130
131
+ let struct_name = Ident :: new ( prefix, Span :: call_site ( ) ) ;
132
+ let derives = query_context. response_derives ( ) ;
133
+
117
134
let ( mut variants, children_definitions, used_variants) =
118
135
union_variants ( selection, query_context, prefix) ?;
119
136
0 commit comments