wdl_cli/
eval.rs

1//! Facilities for performing a typical WDL evaluation using the `wdl-*` crates.
2
3use std::path::Path;
4
5use anyhow::anyhow;
6use tokio_util::sync::CancellationToken;
7use wdl_analysis::Document;
8use wdl_engine::EvaluatedTask;
9use wdl_engine::EvaluationError;
10use wdl_engine::EvaluationResult;
11use wdl_engine::Events;
12use wdl_engine::Inputs;
13use wdl_engine::Outputs;
14use wdl_engine::config::Config;
15use wdl_engine::v1::TaskEvaluator;
16use wdl_engine::v1::WorkflowEvaluator;
17
18use crate::inputs::OriginPaths;
19
20/// An evaluator for a WDL task or workflow.
21pub struct Evaluator<'a> {
22    /// The document that contains the task or workflow to run.
23    document: &'a Document,
24
25    /// The name of the task or workflow to run.
26    name: &'a str,
27
28    /// The inputs to the task or workflow.
29    inputs: Inputs,
30
31    /// The origin paths for the input keys.
32    origins: OriginPaths,
33
34    /// The configuration for the WDL engine.
35    config: Config,
36
37    /// The output directory.
38    output_dir: &'a Path,
39}
40
41impl<'a> Evaluator<'a> {
42    /// Creates a new task or workflow evaluator.
43    pub fn new(
44        document: &'a Document,
45        name: &'a str,
46        inputs: Inputs,
47        origins: OriginPaths,
48        config: Config,
49        output_dir: &'a Path,
50    ) -> Self {
51        Self {
52            document,
53            name,
54            inputs,
55            origins,
56            config,
57            output_dir,
58        }
59    }
60
61    /// Runs a WDL task or workflow evaluation.
62    pub async fn run(
63        mut self,
64        token: CancellationToken,
65        events: Events,
66    ) -> EvaluationResult<Outputs> {
67        match self.inputs {
68            Inputs::Task(ref mut inputs) => {
69                let task = self.document.task_by_name(self.name).ok_or_else(|| {
70                    anyhow!(
71                        "document does not contain a task named `{name}`",
72                        name = self.name
73                    )
74                })?;
75
76                // Ensure all the paths specified in the inputs are relative to
77                // their respective origin paths.
78                inputs.join_paths(task, |key| {
79                    self.origins
80                        .get(key)
81                        .ok_or(anyhow!("unable to find origin path for key `{key}`"))
82                })?;
83
84                let evaluator = TaskEvaluator::new(self.config, token, events).await?;
85
86                evaluator
87                    .evaluate(self.document, task, inputs, self.output_dir)
88                    .await
89                    .and_then(EvaluatedTask::into_result)
90            }
91            Inputs::Workflow(mut inputs) => {
92                let workflow = self
93                    .document
94                    .workflow()
95                    .ok_or_else(|| anyhow!("document does not contain a workflow"))?;
96
97                if workflow.name() != self.name {
98                    return Err(EvaluationError::Other(anyhow!(
99                        "document does not contain a workflow named `{name}`",
100                        name = self.name
101                    )));
102                }
103
104                // Ensure all the paths specified in the inputs are relative to
105                // their respective origin paths.
106                inputs.join_paths(workflow, |key| {
107                    self.origins
108                        .get(key)
109                        .ok_or(anyhow!("unable to find origin path for key `{key}`"))
110                })?;
111
112                let evaluator = WorkflowEvaluator::new(self.config, token, events).await?;
113                evaluator
114                    .evaluate(self.document, inputs, self.output_dir)
115                    .await
116            }
117        }
118    }
119}