1#![warn(missing_docs)]
33#![warn(rust_2018_idioms)]
34#![warn(rust_2021_compatibility)]
35#![warn(missing_debug_implementations)]
36#![warn(clippy::missing_docs_in_private_items)]
37#![warn(rustdoc::broken_intra_doc_links)]
38
39use std::collections::HashSet;
40use std::fmt;
41
42pub use rowan::Direction;
43use rowan::NodeOrToken;
44use v1::CloseBrace;
45use v1::CloseHeredoc;
46use v1::OpenBrace;
47use v1::OpenHeredoc;
48pub use wdl_grammar::Diagnostic;
49pub use wdl_grammar::Label;
50pub use wdl_grammar::Severity;
51pub use wdl_grammar::Span;
52pub use wdl_grammar::SupportedVersion;
53pub use wdl_grammar::SyntaxElement;
54pub use wdl_grammar::SyntaxExt;
55pub use wdl_grammar::SyntaxKind;
56pub use wdl_grammar::SyntaxNode;
57pub use wdl_grammar::SyntaxToken;
58pub use wdl_grammar::SyntaxTokenExt;
59pub use wdl_grammar::SyntaxTree;
60pub use wdl_grammar::WorkflowDescriptionLanguage;
61pub use wdl_grammar::version;
62
63pub mod v1;
64
65mod element;
66mod validation;
67mod visitor;
68
69pub use element::*;
70pub use validation::*;
71pub use visitor::*;
72
73pub trait TreeNode: Clone + fmt::Debug + PartialEq + Eq + std::hash::Hash {
77 type Token: TreeToken;
79
80 fn parent(&self) -> Option<Self>;
84
85 fn kind(&self) -> SyntaxKind;
87
88 fn text(&self) -> impl fmt::Display;
92
93 fn span(&self) -> Span;
95
96 fn children(&self) -> impl Iterator<Item = Self>;
98
99 fn children_with_tokens(&self) -> impl Iterator<Item = NodeOrToken<Self, Self::Token>>;
101
102 fn last_token(&self) -> Option<Self::Token>;
104
105 fn descendants(&self) -> impl Iterator<Item = Self>;
107
108 fn ancestors(&self) -> impl Iterator<Item = Self>;
110
111 fn is_rule_excepted(&self, id: &str) -> bool;
113}
114
115pub trait TreeToken: Clone + fmt::Debug + PartialEq + Eq + std::hash::Hash {
117 type Node: TreeNode;
119
120 fn parent(&self) -> Self::Node;
122
123 fn kind(&self) -> SyntaxKind;
125
126 fn text(&self) -> &str;
128
129 fn span(&self) -> Span;
131}
132
133pub trait AstNode<N: TreeNode>: Sized {
135 fn can_cast(kind: SyntaxKind) -> bool;
137
138 fn cast(inner: N) -> Option<Self>;
140
141 fn inner(&self) -> &N;
143
144 fn kind(&self) -> SyntaxKind {
146 self.inner().kind()
147 }
148
149 fn text<'a>(&'a self) -> impl fmt::Display
154 where
155 N: 'a,
156 {
157 self.inner().text()
158 }
159
160 fn span(&self) -> Span {
162 self.inner().span()
163 }
164
165 fn token<C>(&self) -> Option<C>
167 where
168 C: AstToken<N::Token>,
169 {
170 self.inner()
171 .children_with_tokens()
172 .filter_map(|e| e.into_token())
173 .find_map(|t| C::cast(t))
174 }
175
176 fn tokens<'a, C>(&'a self) -> impl Iterator<Item = C>
178 where
179 C: AstToken<N::Token>,
180 N: 'a,
181 {
182 self.inner()
183 .children_with_tokens()
184 .filter_map(|e| e.into_token().and_then(C::cast))
185 }
186
187 fn last_token<C>(&self) -> Option<C>
193 where
194 C: AstToken<N::Token>,
195 {
196 self.inner().last_token().and_then(C::cast)
197 }
198
199 fn child<C>(&self) -> Option<C>
201 where
202 C: AstNode<N>,
203 {
204 self.inner().children().find_map(C::cast)
205 }
206
207 fn children<'a, C>(&'a self) -> impl Iterator<Item = C>
209 where
210 C: AstNode<N>,
211 N: 'a,
212 {
213 self.inner().children().filter_map(C::cast)
214 }
215
216 fn parent<'a, P>(&self) -> Option<P>
221 where
222 P: AstNode<N>,
223 N: 'a,
224 {
225 P::cast(self.inner().parent()?)
226 }
227
228 fn scope_span<O, C>(&self) -> Option<Span>
234 where
235 O: AstToken<N::Token>,
236 C: AstToken<N::Token>,
237 {
238 let open = self.token::<O>()?.span();
239 let close = self.last_token::<C>()?.span();
240
241 Some(Span::new(open.end(), close.start() - open.end()))
243 }
244
245 fn braced_scope_span(&self) -> Option<Span> {
253 self.scope_span::<OpenBrace<N::Token>, CloseBrace<N::Token>>()
254 }
255
256 fn heredoc_scope_span(&self) -> Option<Span> {
264 self.scope_span::<OpenHeredoc<N::Token>, CloseHeredoc<N::Token>>()
265 }
266
267 fn descendants<'a, D>(&'a self) -> impl Iterator<Item = D>
270 where
271 D: AstNode<N>,
272 N: 'a,
273 {
274 self.inner().descendants().filter_map(|d| D::cast(d))
275 }
276}
277
278pub trait AstToken<T: TreeToken>: Sized {
280 fn can_cast(kind: SyntaxKind) -> bool;
282
283 fn cast(inner: T) -> Option<Self>;
285
286 fn inner(&self) -> &T;
288
289 fn kind(&self) -> SyntaxKind {
291 self.inner().kind()
292 }
293
294 fn text<'a>(&'a self) -> &'a str
296 where
297 T: 'a,
298 {
299 self.inner().text()
300 }
301
302 fn span(&self) -> Span {
304 self.inner().span()
305 }
306
307 fn parent<'a, P>(&self) -> Option<P>
311 where
312 P: AstNode<T::Node>,
313 T: 'a,
314 {
315 P::cast(self.inner().parent())
316 }
317}
318
319pub trait NewRoot<N: TreeNode>: Sized {
322 fn new_root(root: N) -> Self;
325}
326
327impl TreeNode for SyntaxNode {
328 type Token = SyntaxToken;
329
330 fn parent(&self) -> Option<SyntaxNode> {
331 self.parent()
332 }
333
334 fn kind(&self) -> SyntaxKind {
335 self.kind()
336 }
337
338 fn children(&self) -> impl Iterator<Item = Self> {
339 self.children()
340 }
341
342 fn children_with_tokens(&self) -> impl Iterator<Item = NodeOrToken<Self, Self::Token>> {
343 self.children_with_tokens()
344 }
345
346 fn text(&self) -> impl fmt::Display {
347 self.text()
348 }
349
350 fn span(&self) -> Span {
351 let range = self.text_range();
352 let start = usize::from(range.start());
353 Span::new(start, usize::from(range.end()) - start)
354 }
355
356 fn last_token(&self) -> Option<Self::Token> {
357 self.last_token()
358 }
359
360 fn descendants(&self) -> impl Iterator<Item = Self> {
361 self.descendants()
362 }
363
364 fn ancestors(&self) -> impl Iterator<Item = Self> {
365 self.ancestors()
366 }
367
368 fn is_rule_excepted(&self, id: &str) -> bool {
369 <Self as SyntaxNodeExt>::is_rule_excepted(self, id)
370 }
371}
372
373impl TreeToken for SyntaxToken {
374 type Node = SyntaxNode;
375
376 fn parent(&self) -> SyntaxNode {
377 self.parent().expect("token should have a parent")
378 }
379
380 fn kind(&self) -> SyntaxKind {
381 self.kind()
382 }
383
384 fn text(&self) -> &str {
385 self.text()
386 }
387
388 fn span(&self) -> Span {
389 let range = self.text_range();
390 let start = usize::from(range.start());
391 Span::new(start, usize::from(range.end()) - start)
392 }
393}
394
395#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
400pub enum VisitReason {
401 Enter,
403 Exit,
405}
406
407pub trait SyntaxNodeExt {
409 fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_;
411
412 fn rule_exceptions(&self) -> HashSet<String>;
417
418 fn is_rule_excepted(&self, id: &str) -> bool;
420}
421
422impl SyntaxNodeExt for SyntaxNode {
423 fn except_comments(&self) -> impl Iterator<Item = SyntaxToken> + '_ {
424 self.siblings_with_tokens(Direction::Prev)
425 .skip(1)
426 .map_while(|s| {
427 if s.kind() == SyntaxKind::Whitespace || s.kind() == SyntaxKind::Comment {
428 s.into_token()
429 } else {
430 None
431 }
432 })
433 .filter(|t| t.kind() == SyntaxKind::Comment)
434 }
435
436 fn rule_exceptions(&self) -> HashSet<String> {
437 let mut set = HashSet::default();
438 for comment in self.except_comments() {
439 if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
440 for id in ids.split(',') {
441 let id = id.trim();
442 set.insert(id.to_string());
443 }
444 }
445 }
446
447 set
448 }
449
450 fn is_rule_excepted(&self, id: &str) -> bool {
451 for comment in self.except_comments() {
452 if let Some(ids) = comment.text().strip_prefix(EXCEPT_COMMENT_PREFIX) {
453 if ids.split(',').any(|i| i.trim() == id) {
454 return true;
455 }
456 }
457 }
458
459 false
460 }
461}
462
463#[derive(Clone, Debug, PartialEq, Eq)]
467pub enum Ast<N: TreeNode = SyntaxNode> {
468 Unsupported,
470 V1(v1::Ast<N>),
472}
473
474impl<N: TreeNode> Ast<N> {
475 pub fn as_v1(&self) -> Option<&v1::Ast<N>> {
479 match self {
480 Self::V1(ast) => Some(ast),
481 _ => None,
482 }
483 }
484
485 pub fn into_v1(self) -> Option<v1::Ast<N>> {
487 match self {
488 Self::V1(ast) => Some(ast),
489 _ => None,
490 }
491 }
492
493 pub fn unwrap_v1(self) -> v1::Ast<N> {
499 self.into_v1().expect("the AST is not a V1 AST")
500 }
501}
502
503#[derive(Clone, PartialEq, Eq, Hash)]
508pub struct Document<N: TreeNode = SyntaxNode>(N);
509
510impl<N: TreeNode> AstNode<N> for Document<N> {
511 fn can_cast(kind: SyntaxKind) -> bool {
512 kind == SyntaxKind::RootNode
513 }
514
515 fn cast(inner: N) -> Option<Self> {
516 if Self::can_cast(inner.kind()) {
517 Some(Self(inner))
518 } else {
519 None
520 }
521 }
522
523 fn inner(&self) -> &N {
524 &self.0
525 }
526}
527
528impl Document {
529 pub fn parse(source: &str) -> (Self, Vec<Diagnostic>) {
557 let (tree, diagnostics) = SyntaxTree::parse(source);
558 (
559 Document::cast(tree.into_syntax()).expect("document should cast"),
560 diagnostics,
561 )
562 }
563}
564
565impl<N: TreeNode> Document<N> {
566 pub fn version_statement(&self) -> Option<VersionStatement<N>> {
573 self.child()
574 }
575
576 pub fn ast(&self) -> Ast<N> {
578 self.version_statement()
579 .as_ref()
580 .and_then(|s| s.version().text().parse::<SupportedVersion>().ok())
581 .map(|v| match v {
582 SupportedVersion::V1(_) => Ast::V1(v1::Ast(self.0.clone())),
583 _ => Ast::Unsupported,
584 })
585 .unwrap_or(Ast::Unsupported)
586 }
587
588 pub fn morph<U: TreeNode + NewRoot<N>>(self) -> Document<U> {
591 Document(U::new_root(self.0))
592 }
593}
594
595impl Document<SyntaxNode> {
596 pub fn visit<V: Visitor>(&self, state: &mut V::State, visitor: &mut V) {
599 visit(&self.0, state, visitor)
600 }
601}
602
603impl fmt::Debug for Document {
604 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
605 self.0.fmt(f)
606 }
607}
608
609#[derive(Clone, Debug, PartialEq, Eq, Hash)]
611pub struct Whitespace<T: TreeToken = SyntaxToken>(T);
612
613impl<T: TreeToken> AstToken<T> for Whitespace<T> {
614 fn can_cast(kind: SyntaxKind) -> bool {
615 kind == SyntaxKind::Whitespace
616 }
617
618 fn cast(inner: T) -> Option<Self> {
619 match inner.kind() {
620 SyntaxKind::Whitespace => Some(Self(inner)),
621 _ => None,
622 }
623 }
624
625 fn inner(&self) -> &T {
626 &self.0
627 }
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Hash)]
632pub struct Comment<T: TreeToken = SyntaxToken>(T);
633
634impl<T: TreeToken> AstToken<T> for Comment<T> {
635 fn can_cast(kind: SyntaxKind) -> bool {
636 kind == SyntaxKind::Comment
637 }
638
639 fn cast(inner: T) -> Option<Self> {
640 match inner.kind() {
641 SyntaxKind::Comment => Some(Self(inner)),
642 _ => None,
643 }
644 }
645
646 fn inner(&self) -> &T {
647 &self.0
648 }
649}
650
651#[derive(Debug, Clone, PartialEq, Eq, Hash)]
653pub struct VersionStatement<N: TreeNode = SyntaxNode>(N);
654
655impl<N: TreeNode> VersionStatement<N> {
656 pub fn version(&self) -> Version<N::Token> {
658 self.token()
659 .expect("version statement must have a version token")
660 }
661
662 pub fn keyword(&self) -> v1::VersionKeyword<N::Token> {
664 self.token()
665 .expect("version statement must have a version keyword")
666 }
667}
668
669impl<N: TreeNode> AstNode<N> for VersionStatement<N> {
670 fn can_cast(kind: SyntaxKind) -> bool {
671 kind == SyntaxKind::VersionStatementNode
672 }
673
674 fn cast(inner: N) -> Option<Self> {
675 match inner.kind() {
676 SyntaxKind::VersionStatementNode => Some(Self(inner)),
677 _ => None,
678 }
679 }
680
681 fn inner(&self) -> &N {
682 &self.0
683 }
684}
685
686#[derive(Clone, Debug, PartialEq, Eq, Hash)]
688pub struct Version<T: TreeToken = SyntaxToken>(T);
689
690impl<T: TreeToken> AstToken<T> for Version<T> {
691 fn can_cast(kind: SyntaxKind) -> bool {
692 kind == SyntaxKind::Version
693 }
694
695 fn cast(inner: T) -> Option<Self> {
696 match inner.kind() {
697 SyntaxKind::Version => Some(Self(inner)),
698 _ => None,
699 }
700 }
701
702 fn inner(&self) -> &T {
703 &self.0
704 }
705}
706
707#[derive(Debug, Clone, PartialEq, Eq, Hash)]
709pub struct Ident<T: TreeToken = SyntaxToken>(T);
710
711impl<T: TreeToken> Ident<T> {
712 pub fn hashable(&self) -> TokenText<T> {
714 TokenText(self.0.clone())
715 }
716}
717
718impl<T: TreeToken> AstToken<T> for Ident<T> {
719 fn can_cast(kind: SyntaxKind) -> bool {
720 kind == SyntaxKind::Ident
721 }
722
723 fn cast(inner: T) -> Option<Self> {
724 match inner.kind() {
725 SyntaxKind::Ident => Some(Self(inner)),
726 _ => None,
727 }
728 }
729
730 fn inner(&self) -> &T {
731 &self.0
732 }
733}
734
735#[derive(Debug, Clone)]
744pub struct TokenText<T: TreeToken = SyntaxToken>(T);
745
746impl TokenText {
747 pub fn text(&self) -> &str {
749 self.0.text()
750 }
751
752 pub fn span(&self) -> Span {
754 self.0.span()
755 }
756}
757
758impl<T: TreeToken> PartialEq for TokenText<T> {
759 fn eq(&self, other: &Self) -> bool {
760 self.0.text() == other.0.text()
761 }
762}
763
764impl<T: TreeToken> Eq for TokenText<T> {}
765
766impl<T: TreeToken> std::hash::Hash for TokenText<T> {
767 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
768 self.0.text().hash(state);
769 }
770}
771
772impl<T: TreeToken> std::borrow::Borrow<str> for TokenText<T> {
773 fn borrow(&self) -> &str {
774 self.0.text()
775 }
776}