Skip to main content

wdl_format/v1/
workflow.rs

1//! Formatting for workflows.
2
3pub mod call;
4
5use wdl_ast::SyntaxKind;
6use wdl_ast::Token;
7
8use crate::Config;
9use crate::PreToken;
10use crate::TokenStream;
11use crate::Trivia;
12use crate::Writable as _;
13use crate::element::FormatElement;
14
15/// Formats a [`ConditionalStatement`](wdl_ast::v1::ConditionalStatement).
16///
17/// # Panics
18///
19/// This will panic if the element does not have the expected children.
20pub fn format_conditional_statement(
21    element: &FormatElement,
22    stream: &mut TokenStream<PreToken>,
23    config: &Config,
24) {
25    for child in element.children().expect("conditional statement children") {
26        (&child).write(stream, config);
27    }
28    stream.end_line();
29}
30
31/// Formats a [`ConditionalStatementClause`](wdl_ast::v1::ConditionalStatementClause).
32pub fn format_conditional_statement_clause(
33    element: &FormatElement,
34    stream: &mut TokenStream<PreToken>,
35    config: &Config,
36) {
37    let mut children = element
38        .children()
39        .expect("conditional statement clause children")
40        .peekable();
41
42    let mut has_condition = false;
43
44    while let Some(el) = children.peek() {
45        // If the format element doesn't contain a token, it's not a keyword, so
46        // break.
47        let Some(token) = el.element().as_token() else {
48            break;
49        };
50
51        // Write the token if it's an `if` or an `else`.
52        match token {
53            Token::IfKeyword(_) => {
54                has_condition = true;
55            }
56            Token::ElseKeyword(_) => {}
57            _ => break,
58        }
59
60        el.write(stream, config);
61        stream.end_word();
62
63        // Take the child token we just processed.
64        children.next();
65    }
66
67    // If the ConditionalStatementClause contains a condition, we need to process
68    // the parens and all elements inside!
69    if has_condition {
70        let open_paren = children.next().expect("open paren");
71        assert!(open_paren.element().kind() == SyntaxKind::OpenParen);
72        (&open_paren).write(stream, config);
73
74        for child in children.by_ref() {
75            (&child).write(stream, config);
76            if child.element().kind() == SyntaxKind::CloseParen {
77                stream.end_word();
78                break;
79            }
80        }
81    }
82
83    let open_brace = children.next().expect("open brace");
84    assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
85    (&open_brace).write(stream, config);
86    stream.increment_indent();
87
88    for child in children {
89        if child.element().kind() == SyntaxKind::CloseBrace {
90            stream.decrement_indent();
91        }
92        (&child).write(stream, config);
93    }
94    stream.end_word();
95}
96
97/// Formats a [`ScatterStatement`](wdl_ast::v1::ScatterStatement).
98///
99/// # Panics
100///
101/// This will panic if the element does not have the expected children.
102pub fn format_scatter_statement(
103    element: &FormatElement,
104    stream: &mut TokenStream<PreToken>,
105    config: &Config,
106) {
107    let mut children = element.children().expect("scatter statement children");
108
109    let scatter_keyword = children.next().expect("scatter keyword");
110    assert!(scatter_keyword.element().kind() == SyntaxKind::ScatterKeyword);
111    (&scatter_keyword).write(stream, config);
112    stream.end_word();
113
114    let open_paren = children.next().expect("open paren");
115    assert!(open_paren.element().kind() == SyntaxKind::OpenParen);
116    (&open_paren).write(stream, config);
117
118    let variable = children.next().expect("scatter variable");
119    assert!(variable.element().kind() == SyntaxKind::Ident);
120    (&variable).write(stream, config);
121    stream.end_word();
122
123    let in_keyword = children.next().expect("in keyword");
124    assert!(in_keyword.element().kind() == SyntaxKind::InKeyword);
125    (&in_keyword).write(stream, config);
126    stream.end_word();
127
128    for child in children.by_ref() {
129        (&child).write(stream, config);
130        if child.element().kind() == SyntaxKind::CloseParen {
131            stream.end_word();
132            break;
133        }
134    }
135
136    let open_brace = children.next().expect("open brace");
137    assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
138    (&open_brace).write(stream, config);
139    stream.end_line();
140    stream.increment_indent();
141
142    for child in children {
143        if child.element().kind() == SyntaxKind::CloseBrace {
144            stream.decrement_indent();
145        }
146        (&child).write(stream, config);
147    }
148    stream.end_line();
149}
150
151/// Formats a [`WorkflowDefinition`](wdl_ast::v1::WorkflowDefinition).
152///
153/// # Panics
154///
155/// This will panic if the element does not have the expected children.
156pub fn format_workflow_definition(
157    element: &FormatElement,
158    stream: &mut TokenStream<PreToken>,
159    config: &Config,
160) {
161    let mut children = element.children().expect("workflow definition children");
162
163    stream.ignore_trailing_blank_lines();
164
165    let workflow_keyword = children.next().expect("workflow keyword");
166    assert!(workflow_keyword.element().kind() == SyntaxKind::WorkflowKeyword);
167    (&workflow_keyword).write(stream, config);
168    stream.end_word();
169
170    let name = children.next().expect("workflow name");
171    assert!(name.element().kind() == SyntaxKind::Ident);
172    (&name).write(stream, config);
173    stream.end_word();
174
175    let open_brace = children.next().expect("open brace");
176    assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
177    (&open_brace).write(stream, config);
178    stream.increment_indent();
179
180    let mut meta = None;
181    let mut parameter_meta = None;
182    let mut input = None;
183    let mut body = Vec::new();
184    let mut output = None;
185    let mut hints = None;
186    let mut close_brace = None;
187
188    for child in children {
189        match child.element().kind() {
190            SyntaxKind::MetadataSectionNode => {
191                meta = Some(child.clone());
192            }
193            SyntaxKind::ParameterMetadataSectionNode => {
194                parameter_meta = Some(child.clone());
195            }
196            SyntaxKind::InputSectionNode => {
197                input = Some(child.clone());
198            }
199            SyntaxKind::BoundDeclNode => {
200                body.push(child.clone());
201            }
202            SyntaxKind::CallStatementNode => {
203                body.push(child.clone());
204            }
205            SyntaxKind::ConditionalStatementNode => {
206                body.push(child.clone());
207            }
208            SyntaxKind::ScatterStatementNode => {
209                body.push(child.clone());
210            }
211            SyntaxKind::OutputSectionNode => {
212                output = Some(child.clone());
213            }
214            SyntaxKind::WorkflowHintsSectionNode => {
215                hints = Some(child.clone());
216            }
217            SyntaxKind::CloseBrace => {
218                close_brace = Some(child.clone());
219            }
220            _ => {
221                unreachable!(
222                    "unexpected child in workflow definition: {:?}",
223                    child.element().kind()
224                );
225            }
226        }
227    }
228
229    if let Some(meta) = meta {
230        (&meta).write(stream, config);
231        stream.blank_line();
232    }
233
234    if let Some(parameter_meta) = parameter_meta {
235        (&parameter_meta).write(stream, config);
236        stream.blank_line();
237    }
238
239    if let Some(input) = input {
240        (&input).write(stream, config);
241        stream.blank_line();
242    }
243
244    stream.allow_blank_lines();
245    let body_empty = body.is_empty();
246    for child in body {
247        (&child).write(stream, config);
248    }
249    stream.ignore_trailing_blank_lines();
250    if !body_empty {
251        stream.blank_line();
252    }
253
254    if let Some(output) = output {
255        (&output).write(stream, config);
256        stream.blank_line();
257    }
258
259    if let Some(hints) = hints {
260        (&hints).write(stream, config);
261        stream.blank_line();
262    }
263
264    stream.trim_while(|t| matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine)));
265
266    stream.decrement_indent();
267    (&close_brace.expect("workflow close brace")).write(stream, config);
268    stream.end_line();
269}
270
271/// Formats a [`WorkflowHintsArray`](wdl_ast::v1::WorkflowHintsArray).
272///
273/// # Panics
274///
275/// This will panic if the element does not have the expected children.
276pub fn format_workflow_hints_array(
277    element: &FormatElement,
278    stream: &mut TokenStream<PreToken>,
279    config: &Config,
280) {
281    let mut children = element.children().expect("workflow hints array children");
282
283    let open_bracket = children.next().expect("open bracket");
284    assert!(open_bracket.element().kind() == SyntaxKind::OpenBracket);
285    (&open_bracket).write(stream, config);
286    stream.increment_indent();
287
288    let mut items = Vec::new();
289    let mut commas = Vec::new();
290    let mut close_bracket = None;
291
292    for child in children {
293        match child.element().kind() {
294            SyntaxKind::Comma => {
295                commas.push(child.clone());
296            }
297            SyntaxKind::CloseBracket => {
298                close_bracket = Some(child.clone());
299            }
300            _ => {
301                items.push(child.clone());
302            }
303        }
304    }
305
306    let mut commas = commas.into_iter();
307    for item in items {
308        (&item).write(stream, config);
309        match commas.next() {
310            Some(comma) => {
311                (&comma).write(stream, config);
312            }
313            _ if config.trailing_commas => {
314                stream.push_literal(",".to_string(), SyntaxKind::Comma);
315            }
316            _ => {}
317        }
318        stream.end_line();
319    }
320
321    stream.decrement_indent();
322    (&close_bracket.expect("workflow hints array close bracket")).write(stream, config);
323}
324
325/// Formats a [`WorkflowHintsItem`](wdl_ast::v1::WorkflowHintsItem).
326///
327/// # Panics
328///
329/// This will panic if the element does not have the expected children.
330pub fn format_workflow_hints_item(
331    element: &FormatElement,
332    stream: &mut TokenStream<PreToken>,
333    config: &Config,
334) {
335    let mut children = element.children().expect("workflow hints item children");
336
337    let key = children.next().expect("workflow hints item key");
338    assert!(key.element().kind() == SyntaxKind::Ident);
339    (&key).write(stream, config);
340
341    let colon = children.next().expect("workflow hints item colon");
342    assert!(colon.element().kind() == SyntaxKind::Colon);
343    (&colon).write(stream, config);
344    stream.end_word();
345
346    let value = children.next().expect("workflow hints item value");
347    (&value).write(stream, config);
348
349    stream.end_line();
350}
351
352/// Formats a [`WorkflowHintsObjectItem`](wdl_ast::v1::WorkflowHintsObjectItem).
353///
354/// # Panics
355///
356/// This will panic if the element does not have the expected children.
357pub fn format_workflow_hints_object_item(
358    element: &FormatElement,
359    stream: &mut TokenStream<PreToken>,
360    config: &Config,
361) {
362    let mut children = element
363        .children()
364        .expect("workflow hints object item children");
365
366    let key = children.next().expect("workflow hints object item key");
367    assert!(key.element().kind() == SyntaxKind::Ident);
368    (&key).write(stream, config);
369
370    let colon = children.next().expect("workflow hints object item colon");
371    assert!(colon.element().kind() == SyntaxKind::Colon);
372    (&colon).write(stream, config);
373    stream.end_word();
374
375    let value = children.next().expect("workflow hints object item value");
376    (&value).write(stream, config);
377
378    stream.end_line();
379}
380
381/// Formats a [`WorkflowHintsObject`](wdl_ast::v1::WorkflowHintsObject).
382///
383/// # Panics
384///
385/// This will panic if the element does not have the expected children.
386pub fn format_workflow_hints_object(
387    element: &FormatElement,
388    stream: &mut TokenStream<PreToken>,
389    config: &Config,
390) {
391    let mut children = element.children().expect("workflow hints object children");
392
393    let open_brace = children.next().expect("open brace");
394    assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
395    (&open_brace).write(stream, config);
396    stream.increment_indent();
397
398    for child in children {
399        if child.element().kind() == SyntaxKind::CloseBrace {
400            stream.decrement_indent();
401        }
402        (&child).write(stream, config);
403        stream.end_line();
404    }
405}
406
407/// Formats a [`WorkflowHintsSection`](wdl_ast::v1::WorkflowHintsSection).
408///
409/// # Panics
410///
411/// This will panic if the element does not have the expected children.
412pub fn format_workflow_hints_section(
413    element: &FormatElement,
414    stream: &mut TokenStream<PreToken>,
415    config: &Config,
416) {
417    let mut children = element.children().expect("workflow hints section children");
418
419    let hints_keyword = children.next().expect("hints keyword");
420    assert!(hints_keyword.element().kind() == SyntaxKind::HintsKeyword);
421    (&hints_keyword).write(stream, config);
422    stream.end_word();
423
424    let open_brace = children.next().expect("open brace");
425    assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
426    (&open_brace).write(stream, config);
427    stream.increment_indent();
428
429    for child in children {
430        if child.element().kind() == SyntaxKind::CloseBrace {
431            stream.decrement_indent();
432        }
433        (&child).write(stream, config);
434        stream.end_line();
435    }
436}