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