wdl_engine/
value.rs

1//! Implementation of the WDL runtime and values.
2
3use std::cmp::Ordering;
4use std::fmt;
5use std::hash::Hash;
6use std::hash::Hasher;
7use std::path::Path;
8use std::sync::Arc;
9
10use anyhow::Context;
11use anyhow::Result;
12use anyhow::anyhow;
13use anyhow::bail;
14use indexmap::IndexMap;
15use itertools::Either;
16use ordered_float::OrderedFloat;
17use path_clean::PathClean;
18use serde::ser::SerializeMap;
19use serde::ser::SerializeSeq;
20use wdl_analysis::stdlib::STDLIB as ANALYSIS_STDLIB;
21use wdl_analysis::types::ArrayType;
22use wdl_analysis::types::CallType;
23use wdl_analysis::types::Coercible as _;
24use wdl_analysis::types::CompoundType;
25use wdl_analysis::types::Optional;
26use wdl_analysis::types::PrimitiveType;
27use wdl_analysis::types::Type;
28use wdl_ast::AstToken;
29use wdl_ast::TreeNode;
30use wdl_ast::v1;
31use wdl_ast::v1::TASK_FIELD_ATTEMPT;
32use wdl_ast::v1::TASK_FIELD_CONTAINER;
33use wdl_ast::v1::TASK_FIELD_CPU;
34use wdl_ast::v1::TASK_FIELD_DISKS;
35use wdl_ast::v1::TASK_FIELD_END_TIME;
36use wdl_ast::v1::TASK_FIELD_EXT;
37use wdl_ast::v1::TASK_FIELD_FPGA;
38use wdl_ast::v1::TASK_FIELD_GPU;
39use wdl_ast::v1::TASK_FIELD_ID;
40use wdl_ast::v1::TASK_FIELD_MEMORY;
41use wdl_ast::v1::TASK_FIELD_META;
42use wdl_ast::v1::TASK_FIELD_NAME;
43use wdl_ast::v1::TASK_FIELD_PARAMETER_META;
44use wdl_ast::v1::TASK_FIELD_RETURN_CODE;
45use wdl_grammar::lexer::v1::is_ident;
46
47use crate::EvaluationContext;
48use crate::Outputs;
49use crate::TaskExecutionConstraints;
50use crate::path;
51
52/// Implemented on coercible values.
53pub trait Coercible: Sized {
54    /// Coerces the value into the given type.
55    ///
56    /// Returns an error if the coercion is not supported.
57    fn coerce(&self, target: &Type) -> Result<Self>;
58}
59
60/// Represents a WDL runtime value.
61///
62/// Values are cheap to clone.
63#[derive(Debug, Clone)]
64pub enum Value {
65    /// The value is a literal `None` value.
66    None,
67    /// The value is a primitive value.
68    Primitive(PrimitiveValue),
69    /// The value is a compound value.
70    Compound(CompoundValue),
71    /// The value is a task variable.
72    ///
73    /// This value occurs only during command and output section evaluation in
74    /// WDL 1.2 tasks.
75    Task(TaskValue),
76    /// The value is a hints value.
77    ///
78    /// Hints values only appear in a task hints section in WDL 1.2.
79    Hints(HintsValue),
80    /// The value is an input value.
81    ///
82    /// Input values only appear in a task hints section in WDL 1.2.
83    Input(InputValue),
84    /// The value is an output value.
85    ///
86    /// Output values only appear in a task hints section in WDL 1.2.
87    Output(OutputValue),
88    /// The value is the outputs of a call.
89    Call(CallValue),
90}
91
92impl Value {
93    /// Creates an object from an iterator of V1 AST metadata items.
94    ///
95    /// # Panics
96    ///
97    /// Panics if the metadata value contains an invalid numeric value.
98    pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
99        match value {
100            v1::MetadataValue::Boolean(v) => v.value().into(),
101            v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
102            v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
103            v1::MetadataValue::String(v) => PrimitiveValue::new_string(
104                v.text()
105                    .expect("metadata strings shouldn't have placeholders")
106                    .text(),
107            )
108            .into(),
109            v1::MetadataValue::Null(_) => Self::None,
110            v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
111            v1::MetadataValue::Array(a) => Array::new_unchecked(
112                ANALYSIS_STDLIB.array_object_type().clone(),
113                a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
114            )
115            .into(),
116        }
117    }
118
119    /// Gets the type of the value.
120    pub fn ty(&self) -> Type {
121        match self {
122            Self::None => Type::None,
123            Self::Primitive(v) => v.ty(),
124            Self::Compound(v) => v.ty(),
125            Self::Task(_) => Type::Task,
126            Self::Hints(_) => Type::Hints,
127            Self::Input(_) => Type::Input,
128            Self::Output(_) => Type::Output,
129            Self::Call(v) => Type::Call(v.ty.clone()),
130        }
131    }
132
133    /// Determines if the value is `None`.
134    pub fn is_none(&self) -> bool {
135        matches!(self, Self::None)
136    }
137
138    /// Gets the value as a primitive value.
139    ///
140    /// Returns `None` if the value is not a primitive value.
141    pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
142        match self {
143            Self::Primitive(v) => Some(v),
144            _ => None,
145        }
146    }
147
148    /// Gets the value as a compound value.
149    ///
150    /// Returns `None` if the value is not a compound value.
151    pub fn as_compound(&self) -> Option<&CompoundValue> {
152        match self {
153            Self::Compound(v) => Some(v),
154            _ => None,
155        }
156    }
157
158    /// Gets the value as a `Boolean`.
159    ///
160    /// Returns `None` if the value is not a `Boolean`.
161    pub fn as_boolean(&self) -> Option<bool> {
162        match self {
163            Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
164            _ => None,
165        }
166    }
167
168    /// Unwraps the value into a `Boolean`.
169    ///
170    /// # Panics
171    ///
172    /// Panics if the value is not a `Boolean`.
173    pub fn unwrap_boolean(self) -> bool {
174        match self {
175            Self::Primitive(PrimitiveValue::Boolean(v)) => v,
176            _ => panic!("value is not a boolean"),
177        }
178    }
179
180    /// Gets the value as an `Int`.
181    ///
182    /// Returns `None` if the value is not an `Int`.
183    pub fn as_integer(&self) -> Option<i64> {
184        match self {
185            Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
186            _ => None,
187        }
188    }
189
190    /// Unwraps the value into an integer.
191    ///
192    /// # Panics
193    ///
194    /// Panics if the value is not an integer.
195    pub fn unwrap_integer(self) -> i64 {
196        match self {
197            Self::Primitive(PrimitiveValue::Integer(v)) => v,
198            _ => panic!("value is not an integer"),
199        }
200    }
201
202    /// Gets the value as a `Float`.
203    ///
204    /// Returns `None` if the value is not a `Float`.
205    pub fn as_float(&self) -> Option<f64> {
206        match self {
207            Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
208            _ => None,
209        }
210    }
211
212    /// Unwraps the value into a `Float`.
213    ///
214    /// # Panics
215    ///
216    /// Panics if the value is not a `Float`.
217    pub fn unwrap_float(self) -> f64 {
218        match self {
219            Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
220            _ => panic!("value is not a float"),
221        }
222    }
223
224    /// Gets the value as a `String`.
225    ///
226    /// Returns `None` if the value is not a `String`.
227    pub fn as_string(&self) -> Option<&Arc<String>> {
228        match self {
229            Self::Primitive(PrimitiveValue::String(s)) => Some(s),
230            _ => None,
231        }
232    }
233
234    /// Unwraps the value into a `String`.
235    ///
236    /// # Panics
237    ///
238    /// Panics if the value is not a `String`.
239    pub fn unwrap_string(self) -> Arc<String> {
240        match self {
241            Self::Primitive(PrimitiveValue::String(s)) => s,
242            _ => panic!("value is not a string"),
243        }
244    }
245
246    /// Gets the value as a `File`.
247    ///
248    /// Returns `None` if the value is not a `File`.
249    pub fn as_file(&self) -> Option<&Arc<String>> {
250        match self {
251            Self::Primitive(PrimitiveValue::File(s)) => Some(s),
252            _ => None,
253        }
254    }
255
256    /// Unwraps the value into a `File`.
257    ///
258    /// # Panics
259    ///
260    /// Panics if the value is not a `File`.
261    pub fn unwrap_file(self) -> Arc<String> {
262        match self {
263            Self::Primitive(PrimitiveValue::File(s)) => s,
264            _ => panic!("value is not a file"),
265        }
266    }
267
268    /// Gets the value as a `Directory`.
269    ///
270    /// Returns `None` if the value is not a `Directory`.
271    pub fn as_directory(&self) -> Option<&Arc<String>> {
272        match self {
273            Self::Primitive(PrimitiveValue::Directory(s)) => Some(s),
274            _ => None,
275        }
276    }
277
278    /// Unwraps the value into a `Directory`.
279    ///
280    /// # Panics
281    ///
282    /// Panics if the value is not a `Directory`.
283    pub fn unwrap_directory(self) -> Arc<String> {
284        match self {
285            Self::Primitive(PrimitiveValue::Directory(s)) => s,
286            _ => panic!("value is not a directory"),
287        }
288    }
289
290    /// Gets the value as a `Pair`.
291    ///
292    /// Returns `None` if the value is not a `Pair`.
293    pub fn as_pair(&self) -> Option<&Pair> {
294        match self {
295            Self::Compound(CompoundValue::Pair(v)) => Some(v),
296            _ => None,
297        }
298    }
299
300    /// Unwraps the value into a `Pair`.
301    ///
302    /// # Panics
303    ///
304    /// Panics if the value is not a `Pair`.
305    pub fn unwrap_pair(self) -> Pair {
306        match self {
307            Self::Compound(CompoundValue::Pair(v)) => v,
308            _ => panic!("value is not a pair"),
309        }
310    }
311
312    /// Gets the value as an `Array`.
313    ///
314    /// Returns `None` if the value is not an `Array`.
315    pub fn as_array(&self) -> Option<&Array> {
316        match self {
317            Self::Compound(CompoundValue::Array(v)) => Some(v),
318            _ => None,
319        }
320    }
321
322    /// Unwraps the value into an `Array`.
323    ///
324    /// # Panics
325    ///
326    /// Panics if the value is not an `Array`.
327    pub fn unwrap_array(self) -> Array {
328        match self {
329            Self::Compound(CompoundValue::Array(v)) => v,
330            _ => panic!("value is not an array"),
331        }
332    }
333
334    /// Gets the value as a `Map`.
335    ///
336    /// Returns `None` if the value is not a `Map`.
337    pub fn as_map(&self) -> Option<&Map> {
338        match self {
339            Self::Compound(CompoundValue::Map(v)) => Some(v),
340            _ => None,
341        }
342    }
343
344    /// Unwraps the value into a `Map`.
345    ///
346    /// # Panics
347    ///
348    /// Panics if the value is not a `Map`.
349    pub fn unwrap_map(self) -> Map {
350        match self {
351            Self::Compound(CompoundValue::Map(v)) => v,
352            _ => panic!("value is not a map"),
353        }
354    }
355
356    /// Gets the value as an `Object`.
357    ///
358    /// Returns `None` if the value is not an `Object`.
359    pub fn as_object(&self) -> Option<&Object> {
360        match self {
361            Self::Compound(CompoundValue::Object(v)) => Some(v),
362            _ => None,
363        }
364    }
365
366    /// Unwraps the value into an `Object`.
367    ///
368    /// # Panics
369    ///
370    /// Panics if the value is not an `Object`.
371    pub fn unwrap_object(self) -> Object {
372        match self {
373            Self::Compound(CompoundValue::Object(v)) => v,
374            _ => panic!("value is not an object"),
375        }
376    }
377
378    /// Gets the value as a `Struct`.
379    ///
380    /// Returns `None` if the value is not a `Struct`.
381    pub fn as_struct(&self) -> Option<&Struct> {
382        match self {
383            Self::Compound(CompoundValue::Struct(v)) => Some(v),
384            _ => None,
385        }
386    }
387
388    /// Unwraps the value into a `Struct`.
389    ///
390    /// # Panics
391    ///
392    /// Panics if the value is not a `Map`.
393    pub fn unwrap_struct(self) -> Struct {
394        match self {
395            Self::Compound(CompoundValue::Struct(v)) => v,
396            _ => panic!("value is not a struct"),
397        }
398    }
399
400    /// Gets the value as a task.
401    ///
402    /// Returns `None` if the value is not a task.
403    pub fn as_task(&self) -> Option<&TaskValue> {
404        match self {
405            Self::Task(v) => Some(v),
406            _ => None,
407        }
408    }
409
410    /// Gets a mutable reference to the value as a task.
411    ///
412    /// Returns `None` if the value is not a task.
413    pub(crate) fn as_task_mut(&mut self) -> Option<&mut TaskValue> {
414        match self {
415            Self::Task(v) => Some(v),
416            _ => None,
417        }
418    }
419
420    /// Unwraps the value into a task.
421    ///
422    /// # Panics
423    ///
424    /// Panics if the value is not a task.
425    pub fn unwrap_task(self) -> TaskValue {
426        match self {
427            Self::Task(v) => v,
428            _ => panic!("value is not a task"),
429        }
430    }
431
432    /// Gets the value as a hints value.
433    ///
434    /// Returns `None` if the value is not a hints value.
435    pub fn as_hints(&self) -> Option<&HintsValue> {
436        match self {
437            Self::Hints(v) => Some(v),
438            _ => None,
439        }
440    }
441
442    /// Unwraps the value into a hints value.
443    ///
444    /// # Panics
445    ///
446    /// Panics if the value is not a hints value.
447    pub fn unwrap_hints(self) -> HintsValue {
448        match self {
449            Self::Hints(v) => v,
450            _ => panic!("value is not a hints value"),
451        }
452    }
453
454    /// Gets the value as a call value.
455    ///
456    /// Returns `None` if the value is not a call value.
457    pub fn as_call(&self) -> Option<&CallValue> {
458        match self {
459            Self::Call(v) => Some(v),
460            _ => None,
461        }
462    }
463
464    /// Unwraps the value into a call value.
465    ///
466    /// # Panics
467    ///
468    /// Panics if the value is not a call value.
469    pub fn unwrap_call(self) -> CallValue {
470        match self {
471            Self::Call(v) => v,
472            _ => panic!("value is not a call value"),
473        }
474    }
475
476    /// Visits each `File` or `Directory` value contained in this value.
477    ///
478    /// Note that paths may be specified as URLs.
479    pub(crate) fn visit_paths(
480        &self,
481        optional: bool,
482        cb: &mut impl FnMut(bool, &PrimitiveValue) -> Result<()>,
483    ) -> Result<()> {
484        match self {
485            Self::Primitive(v) => v.visit_paths(optional, cb),
486            Self::Compound(v) => v.visit_paths(cb),
487            _ => Ok(()),
488        }
489    }
490
491    /// Mutably visits each `File` or `Directory` value contained in this value.
492    ///
493    /// If the provided callback returns `Ok(false)`, the `File` or `Directory`
494    /// value will be replaced with `None`.
495    ///
496    /// Note that paths may be specified as URLs.
497    pub(crate) fn visit_paths_mut(
498        &mut self,
499        optional: bool,
500        cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
501    ) -> Result<()> {
502        match self {
503            Self::Primitive(v) => {
504                if !v.visit_paths_mut(optional, cb)? {
505                    *self = Value::None;
506                }
507
508                Ok(())
509            }
510            Self::Compound(v) => v.visit_paths_mut(cb),
511            _ => Ok(()),
512        }
513    }
514
515    /// Determines if two values have equality according to the WDL
516    /// specification.
517    ///
518    /// Returns `None` if the two values cannot be compared for equality.
519    pub fn equals(left: &Self, right: &Self) -> Option<bool> {
520        match (left, right) {
521            (Value::None, Value::None) => Some(true),
522            (Value::None, _) | (_, Value::None) => Some(false),
523            (Value::Primitive(left), Value::Primitive(right)) => {
524                Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
525            }
526            (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
527            _ => None,
528        }
529    }
530}
531
532impl fmt::Display for Value {
533    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
534        match self {
535            Self::None => write!(f, "None"),
536            Self::Primitive(v) => v.fmt(f),
537            Self::Compound(v) => v.fmt(f),
538            Self::Task(_) => write!(f, "task"),
539            Self::Hints(v) => v.fmt(f),
540            Self::Input(v) => v.fmt(f),
541            Self::Output(v) => v.fmt(f),
542            Self::Call(c) => c.fmt(f),
543        }
544    }
545}
546
547impl Coercible for Value {
548    fn coerce(&self, target: &Type) -> Result<Self> {
549        if target.is_union() || target.is_none() || self.ty().eq(target) {
550            return Ok(self.clone());
551        }
552
553        match self {
554            Self::None => {
555                if target.is_optional() {
556                    Ok(Self::None)
557                } else {
558                    bail!("cannot coerce `None` to non-optional type `{target}`");
559                }
560            }
561            Self::Primitive(v) => v.coerce(target).map(Self::Primitive),
562            Self::Compound(v) => v.coerce(target).map(Self::Compound),
563            Self::Task(_) => {
564                if matches!(target, Type::Task) {
565                    return Ok(self.clone());
566                }
567
568                bail!("task variables cannot be coerced to any other type");
569            }
570            Self::Hints(_) => {
571                if matches!(target, Type::Hints) {
572                    return Ok(self.clone());
573                }
574
575                bail!("hints values cannot be coerced to any other type");
576            }
577            Self::Input(_) => {
578                if matches!(target, Type::Input) {
579                    return Ok(self.clone());
580                }
581
582                bail!("input values cannot be coerced to any other type");
583            }
584            Self::Output(_) => {
585                if matches!(target, Type::Output) {
586                    return Ok(self.clone());
587                }
588
589                bail!("output values cannot be coerced to any other type");
590            }
591            Self::Call(_) => {
592                bail!("call values cannot be coerced to any other type");
593            }
594        }
595    }
596}
597
598impl From<bool> for Value {
599    fn from(value: bool) -> Self {
600        Self::Primitive(value.into())
601    }
602}
603
604impl From<i64> for Value {
605    fn from(value: i64) -> Self {
606        Self::Primitive(value.into())
607    }
608}
609
610impl TryFrom<u64> for Value {
611    type Error = std::num::TryFromIntError;
612
613    fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
614        let value: i64 = value.try_into()?;
615        Ok(value.into())
616    }
617}
618
619impl From<f64> for Value {
620    fn from(value: f64) -> Self {
621        Self::Primitive(value.into())
622    }
623}
624
625impl From<String> for Value {
626    fn from(value: String) -> Self {
627        Self::Primitive(value.into())
628    }
629}
630
631impl From<PrimitiveValue> for Value {
632    fn from(value: PrimitiveValue) -> Self {
633        Self::Primitive(value)
634    }
635}
636
637impl From<Option<PrimitiveValue>> for Value {
638    fn from(value: Option<PrimitiveValue>) -> Self {
639        match value {
640            Some(v) => v.into(),
641            None => Self::None,
642        }
643    }
644}
645
646impl From<CompoundValue> for Value {
647    fn from(value: CompoundValue) -> Self {
648        Self::Compound(value)
649    }
650}
651
652impl From<Pair> for Value {
653    fn from(value: Pair) -> Self {
654        Self::Compound(value.into())
655    }
656}
657
658impl From<Array> for Value {
659    fn from(value: Array) -> Self {
660        Self::Compound(value.into())
661    }
662}
663
664impl From<Map> for Value {
665    fn from(value: Map) -> Self {
666        Self::Compound(value.into())
667    }
668}
669
670impl From<Object> for Value {
671    fn from(value: Object) -> Self {
672        Self::Compound(value.into())
673    }
674}
675
676impl From<Struct> for Value {
677    fn from(value: Struct) -> Self {
678        Self::Compound(value.into())
679    }
680}
681
682impl From<TaskValue> for Value {
683    fn from(value: TaskValue) -> Self {
684        Self::Task(value)
685    }
686}
687
688impl From<HintsValue> for Value {
689    fn from(value: HintsValue) -> Self {
690        Self::Hints(value)
691    }
692}
693
694impl From<CallValue> for Value {
695    fn from(value: CallValue) -> Self {
696        Self::Call(value)
697    }
698}
699
700impl<'de> serde::Deserialize<'de> for Value {
701    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
702    where
703        D: serde::Deserializer<'de>,
704    {
705        use serde::Deserialize as _;
706
707        /// Helper for deserializing the elements of sequences and maps
708        struct Deserialize;
709
710        impl<'de> serde::de::DeserializeSeed<'de> for Deserialize {
711            type Value = Value;
712
713            fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
714            where
715                D: serde::Deserializer<'de>,
716            {
717                deserializer.deserialize_any(Visitor)
718            }
719        }
720
721        /// Visitor for deserialization.
722        struct Visitor;
723
724        impl<'de> serde::de::Visitor<'de> for Visitor {
725            type Value = Value;
726
727            fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
728            where
729                E: serde::de::Error,
730            {
731                Ok(Value::None)
732            }
733
734            fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
735            where
736                E: serde::de::Error,
737            {
738                Ok(Value::None)
739            }
740
741            fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
742            where
743                D: serde::Deserializer<'de>,
744            {
745                Value::deserialize(deserializer)
746            }
747
748            fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
749            where
750                E: serde::de::Error,
751            {
752                Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
753            }
754
755            fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
756            where
757                E: serde::de::Error,
758            {
759                Ok(Value::Primitive(PrimitiveValue::Integer(v)))
760            }
761
762            fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
763            where
764                E: serde::de::Error,
765            {
766                Ok(Value::Primitive(PrimitiveValue::Integer(
767                    v.try_into().map_err(|_| {
768                        E::custom("integer not in range for a 64-bit signed integer")
769                    })?,
770                )))
771            }
772
773            fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
774            where
775                E: serde::de::Error,
776            {
777                Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
778            }
779
780            fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
781            where
782                E: serde::de::Error,
783            {
784                Ok(Value::Primitive(PrimitiveValue::new_string(v)))
785            }
786
787            fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
788            where
789                E: serde::de::Error,
790            {
791                Ok(Value::Primitive(PrimitiveValue::new_string(v)))
792            }
793
794            fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
795            where
796                A: serde::de::SeqAccess<'de>,
797            {
798                use serde::de::Error;
799
800                let mut elements = Vec::new();
801                while let Some(v) = seq.next_element_seed(Deserialize)? {
802                    elements.push(v);
803                }
804
805                let element_ty = elements
806                    .iter()
807                    .try_fold(None, |mut ty, element| {
808                        let element_ty = element.ty();
809                        let ty = ty.get_or_insert(element_ty.clone());
810                        ty.common_type(&element_ty).map(Some).ok_or_else(|| {
811                            A::Error::custom(format!(
812                                "a common element type does not exist between `{ty}` and \
813                                 `{element_ty}`"
814                            ))
815                        })
816                    })?
817                    .unwrap_or(Type::Union);
818
819                let ty: Type = ArrayType::new(element_ty).into();
820                Ok(Array::new(ty.clone(), elements)
821                    .map_err(|e| A::Error::custom(format!("cannot coerce value to `{ty}`: {e:#}")))?
822                    .into())
823            }
824
825            fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
826            where
827                A: serde::de::MapAccess<'de>,
828            {
829                use serde::de::Error;
830
831                let mut members = IndexMap::new();
832                while let Some(key) = map.next_key::<String>()? {
833                    if !is_ident(&key) {
834                        return Err(A::Error::custom(format!(
835                            "object key `{key}` is not a valid WDL identifier"
836                        )));
837                    }
838
839                    members.insert(key, map.next_value_seed(Deserialize)?);
840                }
841
842                Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
843            }
844
845            fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
846                write!(f, "a WDL value")
847            }
848        }
849
850        deserializer.deserialize_any(Visitor)
851    }
852}
853
854/// Represents a primitive WDL value.
855///
856/// Primitive values are cheap to clone.
857#[derive(Debug, Clone)]
858pub enum PrimitiveValue {
859    /// The value is a `Boolean`.
860    Boolean(bool),
861    /// The value is an `Int`.
862    Integer(i64),
863    /// The value is a `Float`.
864    Float(OrderedFloat<f64>),
865    /// The value is a `String`.
866    String(Arc<String>),
867    /// The value is a `File`.
868    File(Arc<String>),
869    /// The value is a `Directory`.
870    Directory(Arc<String>),
871}
872
873impl PrimitiveValue {
874    /// Creates a new `String` value.
875    pub fn new_string(s: impl Into<String>) -> Self {
876        Self::String(Arc::new(s.into()))
877    }
878
879    /// Creates a new `File` value.
880    pub fn new_file(s: impl Into<String>) -> Self {
881        Self::File(Arc::new(s.into()))
882    }
883
884    /// Creates a new `Directory` value.
885    pub fn new_directory(s: impl Into<String>) -> Self {
886        Self::Directory(Arc::new(s.into()))
887    }
888
889    /// Gets the type of the value.
890    pub fn ty(&self) -> Type {
891        match self {
892            Self::Boolean(_) => PrimitiveType::Boolean.into(),
893            Self::Integer(_) => PrimitiveType::Integer.into(),
894            Self::Float(_) => PrimitiveType::Float.into(),
895            Self::String(_) => PrimitiveType::String.into(),
896            Self::File(_) => PrimitiveType::File.into(),
897            Self::Directory(_) => PrimitiveType::Directory.into(),
898        }
899    }
900
901    /// Gets the value as a `Boolean`.
902    ///
903    /// Returns `None` if the value is not a `Boolean`.
904    pub fn as_boolean(&self) -> Option<bool> {
905        match self {
906            Self::Boolean(v) => Some(*v),
907            _ => None,
908        }
909    }
910
911    /// Unwraps the value into a `Boolean`.
912    ///
913    /// # Panics
914    ///
915    /// Panics if the value is not a `Boolean`.
916    pub fn unwrap_boolean(self) -> bool {
917        match self {
918            Self::Boolean(v) => v,
919            _ => panic!("value is not a boolean"),
920        }
921    }
922
923    /// Gets the value as an `Int`.
924    ///
925    /// Returns `None` if the value is not an `Int`.
926    pub fn as_integer(&self) -> Option<i64> {
927        match self {
928            Self::Integer(v) => Some(*v),
929            _ => None,
930        }
931    }
932
933    /// Unwraps the value into an integer.
934    ///
935    /// # Panics
936    ///
937    /// Panics if the value is not an integer.
938    pub fn unwrap_integer(self) -> i64 {
939        match self {
940            Self::Integer(v) => v,
941            _ => panic!("value is not an integer"),
942        }
943    }
944
945    /// Gets the value as a `Float`.
946    ///
947    /// Returns `None` if the value is not a `Float`.
948    pub fn as_float(&self) -> Option<f64> {
949        match self {
950            Self::Float(v) => Some((*v).into()),
951            _ => None,
952        }
953    }
954
955    /// Unwraps the value into a `Float`.
956    ///
957    /// # Panics
958    ///
959    /// Panics if the value is not a `Float`.
960    pub fn unwrap_float(self) -> f64 {
961        match self {
962            Self::Float(v) => v.into(),
963            _ => panic!("value is not a float"),
964        }
965    }
966
967    /// Gets the value as a `String`.
968    ///
969    /// Returns `None` if the value is not a `String`.
970    pub fn as_string(&self) -> Option<&Arc<String>> {
971        match self {
972            Self::String(s) => Some(s),
973            _ => None,
974        }
975    }
976
977    /// Unwraps the value into a `String`.
978    ///
979    /// # Panics
980    ///
981    /// Panics if the value is not a `String`.
982    pub fn unwrap_string(self) -> Arc<String> {
983        match self {
984            Self::String(s) => s,
985            _ => panic!("value is not a string"),
986        }
987    }
988
989    /// Gets the value as a `File`.
990    ///
991    /// Returns `None` if the value is not a `File`.
992    pub fn as_file(&self) -> Option<&Arc<String>> {
993        match self {
994            Self::File(s) => Some(s),
995            _ => None,
996        }
997    }
998
999    /// Unwraps the value into a `File`.
1000    ///
1001    /// # Panics
1002    ///
1003    /// Panics if the value is not a `File`.
1004    pub fn unwrap_file(self) -> Arc<String> {
1005        match self {
1006            Self::File(s) => s,
1007            _ => panic!("value is not a file"),
1008        }
1009    }
1010
1011    /// Gets the value as a `Directory`.
1012    ///
1013    /// Returns `None` if the value is not a `Directory`.
1014    pub fn as_directory(&self) -> Option<&Arc<String>> {
1015        match self {
1016            Self::Directory(s) => Some(s),
1017            _ => None,
1018        }
1019    }
1020
1021    /// Unwraps the value into a `Directory`.
1022    ///
1023    /// # Panics
1024    ///
1025    /// Panics if the value is not a `Directory`.
1026    pub fn unwrap_directory(self) -> Arc<String> {
1027        match self {
1028            Self::Directory(s) => s,
1029            _ => panic!("value is not a directory"),
1030        }
1031    }
1032
1033    /// Compares two values for an ordering according to the WDL specification.
1034    ///
1035    /// Unlike a `PartialOrd` implementation, this takes into account automatic
1036    /// coercions.
1037    ///
1038    /// Returns `None` if the values cannot be compared based on their types.
1039    pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1040        match (left, right) {
1041            (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1042            (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1043            (Self::Integer(left), Self::Float(right)) => {
1044                Some(OrderedFloat(*left as f64).cmp(right))
1045            }
1046            (Self::Float(left), Self::Integer(right)) => {
1047                Some(left.cmp(&OrderedFloat(*right as f64)))
1048            }
1049            (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1050            (Self::String(left), Self::String(right))
1051            | (Self::String(left), Self::File(right))
1052            | (Self::String(left), Self::Directory(right))
1053            | (Self::File(left), Self::File(right))
1054            | (Self::File(left), Self::String(right))
1055            | (Self::Directory(left), Self::Directory(right))
1056            | (Self::Directory(left), Self::String(right)) => Some(left.cmp(right)),
1057            _ => None,
1058        }
1059    }
1060
1061    /// Gets a raw display of the value.
1062    ///
1063    /// This differs from the [Display][fmt::Display] implementation in that
1064    /// strings, files, and directories are not quoted and not escaped.
1065    ///
1066    /// If an evaluation context is provided, path translation is attempted.
1067    pub fn raw<'a>(
1068        &'a self,
1069        context: Option<&'a dyn EvaluationContext>,
1070    ) -> impl fmt::Display + use<'a> {
1071        /// Helper for displaying a raw value.
1072        struct Display<'a> {
1073            /// The associated evaluation context.
1074            context: Option<&'a dyn EvaluationContext>,
1075            /// The value to display.
1076            value: &'a PrimitiveValue,
1077        }
1078
1079        impl fmt::Display for Display<'_> {
1080            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1081                match self.value {
1082                    PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1083                    PrimitiveValue::Integer(v) => write!(f, "{v}"),
1084                    PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1085                    PrimitiveValue::String(v)
1086                    | PrimitiveValue::File(v)
1087                    | PrimitiveValue::Directory(v) => {
1088                        match self.context.and_then(|c| c.translate_path(v)) {
1089                            Some(path) => write!(f, "{path}", path = path.display()),
1090                            None => {
1091                                write!(f, "{v}")
1092                            }
1093                        }
1094                    }
1095                }
1096            }
1097        }
1098
1099        Display {
1100            context,
1101            value: self,
1102        }
1103    }
1104
1105    /// Visits each `File` or `Directory` value contained in this value.
1106    ///
1107    /// Note that paths may be specified as URLs.
1108    fn visit_paths(
1109        &self,
1110        optional: bool,
1111        cb: &mut impl FnMut(bool, &PrimitiveValue) -> Result<()>,
1112    ) -> Result<()> {
1113        match self {
1114            Self::File(_) | Self::Directory(_) => cb(optional, self),
1115            _ => Ok(()),
1116        }
1117    }
1118
1119    /// Mutably visits each `File` or `Directory` value contained in this value.
1120    ///
1121    /// If the provided callback returns `Ok(false)`, this `File` or `Directory`
1122    /// value will be replaced with `None`.
1123    ///
1124    /// Note that paths may be specified as URLs.
1125    fn visit_paths_mut(
1126        &mut self,
1127        optional: bool,
1128        cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
1129    ) -> Result<bool> {
1130        match self {
1131            Self::File(_) | Self::Directory(_) => cb(optional, self),
1132            _ => Ok(true),
1133        }
1134    }
1135
1136    /// Performs expansions for file and directory paths.
1137    pub(crate) fn expand_path(&mut self) -> Result<()> {
1138        let path = match self {
1139            PrimitiveValue::File(path) => path,
1140            PrimitiveValue::Directory(path) => path,
1141            _ => unreachable!("only file and directory values can be expanded"),
1142        };
1143
1144        let result = shellexpand::full(path.as_str())
1145            .context("expanding file/directory path using shell rules")?;
1146        *Arc::make_mut(path) = result.to_string();
1147
1148        Ok(())
1149    }
1150
1151    /// Joins this path to the given path.
1152    ///
1153    /// # Panics
1154    ///
1155    /// Panics if the value is not a `File` or `Directory`.
1156    pub(crate) fn join_path_to(&mut self, to: &Path) {
1157        let path = match self {
1158            PrimitiveValue::File(path) => path,
1159            PrimitiveValue::Directory(path) => path,
1160            _ => unreachable!("only file and directory values can be joined to a path"),
1161        };
1162
1163        // Don't join URLs
1164        if path::is_url(path) {
1165            return;
1166        }
1167
1168        // Perform the join
1169        if let Ok(s) = to
1170            .join(path.as_str())
1171            .clean()
1172            .into_os_string()
1173            .into_string()
1174        {
1175            *Arc::make_mut(path) = s;
1176        }
1177    }
1178
1179    /// Ensures a path value exists on disk.
1180    ///
1181    /// Returns `Ok(true)` if the path exists.
1182    ///
1183    /// Returns `Ok(false)` if the the path does not exist and the type was
1184    /// optional.
1185    ///
1186    /// Otherwise, returns an error if the path does not exist.
1187    ///
1188    /// # Panics
1189    ///
1190    /// Panics if the value is not a `File` or `Directory`.
1191    pub(crate) fn ensure_path_exists(&self, optional: bool) -> Result<bool> {
1192        let (path, is_file) = match self {
1193            PrimitiveValue::File(path) => (path, true),
1194            PrimitiveValue::Directory(path) => (path, false),
1195            _ => unreachable!("only file and directory values should be passed to the callback"),
1196        };
1197
1198        // If it's a file URL, check that the file exists
1199        if path::is_file_url(path) {
1200            let exists = path::parse_url(path)
1201                .and_then(|url| url.to_file_path().ok())
1202                .map(|p| p.exists())
1203                .unwrap_or(false);
1204            if exists {
1205                return Ok(true);
1206            }
1207
1208            if optional && !exists {
1209                return Ok(false);
1210            }
1211
1212            bail!("path `{path}` does not exist");
1213        } else if path::is_url(path) {
1214            // Treat other URLs as they exist
1215            return Ok(true);
1216        }
1217
1218        // Check for existence
1219        let path = Path::new(path.as_str());
1220        if is_file && !path.is_file() {
1221            if optional {
1222                return Ok(false);
1223            }
1224
1225            bail!("file `{path}` does not exist", path = path.display());
1226        } else if !is_file && !path.is_dir() {
1227            if optional {
1228                return Ok(false);
1229            }
1230
1231            bail!("directory `{path}` does not exist", path = path.display())
1232        }
1233
1234        Ok(true)
1235    }
1236}
1237
1238impl fmt::Display for PrimitiveValue {
1239    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240        match self {
1241            Self::Boolean(v) => write!(f, "{v}"),
1242            Self::Integer(v) => write!(f, "{v}"),
1243            Self::Float(v) => write!(f, "{v:.6?}"),
1244            Self::String(s) | Self::File(s) | Self::Directory(s) => {
1245                // TODO: handle necessary escape sequences
1246                write!(f, "\"{s}\"")
1247            }
1248        }
1249    }
1250}
1251
1252impl PartialEq for PrimitiveValue {
1253    fn eq(&self, other: &Self) -> bool {
1254        Self::compare(self, other) == Some(Ordering::Equal)
1255    }
1256}
1257
1258impl Eq for PrimitiveValue {}
1259
1260impl Hash for PrimitiveValue {
1261    fn hash<H: Hasher>(&self, state: &mut H) {
1262        match self {
1263            Self::Boolean(v) => {
1264                0.hash(state);
1265                v.hash(state);
1266            }
1267            Self::Integer(v) => {
1268                1.hash(state);
1269                v.hash(state);
1270            }
1271            Self::Float(v) => {
1272                // Hash this with the same discriminant as integer; this allows coercion from
1273                // int to float.
1274                1.hash(state);
1275                v.hash(state);
1276            }
1277            Self::String(v) | Self::File(v) | Self::Directory(v) => {
1278                // Hash these with the same discriminant; this allows coercion from file and
1279                // directory to string
1280                2.hash(state);
1281                v.hash(state);
1282            }
1283        }
1284    }
1285}
1286
1287impl From<bool> for PrimitiveValue {
1288    fn from(value: bool) -> Self {
1289        Self::Boolean(value)
1290    }
1291}
1292
1293impl From<i64> for PrimitiveValue {
1294    fn from(value: i64) -> Self {
1295        Self::Integer(value)
1296    }
1297}
1298
1299impl From<f64> for PrimitiveValue {
1300    fn from(value: f64) -> Self {
1301        Self::Float(value.into())
1302    }
1303}
1304
1305impl From<String> for PrimitiveValue {
1306    fn from(value: String) -> Self {
1307        Self::String(value.into())
1308    }
1309}
1310
1311impl Coercible for PrimitiveValue {
1312    fn coerce(&self, target: &Type) -> Result<Self> {
1313        if target.is_union() || target.is_none() || self.ty().eq(target) {
1314            return Ok(self.clone());
1315        }
1316
1317        match self {
1318            Self::Boolean(v) => {
1319                target
1320                    .as_primitive()
1321                    .and_then(|ty| match ty {
1322                        // Boolean -> Boolean
1323                        PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1324                        _ => None,
1325                    })
1326                    .with_context(|| format!("cannot coerce type `Boolean` to type `{target}`"))
1327            }
1328            Self::Integer(v) => {
1329                target
1330                    .as_primitive()
1331                    .and_then(|ty| match ty {
1332                        // Int -> Int
1333                        PrimitiveType::Integer => Some(Self::Integer(*v)),
1334                        // Int -> Float
1335                        PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1336                        _ => None,
1337                    })
1338                    .with_context(|| format!("cannot coerce type `Int` to type `{target}`"))
1339            }
1340            Self::Float(v) => {
1341                target
1342                    .as_primitive()
1343                    .and_then(|ty| match ty {
1344                        // Float -> Float
1345                        PrimitiveType::Float => Some(Self::Float(*v)),
1346                        _ => None,
1347                    })
1348                    .with_context(|| format!("cannot coerce type `Float` to type `{target}`"))
1349            }
1350            Self::String(s) => {
1351                target
1352                    .as_primitive()
1353                    .and_then(|ty| match ty {
1354                        // String -> String
1355                        PrimitiveType::String => Some(Self::String(s.clone())),
1356                        // String -> File
1357                        PrimitiveType::File => Some(Self::File(s.clone())),
1358                        // String -> Directory
1359                        PrimitiveType::Directory => Some(Self::Directory(s.clone())),
1360                        _ => None,
1361                    })
1362                    .with_context(|| format!("cannot coerce type `String` to type `{target}`"))
1363            }
1364            Self::File(s) => {
1365                target
1366                    .as_primitive()
1367                    .and_then(|ty| match ty {
1368                        // File -> File
1369                        PrimitiveType::File => Some(Self::File(s.clone())),
1370                        // File -> String
1371                        PrimitiveType::String => Some(Self::String(s.clone())),
1372                        _ => None,
1373                    })
1374                    .with_context(|| format!("cannot coerce type `File` to type `{target}`"))
1375            }
1376            Self::Directory(s) => {
1377                target
1378                    .as_primitive()
1379                    .and_then(|ty| match ty {
1380                        // Directory -> Directory
1381                        PrimitiveType::Directory => Some(Self::Directory(s.clone())),
1382                        // Directory -> String
1383                        PrimitiveType::String => Some(Self::String(s.clone())),
1384                        _ => None,
1385                    })
1386                    .with_context(|| format!("cannot coerce type `Directory` to type `{target}`"))
1387            }
1388        }
1389    }
1390}
1391
1392impl serde::Serialize for PrimitiveValue {
1393    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1394    where
1395        S: serde::Serializer,
1396    {
1397        match self {
1398            Self::Boolean(v) => v.serialize(serializer),
1399            Self::Integer(v) => v.serialize(serializer),
1400            Self::Float(v) => v.serialize(serializer),
1401            Self::String(s) | Self::File(s) | Self::Directory(s) => s.serialize(serializer),
1402        }
1403    }
1404}
1405
1406/// Represents a `Pair` value.
1407///
1408/// Pairs are cheap to clone.
1409#[derive(Debug, Clone)]
1410pub struct Pair {
1411    /// The type of the pair.
1412    ty: Type,
1413    /// The left and right values of the pair.
1414    values: Arc<(Value, Value)>,
1415}
1416
1417impl Pair {
1418    /// Creates a new `Pair` value.
1419    ///
1420    /// Returns an error if either the `left` value or the `right` value did not
1421    /// coerce to the pair's `left` type or `right` type, respectively.
1422    ///
1423    /// # Panics
1424    ///
1425    /// Panics if the given type is not a pair type.
1426    pub fn new(
1427        ty: impl Into<Type>,
1428        left: impl Into<Value>,
1429        right: impl Into<Value>,
1430    ) -> Result<Self> {
1431        let ty = ty.into();
1432        if let Type::Compound(CompoundType::Pair(ty), _) = ty {
1433            let left = left
1434                .into()
1435                .coerce(ty.left_type())
1436                .context("failed to coerce pair's left value")?;
1437            let right = right
1438                .into()
1439                .coerce(ty.right_type())
1440                .context("failed to coerce pair's right value")?;
1441            return Ok(Self::new_unchecked(
1442                Type::Compound(CompoundType::Pair(ty), false),
1443                left,
1444                right,
1445            ));
1446        }
1447
1448        panic!("type `{ty}` is not a pair type");
1449    }
1450
1451    /// Constructs a new pair without checking the given left and right conform
1452    /// to the given type.
1453    pub(crate) fn new_unchecked(ty: Type, left: Value, right: Value) -> Self {
1454        assert!(ty.as_pair().is_some());
1455        Self {
1456            ty: ty.require(),
1457            values: Arc::new((left, right)),
1458        }
1459    }
1460
1461    /// Gets the type of the `Pair`.
1462    pub fn ty(&self) -> Type {
1463        self.ty.clone()
1464    }
1465
1466    /// Gets the left value of the `Pair`.
1467    pub fn left(&self) -> &Value {
1468        &self.values.0
1469    }
1470
1471    /// Gets the right value of the `Pair`.
1472    pub fn right(&self) -> &Value {
1473        &self.values.1
1474    }
1475}
1476
1477impl fmt::Display for Pair {
1478    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1479        write!(
1480            f,
1481            "({left}, {right})",
1482            left = self.values.0,
1483            right = self.values.1
1484        )
1485    }
1486}
1487
1488/// Represents an `Array` value.
1489///
1490/// Arrays are cheap to clone.
1491#[derive(Debug, Clone)]
1492pub struct Array {
1493    /// The type of the array.
1494    ty: Type,
1495    /// The array's elements.
1496    ///
1497    /// A value of `None` indicates an empty array.
1498    elements: Option<Arc<Vec<Value>>>,
1499}
1500
1501impl Array {
1502    /// Creates a new `Array` value for the given array type.
1503    ///
1504    /// Returns an error if an element did not coerce to the array's element
1505    /// type.
1506    ///
1507    /// # Panics
1508    ///
1509    /// Panics if the given type is not an array type.
1510    pub fn new<V>(ty: impl Into<Type>, elements: impl IntoIterator<Item = V>) -> Result<Self>
1511    where
1512        V: Into<Value>,
1513    {
1514        let ty = ty.into();
1515        if let Type::Compound(CompoundType::Array(ty), _) = ty {
1516            let element_type = ty.element_type();
1517            let elements = elements
1518                .into_iter()
1519                .enumerate()
1520                .map(|(i, v)| {
1521                    let v = v.into();
1522                    v.coerce(element_type)
1523                        .with_context(|| format!("failed to coerce array element at index {i}"))
1524                })
1525                .collect::<Result<Vec<_>>>()?;
1526
1527            return Ok(Self::new_unchecked(
1528                Type::Compound(CompoundType::Array(ty.unqualified()), false),
1529                elements,
1530            ));
1531        }
1532
1533        panic!("type `{ty}` is not an array type");
1534    }
1535
1536    /// Constructs a new array without checking the given elements conform to
1537    /// the given type.
1538    pub(crate) fn new_unchecked(ty: Type, elements: Vec<Value>) -> Self {
1539        let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty {
1540            Type::Compound(CompoundType::Array(ty.unqualified()), false)
1541        } else {
1542            panic!("type is not an array type");
1543        };
1544
1545        Self {
1546            ty,
1547            elements: if elements.is_empty() {
1548                None
1549            } else {
1550                Some(Arc::new(elements))
1551            },
1552        }
1553    }
1554
1555    /// Gets the type of the `Array` value.
1556    pub fn ty(&self) -> Type {
1557        self.ty.clone()
1558    }
1559
1560    /// Converts the array value to a slice of values.
1561    pub fn as_slice(&self) -> &[Value] {
1562        self.elements.as_ref().map(|v| v.as_slice()).unwrap_or(&[])
1563    }
1564
1565    /// Returns the number of elements in the array.
1566    pub fn len(&self) -> usize {
1567        self.elements.as_ref().map(|v| v.len()).unwrap_or(0)
1568    }
1569
1570    /// Returns `true` if the array has no elements.
1571    pub fn is_empty(&self) -> bool {
1572        self.len() == 0
1573    }
1574}
1575
1576impl fmt::Display for Array {
1577    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1578        write!(f, "[")?;
1579
1580        if let Some(elements) = &self.elements {
1581            for (i, element) in elements.iter().enumerate() {
1582                if i > 0 {
1583                    write!(f, ", ")?;
1584                }
1585
1586                write!(f, "{element}")?;
1587            }
1588        }
1589
1590        write!(f, "]")
1591    }
1592}
1593
1594/// Represents a `Map` value.
1595///
1596/// Maps are cheap to clone.
1597#[derive(Debug, Clone)]
1598pub struct Map {
1599    /// The type of the map value.
1600    ty: Type,
1601    /// The elements of the map value.
1602    ///
1603    /// A value of `None` indicates an empty map.
1604    elements: Option<Arc<IndexMap<Option<PrimitiveValue>, Value>>>,
1605}
1606
1607impl Map {
1608    /// Creates a new `Map` value.
1609    ///
1610    /// Returns an error if a key or value did not coerce to the map's key or
1611    /// value type, respectively.
1612    ///
1613    /// # Panics
1614    ///
1615    /// Panics if the given type is not a map type.
1616    pub fn new<K, V>(
1617        ty: impl Into<Type>,
1618        elements: impl IntoIterator<Item = (K, V)>,
1619    ) -> Result<Self>
1620    where
1621        K: Into<Value>,
1622        V: Into<Value>,
1623    {
1624        let ty = ty.into();
1625        if let Type::Compound(CompoundType::Map(ty), _) = ty {
1626            let key_type = ty.key_type();
1627            let value_type = ty.value_type();
1628
1629            let elements = elements
1630                .into_iter()
1631                .enumerate()
1632                .map(|(i, (k, v))| {
1633                    let k = k.into();
1634                    let v = v.into();
1635                    Ok((
1636                        if k.is_none() {
1637                            None
1638                        } else {
1639                            match k.coerce(key_type).with_context(|| {
1640                                format!("failed to coerce map key for element at index {i}")
1641                            })? {
1642                                Value::None => None,
1643                                Value::Primitive(v) => Some(v),
1644                                _ => {
1645                                    bail!("not all key values are primitive")
1646                                }
1647                            }
1648                        },
1649                        v.coerce(value_type).with_context(|| {
1650                            format!("failed to coerce map value for element at index {i}")
1651                        })?,
1652                    ))
1653                })
1654                .collect::<Result<_>>()?;
1655
1656            return Ok(Self::new_unchecked(
1657                Type::Compound(CompoundType::Map(ty), false),
1658                elements,
1659            ));
1660        }
1661
1662        panic!("type `{ty}` is not a map type");
1663    }
1664
1665    /// Constructs a new map without checking the given elements conform to the
1666    /// given type.
1667    pub(crate) fn new_unchecked(
1668        ty: Type,
1669        elements: IndexMap<Option<PrimitiveValue>, Value>,
1670    ) -> Self {
1671        assert!(ty.as_map().is_some());
1672        Self {
1673            ty: ty.require(),
1674            elements: if elements.is_empty() {
1675                None
1676            } else {
1677                Some(Arc::new(elements))
1678            },
1679        }
1680    }
1681
1682    /// Gets the type of the `Map` value.
1683    pub fn ty(&self) -> Type {
1684        self.ty.clone()
1685    }
1686
1687    /// Iterates the elements of the map.
1688    pub fn iter(&self) -> impl Iterator<Item = (&Option<PrimitiveValue>, &Value)> {
1689        self.elements
1690            .as_ref()
1691            .map(|m| Either::Left(m.iter()))
1692            .unwrap_or(Either::Right(std::iter::empty()))
1693    }
1694
1695    /// Iterates the keys of the map.
1696    pub fn keys(&self) -> impl Iterator<Item = &Option<PrimitiveValue>> {
1697        self.elements
1698            .as_ref()
1699            .map(|m| Either::Left(m.keys()))
1700            .unwrap_or(Either::Right(std::iter::empty()))
1701    }
1702
1703    /// Iterates the values of the map.
1704    pub fn values(&self) -> impl Iterator<Item = &Value> {
1705        self.elements
1706            .as_ref()
1707            .map(|m| Either::Left(m.values()))
1708            .unwrap_or(Either::Right(std::iter::empty()))
1709    }
1710
1711    /// Determines if the map contains the given key.
1712    pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1713        self.elements
1714            .as_ref()
1715            .map(|m| m.contains_key(key))
1716            .unwrap_or(false)
1717    }
1718
1719    /// Gets a value from the map by key.
1720    pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1721        self.elements.as_ref().and_then(|m| m.get(key))
1722    }
1723
1724    /// Returns the number of elements in the map.
1725    pub fn len(&self) -> usize {
1726        self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1727    }
1728
1729    /// Returns `true` if the map has no elements.
1730    pub fn is_empty(&self) -> bool {
1731        self.len() == 0
1732    }
1733}
1734
1735impl fmt::Display for Map {
1736    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1737        write!(f, "{{")?;
1738
1739        for (i, (k, v)) in self.iter().enumerate() {
1740            if i > 0 {
1741                write!(f, ", ")?;
1742            }
1743
1744            match k {
1745                Some(k) => write!(f, "{k}: {v}")?,
1746                None => write!(f, "None: {v}")?,
1747            }
1748        }
1749
1750        write!(f, "}}")
1751    }
1752}
1753
1754/// Represents an `Object` value.
1755///
1756/// Objects are cheap to clone.
1757#[derive(Debug, Clone)]
1758pub struct Object {
1759    /// The members of the object.
1760    ///
1761    /// A value of `None` indicates an empty object.
1762    pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1763}
1764
1765impl Object {
1766    /// Creates a new `Object` value.
1767    ///
1768    /// Keys **must** be known WDL identifiers checked by the caller.
1769    pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1770        Self {
1771            members: if members.is_empty() {
1772                None
1773            } else {
1774                Some(Arc::new(members))
1775            },
1776        }
1777    }
1778
1779    /// Returns an empty object.
1780    pub fn empty() -> Self {
1781        Self::new(IndexMap::default())
1782    }
1783
1784    /// Creates an object from an iterator of V1 AST metadata items.
1785    pub fn from_v1_metadata<N: TreeNode>(
1786        items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1787    ) -> Self {
1788        Object::new(
1789            items
1790                .map(|i| {
1791                    (
1792                        i.name().text().to_string(),
1793                        Value::from_v1_metadata(&i.value()),
1794                    )
1795                })
1796                .collect::<IndexMap<_, _>>(),
1797        )
1798    }
1799
1800    /// Gets the type of the `Object` value.
1801    pub fn ty(&self) -> Type {
1802        Type::Object
1803    }
1804
1805    /// Iterates the members of the object.
1806    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1807        self.members
1808            .as_ref()
1809            .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1810            .unwrap_or(Either::Right(std::iter::empty()))
1811    }
1812
1813    /// Iterates the keys of the object.
1814    pub fn keys(&self) -> impl Iterator<Item = &str> {
1815        self.members
1816            .as_ref()
1817            .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1818            .unwrap_or(Either::Right(std::iter::empty()))
1819    }
1820
1821    /// Iterates the values of the object.
1822    pub fn values(&self) -> impl Iterator<Item = &Value> {
1823        self.members
1824            .as_ref()
1825            .map(|m| Either::Left(m.values()))
1826            .unwrap_or(Either::Right(std::iter::empty()))
1827    }
1828
1829    /// Determines if the object contains the given key.
1830    pub fn contains_key(&self, key: &str) -> bool {
1831        self.members
1832            .as_ref()
1833            .map(|m| m.contains_key(key))
1834            .unwrap_or(false)
1835    }
1836
1837    /// Gets a value from the object by key.
1838    pub fn get(&self, key: &str) -> Option<&Value> {
1839        self.members.as_ref().and_then(|m| m.get(key))
1840    }
1841
1842    /// Returns the number of members in the object.
1843    pub fn len(&self) -> usize {
1844        self.members.as_ref().map(|m| m.len()).unwrap_or(0)
1845    }
1846
1847    /// Returns `true` if the object has no members.
1848    pub fn is_empty(&self) -> bool {
1849        self.len() == 0
1850    }
1851}
1852
1853impl fmt::Display for Object {
1854    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1855        write!(f, "object {{")?;
1856
1857        for (i, (k, v)) in self.iter().enumerate() {
1858            if i > 0 {
1859                write!(f, ", ")?;
1860            }
1861
1862            write!(f, "{k}: {v}")?;
1863        }
1864
1865        write!(f, "}}")
1866    }
1867}
1868
1869/// Represents a `Struct` value.
1870///
1871/// Structs are cheap to clone.
1872#[derive(Debug, Clone)]
1873pub struct Struct {
1874    /// The type of the struct value.
1875    ty: Type,
1876    /// The name of the struct.
1877    name: Arc<String>,
1878    /// The members of the struct value.
1879    pub(crate) members: Arc<IndexMap<String, Value>>,
1880}
1881
1882impl Struct {
1883    /// Creates a new struct value.
1884    ///
1885    /// Returns an error if the struct type does not contain a member of a given
1886    /// name or if a value does not coerce to the corresponding member's type.
1887    ///
1888    /// # Panics
1889    ///
1890    /// Panics if the given type is not a struct type.
1891    pub fn new<S, V>(ty: impl Into<Type>, members: impl IntoIterator<Item = (S, V)>) -> Result<Self>
1892    where
1893        S: Into<String>,
1894        V: Into<Value>,
1895    {
1896        let ty = ty.into();
1897        if let Type::Compound(CompoundType::Struct(ty), optional) = ty {
1898            let mut members = members
1899                .into_iter()
1900                .map(|(n, v)| {
1901                    let n = n.into();
1902                    let v = v.into();
1903                    let v = v
1904                        .coerce(ty.members().get(&n).ok_or_else(|| {
1905                            anyhow!("struct does not contain a member named `{n}`")
1906                        })?)
1907                        .with_context(|| format!("failed to coerce struct member `{n}`"))?;
1908                    Ok((n, v))
1909                })
1910                .collect::<Result<IndexMap<_, _>>>()?;
1911
1912            for (name, ty) in ty.members().iter() {
1913                // Check for optional members that should be set to `None`
1914                if ty.is_optional() {
1915                    if !members.contains_key(name) {
1916                        members.insert(name.clone(), Value::None);
1917                    }
1918                } else {
1919                    // Check for a missing required member
1920                    if !members.contains_key(name) {
1921                        bail!("missing a value for struct member `{name}`");
1922                    }
1923                }
1924            }
1925
1926            let name = ty.name().to_string();
1927            return Ok(Self {
1928                ty: Type::Compound(CompoundType::Struct(ty), optional),
1929                name: Arc::new(name),
1930                members: Arc::new(members),
1931            });
1932        }
1933
1934        panic!("type `{ty}` is not a struct type");
1935    }
1936
1937    /// Constructs a new struct without checking the given members conform to
1938    /// the given type.
1939    pub(crate) fn new_unchecked(
1940        ty: Type,
1941        name: Arc<String>,
1942        members: Arc<IndexMap<String, Value>>,
1943    ) -> Self {
1944        assert!(ty.as_struct().is_some());
1945        Self {
1946            ty: ty.require(),
1947            name,
1948            members,
1949        }
1950    }
1951
1952    /// Gets the type of the `Struct` value.
1953    pub fn ty(&self) -> Type {
1954        self.ty.clone()
1955    }
1956
1957    /// Gets the name of the struct.
1958    pub fn name(&self) -> &Arc<String> {
1959        &self.name
1960    }
1961
1962    /// Iterates the members of the struct.
1963    pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1964        self.members.iter().map(|(k, v)| (k.as_str(), v))
1965    }
1966
1967    /// Iterates the keys of the struct.
1968    pub fn keys(&self) -> impl Iterator<Item = &str> {
1969        self.members.keys().map(|k| k.as_str())
1970    }
1971
1972    /// Iterates the values of the struct.
1973    pub fn values(&self) -> impl Iterator<Item = &Value> {
1974        self.members.values()
1975    }
1976
1977    /// Determines if the struct contains the given member name.
1978    pub fn contains_key(&self, key: &str) -> bool {
1979        self.members.contains_key(key)
1980    }
1981
1982    /// Gets a value from the struct by member name.
1983    pub fn get(&self, key: &str) -> Option<&Value> {
1984        self.members.get(key)
1985    }
1986}
1987
1988impl fmt::Display for Struct {
1989    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1990        write!(f, "{name} {{", name = self.name)?;
1991
1992        for (i, (k, v)) in self.members.iter().enumerate() {
1993            if i > 0 {
1994                write!(f, ", ")?;
1995            }
1996
1997            write!(f, "{k}: {v}")?;
1998        }
1999
2000        write!(f, "}}")
2001    }
2002}
2003
2004/// Represents a compound value.
2005///
2006/// Compound values are cheap to clone.
2007#[derive(Debug, Clone)]
2008pub enum CompoundValue {
2009    /// The value is a `Pair` of values.
2010    Pair(Pair),
2011    /// The value is an `Array` of values.
2012    Array(Array),
2013    /// The value is a `Map` of values.
2014    Map(Map),
2015    /// The value is an `Object`.
2016    Object(Object),
2017    /// The value is a struct.
2018    Struct(Struct),
2019}
2020
2021impl CompoundValue {
2022    /// Gets the type of the compound value.
2023    pub fn ty(&self) -> Type {
2024        match self {
2025            CompoundValue::Pair(v) => v.ty(),
2026            CompoundValue::Array(v) => v.ty(),
2027            CompoundValue::Map(v) => v.ty(),
2028            CompoundValue::Object(v) => v.ty(),
2029            CompoundValue::Struct(v) => v.ty(),
2030        }
2031    }
2032
2033    /// Gets the value as a `Pair`.
2034    ///
2035    /// Returns `None` if the value is not a `Pair`.
2036    pub fn as_pair(&self) -> Option<&Pair> {
2037        match self {
2038            Self::Pair(v) => Some(v),
2039            _ => None,
2040        }
2041    }
2042
2043    /// Unwraps the value into a `Pair`.
2044    ///
2045    /// # Panics
2046    ///
2047    /// Panics if the value is not a `Pair`.
2048    pub fn unwrap_pair(self) -> Pair {
2049        match self {
2050            Self::Pair(v) => v,
2051            _ => panic!("value is not a pair"),
2052        }
2053    }
2054
2055    /// Gets the value as an `Array`.
2056    ///
2057    /// Returns `None` if the value is not an `Array`.
2058    pub fn as_array(&self) -> Option<&Array> {
2059        match self {
2060            Self::Array(v) => Some(v),
2061            _ => None,
2062        }
2063    }
2064
2065    /// Unwraps the value into an `Array`.
2066    ///
2067    /// # Panics
2068    ///
2069    /// Panics if the value is not an `Array`.
2070    pub fn unwrap_array(self) -> Array {
2071        match self {
2072            Self::Array(v) => v,
2073            _ => panic!("value is not an array"),
2074        }
2075    }
2076
2077    /// Gets the value as a `Map`.
2078    ///
2079    /// Returns `None` if the value is not a `Map`.
2080    pub fn as_map(&self) -> Option<&Map> {
2081        match self {
2082            Self::Map(v) => Some(v),
2083            _ => None,
2084        }
2085    }
2086
2087    /// Unwraps the value into a `Map`.
2088    ///
2089    /// # Panics
2090    ///
2091    /// Panics if the value is not a `Map`.
2092    pub fn unwrap_map(self) -> Map {
2093        match self {
2094            Self::Map(v) => v,
2095            _ => panic!("value is not a map"),
2096        }
2097    }
2098
2099    /// Gets the value as an `Object`.
2100    ///
2101    /// Returns `None` if the value is not an `Object`.
2102    pub fn as_object(&self) -> Option<&Object> {
2103        match self {
2104            Self::Object(v) => Some(v),
2105            _ => None,
2106        }
2107    }
2108
2109    /// Unwraps the value into an `Object`.
2110    ///
2111    /// # Panics
2112    ///
2113    /// Panics if the value is not an `Object`.
2114    pub fn unwrap_object(self) -> Object {
2115        match self {
2116            Self::Object(v) => v,
2117            _ => panic!("value is not an object"),
2118        }
2119    }
2120
2121    /// Gets the value as a `Struct`.
2122    ///
2123    /// Returns `None` if the value is not a `Struct`.
2124    pub fn as_struct(&self) -> Option<&Struct> {
2125        match self {
2126            Self::Struct(v) => Some(v),
2127            _ => None,
2128        }
2129    }
2130
2131    /// Unwraps the value into a `Struct`.
2132    ///
2133    /// # Panics
2134    ///
2135    /// Panics if the value is not a `Map`.
2136    pub fn unwrap_struct(self) -> Struct {
2137        match self {
2138            Self::Struct(v) => v,
2139            _ => panic!("value is not a struct"),
2140        }
2141    }
2142
2143    /// Compares two compound values for equality based on the WDL
2144    /// specification.
2145    ///
2146    /// Returns `None` if the two compound values cannot be compared for
2147    /// equality.
2148    pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2149        // The values must have type equivalence to compare for compound values
2150        // Coercion doesn't take place for this check
2151        if left.ty() != right.ty() {
2152            return None;
2153        }
2154
2155        match (left, right) {
2156            (Self::Pair(left), Self::Pair(right)) => Some(
2157                Value::equals(left.left(), right.left())?
2158                    && Value::equals(left.right(), right.right())?,
2159            ),
2160            (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2161                left.len() == right.len()
2162                    && left
2163                        .as_slice()
2164                        .iter()
2165                        .zip(right.as_slice())
2166                        .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2167            ),
2168            (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2169                left.len() == right.len()
2170                    // Maps are ordered, so compare via iteration
2171                    && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2172                        match (lk, rk) {
2173                            (None, None) => {},
2174                            (Some(lk), Some(rk)) if lk == rk => {},
2175                            _ => return false
2176                        }
2177
2178                        Value::equals(lv, rv).unwrap_or(false)
2179                    }),
2180            ),
2181            (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2182                left.len() == right.len()
2183                    && left.iter().all(|(k, left)| match right.get(k) {
2184                        Some(right) => Value::equals(left, right).unwrap_or(false),
2185                        None => false,
2186                    }),
2187            ),
2188            (
2189                CompoundValue::Struct(Struct { members: left, .. }),
2190                CompoundValue::Struct(Struct { members: right, .. }),
2191            ) => Some(
2192                left.len() == right.len()
2193                    && left.iter().all(|(k, left)| match right.get(k) {
2194                        Some(right) => Value::equals(left, right).unwrap_or(false),
2195                        None => false,
2196                    }),
2197            ),
2198            _ => None,
2199        }
2200    }
2201
2202    /// Visits each `File` or `Directory` value contained in this value.
2203    ///
2204    /// Note that paths may be specified as URLs.
2205    fn visit_paths(&self, cb: &mut impl FnMut(bool, &PrimitiveValue) -> Result<()>) -> Result<()> {
2206        match self {
2207            Self::Pair(pair) => {
2208                let ty = pair.ty.as_pair().expect("should be a pair type");
2209                pair.left().visit_paths(ty.left_type().is_optional(), cb)?;
2210                pair.right()
2211                    .visit_paths(ty.right_type().is_optional(), cb)?;
2212            }
2213            Self::Array(array) => {
2214                let ty = array.ty.as_array().expect("should be an array type");
2215                let optional = ty.element_type().is_optional();
2216                if let Some(elements) = &array.elements {
2217                    for v in elements.iter() {
2218                        v.visit_paths(optional, cb)?;
2219                    }
2220                }
2221            }
2222            Self::Map(map) => {
2223                let ty = map.ty.as_map().expect("should be a map type");
2224                let (key_optional, value_optional) =
2225                    (ty.key_type().is_optional(), ty.value_type().is_optional());
2226                if let Some(elements) = &map.elements {
2227                    for (k, v) in elements.iter() {
2228                        if let Some(k) = k {
2229                            k.visit_paths(key_optional, cb)?;
2230                        }
2231
2232                        v.visit_paths(value_optional, cb)?;
2233                    }
2234                }
2235            }
2236            Self::Object(object) => {
2237                if let Some(members) = &object.members {
2238                    for v in members.values() {
2239                        v.visit_paths(false, cb)?;
2240                    }
2241                }
2242            }
2243            Self::Struct(s) => {
2244                let ty = s.ty.as_struct().expect("should be a struct type");
2245                for (n, v) in s.members.iter() {
2246                    v.visit_paths(ty.members()[n].is_optional(), cb)?;
2247                }
2248            }
2249        }
2250
2251        Ok(())
2252    }
2253
2254    /// Mutably visits each `File` or `Directory` value contained in this value.
2255    ///
2256    /// If the provided callback returns `Ok(false)`, the `File` or `Directory`
2257    /// value will be replaced with `None`.
2258    ///
2259    /// Note that paths may be specified as URLs.
2260    fn visit_paths_mut(
2261        &mut self,
2262        cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
2263    ) -> Result<()> {
2264        match self {
2265            Self::Pair(pair) => {
2266                let ty = pair.ty.as_pair().expect("should be a pair type");
2267                let (left_optional, right_optional) =
2268                    (ty.left_type().is_optional(), ty.right_type().is_optional());
2269                let values = Arc::make_mut(&mut pair.values);
2270                values.0.visit_paths_mut(left_optional, cb)?;
2271                values.1.visit_paths_mut(right_optional, cb)?;
2272            }
2273            Self::Array(array) => {
2274                let ty = array.ty.as_array().expect("should be an array type");
2275                let optional = ty.element_type().is_optional();
2276                if let Some(elements) = &mut array.elements {
2277                    for v in Arc::make_mut(elements) {
2278                        v.visit_paths_mut(optional, cb)?;
2279                    }
2280                }
2281            }
2282            Self::Map(map) => {
2283                let ty = map.ty.as_map().expect("should be a map type");
2284                let (key_optional, value_optional) =
2285                    (ty.key_type().is_optional(), ty.value_type().is_optional());
2286                if let Some(elements) = &mut map.elements {
2287                    if elements
2288                        .iter()
2289                        .find_map(|(k, _)| {
2290                            k.as_ref().map(|v| {
2291                                matches!(v, PrimitiveValue::File(_) | PrimitiveValue::Directory(_))
2292                            })
2293                        })
2294                        .unwrap_or(false)
2295                    {
2296                        // The key type contains a path, we need to rebuild the map to alter the
2297                        // keys
2298                        let elements = Arc::make_mut(elements);
2299                        let new = elements
2300                            .drain(..)
2301                            .map(|(mut k, mut v)| {
2302                                if let Some(v) = &mut k {
2303                                    if !v.visit_paths_mut(key_optional, cb)? {
2304                                        k = None;
2305                                    }
2306                                }
2307
2308                                v.visit_paths_mut(value_optional, cb)?;
2309                                Ok((k, v))
2310                            })
2311                            .collect::<Result<Vec<_>>>()?;
2312                        elements.extend(new);
2313                    } else {
2314                        // Otherwise, we can just mutable the values in place
2315                        for v in Arc::make_mut(elements).values_mut() {
2316                            v.visit_paths_mut(value_optional, cb)?;
2317                        }
2318                    }
2319                }
2320            }
2321            Self::Object(object) => {
2322                if let Some(members) = &mut object.members {
2323                    for v in Arc::make_mut(members).values_mut() {
2324                        v.visit_paths_mut(false, cb)?;
2325                    }
2326                }
2327            }
2328            Self::Struct(s) => {
2329                let ty = s.ty.as_struct().expect("should be a struct type");
2330                for (n, v) in Arc::make_mut(&mut s.members).iter_mut() {
2331                    v.visit_paths_mut(ty.members()[n].is_optional(), cb)?;
2332                }
2333            }
2334        }
2335
2336        Ok(())
2337    }
2338}
2339
2340impl fmt::Display for CompoundValue {
2341    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2342        match self {
2343            Self::Pair(v) => v.fmt(f),
2344            Self::Array(v) => v.fmt(f),
2345            Self::Map(v) => v.fmt(f),
2346            Self::Object(v) => v.fmt(f),
2347            Self::Struct(v) => v.fmt(f),
2348        }
2349    }
2350}
2351
2352impl Coercible for CompoundValue {
2353    fn coerce(&self, target: &Type) -> Result<Self> {
2354        if target.is_union() || target.is_none() || self.ty().eq(target) {
2355            return Ok(self.clone());
2356        }
2357
2358        if let Type::Compound(target_ty, _) = target {
2359            match (self, target_ty) {
2360                // Array[X] -> Array[Y](+) where X -> Y
2361                (Self::Array(v), CompoundType::Array(target_ty)) => {
2362                    // Don't allow coercion when the source is empty but the target has the
2363                    // non-empty qualifier
2364                    if v.is_empty() && target_ty.is_non_empty() {
2365                        bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2366                    }
2367
2368                    return Ok(Self::Array(Array::new(
2369                        target.clone(),
2370                        v.as_slice().iter().cloned(),
2371                    )?));
2372                }
2373                // Map[W, Y] -> Map[X, Z] where W -> X and Y -> Z
2374                (Self::Map(v), CompoundType::Map(_)) => {
2375                    return Ok(Self::Map(Map::new(
2376                        target.clone(),
2377                        v.iter().map(|(k, v)| {
2378                            (k.clone().map(Into::into).unwrap_or(Value::None), v.clone())
2379                        }),
2380                    )?));
2381                }
2382                // Pair[W, Y] -> Pair[X, Z] where W -> X and Y -> Z
2383                (Self::Pair(v), CompoundType::Pair(_)) => {
2384                    return Ok(Self::Pair(Pair::new(
2385                        target.clone(),
2386                        v.values.0.clone(),
2387                        v.values.1.clone(),
2388                    )?));
2389                }
2390                // Map[String, Y] -> Struct
2391                (Self::Map(v), CompoundType::Struct(target_ty)) => {
2392                    let len = v.len();
2393                    let expected_len = target_ty.members().len();
2394
2395                    if len != expected_len {
2396                        bail!(
2397                            "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2398                             as the struct has {expected_len} member{s2}",
2399                            s1 = if len == 1 { "" } else { "s" },
2400                            s2 = if expected_len == 1 { "" } else { "s" }
2401                        );
2402                    }
2403
2404                    return Ok(Self::Struct(Struct {
2405                        ty: target.clone(),
2406                        name: target_ty.name().clone(),
2407                        members: Arc::new(
2408                            v.iter()
2409                                .map(|(k, v)| {
2410                                    let k: String = k
2411                                        .as_ref()
2412                                        .and_then(|k| k.as_string())
2413                                        .with_context(|| {
2414                                            format!(
2415                                                "cannot coerce a map with a non-string key type \
2416                                                 to struct type `{target}`"
2417                                            )
2418                                        })?
2419                                        .to_string();
2420                                    let ty = target_ty.members().get(&k).with_context(|| {
2421                                        format!(
2422                                            "cannot coerce a map with key `{k}` to struct type \
2423                                             `{target}` as the struct does not contain a member \
2424                                             with that name"
2425                                        )
2426                                    })?;
2427                                    let v = v.coerce(ty).with_context(|| {
2428                                        format!("failed to coerce value of map key `{k}")
2429                                    })?;
2430                                    Ok((k, v))
2431                                })
2432                                .collect::<Result<_>>()?,
2433                        ),
2434                    }));
2435                }
2436                // Struct -> Map[String, Y]
2437                // Object -> Map[String, Y]
2438                (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2439                    if map_ty.key_type().as_primitive() != Some(PrimitiveType::String) {
2440                        bail!(
2441                            "cannot coerce a struct or object to type `{target}` as it requires a \
2442                             `String` key type"
2443                        );
2444                    }
2445
2446                    let value_ty = map_ty.value_type();
2447                    return Ok(Self::Map(Map::new_unchecked(
2448                        target.clone(),
2449                        members
2450                            .iter()
2451                            .map(|(n, v)| {
2452                                let v = v
2453                                    .coerce(value_ty)
2454                                    .with_context(|| format!("failed to coerce member `{n}`"))?;
2455                                Ok((PrimitiveValue::new_string(n).into(), v))
2456                            })
2457                            .collect::<Result<_>>()?,
2458                    )));
2459                }
2460                (Self::Object(object), CompoundType::Map(map_ty)) => {
2461                    if map_ty.key_type().as_primitive() != Some(PrimitiveType::String) {
2462                        bail!(
2463                            "cannot coerce a struct or object to type `{target}` as it requires a \
2464                             `String` key type",
2465                        );
2466                    }
2467
2468                    let value_ty = map_ty.value_type();
2469                    return Ok(Self::Map(Map::new_unchecked(
2470                        target.clone(),
2471                        object
2472                            .iter()
2473                            .map(|(n, v)| {
2474                                let v = v
2475                                    .coerce(value_ty)
2476                                    .with_context(|| format!("failed to coerce member `{n}`"))?;
2477                                Ok((PrimitiveValue::new_string(n).into(), v))
2478                            })
2479                            .collect::<Result<_>>()?,
2480                    )));
2481                }
2482                // Object -> Struct
2483                (Self::Object(v), CompoundType::Struct(_)) => {
2484                    return Ok(Self::Struct(Struct::new(
2485                        target.clone(),
2486                        v.iter().map(|(k, v)| (k, v.clone())),
2487                    )?));
2488                }
2489                // Struct -> Struct
2490                (Self::Struct(v), CompoundType::Struct(struct_ty)) => {
2491                    let len = v.members.len();
2492                    let expected_len = struct_ty.members().len();
2493
2494                    if len != expected_len {
2495                        bail!(
2496                            "cannot coerce a struct of {len} members{s1} to struct type \
2497                             `{target}` as the target struct has {expected_len} member{s2}",
2498                            s1 = if len == 1 { "" } else { "s" },
2499                            s2 = if expected_len == 1 { "" } else { "s" }
2500                        );
2501                    }
2502
2503                    return Ok(Self::Struct(Struct {
2504                        ty: target.clone(),
2505                        name: struct_ty.name().clone(),
2506                        members: Arc::new(
2507                            v.members
2508                                .iter()
2509                                .map(|(k, v)| {
2510                                    let ty = struct_ty.members().get(k).ok_or_else(|| {
2511                                        anyhow!(
2512                                            "cannot coerce a struct with member `{k}` to struct \
2513                                             type `{target}` as the target struct does not \
2514                                             contain a member with that name",
2515                                        )
2516                                    })?;
2517                                    let v = v.coerce(ty).with_context(|| {
2518                                        format!("failed to coerce member `{k}`")
2519                                    })?;
2520                                    Ok((k.clone(), v))
2521                                })
2522                                .collect::<Result<_>>()?,
2523                        ),
2524                    }));
2525                }
2526                _ => {}
2527            }
2528        }
2529
2530        if let Type::Object = target {
2531            match self {
2532                // Map[String, Y] -> Object
2533                Self::Map(v) => {
2534                    return Ok(Self::Object(Object::new(
2535                        v.iter()
2536                            .map(|(k, v)| {
2537                                let k = k
2538                                    .as_ref()
2539                                    .and_then(|k| k.as_string())
2540                                    .context(
2541                                        "cannot coerce a map with a non-string key type to type \
2542                                         `Object`",
2543                                    )?
2544                                    .to_string();
2545                                Ok((k, v.clone()))
2546                            })
2547                            .collect::<Result<IndexMap<_, _>>>()?,
2548                    )));
2549                }
2550                // Struct -> Object
2551                Self::Struct(v) => {
2552                    return Ok(Self::Object(Object {
2553                        members: Some(v.members.clone()),
2554                    }));
2555                }
2556                _ => {}
2557            };
2558        }
2559
2560        bail!(
2561            "cannot coerce a value of type `{ty}` to type `{target}`",
2562            ty = self.ty()
2563        );
2564    }
2565}
2566
2567impl From<Pair> for CompoundValue {
2568    fn from(value: Pair) -> Self {
2569        Self::Pair(value)
2570    }
2571}
2572
2573impl From<Array> for CompoundValue {
2574    fn from(value: Array) -> Self {
2575        Self::Array(value)
2576    }
2577}
2578
2579impl From<Map> for CompoundValue {
2580    fn from(value: Map) -> Self {
2581        Self::Map(value)
2582    }
2583}
2584
2585impl From<Object> for CompoundValue {
2586    fn from(value: Object) -> Self {
2587        Self::Object(value)
2588    }
2589}
2590
2591impl From<Struct> for CompoundValue {
2592    fn from(value: Struct) -> Self {
2593        Self::Struct(value)
2594    }
2595}
2596
2597/// Immutable data for task values.
2598#[derive(Debug)]
2599struct TaskData {
2600    /// The name of the task.
2601    name: Arc<String>,
2602    /// The id of the task.
2603    id: Arc<String>,
2604    /// The container of the task.
2605    container: Option<Arc<String>>,
2606    /// The allocated number of cpus for the task.
2607    cpu: f64,
2608    /// The allocated memory (in bytes) for the task.
2609    memory: i64,
2610    /// The GPU allocations for the task.
2611    ///
2612    /// An array with one specification per allocated GPU; the specification is
2613    /// execution engine-specific.
2614    gpu: Array,
2615    /// The FPGA allocations for the task.
2616    ///
2617    /// An array with one specification per allocated FPGA; the specification is
2618    /// execution engine-specific.
2619    fpga: Array,
2620    /// The disk allocations for the task.
2621    ///
2622    /// A map with one entry for each disk mount point.
2623    ///
2624    /// The key is the mount point and the value is the initial amount of disk
2625    /// space allocated, in bytes.
2626    disks: Map,
2627    /// The time by which the task must be completed, as a Unix time stamp.
2628    ///
2629    /// A value of `None` indicates there is no deadline.
2630    end_time: Option<i64>,
2631    /// The task's `meta` section as an object.
2632    meta: Object,
2633    /// The tasks's `parameter_meta` section as an object.
2634    parameter_meta: Object,
2635    /// The task's extension metadata.
2636    ext: Object,
2637}
2638
2639/// Represents a value for `task` variables in WDL 1.2.
2640///
2641/// Task values are cheap to clone.
2642#[derive(Debug, Clone)]
2643pub struct TaskValue {
2644    /// The immutable data for task values.
2645    data: Arc<TaskData>,
2646    /// The current task attempt count.
2647    ///
2648    /// The value must be 0 the first time the task is executed and incremented
2649    /// by 1 each time the task is retried (if any).
2650    attempt: i64,
2651    /// The task's return code.
2652    ///
2653    /// Initially set to `None`, but set after task execution completes.
2654    return_code: Option<i64>,
2655}
2656
2657impl TaskValue {
2658    /// Constructs a new task value with the given name and identifier.
2659    pub(crate) fn new_v1<N: TreeNode>(
2660        name: impl Into<String>,
2661        id: impl Into<String>,
2662        definition: &v1::TaskDefinition<N>,
2663        constraints: TaskExecutionConstraints,
2664        attempt: i64,
2665    ) -> Self {
2666        Self {
2667            data: Arc::new(TaskData {
2668                name: Arc::new(name.into()),
2669                id: Arc::new(id.into()),
2670                container: constraints.container.map(Into::into),
2671                cpu: constraints.cpu,
2672                memory: constraints.memory,
2673                gpu: Array::new_unchecked(
2674                    ANALYSIS_STDLIB.array_string_type().clone(),
2675                    constraints
2676                        .gpu
2677                        .into_iter()
2678                        .map(|v| PrimitiveValue::new_string(v).into())
2679                        .collect(),
2680                ),
2681                fpga: Array::new_unchecked(
2682                    ANALYSIS_STDLIB.array_string_type().clone(),
2683                    constraints
2684                        .fpga
2685                        .into_iter()
2686                        .map(|v| PrimitiveValue::new_string(v).into())
2687                        .collect(),
2688                ),
2689                disks: Map::new_unchecked(
2690                    ANALYSIS_STDLIB.map_string_int_type().clone(),
2691                    constraints
2692                        .disks
2693                        .into_iter()
2694                        .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), v.into()))
2695                        .collect(),
2696                ),
2697                end_time: None,
2698                meta: definition
2699                    .metadata()
2700                    .map(|s| Object::from_v1_metadata(s.items()))
2701                    .unwrap_or_else(Object::empty),
2702                parameter_meta: definition
2703                    .parameter_metadata()
2704                    .map(|s| Object::from_v1_metadata(s.items()))
2705                    .unwrap_or_else(Object::empty),
2706                ext: Object::empty(),
2707            }),
2708            attempt,
2709            return_code: None,
2710        }
2711    }
2712
2713    /// Gets the task name.
2714    pub fn name(&self) -> &Arc<String> {
2715        &self.data.name
2716    }
2717
2718    /// Gets the unique ID of the task.
2719    pub fn id(&self) -> &Arc<String> {
2720        &self.data.id
2721    }
2722
2723    /// Gets the container in which the task is executing.
2724    pub fn container(&self) -> Option<&Arc<String>> {
2725        self.data.container.as_ref()
2726    }
2727
2728    /// Gets the allocated number of cpus for the task.
2729    pub fn cpu(&self) -> f64 {
2730        self.data.cpu
2731    }
2732
2733    /// Gets the allocated memory (in bytes) for the task.
2734    pub fn memory(&self) -> i64 {
2735        self.data.memory
2736    }
2737
2738    /// Gets the GPU allocations for the task.
2739    ///
2740    /// An array with one specification per allocated GPU; the specification is
2741    /// execution engine-specific.
2742    pub fn gpu(&self) -> &Array {
2743        &self.data.gpu
2744    }
2745
2746    /// Gets the FPGA allocations for the task.
2747    ///
2748    /// An array with one specification per allocated FPGA; the specification is
2749    /// execution engine-specific.
2750    pub fn fpga(&self) -> &Array {
2751        &self.data.fpga
2752    }
2753
2754    /// Gets the disk allocations for the task.
2755    ///
2756    /// A map with one entry for each disk mount point.
2757    ///
2758    /// The key is the mount point and the value is the initial amount of disk
2759    /// space allocated, in bytes.
2760    pub fn disks(&self) -> &Map {
2761        &self.data.disks
2762    }
2763
2764    /// Gets current task attempt count.
2765    ///
2766    /// The value must be 0 the first time the task is executed and incremented
2767    /// by 1 each time the task is retried (if any).
2768    pub fn attempt(&self) -> i64 {
2769        self.attempt
2770    }
2771
2772    /// Gets the time by which the task must be completed, as a Unix time stamp.
2773    ///
2774    /// A value of `None` indicates there is no deadline.
2775    pub fn end_time(&self) -> Option<i64> {
2776        self.data.end_time
2777    }
2778
2779    /// Gets the task's return code.
2780    ///
2781    /// Initially set to `None`, but set after task execution completes.
2782    pub fn return_code(&self) -> Option<i64> {
2783        self.return_code
2784    }
2785
2786    /// Gets the task's `meta` section as an object.
2787    pub fn meta(&self) -> &Object {
2788        &self.data.meta
2789    }
2790
2791    /// Gets the tasks's `parameter_meta` section as an object.
2792    pub fn parameter_meta(&self) -> &Object {
2793        &self.data.parameter_meta
2794    }
2795
2796    /// Gets the task's extension metadata.
2797    pub fn ext(&self) -> &Object {
2798        &self.data.ext
2799    }
2800
2801    /// Sets the return code after the task execution has completed.
2802    pub(crate) fn set_return_code(&mut self, code: i32) {
2803        self.return_code = Some(code as i64);
2804    }
2805
2806    /// Sets the attempt number for the task.
2807    pub(crate) fn set_attempt(&mut self, attempt: i64) {
2808        self.attempt = attempt;
2809    }
2810
2811    /// Accesses a field of the task value by name.
2812    ///
2813    /// Returns `None` if the name is not a known field name.
2814    pub fn field(&self, name: &str) -> Option<Value> {
2815        match name {
2816            n if n == TASK_FIELD_NAME => {
2817                Some(PrimitiveValue::String(self.data.name.clone()).into())
2818            }
2819            n if n == TASK_FIELD_ID => Some(PrimitiveValue::String(self.data.id.clone()).into()),
2820            n if n == TASK_FIELD_CONTAINER => Some(
2821                self.data
2822                    .container
2823                    .clone()
2824                    .map(|c| PrimitiveValue::String(c).into())
2825                    .unwrap_or(Value::None),
2826            ),
2827            n if n == TASK_FIELD_CPU => Some(self.data.cpu.into()),
2828            n if n == TASK_FIELD_MEMORY => Some(self.data.memory.into()),
2829            n if n == TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
2830            n if n == TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
2831            n if n == TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
2832            n if n == TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
2833            n if n == TASK_FIELD_END_TIME => {
2834                Some(self.data.end_time.map(Into::into).unwrap_or(Value::None))
2835            }
2836            n if n == TASK_FIELD_RETURN_CODE => {
2837                Some(self.return_code.map(Into::into).unwrap_or(Value::None))
2838            }
2839            n if n == TASK_FIELD_META => Some(self.data.meta.clone().into()),
2840            n if n == TASK_FIELD_PARAMETER_META => Some(self.data.parameter_meta.clone().into()),
2841            n if n == TASK_FIELD_EXT => Some(self.data.ext.clone().into()),
2842            _ => None,
2843        }
2844    }
2845}
2846
2847/// Represents a hints value from a WDL 1.2 hints section.
2848///
2849/// Hints values are cheap to clone.
2850#[derive(Debug, Clone)]
2851pub struct HintsValue(Object);
2852
2853impl HintsValue {
2854    /// Converts the hints value to an object.
2855    pub fn as_object(&self) -> &Object {
2856        &self.0
2857    }
2858}
2859
2860impl fmt::Display for HintsValue {
2861    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2862        write!(f, "hints {{")?;
2863
2864        for (i, (k, v)) in self.0.iter().enumerate() {
2865            if i > 0 {
2866                write!(f, ", ")?;
2867            }
2868
2869            write!(f, "{k}: {v}")?;
2870        }
2871
2872        write!(f, "}}")
2873    }
2874}
2875
2876impl From<Object> for HintsValue {
2877    fn from(value: Object) -> Self {
2878        Self(value)
2879    }
2880}
2881
2882/// Represents an input value from a WDL 1.2 hints section.
2883///
2884/// Input values are cheap to clone.
2885#[derive(Debug, Clone)]
2886pub struct InputValue(Object);
2887
2888impl InputValue {
2889    /// Converts the input value to an object.
2890    pub fn as_object(&self) -> &Object {
2891        &self.0
2892    }
2893}
2894
2895impl fmt::Display for InputValue {
2896    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2897        write!(f, "input {{")?;
2898
2899        for (i, (k, v)) in self.0.iter().enumerate() {
2900            if i > 0 {
2901                write!(f, ", ")?;
2902            }
2903
2904            write!(f, "{k}: {v}")?;
2905        }
2906
2907        write!(f, "}}")
2908    }
2909}
2910
2911impl From<Object> for InputValue {
2912    fn from(value: Object) -> Self {
2913        Self(value)
2914    }
2915}
2916
2917/// Represents an output value from a WDL 1.2 hints section.
2918///
2919/// Output values are cheap to clone.
2920#[derive(Debug, Clone)]
2921pub struct OutputValue(Object);
2922
2923impl OutputValue {
2924    /// Converts the output value to an object.
2925    pub fn as_object(&self) -> &Object {
2926        &self.0
2927    }
2928}
2929
2930impl fmt::Display for OutputValue {
2931    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2932        write!(f, "output {{")?;
2933
2934        for (i, (k, v)) in self.0.iter().enumerate() {
2935            if i > 0 {
2936                write!(f, ", ")?;
2937            }
2938
2939            write!(f, "{k}: {v}")?;
2940        }
2941
2942        write!(f, "}}")
2943    }
2944}
2945
2946impl From<Object> for OutputValue {
2947    fn from(value: Object) -> Self {
2948        Self(value)
2949    }
2950}
2951
2952/// Represents the outputs of a call.
2953///
2954/// Call values are cheap to clone.
2955#[derive(Debug, Clone)]
2956pub struct CallValue {
2957    /// The type of the call.
2958    ty: CallType,
2959    /// The outputs of the call.
2960    outputs: Arc<Outputs>,
2961}
2962
2963impl CallValue {
2964    /// Constructs a new call value without checking the outputs conform to the
2965    /// call type.
2966    pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
2967        Self { ty, outputs }
2968    }
2969
2970    /// Gets the type of the call.
2971    pub fn ty(&self) -> &CallType {
2972        &self.ty
2973    }
2974
2975    /// Gets the outputs of the call.
2976    pub fn outputs(&self) -> &Outputs {
2977        self.outputs.as_ref()
2978    }
2979}
2980
2981impl fmt::Display for CallValue {
2982    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2983        write!(f, "call output {{")?;
2984
2985        for (i, (k, v)) in self.outputs.iter().enumerate() {
2986            if i > 0 {
2987                write!(f, ", ")?;
2988            }
2989
2990            write!(f, "{k}: {v}")?;
2991        }
2992
2993        write!(f, "}}")
2994    }
2995}
2996
2997/// Serializes a value with optional serialization of pairs.
2998pub struct ValueSerializer<'a> {
2999    /// The value to serialize.
3000    value: &'a Value,
3001    /// Whether pairs should be serialized as a map with `left` and `right`
3002    /// keys.
3003    allow_pairs: bool,
3004}
3005
3006impl<'a> ValueSerializer<'a> {
3007    /// Constructs a new `ValueSerializer`.
3008    pub fn new(value: &'a Value, allow_pairs: bool) -> Self {
3009        Self { value, allow_pairs }
3010    }
3011}
3012
3013impl serde::Serialize for ValueSerializer<'_> {
3014    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3015    where
3016        S: serde::Serializer,
3017    {
3018        use serde::ser::Error;
3019
3020        match &self.value {
3021            Value::None => serializer.serialize_none(),
3022            Value::Primitive(v) => v.serialize(serializer),
3023            Value::Compound(v) => {
3024                CompoundValueSerializer::new(v, self.allow_pairs).serialize(serializer)
3025            }
3026            Value::Task(_)
3027            | Value::Hints(_)
3028            | Value::Input(_)
3029            | Value::Output(_)
3030            | Value::Call(_) => Err(S::Error::custom("value cannot be serialized")),
3031        }
3032    }
3033}
3034
3035/// Serializes a `CompoundValue` with optional serialization of pairs.
3036pub struct CompoundValueSerializer<'a> {
3037    /// The compound value to serialize.
3038    value: &'a CompoundValue,
3039    /// Whether pairs should be serialized as a map with `left` and `right`
3040    /// keys.
3041    allow_pairs: bool,
3042}
3043
3044impl<'a> CompoundValueSerializer<'a> {
3045    /// Constructs a new `CompoundValueSerializer`.
3046    pub fn new(value: &'a CompoundValue, allow_pairs: bool) -> Self {
3047        Self { value, allow_pairs }
3048    }
3049}
3050
3051impl serde::Serialize for CompoundValueSerializer<'_> {
3052    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3053    where
3054        S: serde::Serializer,
3055    {
3056        use serde::ser::Error;
3057
3058        match &self.value {
3059            CompoundValue::Pair(pair) if self.allow_pairs => {
3060                let mut state = serializer.serialize_map(Some(2))?;
3061                let left = ValueSerializer::new(pair.left(), self.allow_pairs);
3062                let right = ValueSerializer::new(pair.right(), self.allow_pairs);
3063                state.serialize_entry("left", &left)?;
3064                state.serialize_entry("right", &right)?;
3065                state.end()
3066            }
3067            CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3068            CompoundValue::Array(v) => {
3069                let mut s = serializer.serialize_seq(Some(v.len()))?;
3070                for v in v.as_slice() {
3071                    s.serialize_element(&ValueSerializer::new(v, self.allow_pairs))?;
3072                }
3073
3074                s.end()
3075            }
3076            CompoundValue::Map(v) => {
3077                if !v
3078                    .ty()
3079                    .as_map()
3080                    .expect("type should be a map")
3081                    .key_type()
3082                    .is_coercible_to(&PrimitiveType::String.into())
3083                {
3084                    return Err(S::Error::custom(
3085                        "only maps with `String` key types may be serialized",
3086                    ));
3087                }
3088
3089                let mut s = serializer.serialize_map(Some(v.len()))?;
3090                for (k, v) in v.iter() {
3091                    s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3092                }
3093
3094                s.end()
3095            }
3096            CompoundValue::Object(object) => {
3097                let mut s = serializer.serialize_map(Some(object.len()))?;
3098                for (k, v) in object.iter() {
3099                    s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3100                }
3101
3102                s.end()
3103            }
3104            CompoundValue::Struct(Struct { members, .. }) => {
3105                let mut s = serializer.serialize_map(Some(members.len()))?;
3106                for (k, v) in members.iter() {
3107                    s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3108                }
3109
3110                s.end()
3111            }
3112        }
3113    }
3114}
3115
3116#[cfg(test)]
3117mod test {
3118    use approx::assert_relative_eq;
3119    use pretty_assertions::assert_eq;
3120    use wdl_analysis::types::ArrayType;
3121    use wdl_analysis::types::MapType;
3122    use wdl_analysis::types::PairType;
3123    use wdl_analysis::types::StructType;
3124
3125    use super::*;
3126
3127    #[test]
3128    fn boolean_coercion() {
3129        // Boolean -> Boolean
3130        assert_eq!(
3131            Value::from(false)
3132                .coerce(&PrimitiveType::Boolean.into())
3133                .expect("should coerce")
3134                .unwrap_boolean(),
3135            Value::from(false).unwrap_boolean()
3136        );
3137        // Boolean -> String (invalid)
3138        assert_eq!(
3139            format!(
3140                "{e:?}",
3141                e = Value::from(true)
3142                    .coerce(&PrimitiveType::String.into())
3143                    .unwrap_err()
3144            ),
3145            "cannot coerce type `Boolean` to type `String`"
3146        );
3147    }
3148
3149    #[test]
3150    fn boolean_display() {
3151        assert_eq!(Value::from(false).to_string(), "false");
3152        assert_eq!(Value::from(true).to_string(), "true");
3153    }
3154
3155    #[test]
3156    fn integer_coercion() {
3157        // Int -> Int
3158        assert_eq!(
3159            Value::from(12345)
3160                .coerce(&PrimitiveType::Integer.into())
3161                .expect("should coerce")
3162                .unwrap_integer(),
3163            Value::from(12345).unwrap_integer()
3164        );
3165        // Int -> Float
3166        assert_relative_eq!(
3167            Value::from(12345)
3168                .coerce(&PrimitiveType::Float.into())
3169                .expect("should coerce")
3170                .unwrap_float(),
3171            Value::from(12345.0).unwrap_float()
3172        );
3173        // Int -> Boolean (invalid)
3174        assert_eq!(
3175            format!(
3176                "{e:?}",
3177                e = Value::from(12345)
3178                    .coerce(&PrimitiveType::Boolean.into())
3179                    .unwrap_err()
3180            ),
3181            "cannot coerce type `Int` to type `Boolean`"
3182        );
3183    }
3184
3185    #[test]
3186    fn integer_display() {
3187        assert_eq!(Value::from(12345).to_string(), "12345");
3188        assert_eq!(Value::from(-12345).to_string(), "-12345");
3189    }
3190
3191    #[test]
3192    fn float_coercion() {
3193        // Float -> Float
3194        assert_relative_eq!(
3195            Value::from(12345.0)
3196                .coerce(&PrimitiveType::Float.into())
3197                .expect("should coerce")
3198                .unwrap_float(),
3199            Value::from(12345.0).unwrap_float()
3200        );
3201        // Float -> Int (invalid)
3202        assert_eq!(
3203            format!(
3204                "{e:?}",
3205                e = Value::from(12345.0)
3206                    .coerce(&PrimitiveType::Integer.into())
3207                    .unwrap_err()
3208            ),
3209            "cannot coerce type `Float` to type `Int`"
3210        );
3211    }
3212
3213    #[test]
3214    fn float_display() {
3215        assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
3216        assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
3217    }
3218
3219    #[test]
3220    fn string_coercion() {
3221        let value = PrimitiveValue::new_string("foo");
3222        // String -> String
3223        assert_eq!(
3224            value
3225                .coerce(&PrimitiveType::String.into())
3226                .expect("should coerce"),
3227            value
3228        );
3229        // String -> File
3230        assert_eq!(
3231            value
3232                .coerce(&PrimitiveType::File.into())
3233                .expect("should coerce"),
3234            PrimitiveValue::File(value.as_string().expect("should be string").clone())
3235        );
3236        // String -> Directory
3237        assert_eq!(
3238            value
3239                .coerce(&PrimitiveType::Directory.into())
3240                .expect("should coerce"),
3241            PrimitiveValue::Directory(value.as_string().expect("should be string").clone())
3242        );
3243        // String -> Boolean (invalid)
3244        assert_eq!(
3245            format!(
3246                "{e:?}",
3247                e = value.coerce(&PrimitiveType::Boolean.into()).unwrap_err()
3248            ),
3249            "cannot coerce type `String` to type `Boolean`"
3250        );
3251    }
3252
3253    #[test]
3254    fn string_display() {
3255        let value = PrimitiveValue::new_string("hello world!");
3256        assert_eq!(value.to_string(), "\"hello world!\"");
3257    }
3258
3259    #[test]
3260    fn file_coercion() {
3261        let value = PrimitiveValue::new_file("foo");
3262
3263        // File -> File
3264        assert_eq!(
3265            value
3266                .coerce(&PrimitiveType::File.into())
3267                .expect("should coerce"),
3268            value
3269        );
3270        // File -> String
3271        assert_eq!(
3272            value
3273                .coerce(&PrimitiveType::String.into())
3274                .expect("should coerce"),
3275            PrimitiveValue::String(value.as_file().expect("should be file").clone())
3276        );
3277        // File -> Directory (invalid)
3278        assert_eq!(
3279            format!(
3280                "{e:?}",
3281                e = value.coerce(&PrimitiveType::Directory.into()).unwrap_err()
3282            ),
3283            "cannot coerce type `File` to type `Directory`"
3284        );
3285    }
3286
3287    #[test]
3288    fn file_display() {
3289        let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
3290        assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
3291    }
3292
3293    #[test]
3294    fn directory_coercion() {
3295        let value = PrimitiveValue::new_directory("foo");
3296
3297        // Directory -> Directory
3298        assert_eq!(
3299            value
3300                .coerce(&PrimitiveType::Directory.into())
3301                .expect("should coerce"),
3302            value
3303        );
3304        // Directory -> String
3305        assert_eq!(
3306            value
3307                .coerce(&PrimitiveType::String.into())
3308                .expect("should coerce"),
3309            PrimitiveValue::String(value.as_directory().expect("should be directory").clone())
3310        );
3311        // Directory -> File (invalid)
3312        assert_eq!(
3313            format!(
3314                "{e:?}",
3315                e = value.coerce(&PrimitiveType::File.into()).unwrap_err()
3316            ),
3317            "cannot coerce type `Directory` to type `File`"
3318        );
3319    }
3320
3321    #[test]
3322    fn directory_display() {
3323        let value = PrimitiveValue::new_directory("/foo/bar/baz");
3324        assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
3325    }
3326
3327    #[test]
3328    fn none_coercion() {
3329        // None -> String?
3330        assert!(
3331            Value::None
3332                .coerce(&Type::from(PrimitiveType::String).optional())
3333                .expect("should coerce")
3334                .is_none(),
3335        );
3336
3337        // None -> String (invalid)
3338        assert_eq!(
3339            format!(
3340                "{e:?}",
3341                e = Value::None
3342                    .coerce(&PrimitiveType::String.into())
3343                    .unwrap_err()
3344            ),
3345            "cannot coerce `None` to non-optional type `String`"
3346        );
3347    }
3348
3349    #[test]
3350    fn none_display() {
3351        assert_eq!(Value::None.to_string(), "None");
3352    }
3353
3354    #[test]
3355    fn array_coercion() {
3356        let src_ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3357        let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
3358
3359        // Array[Int] -> Array[Float]
3360        let src: CompoundValue = Array::new(src_ty, [1, 2, 3])
3361            .expect("should create array value")
3362            .into();
3363        let target = src.coerce(&target_ty).expect("should coerce");
3364        assert_eq!(
3365            target.unwrap_array().to_string(),
3366            "[1.000000, 2.000000, 3.000000]"
3367        );
3368
3369        // Array[Int] -> Array[String] (invalid)
3370        let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
3371        assert_eq!(
3372            format!("{e:?}", e = src.coerce(&target_ty).unwrap_err()),
3373            r#"failed to coerce array element at index 0
3374
3375Caused by:
3376    cannot coerce type `Int` to type `String`"#
3377        );
3378    }
3379
3380    #[test]
3381    fn non_empty_array_coercion() {
3382        let ty: Type = ArrayType::new(PrimitiveType::String).into();
3383        let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
3384
3385        // Array[String] (non-empty) -> Array[String]+
3386        let string = PrimitiveValue::new_string("foo");
3387        let value: Value = Array::new(ty.clone(), [string])
3388            .expect("should create array")
3389            .into();
3390        assert!(value.coerce(&target_ty).is_ok(), "should coerce");
3391
3392        // Array[String] (empty) -> Array[String]+ (invalid)
3393        let value: Value = Array::new::<Value>(ty, [])
3394            .expect("should create array")
3395            .into();
3396        assert_eq!(
3397            format!("{e:?}", e = value.coerce(&target_ty).unwrap_err()),
3398            "cannot coerce empty array value to non-empty array type `Array[String]+`"
3399        );
3400    }
3401
3402    #[test]
3403    fn array_display() {
3404        let ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3405        let value: Value = Array::new(ty, [1, 2, 3])
3406            .expect("should create array")
3407            .into();
3408
3409        assert_eq!(value.to_string(), "[1, 2, 3]");
3410    }
3411
3412    #[test]
3413    fn map_coerce() {
3414        let key1 = PrimitiveValue::new_file("foo");
3415        let value1 = PrimitiveValue::new_string("bar");
3416        let key2 = PrimitiveValue::new_file("baz");
3417        let value2 = PrimitiveValue::new_string("qux");
3418
3419        let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
3420        let value: Value = Map::new(ty, [(key1, value1), (key2, value2)])
3421            .expect("should create map value")
3422            .into();
3423
3424        // Map[File, String] -> Map[String, File]
3425        let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
3426        let value = value.coerce(&ty).expect("value should coerce");
3427        assert_eq!(value.to_string(), r#"{"foo": "bar", "baz": "qux"}"#);
3428
3429        // Map[String, File] -> Map[Int, File] (invalid)
3430        let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
3431        assert_eq!(
3432            format!("{e:?}", e = value.coerce(&ty).unwrap_err()),
3433            r#"failed to coerce map key for element at index 0
3434
3435Caused by:
3436    cannot coerce type `String` to type `Int`"#
3437        );
3438
3439        // Map[String, File] -> Map[String, Int] (invalid)
3440        let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
3441        assert_eq!(
3442            format!("{e:?}", e = value.coerce(&ty).unwrap_err()),
3443            r#"failed to coerce map value for element at index 0
3444
3445Caused by:
3446    cannot coerce type `File` to type `Int`"#
3447        );
3448
3449        // Map[String, File] -> Struct
3450        let ty = StructType::new(
3451            "Foo",
3452            [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
3453        )
3454        .into();
3455        let struct_value = value.coerce(&ty).expect("value should coerce");
3456        assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
3457
3458        // Map[String, File] -> Struct (invalid)
3459        let ty = StructType::new(
3460            "Foo",
3461            [
3462                ("foo", PrimitiveType::File),
3463                ("baz", PrimitiveType::File),
3464                ("qux", PrimitiveType::File),
3465            ],
3466        )
3467        .into();
3468        assert_eq!(
3469            format!("{e:?}", e = value.coerce(&ty).unwrap_err()),
3470            "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
3471        );
3472
3473        // Map[String, File] -> Object
3474        let object_value = value.coerce(&Type::Object).expect("value should coerce");
3475        assert_eq!(
3476            object_value.to_string(),
3477            r#"object {foo: "bar", baz: "qux"}"#
3478        );
3479    }
3480
3481    #[test]
3482    fn map_display() {
3483        let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3484        let value: Value = Map::new(ty, [(1, true), (2, false)])
3485            .expect("should create map value")
3486            .into();
3487        assert_eq!(value.to_string(), "{1: true, 2: false}");
3488    }
3489
3490    #[test]
3491    fn pair_coercion() {
3492        let left = PrimitiveValue::new_file("foo");
3493        let right = PrimitiveValue::new_string("bar");
3494
3495        let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3496        let value: Value = Pair::new(ty, left, right)
3497            .expect("should create pair value")
3498            .into();
3499
3500        // Pair[File, String] -> Pair[String, File]
3501        let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
3502        let value = value.coerce(&ty).expect("value should coerce");
3503        assert_eq!(value.to_string(), r#"("foo", "bar")"#);
3504
3505        // Pair[String, File] -> Pair[Int, Int]
3506        let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
3507        assert_eq!(
3508            format!("{e:?}", e = value.coerce(&ty).unwrap_err()),
3509            r#"failed to coerce pair's left value
3510
3511Caused by:
3512    cannot coerce type `String` to type `Int`"#
3513        );
3514    }
3515
3516    #[test]
3517    fn pair_display() {
3518        let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3519        let value: Value = Pair::new(ty, 12345, false)
3520            .expect("should create pair value")
3521            .into();
3522        assert_eq!(value.to_string(), "(12345, false)");
3523    }
3524
3525    #[test]
3526    fn struct_coercion() {
3527        let ty = StructType::new(
3528            "Foo",
3529            [
3530                ("foo", PrimitiveType::Float),
3531                ("bar", PrimitiveType::Float),
3532                ("baz", PrimitiveType::Float),
3533            ],
3534        );
3535        let value: Value = Struct::new(ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
3536            .expect("should create map value")
3537            .into();
3538
3539        // Struct -> Map[String, Float]
3540        let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
3541        let map_value = value.coerce(&ty).expect("value should coerce");
3542        assert_eq!(
3543            map_value.to_string(),
3544            r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
3545        );
3546
3547        // Struct -> Struct
3548        let ty = StructType::new(
3549            "Bar",
3550            [
3551                ("foo", PrimitiveType::Float),
3552                ("bar", PrimitiveType::Float),
3553                ("baz", PrimitiveType::Float),
3554            ],
3555        )
3556        .into();
3557        let struct_value = value.coerce(&ty).expect("value should coerce");
3558        assert_eq!(
3559            struct_value.to_string(),
3560            r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3561        );
3562
3563        // Struct -> Object
3564        let object_value = value.coerce(&Type::Object).expect("value should coerce");
3565        assert_eq!(
3566            object_value.to_string(),
3567            r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3568        );
3569    }
3570
3571    #[test]
3572    fn struct_display() {
3573        let ty = StructType::new(
3574            "Foo",
3575            [
3576                ("foo", PrimitiveType::Float),
3577                ("bar", PrimitiveType::String),
3578                ("baz", PrimitiveType::Integer),
3579            ],
3580        );
3581        let value: Value = Struct::new(
3582            ty,
3583            [
3584                ("foo", Value::from(1.101)),
3585                ("bar", PrimitiveValue::new_string("foo").into()),
3586                ("baz", 1234.into()),
3587            ],
3588        )
3589        .expect("should create map value")
3590        .into();
3591        assert_eq!(
3592            value.to_string(),
3593            r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
3594        );
3595    }
3596
3597    #[test]
3598    fn pair_serialization() {
3599        let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3600        let pair: Value = Pair::new(
3601            pair_ty,
3602            PrimitiveValue::new_file("foo"),
3603            PrimitiveValue::new_string("bar"),
3604        )
3605        .expect("should create pair value")
3606        .into();
3607        // Serialize pair with `left` and `right` keys
3608        let value_serializer = ValueSerializer::new(&pair, true);
3609        let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3610        assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
3611
3612        // Serialize pair without `left` and `right` keys (should fail)
3613        let value_serializer = ValueSerializer::new(&pair, false);
3614        assert!(serde_json::to_string(&value_serializer).is_err());
3615
3616        let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
3617        let array: Value = Array::new(array_ty, [pair])
3618            .expect("should create array value")
3619            .into();
3620
3621        // Serialize array of pairs with `left` and `right` keys
3622        let value_serializer = ValueSerializer::new(&array, true);
3623        let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3624        assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
3625    }
3626}