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}