1use std::fmt;
4
5use wdl_grammar::SyntaxTokenExt;
6
7use super::MetadataSection;
8use super::ParameterMetadataSection;
9use super::StructKeyword;
10use super::UnboundDecl;
11use crate::AstNode;
12use crate::AstToken;
13use crate::Comment;
14use crate::Documented;
15use crate::Ident;
16use crate::SyntaxKind;
17use crate::SyntaxNode;
18use crate::TreeNode;
19
20#[derive(Clone, Debug, PartialEq, Eq)]
22pub struct StructDefinition<N: TreeNode = SyntaxNode>(N);
23
24impl<N: TreeNode> StructDefinition<N> {
25 pub fn name(&self) -> Ident<N::Token> {
27 self.token().expect("struct should have a name")
28 }
29
30 pub fn keyword(&self) -> StructKeyword<N::Token> {
32 self.token().expect("struct should have a keyword")
33 }
34
35 pub fn items(&self) -> impl Iterator<Item = StructItem<N>> + use<'_, N> {
37 StructItem::children(&self.0)
38 }
39
40 pub fn members(&self) -> impl Iterator<Item = UnboundDecl<N>> + use<'_, N> {
42 self.children()
43 }
44
45 pub fn metadata(&self) -> impl Iterator<Item = MetadataSection<N>> + use<'_, N> {
47 self.children()
48 }
49
50 pub fn parameter_metadata(
52 &self,
53 ) -> impl Iterator<Item = ParameterMetadataSection<N>> + use<'_, N> {
54 self.children()
55 }
56}
57
58impl<N: TreeNode> fmt::Display for StructDefinition<N> {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 writeln!(f, "struct {} {{", self.name().text())?;
61 for member in self.members() {
62 writeln!(
63 f,
64 " {} {}",
65 member.ty().inner().text(),
66 member.name().text()
67 )?;
68 }
69 writeln!(f, "}}")?;
70 Ok(())
71 }
72}
73
74impl<N: TreeNode> AstNode<N> for StructDefinition<N> {
75 fn can_cast(kind: SyntaxKind) -> bool {
76 kind == SyntaxKind::StructDefinitionNode
77 }
78
79 fn cast(inner: N) -> Option<Self> {
80 match inner.kind() {
81 SyntaxKind::StructDefinitionNode => Some(Self(inner)),
82 _ => None,
83 }
84 }
85
86 fn inner(&self) -> &N {
87 &self.0
88 }
89}
90
91impl Documented<SyntaxNode> for StructDefinition<SyntaxNode> {
92 fn doc_comments(&self) -> Option<Vec<Comment<<SyntaxNode as TreeNode>::Token>>> {
93 Some(crate::doc_comments::<SyntaxNode>(self.keyword().inner().preceding_trivia()).collect())
94 }
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
99pub enum StructItem<N: TreeNode = SyntaxNode> {
100 Member(UnboundDecl<N>),
102 Metadata(MetadataSection<N>),
104 ParameterMetadata(ParameterMetadataSection<N>),
106}
107
108impl<N: TreeNode> StructItem<N> {
109 pub fn can_cast(kind: SyntaxKind) -> bool {
112 matches!(
113 kind,
114 SyntaxKind::UnboundDeclNode
115 | SyntaxKind::MetadataSectionNode
116 | SyntaxKind::ParameterMetadataSectionNode
117 )
118 }
119
120 pub fn cast(inner: N) -> Option<Self> {
124 match inner.kind() {
125 SyntaxKind::UnboundDeclNode => Some(Self::Member(
126 UnboundDecl::cast(inner).expect("unbound decl to cast"),
127 )),
128 SyntaxKind::MetadataSectionNode => Some(Self::Metadata(
129 MetadataSection::cast(inner).expect("metadata section to cast"),
130 )),
131 SyntaxKind::ParameterMetadataSectionNode => Some(Self::ParameterMetadata(
132 ParameterMetadataSection::cast(inner).expect("parameter metadata section to cast"),
133 )),
134 _ => None,
135 }
136 }
137
138 pub fn inner(&self) -> &N {
140 match self {
141 Self::Member(element) => element.inner(),
142 Self::Metadata(element) => element.inner(),
143 Self::ParameterMetadata(element) => element.inner(),
144 }
145 }
146
147 pub fn as_unbound_decl(&self) -> Option<&UnboundDecl<N>> {
153 match self {
154 Self::Member(d) => Some(d),
155 _ => None,
156 }
157 }
158
159 pub fn into_unbound_decl(self) -> Option<UnboundDecl<N>> {
165 match self {
166 Self::Member(d) => Some(d),
167 _ => None,
168 }
169 }
170
171 pub fn as_metadata_section(&self) -> Option<&MetadataSection<N>> {
177 match self {
178 Self::Metadata(s) => Some(s),
179 _ => None,
180 }
181 }
182
183 pub fn into_metadata_section(self) -> Option<MetadataSection<N>> {
189 match self {
190 Self::Metadata(s) => Some(s),
191 _ => None,
192 }
193 }
194
195 pub fn as_parameter_metadata_section(&self) -> Option<&ParameterMetadataSection<N>> {
202 match self {
203 Self::ParameterMetadata(s) => Some(s),
204 _ => None,
205 }
206 }
207
208 pub fn into_parameter_metadata_section(self) -> Option<ParameterMetadataSection<N>> {
215 match self {
216 Self::ParameterMetadata(s) => Some(s),
217 _ => None,
218 }
219 }
220
221 pub fn child(node: &N) -> Option<Self> {
223 node.children().find_map(Self::cast)
224 }
225
226 pub fn children(node: &N) -> impl Iterator<Item = Self> + use<'_, N> {
228 node.children().filter_map(Self::cast)
229 }
230}
231
232#[cfg(test)]
233mod test {
234 use pretty_assertions::assert_eq;
235
236 use crate::AstToken;
237 use crate::Document;
238
239 #[test]
240 fn struct_definitions() {
241 let (document, diagnostics) = Document::parse(
242 r#"
243version 1.1
244
245struct Empty {}
246
247struct PrimitiveTypes {
248 Boolean a
249 Boolean? b
250 Int c
251 Int? d
252 Float e
253 Float? f
254 String g
255 String? h
256 File i
257 File? j
258 Directory k
259 Directory? l
260
261 meta {
262 ok: "good"
263 }
264
265 parameter_meta {
266 a: "foo"
267 }
268}
269
270struct ComplexTypes {
271 Map[Boolean, String] a
272 Map[Int?, Array[String]]? b
273 Array[Boolean] c
274 Array[Array[Float]] d
275 Pair[Boolean, Boolean] e
276 Pair[Array[String], Array[String?]] f
277 Object g
278 Object? h
279 MyType i
280 MyType? j
281 Array[Directory] k
282
283 meta {
284 ok: "good"
285 }
286
287 parameter_meta {
288 a: "foo"
289 }
290}
291"#,
292 );
293 assert!(diagnostics.is_empty());
294 let ast = document.ast();
295 let ast = ast.as_v1().expect("should be a V1 AST");
296 let structs: Vec<_> = ast.structs().collect();
297 assert_eq!(structs.len(), 3);
298
299 assert_eq!(structs[0].name().text(), "Empty");
301 assert_eq!(structs[0].members().count(), 0);
302
303 assert_eq!(structs[1].name().text(), "PrimitiveTypes");
305 let members: Vec<_> = structs[1].members().collect();
306 assert_eq!(members.len(), 12);
307
308 assert_eq!(members[0].name().text(), "a");
310 assert_eq!(members[0].ty().to_string(), "Boolean");
311 assert!(!members[0].ty().is_optional());
312
313 assert_eq!(members[1].name().text(), "b");
315 assert_eq!(members[1].ty().to_string(), "Boolean?");
316 assert!(members[1].ty().is_optional());
317
318 assert_eq!(members[2].name().text(), "c");
320 assert_eq!(members[2].ty().to_string(), "Int");
321 assert!(!members[2].ty().is_optional());
322
323 assert_eq!(members[3].name().text(), "d");
325 assert_eq!(members[3].ty().to_string(), "Int?");
326 assert!(members[3].ty().is_optional());
327
328 assert_eq!(members[4].name().text(), "e");
330 assert_eq!(members[4].ty().to_string(), "Float");
331 assert!(!members[4].ty().is_optional());
332
333 assert_eq!(members[5].name().text(), "f");
335 assert_eq!(members[5].ty().to_string(), "Float?");
336 assert!(members[5].ty().is_optional());
337
338 assert_eq!(members[6].name().text(), "g");
340 assert_eq!(members[6].ty().to_string(), "String");
341 assert!(!members[6].ty().is_optional());
342
343 assert_eq!(members[7].name().text(), "h");
345 assert_eq!(members[7].ty().to_string(), "String?");
346 assert!(members[7].ty().is_optional());
347
348 assert_eq!(members[8].name().text(), "i");
350 assert_eq!(members[8].ty().to_string(), "File");
351 assert!(!members[8].ty().is_optional());
352
353 assert_eq!(members[9].name().text(), "j");
355 assert_eq!(members[9].ty().to_string(), "File?");
356 assert!(members[9].ty().is_optional());
357
358 assert_eq!(members[10].name().text(), "k");
360 assert_eq!(members[10].ty().to_string(), "Directory");
361 assert!(!members[10].ty().is_optional());
362
363 assert_eq!(members[11].name().text(), "l");
365 assert_eq!(members[11].ty().to_string(), "Directory?");
366 assert!(members[11].ty().is_optional());
367
368 assert_eq!(structs[2].name().text(), "ComplexTypes");
370 let members: Vec<_> = structs[2].members().collect();
371 assert_eq!(members.len(), 11);
372
373 assert_eq!(members[0].name().text(), "a");
375 assert_eq!(members[0].ty().to_string(), "Map[Boolean, String]");
376 assert!(!members[0].ty().is_optional());
377
378 assert_eq!(members[1].name().text(), "b");
380 assert_eq!(members[1].ty().to_string(), "Map[Int?, Array[String]]?");
381 assert!(members[1].ty().is_optional());
382
383 assert_eq!(members[2].name().text(), "c");
385 assert_eq!(members[2].ty().to_string(), "Array[Boolean]");
386 assert!(!members[2].ty().is_optional());
387
388 assert_eq!(members[3].name().text(), "d");
390 assert_eq!(members[3].ty().to_string(), "Array[Array[Float]]");
391 assert!(!members[3].ty().is_optional());
392
393 assert_eq!(members[4].name().text(), "e");
395 assert_eq!(members[4].ty().to_string(), "Pair[Boolean, Boolean]");
396 assert!(!members[4].ty().is_optional());
397
398 assert_eq!(members[5].name().text(), "f");
400 assert_eq!(
401 members[5].ty().to_string(),
402 "Pair[Array[String], Array[String?]]"
403 );
404 assert!(!members[5].ty().is_optional());
405
406 assert_eq!(members[6].name().text(), "g");
408 assert_eq!(members[6].ty().to_string(), "Object");
409 assert!(!members[6].ty().is_optional());
410
411 assert_eq!(members[7].name().text(), "h");
413 assert_eq!(members[7].ty().to_string(), "Object?");
414 assert!(members[7].ty().is_optional());
415
416 assert_eq!(members[8].name().text(), "i");
418 assert_eq!(members[8].ty().to_string(), "MyType");
419 assert!(!members[8].ty().is_optional());
420
421 assert_eq!(members[9].name().text(), "j");
423 assert_eq!(members[9].ty().to_string(), "MyType?");
424 assert!(members[9].ty().is_optional());
425
426 assert_eq!(members[10].name().text(), "k");
428 assert_eq!(members[10].ty().to_string(), "Array[Directory]");
429 assert!(!members[10].ty().is_optional());
430 }
431}