1use std::borrow::Cow;
4use std::collections::HashMap;
5use std::path::Path;
6use std::str::FromStr;
7use std::sync::Arc;
8
9use indexmap::IndexMap;
10use petgraph::graph::NodeIndex;
11use rowan::GreenNode;
12use url::Url;
13use uuid::Uuid;
14use wdl_ast::Ast;
15use wdl_ast::AstNode;
16use wdl_ast::AstToken;
17use wdl_ast::Diagnostic;
18use wdl_ast::Severity;
19use wdl_ast::Span;
20use wdl_ast::SupportedVersion;
21use wdl_ast::SyntaxNode;
22
23use crate::DiagnosticsConfig;
24use crate::diagnostics::unused_import;
25use crate::graph::DocumentGraph;
26use crate::graph::ParseState;
27use crate::types::CallType;
28use crate::types::Type;
29
30mod v1;
31
32pub const TASK_VAR_NAME: &str = "task";
35
36#[derive(Debug)]
38pub struct Namespace {
39 span: Span,
41 source: Arc<Url>,
43 document: Document,
45 used: bool,
47 excepted: bool,
50}
51
52impl Namespace {
53 pub fn span(&self) -> Span {
55 self.span
56 }
57
58 pub fn source(&self) -> &Arc<Url> {
60 &self.source
61 }
62
63 pub fn document(&self) -> &Document {
65 &self.document
66 }
67}
68
69#[derive(Debug, Clone)]
71pub struct Struct {
72 span: Span,
77 offset: usize,
82 node: rowan::GreenNode,
86 namespace: Option<String>,
90 ty: Option<Type>,
94}
95
96impl Struct {
97 pub fn namespace(&self) -> Option<&str> {
102 self.namespace.as_deref()
103 }
104
105 pub fn ty(&self) -> Option<&Type> {
110 self.ty.as_ref()
111 }
112}
113
114#[derive(Debug, Clone)]
116pub struct Name {
117 span: Span,
119 ty: Type,
121}
122
123impl Name {
124 pub fn span(&self) -> Span {
126 self.span
127 }
128
129 pub fn ty(&self) -> &Type {
131 &self.ty
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
137struct ScopeIndex(usize);
138
139#[derive(Debug)]
141struct Scope {
142 parent: Option<ScopeIndex>,
146 span: Span,
148 names: IndexMap<String, Name>,
150}
151
152impl Scope {
153 fn new(parent: Option<ScopeIndex>, span: Span) -> Self {
155 Self {
156 parent,
157 span,
158 names: Default::default(),
159 }
160 }
161
162 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
164 self.names.insert(name.into(), Name { span, ty });
165 }
166}
167
168#[derive(Debug, Clone, Copy)]
170pub struct ScopeRef<'a> {
171 scopes: &'a [Scope],
173 index: ScopeIndex,
175}
176
177impl<'a> ScopeRef<'a> {
178 fn new(scopes: &'a [Scope], index: ScopeIndex) -> Self {
180 Self { scopes, index }
181 }
182
183 pub fn span(&self) -> Span {
185 self.scopes[self.index.0].span
186 }
187
188 pub fn parent(&self) -> Option<Self> {
192 self.scopes[self.index.0].parent.map(|p| Self {
193 scopes: self.scopes,
194 index: p,
195 })
196 }
197
198 pub fn names(&self) -> impl Iterator<Item = (&str, &Name)> + use<'_> {
200 self.scopes[self.index.0]
201 .names
202 .iter()
203 .map(|(name, n)| (name.as_str(), n))
204 }
205
206 pub fn local(&self, name: &str) -> Option<&Name> {
210 self.scopes[self.index.0].names.get(name)
211 }
212
213 pub fn lookup(&self, name: &str) -> Option<&Name> {
217 let mut current = Some(self.index);
218
219 while let Some(index) = current {
220 if let Some(name) = self.scopes[index.0].names.get(name) {
221 return Some(name);
222 }
223
224 current = self.scopes[index.0].parent;
225 }
226
227 None
228 }
229}
230
231#[derive(Debug)]
233struct ScopeRefMut<'a> {
234 scopes: &'a mut [Scope],
236 index: ScopeIndex,
238}
239
240impl<'a> ScopeRefMut<'a> {
241 fn new(scopes: &'a mut [Scope], index: ScopeIndex) -> Self {
243 Self { scopes, index }
244 }
245
246 pub fn lookup(&self, name: &str) -> Option<&Name> {
250 let mut current = Some(self.index);
251
252 while let Some(index) = current {
253 if let Some(name) = self.scopes[index.0].names.get(name) {
254 return Some(name);
255 }
256
257 current = self.scopes[index.0].parent;
258 }
259
260 None
261 }
262
263 pub fn insert(&mut self, name: impl Into<String>, span: Span, ty: Type) {
265 self.scopes[self.index.0]
266 .names
267 .insert(name.into(), Name { span, ty });
268 }
269
270 pub fn as_scope_ref(&'a self) -> ScopeRef<'a> {
272 ScopeRef {
273 scopes: self.scopes,
274 index: self.index,
275 }
276 }
277}
278
279#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct Input {
282 ty: Type,
284 required: bool,
286}
287
288impl Input {
289 pub fn ty(&self) -> &Type {
291 &self.ty
292 }
293
294 pub fn required(&self) -> bool {
296 self.required
297 }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq)]
302pub struct Output {
303 ty: Type,
305}
306
307impl Output {
308 pub(crate) fn new(ty: Type) -> Self {
310 Self { ty }
311 }
312
313 pub fn ty(&self) -> &Type {
315 &self.ty
316 }
317}
318
319#[derive(Debug)]
321pub struct Task {
322 name_span: Span,
324 name: String,
326 scopes: Vec<Scope>,
332 inputs: Arc<IndexMap<String, Input>>,
334 outputs: Arc<IndexMap<String, Output>>,
336}
337
338impl Task {
339 pub fn name(&self) -> &str {
341 &self.name
342 }
343
344 pub fn name_span(&self) -> Span {
346 self.name_span
347 }
348
349 pub fn scope(&self) -> ScopeRef<'_> {
351 ScopeRef::new(&self.scopes, ScopeIndex(0))
352 }
353
354 pub fn inputs(&self) -> &IndexMap<String, Input> {
356 &self.inputs
357 }
358
359 pub fn outputs(&self) -> &IndexMap<String, Output> {
361 &self.outputs
362 }
363}
364
365#[derive(Debug)]
367pub struct Workflow {
368 name_span: Span,
370 name: String,
372 scopes: Vec<Scope>,
378 inputs: Arc<IndexMap<String, Input>>,
380 outputs: Arc<IndexMap<String, Output>>,
382 calls: HashMap<String, CallType>,
384 allows_nested_inputs: bool,
386}
387
388impl Workflow {
389 pub fn name(&self) -> &str {
391 &self.name
392 }
393
394 pub fn name_span(&self) -> Span {
396 self.name_span
397 }
398
399 pub fn scope(&self) -> ScopeRef<'_> {
401 ScopeRef::new(&self.scopes, ScopeIndex(0))
402 }
403
404 pub fn inputs(&self) -> &IndexMap<String, Input> {
406 &self.inputs
407 }
408
409 pub fn outputs(&self) -> &IndexMap<String, Output> {
411 &self.outputs
412 }
413
414 pub fn calls(&self) -> &HashMap<String, CallType> {
416 &self.calls
417 }
418
419 pub fn allows_nested_inputs(&self) -> bool {
421 self.allows_nested_inputs
422 }
423}
424
425#[derive(Debug)]
427struct DocumentData {
428 root: Option<GreenNode>,
432 id: Arc<String>,
436 uri: Arc<Url>,
438 version: Option<SupportedVersion>,
440 namespaces: IndexMap<String, Namespace>,
442 tasks: IndexMap<String, Task>,
444 workflow: Option<Workflow>,
446 structs: IndexMap<String, Struct>,
448 diagnostics: Vec<Diagnostic>,
450}
451
452impl DocumentData {
453 fn new(
455 uri: Arc<Url>,
456 root: Option<GreenNode>,
457 version: Option<SupportedVersion>,
458 diagnostics: Vec<Diagnostic>,
459 ) -> Self {
460 Self {
461 root,
462 id: Uuid::new_v4().to_string().into(),
463 uri,
464 version,
465 namespaces: Default::default(),
466 tasks: Default::default(),
467 workflow: Default::default(),
468 structs: Default::default(),
469 diagnostics,
470 }
471 }
472}
473
474#[derive(Debug, Clone)]
478pub struct Document {
479 data: Arc<DocumentData>,
481}
482
483impl Document {
484 pub(crate) fn default_from_uri(uri: Arc<Url>) -> Self {
486 Self {
487 data: Arc::new(DocumentData::new(uri, None, None, Default::default())),
488 }
489 }
490
491 pub(crate) fn from_graph_node(
493 config: DiagnosticsConfig,
494 graph: &DocumentGraph,
495 index: NodeIndex,
496 ) -> Self {
497 let node = graph.get(index);
498
499 let diagnostics = match node.parse_state() {
500 ParseState::NotParsed => panic!("node should have been parsed"),
501 ParseState::Error(_) => return Self::default_from_uri(node.uri().clone()),
502 ParseState::Parsed { diagnostics, .. } => diagnostics,
503 };
504
505 let root = node.root().expect("node should have been parsed");
506 let (version, config) = match root.version_statement() {
507 Some(stmt) => (stmt.version(), config.excepted_for_node(stmt.inner())),
508 None => {
509 return Self {
511 data: Arc::new(DocumentData::new(
512 node.uri().clone(),
513 Some(root.inner().green().into()),
514 None,
515 diagnostics.to_vec(),
516 )),
517 };
518 }
519 };
520
521 let mut data = DocumentData::new(
522 node.uri().clone(),
523 Some(root.inner().green().into()),
524 SupportedVersion::from_str(version.text()).ok(),
525 diagnostics.to_vec(),
526 );
527 match root.ast() {
528 Ast::Unsupported => {}
529 Ast::V1(ast) => v1::populate_document(&mut data, config, graph, index, &ast, &version),
530 }
531
532 if let Some(severity) = config.unused_import {
534 let DocumentData {
535 namespaces,
536 diagnostics,
537 ..
538 } = &mut data;
539
540 diagnostics.extend(
541 namespaces
542 .iter()
543 .filter(|(_, ns)| !ns.used && !ns.excepted)
544 .map(|(name, ns)| unused_import(name, ns.span()).with_severity(severity)),
545 );
546 }
547
548 Self {
549 data: Arc::new(data),
550 }
551 }
552
553 pub fn root(&self) -> wdl_ast::Document {
559 wdl_ast::Document::cast(SyntaxNode::new_root(
560 self.data.root.clone().expect("should have a root"),
561 ))
562 .expect("should cast")
563 }
564
565 pub fn id(&self) -> &Arc<String> {
569 &self.data.id
570 }
571
572 pub fn uri(&self) -> &Arc<Url> {
574 &self.data.uri
575 }
576
577 pub fn path(&self) -> Cow<'_, str> {
584 if let Ok(path) = self.data.uri.to_file_path() {
585 if let Some(path) = std::env::current_dir()
586 .ok()
587 .and_then(|cwd| path.strip_prefix(cwd).ok().and_then(Path::to_str))
588 {
589 return path.to_string().into();
590 }
591
592 if let Ok(path) = path.into_os_string().into_string() {
593 return path.into();
594 }
595 }
596
597 self.data.uri.as_str().into()
598 }
599
600 pub fn version(&self) -> Option<SupportedVersion> {
605 self.data.version
606 }
607
608 pub fn namespaces(&self) -> impl Iterator<Item = (&str, &Namespace)> {
610 self.data.namespaces.iter().map(|(n, ns)| (n.as_str(), ns))
611 }
612
613 pub fn namespace(&self, name: &str) -> Option<&Namespace> {
615 self.data.namespaces.get(name)
616 }
617
618 pub fn tasks(&self) -> impl Iterator<Item = &Task> {
620 self.data.tasks.iter().map(|(_, t)| t)
621 }
622
623 pub fn task_by_name(&self, name: &str) -> Option<&Task> {
625 self.data.tasks.get(name)
626 }
627
628 pub fn workflow(&self) -> Option<&Workflow> {
632 self.data.workflow.as_ref()
633 }
634
635 pub fn structs(&self) -> impl Iterator<Item = (&str, &Struct)> {
637 self.data.structs.iter().map(|(n, s)| (n.as_str(), s))
638 }
639
640 pub fn struct_by_name(&self, name: &str) -> Option<&Struct> {
642 self.data.structs.get(name)
643 }
644
645 pub fn diagnostics(&self) -> &[Diagnostic] {
647 &self.data.diagnostics
648 }
649
650 pub fn sort_diagnostics(&mut self) -> Self {
656 let data = &mut self.data;
657 let inner = Arc::get_mut(data).expect("should only have one reference");
658 inner.diagnostics.sort();
659 Self { data: data.clone() }
660 }
661
662 pub fn extend_diagnostics(&mut self, diagnostics: Vec<Diagnostic>) -> Self {
668 let data = &mut self.data;
669 let inner = Arc::get_mut(data).expect("should only have one reference");
670 inner.diagnostics.extend(diagnostics);
671 Self { data: data.clone() }
672 }
673
674 pub fn find_scope_by_position(&self, position: usize) -> Option<ScopeRef<'_>> {
676 fn find_scope(scopes: &[Scope], position: usize) -> Option<ScopeRef<'_>> {
678 let mut index = match scopes.binary_search_by_key(&position, |s| s.span.start()) {
679 Ok(index) => index,
680 Err(index) => {
681 if index == 0 {
684 return None;
685 }
686
687 index - 1
688 }
689 };
690
691 loop {
694 let scope = &scopes[index];
695 if scope.span.contains(position) {
696 return Some(ScopeRef::new(scopes, ScopeIndex(index)));
697 }
698
699 if index == 0 {
700 return None;
701 }
702
703 index -= 1;
704 }
705 }
706
707 if let Some(workflow) = &self.data.workflow {
709 if workflow.scope().span().contains(position) {
710 return find_scope(&workflow.scopes, position);
711 }
712 }
713
714 let task = match self
716 .data
717 .tasks
718 .binary_search_by_key(&position, |_, t| t.scope().span().start())
719 {
720 Ok(index) => &self.data.tasks[index],
721 Err(index) => {
722 if index == 0 {
725 return None;
726 }
727
728 &self.data.tasks[index - 1]
729 }
730 };
731
732 if task.scope().span().contains(position) {
733 return find_scope(&task.scopes, position);
734 }
735
736 None
737 }
738
739 pub fn has_errors(&self) -> bool {
748 if self
750 .diagnostics()
751 .iter()
752 .any(|d| d.severity() == Severity::Error)
753 {
754 return true;
755 }
756
757 for (_, ns) in self.namespaces() {
759 if ns.document.has_errors() {
760 return true;
761 }
762 }
763
764 false
765 }
766
767 pub fn visit<V: crate::Visitor>(&self, diagnostics: &mut crate::Diagnostics, visitor: &mut V) {
770 crate::visit(self, diagnostics, visitor)
771 }
772}