1use std::rc::Rc;
4
5use wdl_ast::SyntaxKind;
6use wdl_ast::v1::StrippedCommandPart;
7
8use crate::Config;
9use crate::PreToken;
10use crate::TokenStream;
11use crate::Trivia;
12use crate::Writable as _;
13use crate::element::FormatElement;
14
15pub fn format_task_definition(
21 element: &FormatElement,
22 stream: &mut TokenStream<PreToken>,
23 config: &Config,
24) {
25 let mut children = element.children().expect("task definition children");
26
27 stream.ignore_trailing_blank_lines();
28
29 let task_keyword = children.next().expect("task keyword");
30 assert!(task_keyword.element().kind() == SyntaxKind::TaskKeyword);
31 (&task_keyword).write(stream, config);
32 stream.end_word();
33
34 let name = children.next().expect("task name");
35 assert!(name.element().kind() == SyntaxKind::Ident);
36 (&name).write(stream, config);
37 stream.end_word();
38
39 let open_brace = children.next().expect("open brace");
40 assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
41 (&open_brace).write(stream, config);
42 stream.end_line();
43 stream.increment_indent();
44
45 let mut meta = None;
46 let mut parameter_meta = None;
47 let mut input = None;
48 let mut body = Vec::new();
49 let mut command = None;
50 let mut output = None;
51 let mut requirements = None;
52 let mut runtime = None;
53 let mut hints = None;
54 let mut close_brace = None;
55
56 for child in children {
57 match child.element().kind() {
58 SyntaxKind::InputSectionNode => {
59 input = Some(child.clone());
60 }
61 SyntaxKind::MetadataSectionNode => {
62 meta = Some(child.clone());
63 }
64 SyntaxKind::ParameterMetadataSectionNode => {
65 parameter_meta = Some(child.clone());
66 }
67 SyntaxKind::BoundDeclNode => {
68 body.push(child.clone());
69 }
70 SyntaxKind::CommandSectionNode => {
71 command = Some(child.clone());
72 }
73 SyntaxKind::OutputSectionNode => {
74 output = Some(child.clone());
75 }
76 SyntaxKind::RequirementsSectionNode => {
77 requirements = Some(child.clone());
78 }
79 SyntaxKind::RuntimeSectionNode => {
80 runtime = Some(child.clone());
81 }
82 SyntaxKind::TaskHintsSectionNode => {
83 hints = Some(child.clone());
84 }
85 SyntaxKind::CloseBrace => {
86 close_brace = Some(child.clone());
87 }
88 _ => {
89 unreachable!(
90 "unexpected child in task definition: {:?}",
91 child.element().kind()
92 );
93 }
94 }
95 }
96
97 if let Some(meta) = meta {
98 (&meta).write(stream, config);
99 stream.blank_line();
100 }
101
102 if let Some(parameter_meta) = parameter_meta {
103 (¶meter_meta).write(stream, config);
104 stream.blank_line();
105 }
106
107 if let Some(input) = input {
108 (&input).write(stream, config);
109 stream.blank_line();
110 }
111
112 stream.allow_blank_lines();
113 let body_empty = body.is_empty();
114 for child in body {
115 (&child).write(stream, config);
116 }
117 stream.ignore_trailing_blank_lines();
118 if !body_empty {
119 stream.blank_line();
120 }
121
122 if let Some(command) = command {
123 (&command).write(stream, config);
124 stream.blank_line();
125 }
126
127 if let Some(output) = output {
128 (&output).write(stream, config);
129 stream.blank_line();
130 }
131
132 match requirements {
133 Some(requirements) => {
134 (&requirements).write(stream, config);
135 stream.blank_line();
136 }
137 _ => {
138 if let Some(runtime) = runtime {
139 (&runtime).write(stream, config);
140 stream.blank_line();
141 }
142 }
143 }
144
145 if let Some(hints) = hints {
146 (&hints).write(stream, config);
147 stream.blank_line();
148 }
149
150 stream.trim_while(|t| matches!(t, PreToken::BlankLine | PreToken::Trivia(Trivia::BlankLine)));
151
152 stream.decrement_indent();
153 (&close_brace.expect("task close brace")).write(stream, config);
154 stream.end_line();
155}
156
157pub fn format_command_section(
163 element: &FormatElement,
164 stream: &mut TokenStream<PreToken>,
165 config: &Config,
166) {
167 let mut children = element.children().expect("command section children");
168
169 let command_keyword = children.next().expect("command keyword");
170 assert!(command_keyword.element().kind() == SyntaxKind::CommandKeyword);
171 (&command_keyword).write(stream, config);
172 stream.end_word();
173
174 let open_delimiter = children.next().expect("open delimiter");
175 match open_delimiter.element().kind() {
176 SyntaxKind::OpenBrace => {
177 stream.push_literal_in_place_of_token(
178 open_delimiter
179 .element()
180 .as_token()
181 .expect("open brace should be token"),
182 "<<<".to_string(),
183 );
184 }
185 SyntaxKind::OpenHeredoc => {
186 (&open_delimiter).write(stream, config);
187 }
188 _ => {
189 unreachable!(
190 "unexpected open delimiter in command section: {:?}",
191 open_delimiter.element().kind()
192 );
193 }
194 }
195
196 let parts = element
197 .element()
198 .as_node()
199 .expect("command section node")
200 .as_command_section()
201 .expect("command section")
202 .strip_whitespace();
203 match parts {
204 None => {
205 for child in children {
209 match child.element().kind() {
210 SyntaxKind::CloseBrace => {
211 stream.push_literal_in_place_of_token(
212 child
213 .element()
214 .as_token()
215 .expect("close brace should be token"),
216 ">>>".to_string(),
217 );
218 }
219 SyntaxKind::CloseHeredoc => {
220 (&child).write(stream, config);
221 }
222 SyntaxKind::LiteralCommandText | SyntaxKind::PlaceholderNode => {
223 (&child).write(stream, config);
224 }
225 _ => {
226 unreachable!(
227 "unexpected child in command section: {:?}",
228 child.element().kind()
229 );
230 }
231 }
232 }
233 }
234 Some(parts) => {
235 stream.increment_indent();
238
239 let mut bash_indent: Option<Rc<String>> = None;
240 for (part, child) in parts.iter().zip(children.by_ref()) {
241 match part {
242 StrippedCommandPart::Text(text) => {
243 let mut lines = text.lines().enumerate().peekable();
245 while let Some((i, line)) = lines.next() {
246 if i > 0 {
247 bash_indent = None;
248 stream.end_line();
249 if lines.peek().is_none() {
250 bash_indent = Some(
252 line.chars()
253 .take_while(|c| matches!(c, ' ' | '\t'))
254 .collect::<String>()
255 .into(),
256 );
257 }
258 }
259 stream.push_literal(line.to_owned(), SyntaxKind::LiteralCommandText);
260 }
261
262 if text.ends_with('\n') {
263 bash_indent = None;
264 stream.end_line();
265 }
266 }
267 StrippedCommandPart::Placeholder(_) => {
268 if let Some(ref temp_indent) = bash_indent
269 && !temp_indent.is_empty()
270 {
271 stream.push(PreToken::TempIndentStart(temp_indent.clone()));
272 (&child).write(stream, config);
273 stream.push(PreToken::TempIndentEnd);
274 } else {
275 (&child).write(stream, config);
276 }
277 }
278 }
279 }
280
281 stream.decrement_indent();
282
283 for child in children {
284 match child.element().kind() {
285 SyntaxKind::CloseBrace => {
286 stream.push_literal_in_place_of_token(
287 child
288 .element()
289 .as_token()
290 .expect("close brace should be token"),
291 ">>>".to_string(),
292 );
293 }
294 SyntaxKind::CloseHeredoc => {
295 (&child).write(stream, config);
296 }
297 _ => {
298 unreachable!(
299 "unexpected child in command section: {:?}",
300 child.element().kind()
301 );
302 }
303 }
304 }
305 }
306 }
307 stream.end_line();
308}
309
310pub fn format_requirements_item(
316 element: &FormatElement,
317 stream: &mut TokenStream<PreToken>,
318 config: &Config,
319) {
320 let mut children = element.children().expect("requirements item children");
321
322 let name = children.next().expect("requirements item name");
323 assert!(name.element().kind() == SyntaxKind::Ident);
324 (&name).write(stream, config);
325
326 let colon = children.next().expect("requirements item colon");
327 assert!(colon.element().kind() == SyntaxKind::Colon);
328 (&colon).write(stream, config);
329 stream.end_word();
330
331 let value = children.next().expect("requirements item value");
332 (&value).write(stream, config);
333}
334
335pub fn format_requirements_section(
341 element: &FormatElement,
342 stream: &mut TokenStream<PreToken>,
343 config: &Config,
344) {
345 let mut children = element.children().expect("requirements section children");
346
347 let requirements_keyword = children.next().expect("requirements keyword");
348 assert!(requirements_keyword.element().kind() == SyntaxKind::RequirementsKeyword);
349 (&requirements_keyword).write(stream, config);
350 stream.end_word();
351
352 let open_brace = children.next().expect("open brace");
353 assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
354 (&open_brace).write(stream, config);
355 stream.increment_indent();
356
357 let mut items = Vec::new();
358 let mut close_brace = None;
359
360 for child in children {
361 match child.element().kind() {
362 SyntaxKind::RequirementsItemNode => {
363 items.push(child.clone());
364 }
365 SyntaxKind::CloseBrace => {
366 close_brace = Some(child.clone());
367 }
368 _ => {
369 unreachable!(
370 "unexpected child in requirements section: {:?}",
371 child.element().kind()
372 );
373 }
374 }
375 }
376
377 for item in items {
378 (&item).write(stream, config);
379 stream.end_line();
380 }
381
382 stream.decrement_indent();
383 (&close_brace.expect("requirements close brace")).write(stream, config);
384 stream.end_line();
385}
386
387pub fn format_task_hints_item(
393 element: &FormatElement,
394 stream: &mut TokenStream<PreToken>,
395 config: &Config,
396) {
397 let mut children = element.children().expect("task hints item children");
398
399 let name = children.next().expect("task hints item name");
400 assert!(name.element().kind() == SyntaxKind::Ident);
401 (&name).write(stream, config);
402
403 let colon = children.next().expect("task hints item colon");
404 assert!(colon.element().kind() == SyntaxKind::Colon);
405 (&colon).write(stream, config);
406 stream.end_word();
407
408 let value = children.next().expect("task hints item value");
409 (&value).write(stream, config);
410
411 assert!(children.next().is_none());
412}
413
414pub fn format_runtime_item(
420 element: &FormatElement,
421 stream: &mut TokenStream<PreToken>,
422 config: &Config,
423) {
424 let mut children = element.children().expect("runtime item children");
425
426 let name = children.next().expect("runtime item name");
427 assert!(name.element().kind() == SyntaxKind::Ident);
428 (&name).write(stream, config);
429
430 let colon = children.next().expect("runtime item colon");
431 assert!(colon.element().kind() == SyntaxKind::Colon);
432 (&colon).write(stream, config);
433 stream.end_word();
434
435 let value = children.next().expect("runtime item value");
436 (&value).write(stream, config);
437}
438
439pub fn format_runtime_section(
445 element: &FormatElement,
446 stream: &mut TokenStream<PreToken>,
447 config: &Config,
448) {
449 let mut children = element.children().expect("runtime section children");
450
451 let runtime_keyword = children.next().expect("runtime keyword");
452 assert!(runtime_keyword.element().kind() == SyntaxKind::RuntimeKeyword);
453 (&runtime_keyword).write(stream, config);
454 stream.end_word();
455
456 let open_brace = children.next().expect("open brace");
457 assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
458 (&open_brace).write(stream, config);
459 stream.increment_indent();
460
461 let mut items = Vec::new();
462 let mut close_brace = None;
463
464 for child in children {
465 match child.element().kind() {
466 SyntaxKind::RuntimeItemNode => {
467 items.push(child.clone());
468 }
469 SyntaxKind::CloseBrace => {
470 close_brace = Some(child.clone());
471 }
472 _ => {
473 unreachable!(
474 "unexpected child in runtime section: {:?}",
475 child.element().kind()
476 );
477 }
478 }
479 }
480
481 for item in items {
482 (&item).write(stream, config);
483 stream.end_line();
484 }
485
486 stream.decrement_indent();
487 (&close_brace.expect("runtime close brace")).write(stream, config);
488 stream.end_line();
489}
490
491pub fn format_task_hints_section(
497 element: &FormatElement,
498 stream: &mut TokenStream<PreToken>,
499 config: &Config,
500) {
501 let mut children = element.children().expect("task hints section children");
502
503 let hints_keyword = children.next().expect("hints keyword");
504 assert!(hints_keyword.element().kind() == SyntaxKind::HintsKeyword);
505 (&hints_keyword).write(stream, config);
506 stream.end_word();
507
508 let open_brace = children.next().expect("open brace");
509 assert!(open_brace.element().kind() == SyntaxKind::OpenBrace);
510 (&open_brace).write(stream, config);
511 stream.increment_indent();
512
513 let mut items = Vec::new();
514 let mut close_brace = None;
515
516 for child in children {
517 match child.element().kind() {
518 SyntaxKind::TaskHintsItemNode => {
519 items.push(child.clone());
520 }
521 SyntaxKind::CloseBrace => {
522 close_brace = Some(child.clone());
523 }
524 _ => {
525 unreachable!(
526 "unexpected child in task hints section: {:?}",
527 child.element().kind()
528 );
529 }
530 }
531 }
532
533 for item in items {
534 (&item).write(stream, config);
535 stream.end_line();
536 }
537
538 stream.decrement_indent();
539 (&close_brace.expect("task hints close brace")).write(stream, config);
540 stream.end_line();
541}