wdl_ast/
v1.rs

1//! AST representation for a 1.x WDL document.
2
3use crate::AstNode;
4use crate::SyntaxKind;
5use crate::SyntaxNode;
6use crate::TreeNode;
7
8mod decls;
9mod display;
10mod expr;
11mod import;
12mod r#struct;
13mod task;
14mod tokens;
15mod workflow;
16
17pub use decls::*;
18pub use expr::*;
19pub use import::*;
20pub use r#struct::*;
21pub use task::*;
22pub use tokens::*;
23pub use workflow::*;
24
25/// Represents a WDL V1 Abstract Syntax Tree (AST).
26///
27/// The AST is a facade over a [SyntaxTree][1].
28///
29/// A syntax tree is comprised of nodes that have either
30/// other nodes or tokens as children.
31///
32/// A token is a span of text from the WDL source text and
33/// is terminal in the tree.
34///
35/// Elements of an AST are trivially cloned.
36///
37/// [1]: crate::SyntaxTree
38#[derive(Clone, Debug, PartialEq, Eq)]
39pub struct Ast<N: TreeNode = SyntaxNode>(pub(crate) N);
40
41impl<N: TreeNode> Ast<N> {
42    /// Gets all of the document items in the AST.
43    pub fn items(&self) -> impl Iterator<Item = DocumentItem<N>> + use<'_, N> {
44        DocumentItem::children(&self.0)
45    }
46
47    /// Gets the import statements in the AST.
48    pub fn imports(&self) -> impl Iterator<Item = ImportStatement<N>> + use<'_, N> {
49        self.children()
50    }
51
52    /// Gets the struct definitions in the AST.
53    pub fn structs(&self) -> impl Iterator<Item = StructDefinition<N>> + use<'_, N> {
54        self.children()
55    }
56
57    /// Gets the task definitions in the AST.
58    pub fn tasks(&self) -> impl Iterator<Item = TaskDefinition<N>> + use<'_, N> {
59        self.children()
60    }
61
62    /// Gets the workflow definitions in the AST.
63    pub fn workflows(&self) -> impl Iterator<Item = WorkflowDefinition<N>> + use<'_, N> {
64        self.children()
65    }
66}
67
68impl<N: TreeNode> AstNode<N> for Ast<N> {
69    fn can_cast(kind: SyntaxKind) -> bool {
70        kind == SyntaxKind::RootNode
71    }
72
73    fn cast(inner: N) -> Option<Self> {
74        match inner.kind() {
75            SyntaxKind::RootNode => Some(Self(inner)),
76            _ => None,
77        }
78    }
79
80    fn inner(&self) -> &N {
81        &self.0
82    }
83}
84
85/// Represents a document item.
86#[derive(Clone, Debug, PartialEq, Eq)]
87pub enum DocumentItem<N: TreeNode = SyntaxNode> {
88    /// The item is an import statement.
89    Import(ImportStatement<N>),
90    /// The item is a struct definition.
91    Struct(StructDefinition<N>),
92    /// The item is a task definition.
93    Task(TaskDefinition<N>),
94    /// The item is a workflow definition.
95    Workflow(WorkflowDefinition<N>),
96}
97
98impl<N: TreeNode> DocumentItem<N> {
99    // Returns whether or not the given syntax kind can be cast to
100    /// [`DocumentItem`].
101    pub fn can_cast(kind: SyntaxKind) -> bool
102    where
103        Self: Sized,
104    {
105        matches!(
106            kind,
107            SyntaxKind::ImportStatementNode
108                | SyntaxKind::StructDefinitionNode
109                | SyntaxKind::TaskDefinitionNode
110                | SyntaxKind::WorkflowDefinitionNode
111        )
112    }
113
114    /// Casts the given node to [`DocumentItem`].
115    ///
116    /// Returns `None` if the node cannot be cast.
117    pub fn cast(inner: N) -> Option<Self> {
118        match inner.kind() {
119            SyntaxKind::ImportStatementNode => Some(Self::Import(
120                ImportStatement::cast(inner).expect("import statement to cast"),
121            )),
122            SyntaxKind::StructDefinitionNode => Some(Self::Struct(
123                StructDefinition::cast(inner).expect("struct definition to cast"),
124            )),
125            SyntaxKind::TaskDefinitionNode => Some(Self::Task(
126                TaskDefinition::cast(inner).expect("task definition to cast"),
127            )),
128            SyntaxKind::WorkflowDefinitionNode => Some(Self::Workflow(
129                WorkflowDefinition::cast(inner).expect("workflow definition to cast"),
130            )),
131            _ => None,
132        }
133    }
134
135    /// Gets a reference to the inner node.
136    pub fn inner(&self) -> &N {
137        match self {
138            Self::Import(e) => e.inner(),
139            Self::Struct(e) => e.inner(),
140            Self::Task(e) => e.inner(),
141            Self::Workflow(e) => e.inner(),
142        }
143    }
144
145    /// Attempts to get a reference to the inner [`ImportStatement`].
146    ///
147    /// * If `self` is a [`DocumentItem::Import`], then a reference to the inner
148    ///   [`ImportStatement`] is returned wrapped in [`Some`].
149    /// * Else, [`None`] is returned.
150    pub fn as_import_statement(&self) -> Option<&ImportStatement<N>> {
151        match self {
152            Self::Import(i) => Some(i),
153            _ => None,
154        }
155    }
156
157    /// Consumes `self` and attempts to return the inner [`ImportStatement`].
158    ///
159    /// * If `self` is a [`DocumentItem::Import`], then the inner
160    ///   [`ImportStatement`] is returned wrapped in [`Some`].
161    /// * Else, [`None`] is returned.
162    pub fn into_import_statement(self) -> Option<ImportStatement<N>> {
163        match self {
164            Self::Import(i) => Some(i),
165            _ => None,
166        }
167    }
168
169    /// Attempts to get a reference to the inner [`StructDefinition`].
170    ///
171    /// * If `self` is a [`DocumentItem::Struct`], then a reference to the inner
172    ///   [`StructDefinition`] is returned wrapped in [`Some`].
173    /// * Else, [`None`] is returned.
174    pub fn as_struct_definition(&self) -> Option<&StructDefinition<N>> {
175        match self {
176            Self::Struct(i) => Some(i),
177            _ => None,
178        }
179    }
180
181    /// Consumes `self` and attempts to return the inner [`StructDefinition`].
182    ///
183    /// * If `self` is a [`DocumentItem::Struct`], then the inner
184    ///   [`StructDefinition`] is returned wrapped in [`Some`].
185    /// * Else, [`None`] is returned.
186    pub fn into_struct_definition(self) -> Option<StructDefinition<N>> {
187        match self {
188            Self::Struct(i) => Some(i),
189            _ => None,
190        }
191    }
192
193    /// Attempts to get a reference to the inner [`TaskDefinition`].
194    ///
195    /// * If `self` is a [`DocumentItem::Task`], then a reference to the inner
196    ///   [`TaskDefinition`] is returned wrapped in [`Some`].
197    /// * Else, [`None`] is returned.
198    pub fn as_task_definition(&self) -> Option<&TaskDefinition<N>> {
199        match self {
200            Self::Task(i) => Some(i),
201            _ => None,
202        }
203    }
204
205    /// Consumes `self` and attempts to return the inner [`TaskDefinition`].
206    ///
207    /// * If `self` is a [`DocumentItem::Task`], then the inner
208    ///   [`TaskDefinition`] is returned wrapped in [`Some`].
209    /// * Else, [`None`] is returned.
210    pub fn into_task_definition(self) -> Option<TaskDefinition<N>> {
211        match self {
212            Self::Task(i) => Some(i),
213            _ => None,
214        }
215    }
216
217    /// Attempts to get a reference to the inner [`WorkflowDefinition`].
218    ///
219    /// * If `self` is a [`DocumentItem::Workflow`], then a reference to the
220    ///   inner [`WorkflowDefinition`] is returned wrapped in [`Some`].
221    /// * Else, [`None`] is returned.
222    pub fn as_workflow_definition(&self) -> Option<&WorkflowDefinition<N>> {
223        match self {
224            Self::Workflow(i) => Some(i),
225            _ => None,
226        }
227    }
228
229    /// Consumes `self` and attempts to return the inner [`WorkflowDefinition`].
230    ///
231    /// * If `self` is a [`DocumentItem::Workflow`], then the inner
232    ///   [`WorkflowDefinition`] is returned wrapped in [`Some`].
233    /// * Else, [`None`] is returned.
234    pub fn into_workflow_definition(self) -> Option<WorkflowDefinition<N>> {
235        match self {
236            Self::Workflow(i) => Some(i),
237            _ => None,
238        }
239    }
240
241    /// Finds the first child that can be cast to a [`DocumentItem`].
242    pub fn child(node: &N) -> Option<Self> {
243        node.children().find_map(Self::cast)
244    }
245
246    /// Finds all children that can be cast to a [`DocumentItem`].
247    pub fn children(node: &N) -> impl Iterator<Item = Self> + use<'_, N> {
248        node.children().filter_map(Self::cast)
249    }
250}