1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::path::Path;
6use std::sync::Arc;
7
8use indexmap::IndexMap;
9use petgraph::graph::NodeIndex;
10use rowan::GreenNode;
11use url::Url;
12use uuid::Uuid;
13use wdl_ast::Ast;
14use wdl_ast::AstNode;
15use wdl_ast::Diagnostic;
16use wdl_ast::Severity;
17use wdl_ast::Span;
18use wdl_ast::SupportedVersion;
19use wdl_ast::SyntaxNode;
20
21use crate::config::Config;
22use crate::diagnostics::unused_import;
23use crate::graph::DocumentGraph;
24use crate::graph::ParseState;
25use crate::types::CallType;
26use crate::types::Type;
27
28mod v1;
29
30pub const TASK_VAR_NAME: &str = "task";
33
34#[derive(Debug)]
36pub struct Namespace {
37 span: Span,
39 source: Arc<Url>,
41 document: Document,
43 used: bool,
45 excepted: bool,
48}
49
50impl Namespace {
51 pub fn span(&self) -> Span {
53 self.span
54 }
55
56 pub fn source(&self) -> &Arc<Url> {
58 &self.source
59 }
60
61 pub fn document(&self) -> &Document {
63 &self.document
64 }
65}
66
67#[derive(Debug, Clone)]
69pub struct Struct {
70 name: String,
72 name_span: Span,
77 offset: usize,
82 node: rowan::GreenNode,
86 namespace: Option<String>,
90 ty: Option<Type>,
94}
95
96impl Struct {
97 pub fn name(&self) -> &str {
99 &self.name
100 }
101
102 pub fn name_span(&self) -> Span {
104 self.name_span
105 }
106
107 pub fn offset(&self) -> usize {
109 self.offset
110 }
111
112 pub fn node(&self) -> &rowan::GreenNode {
114 &self.node
115 }
116
117 pub fn namespace(&self) -> Option<&str> {
122 self.namespace.as_deref()
123 }
124
125 pub fn ty(&self) -> Option<&Type> {
130 self.ty.as_ref()
131 }
132}
133
134#[derive(Debug, Clone)]
136pub struct Name {
137 span: Span,
139 ty: Type,
141}
142
143impl Name {
144 pub fn span(&self) -> Span {
146 self.span
147 }
148
149 pub fn ty(&self) -> &Type {
151 &self.ty
152 }
153}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
157struct ScopeIndex(usize);
158
159#[derive(Debug)]
161struct Scope {
162 parent: Option<ScopeIndex>,
166 span: Span,
168 names: IndexMap<String, Name>,
170}
171
172impl Scope {
173 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
175 Self {
176 parent,
177 span,
178 names: Default::default(),
179 }
180 }
181
182 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
184 self.names.insert(name.into(), Name { span, ty });
185 }
186}
187
188#[derive(Debug, Clone, Copy)]
190pub struct ScopeRef<'a> {
191 scopes: &'a [Scope],
193 index: ScopeIndex,
195}
196
197impl<'a> ScopeRef<'a> {
198 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
200 Self { scopes, index }
201 }
202
203 pub fn span(&self) -> Span {
205 self.scopes[self.index.0].span
206 }
207
208 pub fn parent(&self) -> Option<Self> {
212 self.scopes[self.index.0].parent.map(|p| Self {
213 scopes: self.scopes,
214 index: p,
215 })
216 }
217
218 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
220 self.scopes[self.index.0]
221 .names
222 .iter()
223 .map(|(name, n)| (name.as_str(), n))
224 }
225
226 pub fn local(&self, name: &str) -> Option<&Name> {
230 self.scopes[self.index.0].names.get(name)
231 }
232
233 pub fn lookup(&self, name: &str) -> Option<&Name> {
237 let mut current = Some(self.index);
238
239 while let Some(index) = current {
240 if let Some(name) = self.scopes[index.0].names.get(name) {
241 return Some(name);
242 }
243
244 current = self.scopes[index.0].parent;
245 }
246
247 None
248 }
249}
250
251#[derive(Debug)]
253struct ScopeRefMut<'a> {
254 scopes: &'a mut [Scope],
256 index: ScopeIndex,
258}
259
260impl<'a> ScopeRefMut<'a> {
261 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
263 Self { scopes, index }
264 }
265
266 pub fn lookup(&self, name: &str) -> Option<&Name> {
270 let mut current = Some(self.index);
271
272 while let Some(index) = current {
273 if let Some(name) = self.scopes[index.0].names.get(name) {
274 return Some(name);
275 }
276
277 current = self.scopes[index.0].parent;
278 }
279
280 None
281 }
282
283 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
285 self.scopes[self.index.0]
286 .names
287 .insert(name.into(), Name { span, ty });
288 }
289
290 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
292 ScopeRef {
293 scopes: self.scopes,
294 index: self.index,
295 }
296 }
297}
298
299#[derive(Debug, Clone, PartialEq, Eq)]
301pub struct Input {
302 ty: Type,
304 required: bool,
309}
310
311impl Input {
312 pub fn ty(&self) -> &Type {
314 &self.ty
315 }
316
317 pub fn required(&self) -> bool {
319 self.required
320 }
321}
322
323#[derive(Debug, Clone, PartialEq, Eq)]
325pub struct Output {
326 ty: Type,
328 name_span: Span,
330}
331
332impl Output {
333 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
335 Self { ty, name_span }
336 }
337
338 pub fn ty(&self) -> &Type {
340 &self.ty
341 }
342
343 pub fn name_span(&self) -> Span {
345 self.name_span
346 }
347}
348
349#[derive(Debug)]
351pub struct Task {
352 name_span: Span,
354 name: String,
356 scopes: Vec<Scope>,
362 inputs: Arc<IndexMap<String, Input>>,
364 outputs: Arc<IndexMap<String, Output>>,
366}
367
368impl Task {
369 pub fn name(&self) -> &str {
371 &self.name
372 }
373
374 pub fn name_span(&self) -> Span {
376 self.name_span
377 }
378
379 pub fn scope(&self) -> ScopeRef<'_> {
381 ScopeRef::new(&self.scopes, ScopeIndex(0))
382 }
383
384 pub fn inputs(&self) -> &IndexMap<String, Input> {
386 &self.inputs
387 }
388
389 pub fn outputs(&self) -> &IndexMap<String, Output> {
391 &self.outputs
392 }
393}
394
395#[derive(Debug)]
397pub struct Workflow {
398 name_span: Span,
400 name: String,
402 scopes: Vec<Scope>,
408 inputs: Arc<IndexMap<String, Input>>,
410 outputs: Arc<IndexMap<String, Output>>,
412 calls: HashMap<String, CallType>,
414 allows_nested_inputs: bool,
416}
417
418impl Workflow {
419 pub fn name(&self) -> &str {
421 &self.name
422 }
423
424 pub fn name_span(&self) -> Span {
426 self.name_span
427 }
428
429 pub fn scope(&self) -> ScopeRef<'_> {
431 ScopeRef::new(&self.scopes, ScopeIndex(0))
432 }
433
434 pub fn inputs(&self) -> &IndexMap<String, Input> {
436 &self.inputs
437 }
438
439 pub fn outputs(&self) -> &IndexMap<String, Output> {
441 &self.outputs
442 }
443
444 pub fn calls(&self) -> &HashMap<String, CallType> {
446 &self.calls
447 }
448
449 pub fn allows_nested_inputs(&self) -> bool {
451 self.allows_nested_inputs
452 }
453}
454
455#[derive(Debug)]
457struct DocumentData {
458 config: Config,
460 root: Option<GreenNode>,
464 id: Arc<String>,
468 uri: Arc<Url>,
470 version: Option<SupportedVersion>,
472 namespaces: IndexMap<String, Namespace>,
474 tasks: IndexMap<String, Task>,
476 workflow: Option<Workflow>,
478 structs: IndexMap<String, Struct>,
480 parse_diagnostics: Vec<Diagnostic>,
482 analysis_diagnostics: Vec<Diagnostic>,
484}
485
486impl DocumentData {
487 fn new(
489 config: Config,
490 uri: Arc<Url>,
491 root: Option<GreenNode>,
492 version: Option<SupportedVersion>,
493 diagnostics: Vec<Diagnostic>,
494 ) -> Self {
495 Self {
496 config,
497 root,
498 id: Uuid::new_v4().to_string().into(),
499 uri,
500 version,
501 namespaces: Default::default(),
502 tasks: Default::default(),
503 workflow: Default::default(),
504 structs: Default::default(),
505 parse_diagnostics: diagnostics,
506 analysis_diagnostics: Default::default(),
507 }
508 }
509}
510
511#[derive(Debug, Clone)]
515pub struct Document {
516 data: Arc<DocumentData>,
518}
519
520impl Document {
521 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
523 Self {
524 data: Arc::new(DocumentData::new(
525 Default::default(),
526 uri,
527 None,
528 None,
529 Default::default(),
530 )),
531 }
532 }
533
534 pub(crate) fn from_graph_node(
536 config: &Config,
537 graph: &DocumentGraph,
538 index: NodeIndex,
539 ) -> Self {
540 let node = graph.get(index);
541
542 let (wdl_version, diagnostics) = match node.parse_state() {
543 ParseState::NotParsed => panic!("node should have been parsed"),
544 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
545 ParseState::Parsed {
546 wdl_version,
547 diagnostics,
548 ..
549 } => (wdl_version, diagnostics),
550 };
551
552 let root = node.root().expect("node should have been parsed");
553 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
554 (Some(stmt), Some(wdl_version)) => (
555 config.with_diagnostics_config(
556 config.diagnostics_config().excepted_for_node(stmt.inner()),
557 ),
558 *wdl_version,
559 ),
560 _ => {
561 return Self {
564 data: Arc::new(DocumentData::new(
565 config.clone(),
566 node.uri().clone(),
567 Some(root.inner().green().into()),
568 None,
569 diagnostics.to_vec(),
570 )),
571 };
572 }
573 };
574
575 let mut data = DocumentData::new(
576 config.clone(),
577 node.uri().clone(),
578 Some(root.inner().green().into()),
579 Some(wdl_version),
580 diagnostics.to_vec(),
581 );
582 match root.ast_with_version_fallback(config.fallback_version()) {
583 Ast::Unsupported => {}
584 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
585 }
586
587 if let Some(severity) = config.diagnostics_config().unused_import {
589 let DocumentData {
590 namespaces,
591 analysis_diagnostics,
592 ..
593 } = &mut data;
594
595 analysis_diagnostics.extend(
596 namespaces
597 .iter()
598 .filter(|(_, ns)| !ns.used && !ns.excepted)
599 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
600 );
601 }
602
603 Self {
604 data: Arc::new(data),
605 }
606 }
607
608 pub fn config(&self) -> &Config {
610 &self.data.config
611 }
612
613 pub fn root(&self) -> wdl_ast::Document {
619 wdl_ast::Document::cast(SyntaxNode::new_root(
620 self.data.root.clone().expect("should have a root"),
621 ))
622 .expect("should cast")
623 }
624
625 pub fn id(&self) -> &Arc<String> {
629 &self.data.id
630 }
631
632 pub fn uri(&self) -> &Arc<Url> {
634 &self.data.uri
635 }
636
637 pub fn path(&self) -> Cow<'_, str> {
644 if let Ok(path) = self.data.uri.to_file_path() {
645 if let Some(path) = std::env::current_dir()
646 .ok()
647 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
648 {
649 return path.to_string().into();
650 }
651
652 if let Ok(path) = path.into_os_string().into_string() {
653 return path.into();
654 }
655 }
656
657 self.data.uri.as_str().into()
658 }
659
660 pub fn version(&self) -> Option<SupportedVersion> {
665 self.data.version
666 }
667
668 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
670 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
671 }
672
673 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
675 self.data.namespaces.get(name)
676 }
677
678 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
680 self.data.tasks.iter().map(|(_, t)| t)
681 }
682
683 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
685 self.data.tasks.get(name)
686 }
687
688 pub fn workflow(&self) -> Option<&Workflow> {
692 self.data.workflow.as_ref()
693 }
694
695 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
697 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
698 }
699
700 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
702 self.data.structs.get(name)
703 }
704
705 pub fn parse_diagnostics(&self) -> &[Diagnostic] {
707 &self.data.parse_diagnostics
708 }
709
710 pub fn analysis_diagnostics(&self) -> &[Diagnostic] {
712 &self.data.analysis_diagnostics
713 }
714
715 pub fn diagnostics(&self) -> impl Iterator<Item = &Diagnostic> {
717 self.data
718 .parse_diagnostics
719 .iter()
720 .chain(self.data.analysis_diagnostics.iter())
721 }
722
723 pub fn sort_diagnostics(&mut self) -> Self {
729 let data = &mut self.data;
730 let inner = Arc::get_mut(data).expect("should only have one reference");
731 inner.parse_diagnostics.sort();
732 inner.analysis_diagnostics.sort();
733 Self { data: data.clone() }
734 }
735
736 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
742 let data = &mut self.data;
743 let inner = Arc::get_mut(data).expect("should only have one reference");
744 inner.analysis_diagnostics.extend(diagnostics);
745 Self { data: data.clone() }
746 }
747
748 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
750 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
752 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
753 Ok(index) => index,
754 Err(index) => {
755 if index == 0 {
758 return None;
759 }
760
761 index - 1
762 }
763 };
764
765 loop {
768 let scope = &scopes[index];
769 if scope.span.contains(position) {
770 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
771 }
772
773 if index == 0 {
774 return None;
775 }
776
777 index -= 1;
778 }
779 }
780
781 if let Some(workflow) = &self.data.workflow
783 && workflow.scope().span().contains(position)
784 {
785 return find_scope(&workflow.scopes, position);
786 }
787
788 let task = match self
790 .data
791 .tasks
792 .binary_search_by_key(&position, |_, t| t.scope().span().start())
793 {
794 Ok(index) => &self.data.tasks[index],
795 Err(index) => {
796 if index == 0 {
799 return None;
800 }
801
802 &self.data.tasks[index - 1]
803 }
804 };
805
806 if task.scope().span().contains(position) {
807 return find_scope(&task.scopes, position);
808 }
809
810 None
811 }
812
813 pub fn has_errors(&self) -> bool {
822 if self.diagnostics().any(|d| d.severity() == Severity::Error) {
824 return true;
825 }
826
827 for (_, ns) in self.namespaces() {
829 if ns.document.has_errors() {
830 return true;
831 }
832 }
833
834 false
835 }
836
837 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
840 crate::visit(self, diagnostics, visitor)
841 }
842}