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,
306}
307
308impl Input {
309 pub fn ty(&self) -> &Type {
311 &self.ty
312 }
313
314 pub fn required(&self) -> bool {
316 self.required
317 }
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
322pub struct Output {
323 ty: Type,
325 name_span: Span,
327}
328
329impl Output {
330 pub(crate) fn new(ty: Type, name_span: Span) -> Self {
332 Self { ty, name_span }
333 }
334
335 pub fn ty(&self) -> &Type {
337 &self.ty
338 }
339
340 pub fn name_span(&self) -> Span {
342 self.name_span
343 }
344}
345
346#[derive(Debug)]
348pub struct Task {
349 name_span: Span,
351 name: String,
353 scopes: Vec<Scope>,
359 inputs: Arc<IndexMap<String, Input>>,
361 outputs: Arc<IndexMap<String, Output>>,
363}
364
365impl Task {
366 pub fn name(&self) -> &str {
368 &self.name
369 }
370
371 pub fn name_span(&self) -> Span {
373 self.name_span
374 }
375
376 pub fn scope(&self) -> ScopeRef<'_> {
378 ScopeRef::new(&self.scopes, ScopeIndex(0))
379 }
380
381 pub fn inputs(&self) -> &IndexMap<String, Input> {
383 &self.inputs
384 }
385
386 pub fn outputs(&self) -> &IndexMap<String, Output> {
388 &self.outputs
389 }
390}
391
392#[derive(Debug)]
394pub struct Workflow {
395 name_span: Span,
397 name: String,
399 scopes: Vec<Scope>,
405 inputs: Arc<IndexMap<String, Input>>,
407 outputs: Arc<IndexMap<String, Output>>,
409 calls: HashMap<String, CallType>,
411 allows_nested_inputs: bool,
413}
414
415impl Workflow {
416 pub fn name(&self) -> &str {
418 &self.name
419 }
420
421 pub fn name_span(&self) -> Span {
423 self.name_span
424 }
425
426 pub fn scope(&self) -> ScopeRef<'_> {
428 ScopeRef::new(&self.scopes, ScopeIndex(0))
429 }
430
431 pub fn inputs(&self) -> &IndexMap<String, Input> {
433 &self.inputs
434 }
435
436 pub fn outputs(&self) -> &IndexMap<String, Output> {
438 &self.outputs
439 }
440
441 pub fn calls(&self) -> &HashMap<String, CallType> {
443 &self.calls
444 }
445
446 pub fn allows_nested_inputs(&self) -> bool {
448 self.allows_nested_inputs
449 }
450}
451
452#[derive(Debug)]
454struct DocumentData {
455 config: Config,
457 root: Option<GreenNode>,
461 id: Arc<String>,
465 uri: Arc<Url>,
467 version: Option<SupportedVersion>,
469 namespaces: IndexMap<String, Namespace>,
471 tasks: IndexMap<String, Task>,
473 workflow: Option<Workflow>,
475 structs: IndexMap<String, Struct>,
477 diagnostics: Vec<Diagnostic>,
479}
480
481impl DocumentData {
482 fn new(
484 config: Config,
485 uri: Arc<Url>,
486 root: Option<GreenNode>,
487 version: Option<SupportedVersion>,
488 diagnostics: Vec<Diagnostic>,
489 ) -> Self {
490 Self {
491 config,
492 root,
493 id: Uuid::new_v4().to_string().into(),
494 uri,
495 version,
496 namespaces: Default::default(),
497 tasks: Default::default(),
498 workflow: Default::default(),
499 structs: Default::default(),
500 diagnostics,
501 }
502 }
503}
504
505#[derive(Debug, Clone)]
509pub struct Document {
510 data: Arc<DocumentData>,
512}
513
514impl Document {
515 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
517 Self {
518 data: Arc::new(DocumentData::new(
519 Default::default(),
520 uri,
521 None,
522 None,
523 Default::default(),
524 )),
525 }
526 }
527
528 pub(crate) fn from_graph_node(
530 config: &Config,
531 graph: &DocumentGraph,
532 index: NodeIndex,
533 ) -> Self {
534 let node = graph.get(index);
535
536 let (wdl_version, diagnostics) = match node.parse_state() {
537 ParseState::NotParsed => panic!("node should have been parsed"),
538 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
539 ParseState::Parsed {
540 wdl_version,
541 diagnostics,
542 ..
543 } => (wdl_version, diagnostics),
544 };
545
546 let root = node.root().expect("node should have been parsed");
547 let (config, wdl_version) = match (root.version_statement(), wdl_version) {
548 (Some(stmt), Some(wdl_version)) => (
549 config.with_diagnostics_config(
550 config.diagnostics_config().excepted_for_node(stmt.inner()),
551 ),
552 *wdl_version,
553 ),
554 _ => {
555 return Self {
558 data: Arc::new(DocumentData::new(
559 config.clone(),
560 node.uri().clone(),
561 Some(root.inner().green().into()),
562 None,
563 diagnostics.to_vec(),
564 )),
565 };
566 }
567 };
568
569 let mut data = DocumentData::new(
570 config.clone(),
571 node.uri().clone(),
572 Some(root.inner().green().into()),
573 Some(wdl_version),
574 diagnostics.to_vec(),
575 );
576 match root.ast_with_version_fallback(config.fallback_version()) {
577 Ast::Unsupported => {}
578 Ast::V1(ast) => v1::populate_document(&mut data, &config, graph, index, &ast),
579 }
580
581 if let Some(severity) = config.diagnostics_config().unused_import {
583 let DocumentData {
584 namespaces,
585 diagnostics,
586 ..
587 } = &mut data;
588
589 diagnostics.extend(
590 namespaces
591 .iter()
592 .filter(|(_, ns)| !ns.used && !ns.excepted)
593 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
594 );
595 }
596
597 Self {
598 data: Arc::new(data),
599 }
600 }
601
602 pub fn config(&self) -> &Config {
604 &self.data.config
605 }
606
607 pub fn root(&self) -> wdl_ast::Document {
613 wdl_ast::Document::cast(SyntaxNode::new_root(
614 self.data.root.clone().expect("should have a root"),
615 ))
616 .expect("should cast")
617 }
618
619 pub fn id(&self) -> &Arc<String> {
623 &self.data.id
624 }
625
626 pub fn uri(&self) -> &Arc<Url> {
628 &self.data.uri
629 }
630
631 pub fn path(&self) -> Cow<'_, str> {
638 if let Ok(path) = self.data.uri.to_file_path() {
639 if let Some(path) = std::env::current_dir()
640 .ok()
641 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
642 {
643 return path.to_string().into();
644 }
645
646 if let Ok(path) = path.into_os_string().into_string() {
647 return path.into();
648 }
649 }
650
651 self.data.uri.as_str().into()
652 }
653
654 pub fn version(&self) -> Option<SupportedVersion> {
659 self.data.version
660 }
661
662 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
664 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
665 }
666
667 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
669 self.data.namespaces.get(name)
670 }
671
672 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
674 self.data.tasks.iter().map(|(_, t)| t)
675 }
676
677 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
679 self.data.tasks.get(name)
680 }
681
682 pub fn workflow(&self) -> Option<&Workflow> {
686 self.data.workflow.as_ref()
687 }
688
689 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
691 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
692 }
693
694 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
696 self.data.structs.get(name)
697 }
698
699 pub fn diagnostics(&self) -> &[Diagnostic] {
701 &self.data.diagnostics
702 }
703
704 pub fn sort_diagnostics(&mut self) -> Self {
710 let data = &mut self.data;
711 let inner = Arc::get_mut(data).expect("should only have one reference");
712 inner.diagnostics.sort();
713 Self { data: data.clone() }
714 }
715
716 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
722 let data = &mut self.data;
723 let inner = Arc::get_mut(data).expect("should only have one reference");
724 inner.diagnostics.extend(diagnostics);
725 Self { data: data.clone() }
726 }
727
728 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
730 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
732 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
733 Ok(index) => index,
734 Err(index) => {
735 if index == 0 {
738 return None;
739 }
740
741 index - 1
742 }
743 };
744
745 loop {
748 let scope = &scopes[index];
749 if scope.span.contains(position) {
750 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
751 }
752
753 if index == 0 {
754 return None;
755 }
756
757 index -= 1;
758 }
759 }
760
761 if let Some(workflow) = &self.data.workflow {
763 if workflow.scope().span().contains(position) {
764 return find_scope(&workflow.scopes, position);
765 }
766 }
767
768 let task = match self
770 .data
771 .tasks
772 .binary_search_by_key(&position, |_, t| t.scope().span().start())
773 {
774 Ok(index) => &self.data.tasks[index],
775 Err(index) => {
776 if index == 0 {
779 return None;
780 }
781
782 &self.data.tasks[index - 1]
783 }
784 };
785
786 if task.scope().span().contains(position) {
787 return find_scope(&task.scopes, position);
788 }
789
790 None
791 }
792
793 pub fn has_errors(&self) -> bool {
802 if self
804 .diagnostics()
805 .iter()
806 .any(|d| d.severity() == Severity::Error)
807 {
808 return true;
809 }
810
811 for (_, ns) in self.namespaces() {
813 if ns.document.has_errors() {
814 return true;
815 }
816 }
817
818 false
819 }
820
821 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
824 crate::visit(self, diagnostics, visitor)
825 }
826}