wdl_engine/
outputs.rs

1//! Implementation of workflow and task outputs.
2
3use std::cmp::Ordering;
4
5use indexmap::IndexMap;
6use serde::Serialize;
7
8use crate::Scope;
9use crate::Value;
10
11/// Represents outputs of a WDL workflow or task.
12#[derive(Default, Debug)]
13pub struct Outputs {
14    /// The name of the outputs.
15    ///
16    /// This may be set to the name of the call in a workflow or the task name
17    /// for a direct task execution.
18    name: Option<String>,
19    /// The map of output name to value.
20    values: IndexMap<String, Value>,
21}
22
23impl Outputs {
24    /// Constructs a new outputs collection.
25    pub fn new() -> Self {
26        Self::default()
27    }
28
29    /// Sets the name of the outputs collection.
30    ///
31    /// Typically this is the name of the call in a workflow.
32    pub fn with_name(mut self, name: impl Into<String>) -> Self {
33        self.name = Some(name.into());
34        self
35    }
36
37    /// Iterates over the outputs in the collection.
38    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> + use<'_> {
39        self.values.iter().map(|(k, v)| (k.as_str(), v))
40    }
41
42    /// Gets an output of the collection by name.
43    ///
44    /// Returns `None` if an output with the given name doesn't exist.
45    pub fn get(&self, name: &str) -> Option<&Value> {
46        self.values.get(name)
47    }
48
49    /// Sorts the outputs according to a callback.
50    pub(crate) fn sort_by(&mut self, mut cmp: impl FnMut(&str, &str) -> Ordering) {
51        // We can sort unstable as none of the keys are equivalent in ordering; thus the
52        // resulting sort is still considered to be stable
53        self.values.sort_unstable_by(move |a, _, b, _| {
54            let ordering = cmp(a, b);
55            assert!(ordering != Ordering::Equal);
56            ordering
57        });
58    }
59}
60
61impl Serialize for Outputs {
62    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
63    where
64        S: serde::Serializer,
65    {
66        use serde::ser::SerializeMap;
67
68        /// Helper `Serialize` implementation for serializing element values.
69        struct Serialize<'a> {
70            /// The value being serialized.
71            value: &'a Value,
72        }
73
74        impl serde::Serialize for Serialize<'_> {
75            fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
76            where
77                S: serde::Serializer,
78            {
79                self.value.serialize(serializer)
80            }
81        }
82
83        let mut s = serializer.serialize_map(Some(self.values.len()))?;
84        for (k, v) in &self.values {
85            match &self.name {
86                Some(prefix) => {
87                    s.serialize_entry(&format!("{prefix}.{k}"), &Serialize { value: v })?
88                }
89                None => s.serialize_entry(k, &Serialize { value: v })?,
90            }
91        }
92
93        s.end()
94    }
95}
96
97impl From<Scope> for Outputs {
98    fn from(scope: Scope) -> Self {
99        Self {
100            name: None,
101            values: scope.into(),
102        }
103    }
104}
105
106impl FromIterator<(String, Value)> for Outputs {
107    fn from_iter<T: IntoIterator<Item = (String, Value)>>(iter: T) -> Self {
108        Self {
109            name: None,
110            values: iter.into_iter().collect(),
111        }
112    }
113}