wdl_cli/analysis/
results.rs

1//! Results of an analysis.
2
3use std::rc::Rc;
4use std::sync::Arc;
5
6use anyhow::Error;
7use nonempty::NonEmpty;
8use wdl_analysis::AnalysisResult;
9use wdl_ast::AstNode as _;
10use wdl_ast::Diagnostic;
11
12use crate::analysis::Source;
13
14/// A set of analysis results.
15///
16/// If successfully created, the set of analysis results are guaranteed not to
17/// have any associated errors (but they may contain diagnostics).
18#[derive(Debug)]
19pub struct AnalysisResults(Vec<AnalysisResult>);
20
21impl AnalysisResults {
22    /// Attempts to create a new set of analysis results.
23    ///
24    /// Returns any errors encountered during analysis. That being said, each
25    /// analysis result may have diagnostics.
26    pub fn try_new(
27        results: Vec<AnalysisResult>,
28    ) -> std::result::Result<Self, NonEmpty<Arc<Error>>> {
29        let mut errors = results.iter().flat_map(|result| result.error().cloned());
30
31        if let Some(error) = errors.next() {
32            let mut results = NonEmpty::new(error);
33            results.extend(errors);
34            Err(results)
35        } else {
36            Ok(Self(results))
37        }
38    }
39
40    /// Consumes `self` and returns the inner vector of analysis results.
41    pub fn into_inner(self) -> Vec<AnalysisResult> {
42        self.0
43    }
44
45    /// Attempts to find all analysis results that match any of the provided
46    /// sources.
47    pub fn filter(&self, sources: &[&Source]) -> impl Iterator<Item = &AnalysisResult> {
48        self.0.iter().filter(|r| {
49            let mut path = None;
50            sources.iter().any(|s| match s {
51                Source::Remote(url) | Source::File(url) => url == r.document().uri().as_ref(),
52                Source::Directory(dir) => path
53                    .get_or_insert_with(|| r.document().uri().to_file_path())
54                    .as_ref()
55                    .map(|p| p.starts_with(dir))
56                    .unwrap_or(false),
57            })
58        })
59    }
60
61    /// Iterates over the diagnostics within the analysis result set.
62    ///
63    /// The return type is an iterator that yields tuples that contain the
64    /// following:
65    ///
66    /// - The path to the file containing the diagnostic.
67    /// - The source of the file containing the diagnostic.
68    /// - A reference to the diagnostic itself.
69    pub fn diagnostics(&self) -> impl Iterator<Item = (Rc<String>, Rc<String>, &Diagnostic)> {
70        self.0.iter().flat_map(|result| {
71            let path = Rc::new(result.document().path().to_string());
72            let source = Rc::new(result.document().root().text().to_string());
73
74            result
75                .document()
76                .diagnostics()
77                .iter()
78                .map(move |diagnostic| (path.clone(), source.clone(), diagnostic))
79        })
80    }
81}
82
83impl IntoIterator for AnalysisResults {
84    type IntoIter = std::vec::IntoIter<AnalysisResult>;
85    type Item = AnalysisResult;
86
87    fn into_iter(self) -> Self::IntoIter {
88        self.into_inner().into_iter()
89    }
90}