wdl_engine/
value.rs

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