Skip to content

Commit 1bd8e92

Browse files
feat(selection): support @include and @Skip on fragment spreads
1 parent c682a42 commit 1bd8e92

File tree

4 files changed

+45
-25
lines changed

4 files changed

+45
-25
lines changed

graphql_client_codegen/src/codegen/selection.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn calculate_custom_response_type_selection<'a>(
9292
name: custom_response_type.as_str(),
9393
struct_id,
9494
boxed: false,
95+
has_skip_or_include: false
9596
});
9697
}
9798

@@ -128,7 +129,7 @@ pub(super) fn render_fragment<'a>(
128129
/// A sub-selection set (spread) on one of the variants of a union or interface.
129130
enum VariantSelection<'a> {
130131
InlineFragment(&'a InlineFragment),
131-
FragmentSpread((ResolvedFragmentId, &'a ResolvedFragment)),
132+
FragmentSpread((ResolvedFragmentId, &'a ResolvedFragment), bool),
132133
}
133134

134135
impl<'a> VariantSelection<'a> {
@@ -142,15 +143,15 @@ impl<'a> VariantSelection<'a> {
142143
Selection::InlineFragment(inline_fragment) => {
143144
Some(VariantSelection::InlineFragment(inline_fragment))
144145
}
145-
Selection::FragmentSpread(fragment_id) => {
146+
Selection::FragmentSpread(fragment_id, has_skip_or_include) => {
146147
let fragment = query.query.get_fragment(*fragment_id);
147148

148149
if fragment.on == type_id {
149150
// The selection is on the type itself.
150151
None
151152
} else {
152153
// The selection is on one of the variants of the type.
153-
Some(VariantSelection::FragmentSpread((*fragment_id, fragment)))
154+
Some(VariantSelection::FragmentSpread((*fragment_id, fragment), *has_skip_or_include))
154155
}
155156
}
156157
Selection::Field(_) | Selection::Typename => None,
@@ -160,7 +161,7 @@ impl<'a> VariantSelection<'a> {
160161
fn variant_type_id(&self) -> TypeId {
161162
match self {
162163
VariantSelection::InlineFragment(f) => f.type_id,
163-
VariantSelection::FragmentSpread((_id, f)) => f.on,
164+
VariantSelection::FragmentSpread((_id, f), _) => f.on,
164165
}
165166
}
166167
}
@@ -175,14 +176,15 @@ fn calculate_selection<'a>(
175176
// If the selection only contains a fragment, replace the selection with
176177
// that fragment.
177178
if selection_set.len() == 1 {
178-
if let Selection::FragmentSpread(fragment_id) =
179+
if let Selection::FragmentSpread(fragment_id, has_skip_or_include) =
179180
context.query.query.get_selection(selection_set[0])
180181
{
181182
let fragment = context.query.query.get_fragment(*fragment_id);
182183
context.push_type_alias(TypeAlias {
183184
name: &fragment.name,
184185
struct_id,
185186
boxed: fragment_is_recursive(*fragment_id, context.query.query),
187+
has_skip_or_include: *has_skip_or_include
186188
});
187189
return;
188190
}
@@ -253,13 +255,14 @@ fn calculate_selection<'a>(
253255
let struct_id = context.push_type(expanded_type);
254256

255257
if variant_selections.len() == 1 {
256-
if let VariantSelection::FragmentSpread((fragment_id, fragment)) =
258+
if let VariantSelection::FragmentSpread((fragment_id, fragment), has_skip_or_include) =
257259
variant_selections[0].2
258260
{
259261
context.push_type_alias(TypeAlias {
260262
boxed: fragment_is_recursive(fragment_id, context.query.query),
261263
name: &fragment.name,
262264
struct_id,
265+
has_skip_or_include
263266
});
264267
continue;
265268
}
@@ -276,7 +279,7 @@ fn calculate_selection<'a>(
276279
options,
277280
);
278281
}
279-
VariantSelection::FragmentSpread((fragment_id, fragment)) => context
282+
VariantSelection::FragmentSpread((fragment_id, fragment), has_skip_or_include) => context
280283
.push_field(ExpandedField {
281284
field_type: fragment.name.as_str().into(),
282285
field_type_qualifiers: &[GraphqlTypeQualifier::Required],
@@ -286,8 +289,7 @@ fn calculate_selection<'a>(
286289
struct_id,
287290
deprecation: None,
288291
boxed: fragment_is_recursive(*fragment_id, context.query.query),
289-
// TODO
290-
skip_or_include: false
292+
skip_or_include: *has_skip_or_include
291293
}),
292294
}
293295
}
@@ -387,7 +389,7 @@ fn calculate_selection<'a>(
387389
}
388390
Selection::Typename => (),
389391
Selection::InlineFragment(_inline) => (),
390-
Selection::FragmentSpread(fragment_id) => {
392+
Selection::FragmentSpread(fragment_id, has_skip_or_include) => {
391393
// Here we only render fragments that are directly on the type
392394
// itself, and not on one of its variants.
393395

@@ -413,7 +415,7 @@ fn calculate_selection<'a>(
413415
flatten: true,
414416
deprecation: None,
415417
boxed: fragment_is_recursive(*fragment_id, context.query.query),
416-
skip_or_include: false
418+
skip_or_include: *has_skip_or_include
417419
});
418420

419421
// We stop here, because the structs for the fragments are generated separately, to
@@ -430,6 +432,7 @@ struct TypeAlias<'a> {
430432
name: &'a str,
431433
struct_id: ResponseTypeId,
432434
boxed: bool,
435+
has_skip_or_include: bool
433436
}
434437

435438
struct ExpandedField<'a> {

graphql_client_codegen/src/query.rs

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use crate::{
1818
StoredInputType, StoredScalar, TypeId, UnionId,
1919
},
2020
};
21+
use graphql_parser::query::Directive;
2122
use std::{
2223
collections::{BTreeMap, BTreeSet},
2324
fmt::Display,
@@ -279,7 +280,13 @@ where
279280
))
280281
})?;
281282

282-
let id = query.push_selection(Selection::FragmentSpread(fragment_id), parent);
283+
let id = query.push_selection(
284+
Selection::FragmentSpread(
285+
fragment_id,
286+
has_skip_or_include(fragment_spread.directives.as_slice()),
287+
),
288+
parent,
289+
);
283290

284291
parent.add_to_selection_set(query, id);
285292
}
@@ -318,17 +325,12 @@ where
318325
))
319326
})?;
320327

321-
let has_skip_or_include = field
322-
.directives
323-
.iter()
324-
.any(|directive| ["skip", "include"].contains(&directive.name.as_ref()));
325-
326328
let id = query.push_selection(
327329
Selection::Field(SelectedField {
328330
alias: field.alias.as_ref().map(|alias| alias.as_ref().into()),
329331
field_id,
330332
selection_set: Vec::with_capacity(selection_set.items.len()),
331-
skip_or_include: has_skip_or_include
333+
skip_or_include: has_skip_or_include(field.directives.as_slice()),
332334
}),
333335
parent,
334336
);
@@ -358,7 +360,13 @@ where
358360
))
359361
})?;
360362

361-
let id = query.push_selection(Selection::FragmentSpread(fragment_id), parent);
363+
let id = query.push_selection(
364+
Selection::FragmentSpread(
365+
fragment_id,
366+
has_skip_or_include(fragment_spread.directives.as_slice()),
367+
),
368+
parent,
369+
);
362370

363371
parent.add_to_selection_set(query, id);
364372
}
@@ -368,6 +376,15 @@ where
368376
Ok(())
369377
}
370378

379+
fn has_skip_or_include<'doc, T>(directives: &[Directive<'doc, T>]) -> bool
380+
where
381+
T: graphql_parser::query::Text<'doc>,
382+
{
383+
directives
384+
.iter()
385+
.any(|directive| ["skip", "include"].contains(&directive.name.as_ref()))
386+
}
387+
371388
fn resolve_selection<'doc, T>(
372389
ctx: &mut Query,
373390
on: TypeId,
@@ -731,7 +748,7 @@ pub(crate) fn all_used_types(operation_id: OperationId, query: &BoundQuery<'_>)
731748

732749
pub(crate) fn full_path_prefix(selection_id: SelectionId, query: &BoundQuery<'_>) -> String {
733750
let mut path = match query.query.get_selection(selection_id) {
734-
Selection::FragmentSpread(_) | Selection::InlineFragment(_) => Vec::new(),
751+
Selection::FragmentSpread(..) | Selection::InlineFragment(_) => Vec::new(),
735752
selection => vec![selection.to_path_segment(query)],
736753
};
737754

graphql_client_codegen/src/query/selection.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ pub(super) fn validate_type_conditions(
1414
let selection = query.query.get_selection(selection_id);
1515

1616
let selected_type = match selection {
17-
Selection::FragmentSpread(fragment_id) => query.query.get_fragment(*fragment_id).on,
17+
Selection::FragmentSpread(fragment_id, _) => query.query.get_fragment(*fragment_id).on,
1818
Selection::InlineFragment(inline_fragment) => inline_fragment.type_id,
1919
_ => return Ok(()),
2020
};
@@ -146,7 +146,7 @@ impl SelectionParent {
146146
pub(crate) enum Selection {
147147
Field(SelectedField),
148148
InlineFragment(InlineFragment),
149-
FragmentSpread(ResolvedFragmentId),
149+
FragmentSpread(ResolvedFragmentId, bool),
150150
Typename,
151151
}
152152

@@ -184,7 +184,7 @@ impl Selection {
184184
selection.collect_used_types(used_types, query);
185185
}
186186
}
187-
Selection::FragmentSpread(fragment_id) => {
187+
Selection::FragmentSpread(fragment_id, _) => {
188188
// This is necessary to avoid infinite recursion.
189189
if used_types.fragments.contains(fragment_id) {
190190
return;
@@ -204,7 +204,7 @@ impl Selection {
204204

205205
pub(crate) fn contains_fragment(&self, fragment_id: ResolvedFragmentId, query: &Query) -> bool {
206206
match self {
207-
Selection::FragmentSpread(id) => *id == fragment_id,
207+
Selection::FragmentSpread(id, _) => *id == fragment_id,
208208
_ => self.subselection().iter().any(|selection_id| {
209209
query
210210
.get_selection(*selection_id)

graphql_client_codegen/src/query/validation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn selection_set_contains_type_name(
5656

5757
match selection {
5858
Selection::Typename => return true,
59-
Selection::FragmentSpread(fragment_id) => {
59+
Selection::FragmentSpread(fragment_id, _) => {
6060
let fragment = query.get_fragment(*fragment_id);
6161
if fragment.on == parent_type_id
6262
&& selection_set_contains_type_name(fragment.on, &fragment.selection_set, query)

0 commit comments

Comments
 (0)