Skip to main content

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