apollo_federation/query_plan/
display.rs1use std::fmt;
2
3use apollo_compiler::executable;
4
5use super::*;
6use crate::display_helpers::State;
7use crate::display_helpers::write_indented_lines;
8
9impl QueryPlan {
10 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
11 let Self {
12 node,
13 statistics: _,
14 } = self;
15 state.write("QueryPlan {")?;
16 if let Some(node) = node {
17 state.indent()?;
18 node.write_indented(state)?;
19 state.dedent()?;
20 }
21 state.write("}")
22 }
23}
24
25impl TopLevelPlanNode {
26 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
27 match self {
28 Self::Subscription(node) => node.write_indented(state),
29 Self::Fetch(node) => node.write_indented(state),
30 Self::Sequence(node) => node.write_indented(state),
31 Self::Parallel(node) => node.write_indented(state),
32 Self::Flatten(node) => node.write_indented(state),
33 Self::Defer(node) => node.write_indented(state),
34 Self::Condition(node) => node.write_indented(state),
35 }
36 }
37}
38
39impl PlanNode {
40 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
41 match self {
42 Self::Fetch(node) => node.write_indented(state),
43 Self::Sequence(node) => node.write_indented(state),
44 Self::Parallel(node) => node.write_indented(state),
45 Self::Flatten(node) => node.write_indented(state),
46 Self::Defer(node) => node.write_indented(state),
47 Self::Condition(node) => node.write_indented(state),
48 }
49 }
50}
51
52impl SubscriptionNode {
53 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
54 let Self { primary, rest } = self;
55 state.write("Subscription {")?;
56 state.indent()?;
57
58 state.write("Primary: {")?;
59 state.indent()?;
60 primary.write_indented(state)?;
61 state.dedent()?;
62 state.write("},")?;
63
64 if let Some(rest) = rest {
65 state.new_line()?;
66 state.write("Rest: {")?;
67 state.indent()?;
68 rest.write_indented(state)?;
69 state.dedent()?;
70 state.write("},")?;
71 }
72
73 state.dedent()?;
74 state.write("},")
75 }
76}
77
78impl FetchNode {
79 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
80 let Self {
81 subgraph_name,
82 id,
83 variable_usages: _,
84 requires,
85 operation_document,
86 operation_name: _,
87 operation_kind: _,
88 input_rewrites: _,
89 output_rewrites: _,
90 context_rewrites: _,
91 } = self;
92 state.write(format_args!("Fetch(service: {subgraph_name:?}"))?;
93 if let Some(id) = id {
94 state.write(format_args!(", id: {id:?}"))?;
95 }
96 state.write(") {")?;
97 state.indent()?;
98
99 if let Some(v) = requires.as_ref() {
100 if !v.is_empty() {
101 write_selections(state, v)?;
102 state.write(" =>")?;
103 state.new_line()?;
104 }
105 }
106 write_operation(state, operation_document)?;
107
108 state.dedent()?;
109 state.write("},")
110 }
111}
112
113impl SequenceNode {
114 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
115 let Self { nodes } = self;
116 state.write("Sequence {")?;
117
118 write_indented_lines(state, nodes, |state, node| node.write_indented(state))?;
119
120 state.write("},")
121 }
122}
123
124impl ParallelNode {
125 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
126 let Self { nodes } = self;
127 state.write("Parallel {")?;
128
129 write_indented_lines(state, nodes, |state, node| node.write_indented(state))?;
130
131 state.write("},")
132 }
133}
134
135impl FlattenNode {
136 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
137 let Self { path, node } = self;
138 state.write("Flatten(path: \"")?;
139 if let Some((first, rest)) = path.split_first() {
140 state.write(first)?;
141 for element in rest {
142 state.write(".")?;
143 state.write(element)?;
144 }
145 }
146 state.write("\") {")?;
147 state.indent()?;
148
149 node.write_indented(state)?;
150
151 state.dedent()?;
152 state.write("},")
153 }
154}
155
156impl ConditionNode {
157 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
158 let Self {
159 condition_variable,
160 if_clause,
161 else_clause,
162 } = self;
163 match (if_clause, else_clause) {
164 (Some(if_clause), Some(else_clause)) => {
165 state.write(format_args!("Condition(if: ${condition_variable}) {{"))?;
166 state.indent()?;
167
168 state.write("Then {")?;
169 state.indent()?;
170 if_clause.write_indented(state)?;
171 state.dedent()?;
172 state.write("}")?;
173
174 state.write(" Else {")?;
175 state.indent()?;
176 else_clause.write_indented(state)?;
177 state.dedent()?;
178 state.write("},")?;
179
180 state.dedent()?;
181 state.write("},")
182 }
183
184 (Some(if_clause), None) => {
185 state.write(format_args!("Include(if: ${condition_variable}) {{"))?;
186 state.indent()?;
187
188 if_clause.write_indented(state)?;
189
190 state.dedent()?;
191 state.write("},")
192 }
193
194 (None, Some(else_clause)) => {
195 state.write(format_args!("Skip(if: ${condition_variable}) {{"))?;
196 state.indent()?;
197
198 else_clause.write_indented(state)?;
199
200 state.dedent()?;
201 state.write("},")
202 }
203
204 (None, None) => state.write("Condition {}"),
206 }
207 }
208}
209
210impl DeferNode {
211 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
212 let Self { primary, deferred } = self;
213 state.write("Defer {")?;
214 state.indent()?;
215
216 primary.write_indented(state)?;
217 if !deferred.is_empty() {
218 state.write(" [")?;
219 write_indented_lines(state, deferred, |state, deferred| {
220 deferred.write_indented(state)
221 })?;
222 state.write("]")?;
223 }
224
225 state.dedent()?;
226 state.write("},")
227 }
228}
229
230impl PrimaryDeferBlock {
231 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
232 let Self {
233 sub_selection,
234 node,
235 } = self;
236 state.write("Primary {")?;
237 if sub_selection.is_some() || node.is_some() {
238 if let Some(sub_selection) = sub_selection {
239 state.indent_no_new_line();
242 state.write("\n")?;
243
244 state.write(
245 sub_selection
246 .serialize()
247 .initial_indent_level(state.indent_level()),
248 )?;
249 if node.is_some() {
250 state.write(":")?;
251 state.new_line()?;
252 }
253 } else {
254 state.indent()?;
256 }
257
258 if let Some(node) = node {
259 node.write_indented(state)?;
260 }
261
262 state.dedent()?;
263 }
264 state.write("},")
265 }
266}
267
268impl DeferredDeferBlock {
269 fn write_indented(&self, state: &mut State<'_, '_>) -> fmt::Result {
270 let Self {
271 depends,
272 label,
273 query_path,
274 sub_selection,
275 node,
276 } = self;
277
278 state.write("Deferred(depends: [")?;
279 if let Some((DeferredDependency { id }, rest)) = depends.split_first() {
280 state.write(id)?;
281 for DeferredDependency { id } in rest {
282 state.write(", ")?;
283 state.write(id)?;
284 }
285 }
286 state.write("], path: \"")?;
287 if let Some((first, rest)) = query_path.split_first() {
288 state.write(first)?;
289 for element in rest {
290 state.write("/")?;
291 state.write(element)?;
292 }
293 }
294 state.write("\"")?;
295 if let Some(label) = label {
296 state.write_fmt(format_args!(r#", label: "{label}""#))?;
297 }
298 state.write(") {")?;
299
300 if sub_selection.is_some() || node.is_some() {
301 state.indent()?;
302
303 if let Some(sub_selection) = sub_selection {
304 write_selections(state, &sub_selection.selections)?;
305 state.write(":")?;
306 }
307 if sub_selection.is_some() && node.is_some() {
308 state.new_line()?;
309 }
310 if let Some(node) = node {
311 node.write_indented(state)?;
312 }
313
314 state.dedent()?;
315 }
316
317 state.write("},")
318 }
319}
320
321fn write_operation(
325 state: &mut State<'_, '_>,
326 operation_document: &ExecutableDocument,
327) -> fmt::Result {
328 let operation = operation_document
329 .operations
330 .get(None)
331 .expect("expected a single-operation document");
332 write_selections(state, &operation.selection_set.selections)?;
333 for fragment in operation_document.fragments.values() {
334 state.write("\n\n")?; state.write(
336 fragment
337 .serialize()
338 .initial_indent_level(state.indent_level()),
339 )?
340 }
341 Ok(())
342}
343
344fn write_selections(
345 state: &mut State<'_, '_>,
346 mut selections: &[executable::Selection],
347) -> fmt::Result {
348 if let Some(executable::Selection::Field(field)) = selections.first() {
349 if field.name == "_entities" {
350 selections = &field.selection_set.selections
351 }
352 }
353 state.write("{")?;
354
355 state.indent_no_new_line();
358 for sel in selections {
359 state.write("\n")?;
360 state.write(sel.serialize().initial_indent_level(state.indent_level()))?;
361 }
362 state.dedent()?;
363
364 state.write("}")
365}
366
367impl fmt::Display for FetchDataPathElement {
369 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
370 match self {
371 Self::Key(name, conditions) => {
372 f.write_str(name)?;
373 write_conditions(conditions, f)
374 }
375 Self::AnyIndex(conditions) => {
376 f.write_str("@")?;
377 write_conditions(conditions, f)
378 }
379 Self::TypenameEquals(name) => write!(f, "... on {name}"),
380 Self::Parent => write!(f, ".."),
381 }
382 }
383}
384
385fn write_conditions(conditions: &Option<Vec<Name>>, f: &mut fmt::Formatter<'_>) -> fmt::Result {
386 if let Some(conditions) = conditions {
387 write!(f, "|[{}]", conditions.join(","))
388 } else {
389 Ok(())
390 }
391}
392
393impl fmt::Display for QueryPathElement {
394 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
395 match self {
396 Self::Field(field) => f.write_str(field.response_key()),
397 Self::InlineFragment(inline) => {
398 if let Some(cond) = &inline.type_condition {
399 write!(f, "... on {cond}")
400 } else {
401 Ok(())
402 }
403 }
404 }
405 }
406}
407
408macro_rules! impl_display {
409 ($( $Ty: ty )+) => {
410 $(
411 impl fmt::Display for $Ty {
412 fn fmt(&self, output: &mut fmt::Formatter<'_>) -> fmt::Result {
413 self.write_indented(&mut State::new(output))
414 }
415 }
416 )+
417 };
418}
419
420impl_display! {
421 QueryPlan
422 TopLevelPlanNode
423 PlanNode
424 SubscriptionNode
425 FetchNode
426 SequenceNode
427 ParallelNode
428 FlattenNode
429 ConditionNode
430 DeferNode
431 PrimaryDeferBlock
432 DeferredDeferBlock
433}