wdl_ast/v1/
workflow.rs

1//! V1 AST representation for workflows.
2
3use std::fmt;
4
5use rowan::NodeOrToken;
6use wdl_grammar::SupportedVersion;
7use wdl_grammar::version::V1;
8
9use super::BoundDecl;
10use super::Expr;
11use super::InputSection;
12use super::LiteralBoolean;
13use super::LiteralFloat;
14use super::LiteralInteger;
15use super::LiteralString;
16use super::MetadataSection;
17use super::MetadataValue;
18use super::OutputSection;
19use super::ParameterMetadataSection;
20use crate::AstNode;
21use crate::AstToken;
22use crate::Ident;
23use crate::SyntaxKind;
24use crate::SyntaxNode;
25use crate::TreeNode;
26use crate::v1::display::write_input_section;
27use crate::v1::display::write_output_section;
28
29/// The name of the `allow_nested_inputs` workflow hint. Note that this
30/// is not a standard WDL v1.1 hint, but is used in WDL >=v1.2.
31pub const WORKFLOW_HINT_ALLOW_NESTED_INPUTS: &str = "allow_nested_inputs";
32
33/// The alias of the `allow_nested_inputs` workflow hint (e.g.
34/// `allowNestedInputs`). Note that in WDL v1.1, this is the only
35/// form of the hint.
36pub const WORKFLOW_HINT_ALLOW_NESTED_INPUTS_ALIAS: &str = "allowNestedInputs";
37
38/// The set of all valid workflow hints section keys.
39pub const WORKFLOW_HINT_KEYS: &[(&str, &str)] = &[(
40    WORKFLOW_HINT_ALLOW_NESTED_INPUTS,
41    "If `true`, allows nested input objects for the workflow.",
42)];
43
44/// Represents a workflow definition.
45#[derive(Clone, Debug, PartialEq, Eq)]
46pub struct WorkflowDefinition<N: TreeNode = SyntaxNode>(N);
47
48impl<N: TreeNode> WorkflowDefinition<N> {
49    /// Gets the name of the workflow.
50    pub fn name(&self) -> Ident<N::Token> {
51        self.token().expect("workflow should have a name")
52    }
53
54    /// Gets the items of the workflow.
55    pub fn items(&self) -> impl Iterator<Item = WorkflowItem<N>> + use<'_, N> {
56        WorkflowItem::children(&self.0)
57    }
58
59    /// Gets the input section of the workflow.
60    pub fn input(&self) -> Option<InputSection<N>> {
61        self.child()
62    }
63
64    /// Gets the output section of the workflow.
65    pub fn output(&self) -> Option<OutputSection<N>> {
66        self.child()
67    }
68
69    /// Gets the statements of the workflow.
70    pub fn statements(&self) -> impl Iterator<Item = WorkflowStatement<N>> + use<'_, N> {
71        WorkflowStatement::children(&self.0)
72    }
73
74    /// Gets the metadata section of the workflow.
75    pub fn metadata(&self) -> Option<MetadataSection<N>> {
76        self.child()
77    }
78
79    /// Gets the parameter section of the workflow.
80    pub fn parameter_metadata(&self) -> Option<ParameterMetadataSection<N>> {
81        self.child()
82    }
83
84    /// Gets the hints section of the workflow.
85    pub fn hints(&self) -> Option<WorkflowHintsSection<N>> {
86        self.child()
87    }
88
89    /// Gets the private declarations of the workflow.
90    pub fn declarations(&self) -> impl Iterator<Item = BoundDecl<N>> + use<'_, N> {
91        self.children()
92    }
93
94    /// Determines if the workflow definition allows nested inputs.
95    pub fn allows_nested_inputs(&self, version: SupportedVersion) -> bool {
96        match version {
97            SupportedVersion::V1(V1::Zero) => return true,
98            SupportedVersion::V1(V1::One) => {
99                // Fall through to below
100            }
101            SupportedVersion::V1(V1::Two) => {
102                // Check the hints section
103                let allow = self.hints().and_then(|s| {
104                    s.items().find_map(|i| {
105                        let name = i.name();
106                        if name.text() == WORKFLOW_HINT_ALLOW_NESTED_INPUTS
107                            || name.text() == WORKFLOW_HINT_ALLOW_NESTED_INPUTS_ALIAS
108                        {
109                            match i.value() {
110                                WorkflowHintsItemValue::Boolean(v) => Some(v.value()),
111                                _ => None,
112                            }
113                        } else {
114                            None
115                        }
116                    })
117                });
118
119                if let Some(allow) = allow {
120                    return allow;
121                }
122
123                // Fall through to below
124            }
125            _ => return false,
126        }
127
128        // Check the metadata section
129        self.metadata()
130            .and_then(|s| {
131                s.items().find_map(|i| {
132                    if i.name().text() == WORKFLOW_HINT_ALLOW_NESTED_INPUTS_ALIAS {
133                        match i.value() {
134                            MetadataValue::Boolean(v) => Some(v.value()),
135                            _ => None,
136                        }
137                    } else {
138                        None
139                    }
140                })
141            })
142            .unwrap_or(false)
143    }
144
145    /// Writes a Markdown formatted description of the workflow.
146    pub fn markdown_description(&self, f: &mut impl fmt::Write) -> fmt::Result {
147        writeln!(f, "```wdl\nworkflow {}\n```\n---", self.name().text())?;
148
149        if let Some(meta) = self.metadata()
150            && let Some(desc) = meta.items().find(|i| i.name().text() == "description")
151            && let MetadataValue::String(s) = desc.value()
152            && let Some(text) = s.text()
153        {
154            writeln!(f, "# {}\n", text.text())?;
155        }
156
157        write_input_section(f, self.input().as_ref(), self.parameter_metadata().as_ref())?;
158        write_output_section(
159            f,
160            self.output().as_ref(),
161            self.parameter_metadata().as_ref(),
162        )?;
163
164        Ok(())
165    }
166}
167
168impl<N: TreeNode> AstNode<N> for WorkflowDefinition<N> {
169    fn can_cast(kind: SyntaxKind) -> bool {
170        kind == SyntaxKind::WorkflowDefinitionNode
171    }
172
173    fn cast(inner: N) -> Option<Self> {
174        match inner.kind() {
175            SyntaxKind::WorkflowDefinitionNode => Some(Self(inner)),
176            _ => None,
177        }
178    }
179
180    fn inner(&self) -> &N {
181        &self.0
182    }
183}
184
185/// Represents an item in a workflow definition.
186#[derive(Clone, Debug, PartialEq, Eq)]
187pub enum WorkflowItem<N: TreeNode = SyntaxNode> {
188    /// The item is an input section.
189    Input(InputSection<N>),
190    /// The item is an output section.
191    Output(OutputSection<N>),
192    /// The item is a conditional statement.
193    Conditional(ConditionalStatement<N>),
194    /// The item is a scatter statement.
195    Scatter(ScatterStatement<N>),
196    /// The item is a call statement.
197    Call(CallStatement<N>),
198    /// The item is a metadata section.
199    Metadata(MetadataSection<N>),
200    /// The item is a parameter meta section.
201    ParameterMetadata(ParameterMetadataSection<N>),
202    /// The item is a workflow hints section.
203    Hints(WorkflowHintsSection<N>),
204    /// The item is a private bound declaration.
205    Declaration(BoundDecl<N>),
206}
207
208impl<N: TreeNode> WorkflowItem<N> {
209    /// Returns whether or not the given syntax kind can be cast to
210    /// [`WorkflowItem`].
211    pub fn can_cast(kind: SyntaxKind) -> bool {
212        matches!(
213            kind,
214            SyntaxKind::InputSectionNode
215                | SyntaxKind::OutputSectionNode
216                | SyntaxKind::ConditionalStatementNode
217                | SyntaxKind::ScatterStatementNode
218                | SyntaxKind::CallStatementNode
219                | SyntaxKind::MetadataSectionNode
220                | SyntaxKind::ParameterMetadataSectionNode
221                | SyntaxKind::WorkflowHintsSectionNode
222                | SyntaxKind::BoundDeclNode
223        )
224    }
225
226    /// Casts the given node to [`WorkflowItem`].
227    ///
228    /// Returns `None` if the node cannot be cast.
229    pub fn cast(inner: N) -> Option<Self> {
230        match inner.kind() {
231            SyntaxKind::InputSectionNode => Some(Self::Input(
232                InputSection::cast(inner).expect("input section to cast"),
233            )),
234            SyntaxKind::OutputSectionNode => Some(Self::Output(
235                OutputSection::cast(inner).expect("output section to cast"),
236            )),
237            SyntaxKind::ConditionalStatementNode => Some(Self::Conditional(
238                ConditionalStatement::cast(inner).expect("conditional statement to cast"),
239            )),
240            SyntaxKind::ScatterStatementNode => Some(Self::Scatter(
241                ScatterStatement::cast(inner).expect("scatter statement to cast"),
242            )),
243            SyntaxKind::CallStatementNode => Some(Self::Call(
244                CallStatement::cast(inner).expect("call statement to cast"),
245            )),
246            SyntaxKind::MetadataSectionNode => Some(Self::Metadata(
247                MetadataSection::cast(inner).expect("metadata section to cast"),
248            )),
249            SyntaxKind::ParameterMetadataSectionNode => Some(Self::ParameterMetadata(
250                ParameterMetadataSection::cast(inner).expect("parameter metadata section to cast"),
251            )),
252            SyntaxKind::WorkflowHintsSectionNode => Some(Self::Hints(
253                WorkflowHintsSection::cast(inner).expect("workflow hints section to cast"),
254            )),
255            SyntaxKind::BoundDeclNode => Some(Self::Declaration(
256                BoundDecl::cast(inner).expect("bound decl to cast"),
257            )),
258            _ => None,
259        }
260    }
261
262    /// Gets a reference to the inner node.
263    pub fn inner(&self) -> &N {
264        match self {
265            Self::Input(element) => element.inner(),
266            Self::Output(element) => element.inner(),
267            Self::Conditional(element) => element.inner(),
268            Self::Scatter(element) => element.inner(),
269            Self::Call(element) => element.inner(),
270            Self::Metadata(element) => element.inner(),
271            Self::ParameterMetadata(element) => element.inner(),
272            Self::Hints(element) => element.inner(),
273            Self::Declaration(element) => element.inner(),
274        }
275    }
276
277    /// Attempts to get a reference to the inner [`InputSection`].
278    ///
279    /// * If `self` is a [`WorkflowItem::Input`], then a reference to the inner
280    ///   [`InputSection`] is returned wrapped in [`Some`].
281    /// * Else, [`None`] is returned.
282    pub fn as_input_section(&self) -> Option<&InputSection<N>> {
283        match self {
284            Self::Input(s) => Some(s),
285            _ => None,
286        }
287    }
288
289    /// Consumes `self` and attempts to return the inner [`InputSection`].
290    ///
291    /// * If `self` is a [`WorkflowItem::Input`], then the inner
292    ///   [`InputSection`] is returned wrapped in [`Some`].
293    /// * Else, [`None`] is returned.
294    pub fn into_input_section(self) -> Option<InputSection<N>> {
295        match self {
296            Self::Input(s) => Some(s),
297            _ => None,
298        }
299    }
300
301    /// Attempts to get a reference to the inner [`OutputSection`].
302    ///
303    /// * If `self` is a [`WorkflowItem::Output`], then a reference to the inner
304    ///   [`OutputSection`] is returned wrapped in [`Some`].
305    /// * Else, [`None`] is returned.
306    pub fn as_output_section(&self) -> Option<&OutputSection<N>> {
307        match self {
308            Self::Output(s) => Some(s),
309            _ => None,
310        }
311    }
312
313    /// Consumes `self` and attempts to return the inner [`OutputSection`].
314    ///
315    /// * If `self` is a [`WorkflowItem::Output`], then the inner
316    ///   [`OutputSection`] is returned wrapped in [`Some`].
317    /// * Else, [`None`] is returned.
318    pub fn into_output_section(self) -> Option<OutputSection<N>> {
319        match self {
320            Self::Output(s) => Some(s),
321            _ => None,
322        }
323    }
324
325    /// Attempts to get a reference to the inner [`ConditionalStatement`].
326    ///
327    /// * If `self` is a [`WorkflowItem::Conditional`], then a reference to the
328    ///   inner [`ConditionalStatement`] is returned wrapped in [`Some`].
329    /// * Else, [`None`] is returned.
330    pub fn as_conditional(&self) -> Option<&ConditionalStatement<N>> {
331        match self {
332            Self::Conditional(s) => Some(s),
333            _ => None,
334        }
335    }
336
337    /// Consumes `self` and attempts to return the inner
338    /// [`ConditionalStatement`].
339    ///
340    /// * If `self` is a [`WorkflowItem::Conditional`], then the inner
341    ///   [`ConditionalStatement`] is returned wrapped in [`Some`].
342    /// * Else, [`None`] is returned.
343    pub fn into_conditional(self) -> Option<ConditionalStatement<N>> {
344        match self {
345            Self::Conditional(s) => Some(s),
346            _ => None,
347        }
348    }
349
350    /// Attempts to get a reference to the inner [`ScatterStatement`].
351    ///
352    /// * If `self` is a [`WorkflowItem::Scatter`], then a reference to the
353    ///   inner [`ScatterStatement`] is returned wrapped in [`Some`].
354    /// * Else, [`None`] is returned.
355    pub fn as_scatter(&self) -> Option<&ScatterStatement<N>> {
356        match self {
357            Self::Scatter(s) => Some(s),
358            _ => None,
359        }
360    }
361
362    /// Consumes `self` and attempts to return the inner
363    /// [`ScatterStatement`].
364    ///
365    /// * If `self` is a [`WorkflowItem::Scatter`], then the inner
366    ///   [`ScatterStatement`] is returned wrapped in [`Some`].
367    /// * Else, [`None`] is returned.
368    pub fn into_scatter(self) -> Option<ScatterStatement<N>> {
369        match self {
370            Self::Scatter(s) => Some(s),
371            _ => None,
372        }
373    }
374
375    /// Attempts to get a reference to the inner [`CallStatement`].
376    ///
377    /// * If `self` is a [`WorkflowItem::Call`], then a reference to the inner
378    ///   [`CallStatement`] is returned wrapped in [`Some`].
379    /// * Else, [`None`] is returned.
380    pub fn as_call(&self) -> Option<&CallStatement<N>> {
381        match self {
382            Self::Call(s) => Some(s),
383            _ => None,
384        }
385    }
386
387    /// Consumes `self` and attempts to return the inner [`CallStatement`].
388    ///
389    /// * If `self` is a [`WorkflowItem::Call`], then the inner
390    ///   [`CallStatement`] is returned wrapped in [`Some`].
391    /// * Else, [`None`] is returned.
392    pub fn into_call(self) -> Option<CallStatement<N>> {
393        match self {
394            Self::Call(s) => Some(s),
395            _ => None,
396        }
397    }
398
399    /// Attempts to get a reference to the inner [`MetadataSection`].
400    ///
401    /// * If `self` is a [`WorkflowItem::Metadata`], then a reference to the
402    ///   inner [`MetadataSection`] is returned wrapped in [`Some`].
403    /// * Else, [`None`] is returned.
404    pub fn as_metadata_section(&self) -> Option<&MetadataSection<N>> {
405        match self {
406            Self::Metadata(s) => Some(s),
407            _ => None,
408        }
409    }
410
411    /// Consumes `self` and attempts to return the inner [`MetadataSection`].
412    ///
413    /// * If `self` is a [`WorkflowItem::Metadata`], then the inner
414    ///   [`MetadataSection`] is returned wrapped in [`Some`].
415    /// * Else, [`None`] is returned.
416    pub fn into_metadata_section(self) -> Option<MetadataSection<N>> {
417        match self {
418            Self::Metadata(s) => Some(s),
419            _ => None,
420        }
421    }
422
423    /// Attempts to get a reference to the inner [`ParameterMetadataSection`].
424    ///
425    /// * If `self` is a [`WorkflowItem::ParameterMetadata`], then a reference
426    ///   to the inner [`ParameterMetadataSection`] is returned wrapped in
427    ///   [`Some`].
428    /// * Else, [`None`] is returned.
429    pub fn as_parameter_metadata_section(&self) -> Option<&ParameterMetadataSection<N>> {
430        match self {
431            Self::ParameterMetadata(s) => Some(s),
432            _ => None,
433        }
434    }
435
436    /// Consumes `self` and attempts to return the inner
437    /// [`ParameterMetadataSection`].
438    ///
439    /// * If `self` is a [`WorkflowItem::ParameterMetadata`], then the inner
440    ///   [`ParameterMetadataSection`] is returned wrapped in [`Some`].
441    /// * Else, [`None`] is returned.
442    pub fn into_parameter_metadata_section(self) -> Option<ParameterMetadataSection<N>> {
443        match self {
444            Self::ParameterMetadata(s) => Some(s),
445            _ => None,
446        }
447    }
448
449    /// Attempts to get a reference to the inner [`WorkflowHintsSection`].
450    ///
451    /// * If `self` is a [`WorkflowItem::Hints`], then a reference to the inner
452    ///   [`WorkflowHintsSection`] is returned wrapped in [`Some`].
453    /// * Else, [`None`] is returned.
454    pub fn as_hints_section(&self) -> Option<&WorkflowHintsSection<N>> {
455        match self {
456            Self::Hints(s) => Some(s),
457            _ => None,
458        }
459    }
460
461    /// Consumes `self` and attempts to return the inner
462    /// [`WorkflowHintsSection`].
463    ///
464    /// * If `self` is a [`WorkflowItem::Hints`], then the inner
465    ///   [`WorkflowHintsSection`] is returned wrapped in [`Some`].
466    /// * Else, [`None`] is returned.
467    pub fn into_hints_section(self) -> Option<WorkflowHintsSection<N>> {
468        match self {
469            Self::Hints(s) => Some(s),
470            _ => None,
471        }
472    }
473
474    /// Attempts to get a reference to the inner [`BoundDecl`].
475    ///
476    /// * If `self` is a [`WorkflowItem::Declaration`], then a reference to the
477    ///   inner [`BoundDecl`] is returned wrapped in [`Some`].
478    /// * Else, [`None`] is returned.
479    pub fn as_declaration(&self) -> Option<&BoundDecl<N>> {
480        match self {
481            Self::Declaration(d) => Some(d),
482            _ => None,
483        }
484    }
485
486    /// Consumes `self` and attempts to return the inner [`BoundDecl`].
487    ///
488    /// * If `self` is a [`WorkflowItem::Declaration`], then the inner
489    ///   [`BoundDecl`] is returned wrapped in [`Some`].
490    /// * Else, [`None`] is returned.
491    pub fn into_declaration(self) -> Option<BoundDecl<N>> {
492        match self {
493            Self::Declaration(d) => Some(d),
494            _ => None,
495        }
496    }
497
498    /// Finds the first child that can be cast to a [`WorkflowItem`].
499    pub fn child(node: &N) -> Option<Self> {
500        node.children().find_map(Self::cast)
501    }
502
503    /// Finds all children that can be cast to a [`WorkflowItem`].
504    pub fn children(node: &N) -> impl Iterator<Item = Self> + use<'_, N> {
505        node.children().filter_map(Self::cast)
506    }
507}
508
509/// Represents a statement in a workflow definition.
510#[derive(Clone, Debug, PartialEq, Eq)]
511pub enum WorkflowStatement<N: TreeNode = SyntaxNode> {
512    /// The statement is a conditional statement.
513    Conditional(ConditionalStatement<N>),
514    /// The statement is a scatter statement.
515    Scatter(ScatterStatement<N>),
516    /// The statement is a call statement.
517    Call(CallStatement<N>),
518    /// The statement is a private bound declaration.
519    Declaration(BoundDecl<N>),
520}
521
522impl<N: TreeNode> WorkflowStatement<N> {
523    /// Returns whether or not the given syntax kind can be cast to
524    /// [`WorkflowStatement`].
525    pub fn can_cast(kind: SyntaxKind) -> bool {
526        matches!(
527            kind,
528            SyntaxKind::ConditionalStatementNode
529                | SyntaxKind::ScatterStatementNode
530                | SyntaxKind::CallStatementNode
531                | SyntaxKind::BoundDeclNode
532        )
533    }
534
535    /// Casts the given node to [`WorkflowStatement`].
536    ///
537    /// Returns `None` if the node cannot be cast.
538    pub fn cast(inner: N) -> Option<Self> {
539        match inner.kind() {
540            SyntaxKind::ConditionalStatementNode => Some(Self::Conditional(
541                ConditionalStatement::cast(inner).expect("conditional statement to cast"),
542            )),
543            SyntaxKind::ScatterStatementNode => Some(Self::Scatter(
544                ScatterStatement::cast(inner).expect("scatter statement to cast"),
545            )),
546            SyntaxKind::CallStatementNode => Some(Self::Call(
547                CallStatement::cast(inner).expect("call statement to cast"),
548            )),
549            SyntaxKind::BoundDeclNode => Some(Self::Declaration(
550                BoundDecl::cast(inner).expect("bound decl to cast"),
551            )),
552            _ => None,
553        }
554    }
555
556    /// Gets a reference to the inner node.
557    pub fn inner(&self) -> &N {
558        match self {
559            Self::Conditional(s) => s.inner(),
560            Self::Scatter(s) => s.inner(),
561            Self::Call(s) => s.inner(),
562            Self::Declaration(s) => s.inner(),
563        }
564    }
565
566    /// Attempts to get a reference to the inner [`ConditionalStatement`].
567    ///
568    /// * If `self` is a [`WorkflowStatement::Conditional`], then a reference to
569    ///   the inner [`ConditionalStatement`] is returned wrapped in [`Some`].
570    /// * Else, [`None`] is returned.
571    pub fn as_conditional(&self) -> Option<&ConditionalStatement<N>> {
572        match self {
573            Self::Conditional(s) => Some(s),
574            _ => None,
575        }
576    }
577
578    /// Consumes `self` and attempts to return the inner
579    /// [`ConditionalStatement`].
580    ///
581    /// * If `self` is a [`WorkflowStatement::Conditional`], then the inner
582    ///   [`ConditionalStatement`] is returned wrapped in [`Some`].
583    /// * Else, [`None`] is returned.
584    pub fn into_conditional(self) -> Option<ConditionalStatement<N>> {
585        match self {
586            Self::Conditional(s) => Some(s),
587            _ => None,
588        }
589    }
590
591    /// Unwraps the statement into a conditional statement.
592    ///
593    /// # Panics
594    ///
595    /// Panics if the statement is not a conditional statement.
596    pub fn unwrap_conditional(self) -> ConditionalStatement<N> {
597        match self {
598            Self::Conditional(s) => s,
599            _ => panic!("not a conditional statement"),
600        }
601    }
602
603    /// Attempts to get a reference to the inner [`ScatterStatement`].
604    ///
605    /// * If `self` is a [`WorkflowStatement::Scatter`], then a reference to the
606    ///   inner [`ScatterStatement`] is returned wrapped in [`Some`].
607    /// * Else, [`None`] is returned.
608    pub fn as_scatter(&self) -> Option<&ScatterStatement<N>> {
609        match self {
610            Self::Scatter(s) => Some(s),
611            _ => None,
612        }
613    }
614
615    /// Consumes `self` and attempts to return the inner
616    /// [`ScatterStatement`].
617    ///
618    /// * If `self` is a [`WorkflowStatement::Scatter`], then the inner
619    ///   [`ScatterStatement`] is returned wrapped in [`Some`].
620    /// * Else, [`None`] is returned.
621    pub fn into_scatter(self) -> Option<ScatterStatement<N>> {
622        match self {
623            Self::Scatter(s) => Some(s),
624            _ => None,
625        }
626    }
627
628    /// Unwraps the statement into a scatter statement.
629    ///
630    /// # Panics
631    ///
632    /// Panics if the statement is not a scatter statement.
633    pub fn unwrap_scatter(self) -> ScatterStatement<N> {
634        match self {
635            Self::Scatter(s) => s,
636            _ => panic!("not a scatter statement"),
637        }
638    }
639
640    /// Attempts to get a reference to the inner [`CallStatement`].
641    ///
642    /// * If `self` is a [`WorkflowStatement::Call`], then a reference to the
643    ///   inner [`CallStatement`] is returned wrapped in [`Some`].
644    /// * Else, [`None`] is returned.
645    pub fn as_call(&self) -> Option<&CallStatement<N>> {
646        match self {
647            Self::Call(s) => Some(s),
648            _ => None,
649        }
650    }
651
652    /// Consumes `self` and attempts to return the inner
653    /// [`CallStatement`].
654    ///
655    /// * If `self` is a [`WorkflowStatement::Call`], then the inner
656    ///   [`CallStatement`] is returned wrapped in [`Some`].
657    /// * Else, [`None`] is returned.
658    pub fn into_call(self) -> Option<CallStatement<N>> {
659        match self {
660            Self::Call(s) => Some(s),
661            _ => None,
662        }
663    }
664
665    /// Unwraps the statement into a call statement.
666    ///
667    /// # Panics
668    ///
669    /// Panics if the statement is not a call statement.
670    pub fn unwrap_call(self) -> CallStatement<N> {
671        match self {
672            Self::Call(s) => s,
673            _ => panic!("not a call statement"),
674        }
675    }
676
677    /// Attempts to get a reference to the inner [`BoundDecl`].
678    ///
679    /// * If `self` is a [`WorkflowStatement::Declaration`], then a reference to
680    ///   the inner [`BoundDecl`] is returned wrapped in [`Some`].
681    /// * Else, [`None`] is returned.
682    pub fn as_declaration(&self) -> Option<&BoundDecl<N>> {
683        match self {
684            Self::Declaration(d) => Some(d),
685            _ => None,
686        }
687    }
688
689    /// Consumes `self` and attempts to return the inner
690    /// [`BoundDecl`].
691    ///
692    /// * If `self` is a [`WorkflowStatement::Declaration`], then the inner
693    ///   [`BoundDecl`] is returned wrapped in [`Some`].
694    /// * Else, [`None`] is returned.
695    pub fn into_declaration(self) -> Option<BoundDecl<N>> {
696        match self {
697            Self::Declaration(d) => Some(d),
698            _ => None,
699        }
700    }
701
702    /// Unwraps the statement into a bound declaration.
703    ///
704    /// # Panics
705    ///
706    /// Panics if the statement is not a bound declaration.
707    pub fn unwrap_declaration(self) -> BoundDecl<N> {
708        match self {
709            Self::Declaration(d) => d,
710            _ => panic!("not a bound declaration"),
711        }
712    }
713
714    /// Finds the first child that can be cast to a [`WorkflowStatement`].
715    pub fn child(node: &N) -> Option<Self> {
716        node.children().find_map(Self::cast)
717    }
718
719    /// Finds all children that can be cast to a [`WorkflowStatement`].
720    pub fn children(node: &N) -> impl Iterator<Item = Self> + use<'_, N> {
721        node.children().filter_map(Self::cast)
722    }
723}
724
725/// Represents a workflow conditional statement.
726#[derive(Clone, Debug, PartialEq, Eq)]
727pub struct ConditionalStatement<N: TreeNode = SyntaxNode>(N);
728
729impl<N: TreeNode> ConditionalStatement<N> {
730    /// Gets the expression of the conditional statement
731    pub fn expr(&self) -> Expr<N> {
732        Expr::child(&self.0).expect("expected a conditional expression")
733    }
734
735    /// Gets the statements of the conditional body.
736    pub fn statements(&self) -> impl Iterator<Item = WorkflowStatement<N>> + use<'_, N> {
737        WorkflowStatement::children(&self.0)
738    }
739}
740
741impl<N: TreeNode> AstNode<N> for ConditionalStatement<N> {
742    fn can_cast(kind: SyntaxKind) -> bool {
743        kind == SyntaxKind::ConditionalStatementNode
744    }
745
746    fn cast(inner: N) -> Option<Self> {
747        match inner.kind() {
748            SyntaxKind::ConditionalStatementNode => Some(Self(inner)),
749            _ => None,
750        }
751    }
752
753    fn inner(&self) -> &N {
754        &self.0
755    }
756}
757
758/// Represents a workflow scatter statement.
759#[derive(Clone, Debug, PartialEq, Eq)]
760pub struct ScatterStatement<N: TreeNode = SyntaxNode>(N);
761
762impl<N: TreeNode> ScatterStatement<N> {
763    /// Gets the scatter variable identifier.
764    pub fn variable(&self) -> Ident<N::Token> {
765        self.token()
766            .expect("expected a scatter variable identifier")
767    }
768
769    /// Gets the scatter expression.
770    pub fn expr(&self) -> Expr<N> {
771        Expr::child(&self.0).expect("expected a scatter expression")
772    }
773
774    /// Gets the statements of the scatter body.
775    pub fn statements(&self) -> impl Iterator<Item = WorkflowStatement<N>> + use<'_, N> {
776        WorkflowStatement::children(&self.0)
777    }
778}
779
780impl<N: TreeNode> AstNode<N> for ScatterStatement<N> {
781    fn can_cast(kind: SyntaxKind) -> bool {
782        kind == SyntaxKind::ScatterStatementNode
783    }
784
785    fn cast(inner: N) -> Option<Self> {
786        match inner.kind() {
787            SyntaxKind::ScatterStatementNode => Some(Self(inner)),
788            _ => None,
789        }
790    }
791
792    fn inner(&self) -> &N {
793        &self.0
794    }
795}
796
797/// Represents a workflow call statement.
798#[derive(Clone, Debug, PartialEq, Eq)]
799pub struct CallStatement<N: TreeNode = SyntaxNode>(N);
800
801impl<N: TreeNode> CallStatement<N> {
802    /// Gets the target of the call.
803    pub fn target(&self) -> CallTarget<N> {
804        self.child().expect("expected a call target")
805    }
806
807    /// Gets the optional alias for the call.
808    pub fn alias(&self) -> Option<CallAlias<N>> {
809        self.child()
810    }
811
812    /// Gets the after clauses for the call statement.
813    pub fn after(&self) -> impl Iterator<Item = CallAfter<N>> + use<'_, N> {
814        self.children()
815    }
816
817    /// Gets the inputs for the call statement.
818    pub fn inputs(&self) -> impl Iterator<Item = CallInputItem<N>> + use<'_, N> {
819        self.children()
820    }
821}
822
823impl<N: TreeNode> AstNode<N> for CallStatement<N> {
824    fn can_cast(kind: SyntaxKind) -> bool {
825        kind == SyntaxKind::CallStatementNode
826    }
827
828    fn cast(inner: N) -> Option<Self> {
829        match inner.kind() {
830            SyntaxKind::CallStatementNode => Some(Self(inner)),
831            _ => None,
832        }
833    }
834
835    fn inner(&self) -> &N {
836        &self.0
837    }
838}
839
840/// Represents a target in a call statement.
841#[derive(Clone, Debug, PartialEq, Eq)]
842pub struct CallTarget<N: TreeNode = SyntaxNode>(N);
843
844impl<N: TreeNode> CallTarget<N> {
845    /// Gets an iterator of the names of the call target.
846    ///
847    /// The last name in the iteration is considered to be the task or workflow
848    /// being called.
849    pub fn names(&self) -> impl Iterator<Item = Ident<N::Token>> + use<'_, N> {
850        self.0
851            .children_with_tokens()
852            .filter_map(NodeOrToken::into_token)
853            .filter_map(Ident::cast)
854    }
855}
856
857impl<N: TreeNode> AstNode<N> for CallTarget<N> {
858    fn can_cast(kind: SyntaxKind) -> bool {
859        kind == SyntaxKind::CallTargetNode
860    }
861
862    fn cast(inner: N) -> Option<Self> {
863        match inner.kind() {
864            SyntaxKind::CallTargetNode => Some(Self(inner)),
865            _ => None,
866        }
867    }
868
869    fn inner(&self) -> &N {
870        &self.0
871    }
872}
873
874/// Represents an alias in a call statement.
875#[derive(Clone, Debug, PartialEq, Eq)]
876pub struct CallAlias<N: TreeNode = SyntaxNode>(N);
877
878impl<N: TreeNode> CallAlias<N> {
879    /// Gets the alias name.
880    pub fn name(&self) -> Ident<N::Token> {
881        self.token().expect("expected an alias identifier")
882    }
883}
884
885impl<N: TreeNode> AstNode<N> for CallAlias<N> {
886    fn can_cast(kind: SyntaxKind) -> bool {
887        kind == SyntaxKind::CallAliasNode
888    }
889
890    fn cast(inner: N) -> Option<Self> {
891        match inner.kind() {
892            SyntaxKind::CallAliasNode => Some(Self(inner)),
893            _ => None,
894        }
895    }
896
897    fn inner(&self) -> &N {
898        &self.0
899    }
900}
901
902/// Represents an after clause in a call statement.
903#[derive(Clone, Debug, PartialEq, Eq)]
904pub struct CallAfter<N: TreeNode = SyntaxNode>(N);
905
906impl<N: TreeNode> CallAfter<N> {
907    /// Gets the name from the `after` clause.
908    pub fn name(&self) -> Ident<N::Token> {
909        self.token().expect("expected an after identifier")
910    }
911}
912
913impl<N: TreeNode> AstNode<N> for CallAfter<N> {
914    fn can_cast(kind: SyntaxKind) -> bool {
915        kind == SyntaxKind::CallAfterNode
916    }
917
918    fn cast(inner: N) -> Option<Self> {
919        match inner.kind() {
920            SyntaxKind::CallAfterNode => Some(Self(inner)),
921            _ => None,
922        }
923    }
924
925    fn inner(&self) -> &N {
926        &self.0
927    }
928}
929
930/// Represents an input item in a call statement.
931#[derive(Clone, Debug, PartialEq, Eq)]
932pub struct CallInputItem<N: TreeNode = SyntaxNode>(N);
933
934impl<N: TreeNode> CallInputItem<N> {
935    /// Gets the name of the input.
936    pub fn name(&self) -> Ident<N::Token> {
937        self.token().expect("expected an input name")
938    }
939
940    /// The optional expression for the input.
941    pub fn expr(&self) -> Option<Expr<N>> {
942        Expr::child(&self.0)
943    }
944
945    /// Gets the call statement for the call input item.
946    pub fn parent(&self) -> CallStatement<N> {
947        <Self as AstNode<N>>::parent(self).expect("should have parent")
948    }
949
950    /// If a call input has the same name as a declaration from the current
951    /// scope, the name of the input may appear alone (without an expression) to
952    /// implicitly bind the value of that declaration.
953    ///
954    /// For example, if a `workflow` and `task` both have inputs `x` and `z` of
955    /// the same types, then `call mytask {x, y=b, z}` is equivalent to
956    /// `call mytask {x=x, y=b, z=z}`.
957    pub fn is_implicit_bind(&self) -> bool {
958        self.expr().is_none()
959    }
960}
961
962impl<N: TreeNode> AstNode<N> for CallInputItem<N> {
963    fn can_cast(kind: SyntaxKind) -> bool {
964        kind == SyntaxKind::CallInputItemNode
965    }
966
967    fn cast(inner: N) -> Option<Self> {
968        match inner.kind() {
969            SyntaxKind::CallInputItemNode => Some(Self(inner)),
970            _ => None,
971        }
972    }
973
974    fn inner(&self) -> &N {
975        &self.0
976    }
977}
978
979/// Represents a hints section in a workflow definition.
980#[derive(Clone, Debug, PartialEq, Eq)]
981pub struct WorkflowHintsSection<N: TreeNode = SyntaxNode>(N);
982
983impl<N: TreeNode> WorkflowHintsSection<N> {
984    /// Gets the items in the hints section.
985    pub fn items(&self) -> impl Iterator<Item = WorkflowHintsItem<N>> + use<'_, N> {
986        self.children()
987    }
988
989    /// Gets the parent of the hints section.
990    pub fn parent(&self) -> WorkflowDefinition<N> {
991        <Self as AstNode<N>>::parent(self).expect("should have parent")
992    }
993}
994
995impl<N: TreeNode> AstNode<N> for WorkflowHintsSection<N> {
996    fn can_cast(kind: SyntaxKind) -> bool {
997        kind == SyntaxKind::WorkflowHintsSectionNode
998    }
999
1000    fn cast(inner: N) -> Option<Self> {
1001        match inner.kind() {
1002            SyntaxKind::WorkflowHintsSectionNode => Some(Self(inner)),
1003            _ => None,
1004        }
1005    }
1006
1007    fn inner(&self) -> &N {
1008        &self.0
1009    }
1010}
1011
1012/// Represents an item in a workflow hints section.
1013#[derive(Clone, Debug, PartialEq, Eq)]
1014pub struct WorkflowHintsItem<N: TreeNode = SyntaxNode>(N);
1015
1016impl<N: TreeNode> WorkflowHintsItem<N> {
1017    /// Gets the name of the hints item.
1018    pub fn name(&self) -> Ident<N::Token> {
1019        self.token().expect("expected an item name")
1020    }
1021
1022    /// Gets the value of the hints item.
1023    pub fn value(&self) -> WorkflowHintsItemValue<N> {
1024        self.child().expect("expected an item value")
1025    }
1026}
1027
1028impl<N: TreeNode> AstNode<N> for WorkflowHintsItem<N> {
1029    fn can_cast(kind: SyntaxKind) -> bool {
1030        kind == SyntaxKind::WorkflowHintsItemNode
1031    }
1032
1033    fn cast(inner: N) -> Option<Self> {
1034        match inner.kind() {
1035            SyntaxKind::WorkflowHintsItemNode => Some(Self(inner)),
1036            _ => None,
1037        }
1038    }
1039
1040    fn inner(&self) -> &N {
1041        &self.0
1042    }
1043}
1044
1045/// Represents a workflow hints item value.
1046#[derive(Clone, Debug, PartialEq, Eq)]
1047pub enum WorkflowHintsItemValue<N: TreeNode = SyntaxNode> {
1048    /// The value is a literal boolean.
1049    Boolean(LiteralBoolean<N>),
1050    /// The value is a literal integer.
1051    Integer(LiteralInteger<N>),
1052    /// The value is a literal float.
1053    Float(LiteralFloat<N>),
1054    /// The value is a literal string.
1055    String(LiteralString<N>),
1056    /// The value is a literal object.
1057    Object(WorkflowHintsObject<N>),
1058    /// The value is a literal array.
1059    Array(WorkflowHintsArray<N>),
1060}
1061
1062impl<N: TreeNode> WorkflowHintsItemValue<N> {
1063    /// Unwraps the value into a boolean.
1064    ///
1065    /// # Panics
1066    ///
1067    /// Panics if the value is not a boolean.
1068    pub fn unwrap_boolean(self) -> LiteralBoolean<N> {
1069        match self {
1070            Self::Boolean(b) => b,
1071            _ => panic!("not a boolean"),
1072        }
1073    }
1074
1075    /// Unwraps the value into an integer.
1076    ///
1077    /// # Panics
1078    ///
1079    /// Panics if the value is not an integer.
1080    pub fn unwrap_integer(self) -> LiteralInteger<N> {
1081        match self {
1082            Self::Integer(i) => i,
1083            _ => panic!("not an integer"),
1084        }
1085    }
1086
1087    /// Unwraps the value into a float.
1088    ///
1089    /// # Panics
1090    ///
1091    /// Panics if the value is not a float.
1092    pub fn unwrap_float(self) -> LiteralFloat<N> {
1093        match self {
1094            Self::Float(f) => f,
1095            _ => panic!("not a float"),
1096        }
1097    }
1098
1099    /// Unwraps the value into a string.
1100    ///
1101    /// # Panics
1102    ///
1103    /// Panics if the value is not a string.
1104    pub fn unwrap_string(self) -> LiteralString<N> {
1105        match self {
1106            Self::String(s) => s,
1107            _ => panic!("not a string"),
1108        }
1109    }
1110
1111    /// Unwraps the value into an object.
1112    ///
1113    /// # Panics
1114    ///
1115    /// Panics if the value is not an object.
1116    pub fn unwrap_object(self) -> WorkflowHintsObject<N> {
1117        match self {
1118            Self::Object(o) => o,
1119            _ => panic!("not an object"),
1120        }
1121    }
1122
1123    /// Unwraps the value into an array.
1124    ///
1125    /// # Panics
1126    ///
1127    /// Panics if the value is not an array.
1128    pub fn unwrap_array(self) -> WorkflowHintsArray<N> {
1129        match self {
1130            Self::Array(a) => a,
1131            _ => panic!("not an array"),
1132        }
1133    }
1134}
1135
1136impl<N: TreeNode> AstNode<N> for WorkflowHintsItemValue<N> {
1137    fn can_cast(kind: SyntaxKind) -> bool {
1138        matches!(
1139            kind,
1140            SyntaxKind::LiteralBooleanNode
1141                | SyntaxKind::LiteralIntegerNode
1142                | SyntaxKind::LiteralFloatNode
1143                | SyntaxKind::LiteralStringNode
1144                | SyntaxKind::WorkflowHintsObjectNode
1145                | SyntaxKind::WorkflowHintsArrayNode
1146        )
1147    }
1148
1149    fn cast(inner: N) -> Option<Self> {
1150        match inner.kind() {
1151            SyntaxKind::LiteralBooleanNode => Some(Self::Boolean(LiteralBoolean(inner))),
1152            SyntaxKind::LiteralIntegerNode => Some(Self::Integer(LiteralInteger(inner))),
1153            SyntaxKind::LiteralFloatNode => Some(Self::Float(LiteralFloat(inner))),
1154            SyntaxKind::LiteralStringNode => Some(Self::String(LiteralString(inner))),
1155            SyntaxKind::WorkflowHintsObjectNode => Some(Self::Object(WorkflowHintsObject(inner))),
1156            SyntaxKind::WorkflowHintsArrayNode => Some(Self::Array(WorkflowHintsArray(inner))),
1157            _ => None,
1158        }
1159    }
1160
1161    fn inner(&self) -> &N {
1162        match self {
1163            Self::Boolean(b) => &b.0,
1164            Self::Integer(i) => &i.0,
1165            Self::Float(f) => &f.0,
1166            Self::String(s) => &s.0,
1167            Self::Object(o) => &o.0,
1168            Self::Array(a) => &a.0,
1169        }
1170    }
1171}
1172
1173/// Represents a workflow hints object.
1174#[derive(Clone, Debug, PartialEq, Eq)]
1175pub struct WorkflowHintsObject<N: TreeNode = SyntaxNode>(N);
1176
1177impl<N: TreeNode> WorkflowHintsObject<N> {
1178    /// Gets the items of the workflow hints object.
1179    pub fn items(&self) -> impl Iterator<Item = WorkflowHintsObjectItem<N>> + use<'_, N> {
1180        self.children()
1181    }
1182}
1183
1184impl<N: TreeNode> AstNode<N> for WorkflowHintsObject<N> {
1185    fn can_cast(kind: SyntaxKind) -> bool {
1186        kind == SyntaxKind::WorkflowHintsObjectNode
1187    }
1188
1189    fn cast(inner: N) -> Option<Self> {
1190        match inner.kind() {
1191            SyntaxKind::WorkflowHintsObjectNode => Some(Self(inner)),
1192            _ => None,
1193        }
1194    }
1195
1196    fn inner(&self) -> &N {
1197        &self.0
1198    }
1199}
1200
1201/// Represents a workflow hints object item.
1202#[derive(Clone, Debug, PartialEq, Eq)]
1203pub struct WorkflowHintsObjectItem<N: TreeNode = SyntaxNode>(N);
1204
1205impl<N: TreeNode> WorkflowHintsObjectItem<N> {
1206    /// Gets the name of the item.
1207    pub fn name(&self) -> Ident<N::Token> {
1208        self.token().expect("expected a name")
1209    }
1210
1211    /// Gets the value of the item.
1212    pub fn value(&self) -> WorkflowHintsItemValue<N> {
1213        self.child().expect("expected a value")
1214    }
1215}
1216
1217impl<N: TreeNode> AstNode<N> for WorkflowHintsObjectItem<N> {
1218    fn can_cast(kind: SyntaxKind) -> bool {
1219        kind == SyntaxKind::WorkflowHintsObjectItemNode
1220    }
1221
1222    fn cast(inner: N) -> Option<Self> {
1223        match inner.kind() {
1224            SyntaxKind::WorkflowHintsObjectItemNode => Some(Self(inner)),
1225            _ => None,
1226        }
1227    }
1228
1229    fn inner(&self) -> &N {
1230        &self.0
1231    }
1232}
1233
1234/// Represents a workflow hints array.
1235#[derive(Clone, Debug, PartialEq, Eq)]
1236pub struct WorkflowHintsArray<N: TreeNode = SyntaxNode>(N);
1237
1238impl<N: TreeNode> WorkflowHintsArray<N> {
1239    /// Gets the elements of the workflow hints array.
1240    pub fn elements(&self) -> impl Iterator<Item = WorkflowHintsItemValue<N>> + use<'_, N> {
1241        self.children()
1242    }
1243}
1244
1245impl<N: TreeNode> AstNode<N> for WorkflowHintsArray<N> {
1246    fn can_cast(kind: SyntaxKind) -> bool {
1247        kind == SyntaxKind::WorkflowHintsArrayNode
1248    }
1249
1250    fn cast(inner: N) -> Option<Self> {
1251        match inner.kind() {
1252            SyntaxKind::WorkflowHintsArrayNode => Some(Self(inner)),
1253            _ => None,
1254        }
1255    }
1256
1257    fn inner(&self) -> &N {
1258        &self.0
1259    }
1260}
1261
1262#[cfg(test)]
1263mod test {
1264    use super::*;
1265    use crate::Document;
1266
1267    #[test]
1268    fn workflows() {
1269        let (document, diagnostics) = Document::parse(
1270            r#"
1271version 1.1
1272
1273workflow test {
1274    input {
1275        String name
1276        Boolean do_thing
1277    }
1278
1279    output {
1280        String output = "hello, ~{name}!"
1281    }
1282
1283    if (do_thing) {
1284        call foo.my_task
1285
1286        scatter (a in [1, 2, 3]) {
1287            call my_task as my_task2 { input: a }
1288        }
1289    }
1290
1291    call my_task as my_task3 after my_task2 after my_task { input: a = 1 }
1292
1293    scatter (a in ["1", "2", "3"]) {
1294        # Do nothing
1295    }
1296
1297    meta {
1298        description: "a test"
1299        foo: null
1300    }
1301
1302    parameter_meta {
1303        name: {
1304            help: "a name to greet"
1305        }
1306    }
1307
1308    hints {
1309        foo: "bar"
1310    }
1311
1312    String x = "private"
1313}
1314"#,
1315        );
1316
1317        assert!(diagnostics.is_empty());
1318        let ast = document.ast();
1319        let ast = ast.as_v1().expect("should be a V1 AST");
1320        let workflows: Vec<_> = ast.workflows().collect();
1321        assert_eq!(workflows.len(), 1);
1322        assert_eq!(workflows[0].name().text(), "test");
1323
1324        // Workflow inputs
1325        let input = workflows[0]
1326            .input()
1327            .expect("workflow should have an input section");
1328        assert_eq!(input.parent().unwrap_workflow().name().text(), "test");
1329        let decls: Vec<_> = input.declarations().collect();
1330        assert_eq!(decls.len(), 2);
1331
1332        // First declaration
1333        assert_eq!(
1334            decls[0].clone().unwrap_unbound_decl().ty().to_string(),
1335            "String"
1336        );
1337        assert_eq!(decls[0].clone().unwrap_unbound_decl().name().text(), "name");
1338
1339        // Second declaration
1340        assert_eq!(
1341            decls[1].clone().unwrap_unbound_decl().ty().to_string(),
1342            "Boolean"
1343        );
1344        assert_eq!(
1345            decls[1].clone().unwrap_unbound_decl().name().text(),
1346            "do_thing"
1347        );
1348
1349        // Workflow outputs
1350        let output = workflows[0]
1351            .output()
1352            .expect("workflow should have an output section");
1353        assert_eq!(output.parent().unwrap_workflow().name().text(), "test");
1354        let decls: Vec<_> = output.declarations().collect();
1355        assert_eq!(decls.len(), 1);
1356
1357        // First declaration
1358        assert_eq!(decls[0].ty().to_string(), "String");
1359        assert_eq!(decls[0].name().text(), "output");
1360        let parts: Vec<_> = decls[0]
1361            .expr()
1362            .unwrap_literal()
1363            .unwrap_string()
1364            .parts()
1365            .collect();
1366        assert_eq!(parts.len(), 3);
1367        assert_eq!(parts[0].clone().unwrap_text().text(), "hello, ");
1368        assert_eq!(
1369            parts[1]
1370                .clone()
1371                .unwrap_placeholder()
1372                .expr()
1373                .unwrap_name_ref()
1374                .name()
1375                .text(),
1376            "name"
1377        );
1378        assert_eq!(parts[2].clone().unwrap_text().text(), "!");
1379
1380        // Workflow statements
1381        let statements: Vec<_> = workflows[0].statements().collect();
1382        assert_eq!(statements.len(), 4);
1383
1384        // First workflow statement
1385        let conditional = statements[0].clone().unwrap_conditional();
1386        assert_eq!(
1387            conditional.expr().unwrap_name_ref().name().text(),
1388            "do_thing"
1389        );
1390
1391        // Inner statements
1392        let inner: Vec<_> = conditional.statements().collect();
1393        assert_eq!(inner.len(), 2);
1394
1395        // First inner statement
1396        let call = inner[0].clone().unwrap_call();
1397        let names = call.target().names().collect::<Vec<_>>();
1398        assert_eq!(names.len(), 2);
1399        assert_eq!(names[0].text(), "foo");
1400        assert_eq!(names[1].text(), "my_task");
1401        assert!(call.alias().is_none());
1402        assert_eq!(call.after().count(), 0);
1403        assert_eq!(call.inputs().count(), 0);
1404
1405        // Second inner statement
1406        let scatter = inner[1].clone().unwrap_scatter();
1407        assert_eq!(scatter.variable().text(), "a");
1408        let elements: Vec<_> = scatter
1409            .expr()
1410            .unwrap_literal()
1411            .unwrap_array()
1412            .elements()
1413            .collect();
1414        assert_eq!(elements.len(), 3);
1415        assert_eq!(
1416            elements[0]
1417                .clone()
1418                .unwrap_literal()
1419                .unwrap_integer()
1420                .value()
1421                .unwrap(),
1422            1
1423        );
1424        assert_eq!(
1425            elements[1]
1426                .clone()
1427                .unwrap_literal()
1428                .unwrap_integer()
1429                .value()
1430                .unwrap(),
1431            2
1432        );
1433        assert_eq!(
1434            elements[2]
1435                .clone()
1436                .unwrap_literal()
1437                .unwrap_integer()
1438                .value()
1439                .unwrap(),
1440            3
1441        );
1442
1443        // Inner statements
1444        let inner: Vec<_> = scatter.statements().collect();
1445        assert_eq!(inner.len(), 1);
1446
1447        // First inner statement
1448        let call = inner[0].clone().unwrap_call();
1449        let names = call.target().names().collect::<Vec<_>>();
1450        assert_eq!(names.len(), 1);
1451        assert_eq!(names[0].text(), "my_task");
1452        assert_eq!(call.alias().unwrap().name().text(), "my_task2");
1453        assert_eq!(call.after().count(), 0);
1454        let inputs: Vec<_> = call.inputs().collect();
1455        assert_eq!(inputs.len(), 1);
1456        assert_eq!(inputs[0].name().text(), "a");
1457        assert!(inputs[0].expr().is_none());
1458
1459        // Second workflow statement
1460        let call = statements[1].clone().unwrap_call();
1461        assert_eq!(names.len(), 1);
1462        assert_eq!(names[0].text(), "my_task");
1463        assert_eq!(call.alias().unwrap().name().text(), "my_task3");
1464        let after: Vec<_> = call.after().collect();
1465        assert_eq!(after.len(), 2);
1466        assert_eq!(after[0].name().text(), "my_task2");
1467        assert_eq!(after[1].name().text(), "my_task");
1468        let inputs: Vec<_> = call.inputs().collect();
1469        assert_eq!(inputs.len(), 1);
1470        assert_eq!(inputs[0].name().text(), "a");
1471        assert_eq!(
1472            inputs[0]
1473                .expr()
1474                .unwrap()
1475                .unwrap_literal()
1476                .unwrap_integer()
1477                .value()
1478                .unwrap(),
1479            1
1480        );
1481
1482        // Third workflow statement
1483        let scatter = statements[2].clone().unwrap_scatter();
1484        assert_eq!(scatter.variable().text(), "a");
1485        let elements: Vec<_> = scatter
1486            .expr()
1487            .unwrap_literal()
1488            .unwrap_array()
1489            .elements()
1490            .collect();
1491        assert_eq!(elements.len(), 3);
1492        assert_eq!(
1493            elements[0]
1494                .clone()
1495                .unwrap_literal()
1496                .unwrap_string()
1497                .text()
1498                .unwrap()
1499                .text(),
1500            "1"
1501        );
1502        assert_eq!(
1503            elements[1]
1504                .clone()
1505                .unwrap_literal()
1506                .unwrap_string()
1507                .text()
1508                .unwrap()
1509                .text(),
1510            "2"
1511        );
1512        assert_eq!(
1513            elements[2]
1514                .clone()
1515                .unwrap_literal()
1516                .unwrap_string()
1517                .text()
1518                .unwrap()
1519                .text(),
1520            "3"
1521        );
1522
1523        // Inner statements
1524        let inner: Vec<_> = scatter.statements().collect();
1525        assert_eq!(inner.len(), 0);
1526
1527        // Workflow metadata
1528        let metadata = workflows[0]
1529            .metadata()
1530            .expect("workflow should have a metadata section");
1531        assert_eq!(metadata.parent().unwrap_workflow().name().text(), "test");
1532        let items: Vec<_> = metadata.items().collect();
1533        assert_eq!(items.len(), 2);
1534        assert_eq!(items[0].name().text(), "description");
1535        assert_eq!(
1536            items[0].value().unwrap_string().text().unwrap().text(),
1537            "a test"
1538        );
1539        assert_eq!(items[1].name().text(), "foo");
1540        items[1].value().unwrap_null();
1541
1542        // Workflow parameter metadata
1543        let param_meta = workflows[0]
1544            .parameter_metadata()
1545            .expect("workflow should have a parameter metadata section");
1546        assert_eq!(param_meta.parent().unwrap_workflow().name().text(), "test");
1547        let items: Vec<_> = param_meta.items().collect();
1548        assert_eq!(items.len(), 1);
1549        assert_eq!(items[0].name().text(), "name");
1550        let items: Vec<_> = items[0].value().unwrap_object().items().collect();
1551        assert_eq!(items.len(), 1);
1552        assert_eq!(items[0].name().text(), "help");
1553        assert_eq!(
1554            items[0].value().unwrap_string().text().unwrap().text(),
1555            "a name to greet"
1556        );
1557
1558        // Workflow hints
1559        let hints = workflows[0]
1560            .hints()
1561            .expect("workflow should have a hints section");
1562        assert_eq!(hints.parent().name().text(), "test");
1563        let items: Vec<_> = hints.items().collect();
1564        assert_eq!(items.len(), 1);
1565        assert_eq!(items[0].name().text(), "foo");
1566        assert_eq!(
1567            items[0].value().unwrap_string().text().unwrap().text(),
1568            "bar"
1569        );
1570
1571        // Workflow declarations
1572        let decls: Vec<_> = workflows[0].declarations().collect();
1573        assert_eq!(decls.len(), 1);
1574
1575        // First declaration
1576        assert_eq!(decls[0].ty().to_string(), "String");
1577        assert_eq!(decls[0].name().text(), "x");
1578        assert_eq!(
1579            decls[0]
1580                .expr()
1581                .unwrap_literal()
1582                .unwrap_string()
1583                .text()
1584                .unwrap()
1585                .text(),
1586            "private"
1587        );
1588    }
1589}