1use 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;
11use std::sync::LazyLock;
12
13use anyhow::Context;
14use anyhow::Result;
15use anyhow::anyhow;
16use anyhow::bail;
17use futures::FutureExt;
18use futures::StreamExt as _;
19use futures::TryStreamExt as _;
20use futures::future::BoxFuture;
21use indexmap::IndexMap;
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#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
75pub struct HostPath(pub Arc<String>);
76
77impl HostPath {
78 pub fn new(path: impl Into<String>) -> Self {
80 Self(Arc::new(path.into()))
81 }
82
83 pub fn as_str(&self) -> &str {
85 &self.0
86 }
87
88 pub fn expand(&self, base_dir: &EvaluationPath) -> Result<Self> {
92 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 if path::is_supported_url(&shell_expanded) {
99 Ok(Self::new(shell_expanded))
100 } else {
101 Ok(Self::new(base_dir.join(&shell_expanded)?.to_string()))
103 }
104 }
105
106 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
118fn write_escaped_wdl_string(f: &mut fmt::Formatter<'_>, s: &str) -> fmt::Result {
120 let mut chars = s.char_indices().peekable();
121 while let Some((_, c)) = chars.next() {
122 let next_is_brace = chars.peek().map(|(_, n)| *n == '{').unwrap_or(false);
123 match c {
124 '\\' => f.write_str(r"\\")?,
125 '\n' => f.write_str(r"\n")?,
126 '\r' => f.write_str(r"\r")?,
127 '\t' => f.write_str(r"\t")?,
128 '"' => f.write_str("\\\"")?,
129 '$' if next_is_brace => f.write_str(r"\$")?,
130 '~' if next_is_brace => f.write_str(r"\~")?,
131 c if c.is_control() => write!(f, "\\x{code:02X}", code = c as u32)?,
132 c => write!(f, "{c}")?,
133 }
134 }
135 Ok(())
136}
137
138impl From<Arc<String>> for HostPath {
139 fn from(path: Arc<String>) -> Self {
140 Self(path)
141 }
142}
143
144impl From<HostPath> for Arc<String> {
145 fn from(path: HostPath) -> Self {
146 path.0
147 }
148}
149
150impl From<String> for HostPath {
151 fn from(s: String) -> Self {
152 Arc::new(s).into()
153 }
154}
155
156impl<'a> From<&'a str> for HostPath {
157 fn from(s: &'a str) -> Self {
158 s.to_string().into()
159 }
160}
161
162impl From<url::Url> for HostPath {
163 fn from(url: url::Url) -> Self {
164 url.as_str().into()
165 }
166}
167
168impl From<HostPath> for PathBuf {
169 fn from(path: HostPath) -> Self {
170 PathBuf::from(path.0.as_str())
171 }
172}
173
174impl From<&HostPath> for PathBuf {
175 fn from(path: &HostPath) -> Self {
176 PathBuf::from(path.as_str())
177 }
178}
179
180#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
184pub struct GuestPath(pub Arc<String>);
185
186impl GuestPath {
187 pub fn new(path: impl Into<String>) -> Self {
189 Self(Arc::new(path.into()))
190 }
191
192 pub fn as_str(&self) -> &str {
194 &self.0
195 }
196}
197
198impl fmt::Display for GuestPath {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 self.0.fmt(f)
201 }
202}
203
204impl From<Arc<String>> for GuestPath {
205 fn from(path: Arc<String>) -> Self {
206 Self(path)
207 }
208}
209
210impl From<GuestPath> for Arc<String> {
211 fn from(path: GuestPath) -> Self {
212 path.0
213 }
214}
215
216pub(crate) trait Coercible: Sized {
218 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self>;
226}
227
228#[derive(Debug, Clone)]
232pub struct NoneValue(Arc<Type>);
233
234impl NoneValue {
235 pub fn new(ty: Type) -> Self {
237 Self(Arc::new(ty))
238 }
239
240 pub fn untyped() -> Self {
247 static INSTANCE: LazyLock<NoneValue> = LazyLock::new(|| NoneValue::new(Type::None));
248 INSTANCE.clone()
249 }
250
251 pub fn ty(&self) -> &Type {
253 &self.0
254 }
255}
256
257#[derive(Debug, Clone)]
261pub struct TypeNameRefValue(Arc<Type>);
262
263impl TypeNameRefValue {
264 pub fn new(ty: Type) -> Self {
266 Self(Arc::new(ty))
267 }
268
269 pub fn ty(&self) -> &Type {
271 &self.0
272 }
273}
274
275#[derive(Debug, Clone)]
279pub enum Value {
280 None(NoneValue),
284 Primitive(PrimitiveValue),
286 Compound(CompoundValue),
288 Hidden(HiddenValue),
293 Call(CallValue),
295 TypeNameRef(TypeNameRefValue),
297}
298
299const _: () = {
302 assert!(std::mem::size_of::<Value>() <= 24);
303};
304
305impl Value {
306 pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
312 match value {
313 v1::MetadataValue::Boolean(v) => v.value().into(),
314 v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
315 v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
316 v1::MetadataValue::String(v) => PrimitiveValue::new_string(
317 v.text()
318 .expect("metadata strings shouldn't have placeholders")
319 .text(),
320 )
321 .into(),
322 v1::MetadataValue::Null(_) => Self::new_none(Type::None),
323 v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
324 v1::MetadataValue::Array(a) => Array::new_unchecked(
325 ANALYSIS_STDLIB.array_object_type().clone(),
326 a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
327 )
328 .into(),
329 }
330 }
331
332 pub fn new_none(ty: Type) -> Self {
338 assert!(ty.is_optional(), "the provided `None` type is not optional");
339 Self::None(NoneValue::new(ty))
340 }
341
342 pub fn ty(&self) -> Type {
344 match self {
345 Self::None(v) => v.ty().clone(),
346 Self::Primitive(v) => v.ty(),
347 Self::Compound(v) => v.ty(),
348 Self::Hidden(v) => v.ty(),
349 Self::Call(v) => Type::Call(v.ty().clone()),
350 Self::TypeNameRef(v) => v.ty().clone(),
351 }
352 }
353
354 pub fn is_none(&self) -> bool {
356 matches!(self, Self::None(_))
357 }
358
359 pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
363 match self {
364 Self::Primitive(v) => Some(v),
365 _ => None,
366 }
367 }
368
369 pub fn as_compound(&self) -> Option<&CompoundValue> {
373 match self {
374 Self::Compound(v) => Some(v),
375 _ => None,
376 }
377 }
378
379 pub fn as_boolean(&self) -> Option<bool> {
383 match self {
384 Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
385 _ => None,
386 }
387 }
388
389 pub fn unwrap_boolean(self) -> bool {
395 match self {
396 Self::Primitive(PrimitiveValue::Boolean(v)) => v,
397 _ => panic!("value is not a boolean"),
398 }
399 }
400
401 pub fn as_integer(&self) -> Option<i64> {
405 match self {
406 Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
407 _ => None,
408 }
409 }
410
411 pub fn unwrap_integer(self) -> i64 {
417 match self {
418 Self::Primitive(PrimitiveValue::Integer(v)) => v,
419 _ => panic!("value is not an integer"),
420 }
421 }
422
423 pub fn as_float(&self) -> Option<f64> {
427 match self {
428 Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
429 _ => None,
430 }
431 }
432
433 pub fn unwrap_float(self) -> f64 {
439 match self {
440 Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
441 _ => panic!("value is not a float"),
442 }
443 }
444
445 pub fn as_string(&self) -> Option<&Arc<String>> {
449 match self {
450 Self::Primitive(PrimitiveValue::String(s)) => Some(s),
451 _ => None,
452 }
453 }
454
455 pub fn unwrap_string(self) -> Arc<String> {
461 match self {
462 Self::Primitive(PrimitiveValue::String(s)) => s,
463 _ => panic!("value is not a string"),
464 }
465 }
466
467 pub fn as_file(&self) -> Option<&HostPath> {
471 match self {
472 Self::Primitive(PrimitiveValue::File(p)) => Some(p),
473 _ => None,
474 }
475 }
476
477 pub fn unwrap_file(self) -> HostPath {
483 match self {
484 Self::Primitive(PrimitiveValue::File(p)) => p,
485 _ => panic!("value is not a file"),
486 }
487 }
488
489 pub fn as_directory(&self) -> Option<&HostPath> {
493 match self {
494 Self::Primitive(PrimitiveValue::Directory(p)) => Some(p),
495 _ => None,
496 }
497 }
498
499 pub fn unwrap_directory(self) -> HostPath {
505 match self {
506 Self::Primitive(PrimitiveValue::Directory(p)) => p,
507 _ => panic!("value is not a directory"),
508 }
509 }
510
511 pub fn as_pair(&self) -> Option<&Pair> {
515 match self {
516 Self::Compound(CompoundValue::Pair(v)) => Some(v),
517 _ => None,
518 }
519 }
520
521 pub fn unwrap_pair(self) -> Pair {
527 match self {
528 Self::Compound(CompoundValue::Pair(v)) => v,
529 _ => panic!("value is not a pair"),
530 }
531 }
532
533 pub fn as_array(&self) -> Option<&Array> {
537 match self {
538 Self::Compound(CompoundValue::Array(v)) => Some(v),
539 _ => None,
540 }
541 }
542
543 pub fn unwrap_array(self) -> Array {
549 match self {
550 Self::Compound(CompoundValue::Array(v)) => v,
551 _ => panic!("value is not an array"),
552 }
553 }
554
555 pub fn as_map(&self) -> Option<&Map> {
559 match self {
560 Self::Compound(CompoundValue::Map(v)) => Some(v),
561 _ => None,
562 }
563 }
564
565 pub fn unwrap_map(self) -> Map {
571 match self {
572 Self::Compound(CompoundValue::Map(v)) => v,
573 _ => panic!("value is not a map"),
574 }
575 }
576
577 pub fn as_object(&self) -> Option<&Object> {
581 match self {
582 Self::Compound(CompoundValue::Object(v)) => Some(v),
583 _ => None,
584 }
585 }
586
587 pub fn unwrap_object(self) -> Object {
593 match self {
594 Self::Compound(CompoundValue::Object(v)) => v,
595 _ => panic!("value is not an object"),
596 }
597 }
598
599 pub fn as_struct(&self) -> Option<&Struct> {
603 match self {
604 Self::Compound(CompoundValue::Struct(v)) => Some(v),
605 _ => None,
606 }
607 }
608
609 pub fn unwrap_struct(self) -> Struct {
615 match self {
616 Self::Compound(CompoundValue::Struct(v)) => v,
617 _ => panic!("value is not a struct"),
618 }
619 }
620
621 pub fn as_task_pre_evaluation(&self) -> Option<&TaskPreEvaluationValue> {
625 match self {
626 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => Some(v),
627 _ => None,
628 }
629 }
630
631 pub fn unwrap_task_pre_evaluation(self) -> TaskPreEvaluationValue {
637 match self {
638 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => v,
639 _ => panic!("value is not a pre-evaluation task"),
640 }
641 }
642
643 pub fn as_task_post_evaluation(&self) -> Option<&TaskPostEvaluationValue> {
647 match self {
648 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
649 _ => None,
650 }
651 }
652
653 pub(crate) fn as_task_post_evaluation_mut(&mut self) -> Option<&mut TaskPostEvaluationValue> {
657 match self {
658 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
659 _ => None,
660 }
661 }
662
663 pub fn unwrap_task_post_evaluation(self) -> TaskPostEvaluationValue {
669 match self {
670 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => v,
671 _ => panic!("value is not a post-evaluation task"),
672 }
673 }
674
675 pub fn as_hints(&self) -> Option<&HintsValue> {
679 match self {
680 Self::Hidden(HiddenValue::Hints(v)) => Some(v),
681 _ => None,
682 }
683 }
684
685 pub fn unwrap_hints(self) -> HintsValue {
691 match self {
692 Self::Hidden(HiddenValue::Hints(v)) => v,
693 _ => panic!("value is not a hints value"),
694 }
695 }
696
697 pub fn as_call(&self) -> Option<&CallValue> {
701 match self {
702 Self::Call(v) => Some(v),
703 _ => None,
704 }
705 }
706
707 pub fn unwrap_call(self) -> CallValue {
713 match self {
714 Self::Call(v) => v,
715 _ => panic!("value is not a call value"),
716 }
717 }
718
719 pub(crate) fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
724 where
725 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
726 {
727 match self {
728 Self::Primitive(PrimitiveValue::File(path)) => cb(true, path),
729 Self::Primitive(PrimitiveValue::Directory(path)) => cb(false, path),
730 Self::Compound(v) => v.visit_paths(cb),
731 _ => Ok(()),
732 }
733 }
734
735 pub(crate) async fn resolve_paths<F>(
754 &self,
755 optional: bool,
756 base_dir: Option<&Path>,
757 transferer: Option<&dyn Transferer>,
758 translate: &F,
759 ) -> Result<Self>
760 where
761 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
762 {
763 fn new_file_or_directory(is_file: bool, path: impl Into<HostPath>) -> PrimitiveValue {
764 if is_file {
765 PrimitiveValue::File(path.into())
766 } else {
767 PrimitiveValue::Directory(path.into())
768 }
769 }
770
771 match self {
772 Self::Primitive(v @ PrimitiveValue::File(path))
773 | Self::Primitive(v @ PrimitiveValue::Directory(path)) => {
774 let is_file = v.as_file().is_some();
777 let path = translate(path)?;
778
779 if path::is_file_url(path.as_str()) {
780 let exists = path
783 .as_str()
784 .parse::<Url>()
785 .ok()
786 .and_then(|url| url.to_file_path().ok())
787 .map(|p| p.exists())
788 .unwrap_or(false);
789 if exists {
790 let v = new_file_or_directory(is_file, path);
791 return Ok(Self::Primitive(v));
792 }
793
794 if optional && !exists {
795 return Ok(Value::new_none(self.ty().optional()));
796 }
797
798 bail!("path `{path}` does not exist");
799 } else if path::is_supported_url(path.as_str()) {
800 match transferer {
801 Some(transferer) => {
802 let exists = transferer
803 .exists(
804 &path
805 .as_str()
806 .parse()
807 .with_context(|| format!("invalid URL `{path}`"))?,
808 )
809 .await?;
810 if exists {
811 let v = new_file_or_directory(is_file, path);
812 return Ok(Self::Primitive(v));
813 }
814
815 if optional && !exists {
816 return Ok(Value::new_none(self.ty().optional()));
817 }
818
819 bail!("URL `{path}` does not exist");
820 }
821 None => {
822 let v = new_file_or_directory(is_file, path);
824 return Ok(Self::Primitive(v));
825 }
826 }
827 }
828
829 let exists_path: Cow<'_, Path> = base_dir
831 .map(|d| d.join(path.as_str()).into())
832 .unwrap_or_else(|| Path::new(path.as_str()).into());
833 if is_file && !exists_path.is_file() {
834 if optional {
835 return Ok(Value::new_none(self.ty().optional()));
836 } else {
837 bail!("file `{}` does not exist", exists_path.display());
838 }
839 } else if !is_file && !exists_path.is_dir() {
840 if optional {
841 return Ok(Value::new_none(self.ty().optional()));
842 } else {
843 bail!("directory `{}` does not exist", exists_path.display())
844 }
845 }
846
847 let v = new_file_or_directory(is_file, path);
848 Ok(Self::Primitive(v))
849 }
850 Self::Compound(v) => Ok(Self::Compound(
851 v.resolve_paths(base_dir, transferer, translate)
852 .boxed()
853 .await?,
854 )),
855 v => Ok(v.clone()),
856 }
857 }
858
859 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
864 match (left, right) {
865 (Value::None(_), Value::None(_)) => Some(true),
866 (Value::None(_), _) | (_, Value::None(_)) => Some(false),
867 (Value::Primitive(left), Value::Primitive(right)) => {
868 Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
869 }
870 (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
871 _ => None,
872 }
873 }
874}
875
876impl fmt::Display for Value {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 match self {
879 Self::None(_) => write!(f, "None"),
880 Self::Primitive(v) => v.fmt(f),
881 Self::Compound(v) => v.fmt(f),
882 Self::Hidden(v) => v.fmt(f),
883 Self::Call(c) => c.fmt(f),
884 Self::TypeNameRef(v) => v.ty().fmt(f),
885 }
886 }
887}
888
889impl Coercible for Value {
890 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
891 if target.is_union() || target.is_none() || self.ty().eq(target) {
892 return Ok(self.clone());
893 }
894
895 match self {
896 Self::None(_) => {
897 if target.is_optional() {
898 Ok(Self::new_none(target.clone()))
899 } else {
900 bail!("cannot coerce `None` to non-optional {target:#}");
901 }
902 }
903 Self::Primitive(PrimitiveValue::String(s)) if target.as_enum().is_some() => {
905 let enum_ty = target.as_enum().unwrap();
907
908 if enum_ty
909 .variants()
910 .iter()
911 .any(|variant_name| variant_name == s.as_str())
912 {
913 if let Some(context) = context {
914 if let Ok(value) = context.enum_variant_value(enum_ty.name(), s) {
915 return Ok(Value::Compound(CompoundValue::EnumVariant(
916 EnumVariant::new(enum_ty.clone(), s.as_str(), value),
917 )));
918 } else {
919 bail!(
920 "enum variant value lookup failed for variant `{s}` in enum `{}`",
921 enum_ty.name()
922 );
923 }
924 } else {
925 bail!(
926 "context does not exist when creating enum variant value `{s}` in \
927 enum `{}`",
928 enum_ty.name()
929 );
930 }
931 }
932
933 let variants = if enum_ty.variants().is_empty() {
934 None
935 } else {
936 let mut variant_names = enum_ty.variants().to_vec();
937 variant_names.sort();
938 Some(format!(" (variants: `{}`)", variant_names.join("`, `")))
939 }
940 .unwrap_or_default();
941
942 bail!(
943 "cannot coerce type `String` to {target:#}: variant `{s}` not found in enum \
944 `{}`{variants}",
945 enum_ty.name()
946 );
947 }
948 Self::Compound(CompoundValue::EnumVariant(e))
950 if target
951 .as_primitive()
952 .map(|t| matches!(t, PrimitiveType::String))
953 .unwrap_or(false) =>
954 {
955 Ok(Value::Primitive(PrimitiveValue::new_string(e.name())))
956 }
957 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
958 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
959 Self::Hidden(v) => v.coerce(context, target).map(Self::Hidden),
960 Self::Call(_) => {
961 bail!("call values cannot be coerced to any other type");
962 }
963 Self::TypeNameRef(_) => {
964 bail!("type name references cannot be coerced to any other type");
965 }
966 }
967 }
968}
969
970impl From<bool> for Value {
971 fn from(value: bool) -> Self {
972 Self::Primitive(value.into())
973 }
974}
975
976impl From<i64> for Value {
977 fn from(value: i64) -> Self {
978 Self::Primitive(value.into())
979 }
980}
981
982impl TryFrom<u64> for Value {
983 type Error = std::num::TryFromIntError;
984
985 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
986 let value: i64 = value.try_into()?;
987 Ok(value.into())
988 }
989}
990
991impl From<f64> for Value {
992 fn from(value: f64) -> Self {
993 Self::Primitive(value.into())
994 }
995}
996
997impl From<String> for Value {
998 fn from(value: String) -> Self {
999 Self::Primitive(value.into())
1000 }
1001}
1002
1003impl From<PrimitiveValue> for Value {
1004 fn from(value: PrimitiveValue) -> Self {
1005 Self::Primitive(value)
1006 }
1007}
1008
1009impl From<Option<PrimitiveValue>> for Value {
1010 fn from(value: Option<PrimitiveValue>) -> Self {
1011 match value {
1012 Some(v) => v.into(),
1013 None => Self::new_none(Type::None),
1014 }
1015 }
1016}
1017
1018impl From<CompoundValue> for Value {
1019 fn from(value: CompoundValue) -> Self {
1020 Self::Compound(value)
1021 }
1022}
1023
1024impl From<HiddenValue> for Value {
1025 fn from(value: HiddenValue) -> Self {
1026 Self::Hidden(value)
1027 }
1028}
1029
1030impl From<Pair> for Value {
1031 fn from(value: Pair) -> Self {
1032 Self::Compound(value.into())
1033 }
1034}
1035
1036impl From<Array> for Value {
1037 fn from(value: Array) -> Self {
1038 Self::Compound(value.into())
1039 }
1040}
1041
1042impl From<Map> for Value {
1043 fn from(value: Map) -> Self {
1044 Self::Compound(value.into())
1045 }
1046}
1047
1048impl From<Object> for Value {
1049 fn from(value: Object) -> Self {
1050 Self::Compound(value.into())
1051 }
1052}
1053
1054impl From<Struct> for Value {
1055 fn from(value: Struct) -> Self {
1056 Self::Compound(value.into())
1057 }
1058}
1059
1060impl From<CallValue> for Value {
1061 fn from(value: CallValue) -> Self {
1062 Self::Call(value)
1063 }
1064}
1065
1066impl<'de> serde::Deserialize<'de> for Value {
1067 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
1068 where
1069 D: serde::Deserializer<'de>,
1070 {
1071 use serde::Deserialize as _;
1072
1073 struct Visitor;
1075
1076 impl<'de> serde::de::Visitor<'de> for Visitor {
1077 type Value = Value;
1078
1079 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
1080 where
1081 E: serde::de::Error,
1082 {
1083 Ok(Value::new_none(Type::None))
1084 }
1085
1086 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
1087 where
1088 E: serde::de::Error,
1089 {
1090 Ok(Value::new_none(Type::None))
1091 }
1092
1093 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
1094 where
1095 D: serde::Deserializer<'de>,
1096 {
1097 Value::deserialize(deserializer)
1098 }
1099
1100 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
1101 where
1102 E: serde::de::Error,
1103 {
1104 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
1105 }
1106
1107 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
1108 where
1109 E: serde::de::Error,
1110 {
1111 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
1112 }
1113
1114 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
1115 where
1116 E: serde::de::Error,
1117 {
1118 Ok(Value::Primitive(PrimitiveValue::Integer(
1119 v.try_into().map_err(|_| {
1120 E::custom("integer not in range for a 64-bit signed integer")
1121 })?,
1122 )))
1123 }
1124
1125 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
1126 where
1127 E: serde::de::Error,
1128 {
1129 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
1130 }
1131
1132 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
1133 where
1134 E: serde::de::Error,
1135 {
1136 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1137 }
1138
1139 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
1140 where
1141 E: serde::de::Error,
1142 {
1143 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1144 }
1145
1146 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
1147 where
1148 A: serde::de::SeqAccess<'de>,
1149 {
1150 use serde::de::Error as _;
1151
1152 let mut elements = vec![];
1153 while let Some(element) = seq.next_element::<Value>()? {
1154 elements.push(element);
1155 }
1156
1157 let mut candidate_ty = None;
1159 for element in elements.iter() {
1160 let new_candidate_ty = element.ty();
1161 let old_candidate_ty =
1162 candidate_ty.get_or_insert_with(|| new_candidate_ty.clone());
1163 let Some(new_common_ty) = old_candidate_ty.common_type(&new_candidate_ty)
1164 else {
1165 return Err(A::Error::custom(format!(
1166 "a common element type does not exist between {old_candidate_ty:#} \
1167 and {new_candidate_ty:#}"
1168 )));
1169 };
1170 candidate_ty = Some(new_common_ty);
1171 }
1172 let array_ty = ArrayType::new(candidate_ty.unwrap_or(Type::Union));
1174 Ok(Array::new(array_ty.clone(), elements)
1175 .map_err(|e| {
1176 A::Error::custom(format!("cannot coerce value to {array_ty:#}: {e:#}"))
1177 })?
1178 .into())
1179 }
1180
1181 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
1182 where
1183 A: serde::de::MapAccess<'de>,
1184 {
1185 let mut members = IndexMap::new();
1186 while let Some(key) = map.next_key::<String>()? {
1187 members.insert(key, map.next_value()?);
1188 }
1189
1190 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
1191 }
1192
1193 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1194 write!(f, "a WDL value")
1195 }
1196 }
1197
1198 deserializer.deserialize_any(Visitor)
1199 }
1200}
1201
1202#[derive(Debug, Clone)]
1206pub enum PrimitiveValue {
1207 Boolean(bool),
1209 Integer(i64),
1211 Float(OrderedFloat<f64>),
1213 String(Arc<String>),
1215 File(HostPath),
1217 Directory(HostPath),
1219}
1220
1221impl PrimitiveValue {
1222 pub fn new_string(s: impl Into<String>) -> Self {
1224 Self::String(Arc::new(s.into()))
1225 }
1226
1227 pub fn new_file(path: impl Into<HostPath>) -> Self {
1229 Self::File(path.into())
1230 }
1231
1232 pub fn new_directory(path: impl Into<HostPath>) -> Self {
1234 Self::Directory(path.into())
1235 }
1236
1237 pub fn ty(&self) -> Type {
1239 match self {
1240 Self::Boolean(_) => PrimitiveType::Boolean.into(),
1241 Self::Integer(_) => PrimitiveType::Integer.into(),
1242 Self::Float(_) => PrimitiveType::Float.into(),
1243 Self::String(_) => PrimitiveType::String.into(),
1244 Self::File(_) => PrimitiveType::File.into(),
1245 Self::Directory(_) => PrimitiveType::Directory.into(),
1246 }
1247 }
1248
1249 pub fn as_boolean(&self) -> Option<bool> {
1253 match self {
1254 Self::Boolean(v) => Some(*v),
1255 _ => None,
1256 }
1257 }
1258
1259 pub fn unwrap_boolean(self) -> bool {
1265 match self {
1266 Self::Boolean(v) => v,
1267 _ => panic!("value is not a boolean"),
1268 }
1269 }
1270
1271 pub fn as_integer(&self) -> Option<i64> {
1275 match self {
1276 Self::Integer(v) => Some(*v),
1277 _ => None,
1278 }
1279 }
1280
1281 pub fn unwrap_integer(self) -> i64 {
1287 match self {
1288 Self::Integer(v) => v,
1289 _ => panic!("value is not an integer"),
1290 }
1291 }
1292
1293 pub fn as_float(&self) -> Option<f64> {
1297 match self {
1298 Self::Float(v) => Some((*v).into()),
1299 _ => None,
1300 }
1301 }
1302
1303 pub fn unwrap_float(self) -> f64 {
1309 match self {
1310 Self::Float(v) => v.into(),
1311 _ => panic!("value is not a float"),
1312 }
1313 }
1314
1315 pub fn as_string(&self) -> Option<&Arc<String>> {
1319 match self {
1320 Self::String(s) => Some(s),
1321 _ => None,
1322 }
1323 }
1324
1325 pub fn unwrap_string(self) -> Arc<String> {
1331 match self {
1332 Self::String(s) => s,
1333 _ => panic!("value is not a string"),
1334 }
1335 }
1336
1337 pub fn as_file(&self) -> Option<&HostPath> {
1341 match self {
1342 Self::File(p) => Some(p),
1343 _ => None,
1344 }
1345 }
1346
1347 pub fn unwrap_file(self) -> HostPath {
1353 match self {
1354 Self::File(p) => p,
1355 _ => panic!("value is not a file"),
1356 }
1357 }
1358
1359 pub fn as_directory(&self) -> Option<&HostPath> {
1363 match self {
1364 Self::Directory(p) => Some(p),
1365 _ => None,
1366 }
1367 }
1368
1369 pub fn unwrap_directory(self) -> HostPath {
1375 match self {
1376 Self::Directory(p) => p,
1377 _ => panic!("value is not a directory"),
1378 }
1379 }
1380
1381 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1388 match (left, right) {
1389 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1390 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1391 (Self::Integer(left), Self::Float(right)) => {
1392 Some(OrderedFloat(*left as f64).cmp(right))
1393 }
1394 (Self::Float(left), Self::Integer(right)) => {
1395 Some(left.cmp(&OrderedFloat(*right as f64)))
1396 }
1397 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1398 (Self::String(left), Self::String(right))
1399 | (Self::String(left), Self::File(HostPath(right)))
1400 | (Self::String(left), Self::Directory(HostPath(right)))
1401 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1402 | (Self::File(HostPath(left)), Self::String(right))
1403 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1404 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1405 _ => None,
1406 }
1407 }
1408
1409 pub(crate) fn raw<'a>(
1417 &'a self,
1418 context: Option<&'a dyn EvaluationContext>,
1419 ) -> impl fmt::Display + use<'a> {
1420 struct Display<'a> {
1422 value: &'a PrimitiveValue,
1424 context: Option<&'a dyn EvaluationContext>,
1426 }
1427
1428 impl fmt::Display for Display<'_> {
1429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1430 match self.value {
1431 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1432 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1433 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1434 PrimitiveValue::String(v) => write!(f, "{v}"),
1435 PrimitiveValue::File(v) => {
1436 write!(
1437 f,
1438 "{v}",
1439 v = self
1440 .context
1441 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1442 .unwrap_or(Cow::Borrowed(&v.0))
1443 )
1444 }
1445 PrimitiveValue::Directory(v) => {
1446 write!(
1447 f,
1448 "{v}",
1449 v = self
1450 .context
1451 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1452 .unwrap_or(Cow::Borrowed(&v.0))
1453 )
1454 }
1455 }
1456 }
1457 }
1458
1459 Display {
1460 value: self,
1461 context,
1462 }
1463 }
1464}
1465
1466impl fmt::Display for PrimitiveValue {
1467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1468 match self {
1469 Self::Boolean(v) => write!(f, "{v}"),
1470 Self::Integer(v) => write!(f, "{v}"),
1471 Self::Float(v) => write!(f, "{v:.6?}"),
1472 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1473 f.write_str("\"")?;
1474 write_escaped_wdl_string(f, s.as_str())?;
1475 f.write_str("\"")
1476 }
1477 }
1478 }
1479}
1480
1481impl PartialEq for PrimitiveValue {
1482 fn eq(&self, other: &Self) -> bool {
1483 Self::compare(self, other) == Some(Ordering::Equal)
1484 }
1485}
1486
1487impl Eq for PrimitiveValue {}
1488
1489impl Hash for PrimitiveValue {
1490 fn hash<H: Hasher>(&self, state: &mut H) {
1491 match self {
1492 Self::Boolean(v) => {
1493 0.hash(state);
1494 v.hash(state);
1495 }
1496 Self::Integer(v) => {
1497 1.hash(state);
1498 v.hash(state);
1499 }
1500 Self::Float(v) => {
1501 1.hash(state);
1504 v.hash(state);
1505 }
1506 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1507 2.hash(state);
1510 v.hash(state);
1511 }
1512 }
1513 }
1514}
1515
1516impl From<bool> for PrimitiveValue {
1517 fn from(value: bool) -> Self {
1518 Self::Boolean(value)
1519 }
1520}
1521
1522impl From<i64> for PrimitiveValue {
1523 fn from(value: i64) -> Self {
1524 Self::Integer(value)
1525 }
1526}
1527
1528impl From<f64> for PrimitiveValue {
1529 fn from(value: f64) -> Self {
1530 Self::Float(value.into())
1531 }
1532}
1533
1534impl From<String> for PrimitiveValue {
1535 fn from(value: String) -> Self {
1536 Self::String(value.into())
1537 }
1538}
1539
1540impl Coercible for PrimitiveValue {
1541 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1542 if target.is_union() || target.is_none() || self.ty().eq(target) {
1543 return Ok(self.clone());
1544 }
1545
1546 match self {
1547 Self::Boolean(v) => {
1548 target
1549 .as_primitive()
1550 .and_then(|ty| match ty {
1551 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1553 _ => None,
1554 })
1555 .with_context(|| format!("cannot coerce type `Boolean` to {target:#}"))
1556 }
1557 Self::Integer(v) => {
1558 target
1559 .as_primitive()
1560 .and_then(|ty| match ty {
1561 PrimitiveType::Integer => Some(Self::Integer(*v)),
1563 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1565 _ => None,
1566 })
1567 .with_context(|| format!("cannot coerce type `Int` to {target:#}"))
1568 }
1569 Self::Float(v) => {
1570 target
1571 .as_primitive()
1572 .and_then(|ty| match ty {
1573 PrimitiveType::Float => Some(Self::Float(*v)),
1575 _ => None,
1576 })
1577 .with_context(|| format!("cannot coerce type `Float` to {target:#}"))
1578 }
1579 Self::String(s) => {
1580 target
1581 .as_primitive()
1582 .and_then(|ty| match ty {
1583 PrimitiveType::String => Some(Self::String(s.clone())),
1585 PrimitiveType::File => Some(Self::File(
1587 context
1588 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1589 .unwrap_or_else(|| s.clone().into()),
1590 )),
1591 PrimitiveType::Directory => Some(Self::Directory(
1593 context
1594 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1595 .unwrap_or_else(|| s.clone().into()),
1596 )),
1597 _ => None,
1598 })
1599 .with_context(|| format!("cannot coerce type `String` to {target:#}"))
1600 }
1601 Self::File(p) => {
1602 target
1603 .as_primitive()
1604 .and_then(|ty| match ty {
1605 PrimitiveType::File => Some(Self::File(p.clone())),
1607 PrimitiveType::String => Some(Self::String(
1609 context
1610 .and_then(|c| c.guest_path(p).map(Into::into))
1611 .unwrap_or_else(|| p.clone().into()),
1612 )),
1613 _ => None,
1614 })
1615 .with_context(|| format!("cannot coerce type `File` to {target:#}"))
1616 }
1617 Self::Directory(p) => {
1618 target
1619 .as_primitive()
1620 .and_then(|ty| match ty {
1621 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1623 PrimitiveType::String => Some(Self::String(
1625 context
1626 .and_then(|c| c.guest_path(p).map(Into::into))
1627 .unwrap_or_else(|| p.clone().into()),
1628 )),
1629 _ => None,
1630 })
1631 .with_context(|| format!("cannot coerce type `Directory` to {target:#}"))
1632 }
1633 }
1634 }
1635}
1636
1637#[derive(Debug)]
1639struct PairInner {
1640 ty: Type,
1642 left: Value,
1644 right: Value,
1646}
1647
1648#[derive(Debug, Clone)]
1652pub struct Pair(Arc<PairInner>);
1653
1654impl Pair {
1655 pub fn new(ty: PairType, left: impl Into<Value>, right: impl Into<Value>) -> Result<Self> {
1660 Self::new_with_context(None, ty, left, right)
1661 }
1662
1663 pub(crate) fn new_with_context(
1668 context: Option<&dyn EvaluationContext>,
1669 ty: PairType,
1670 left: impl Into<Value>,
1671 right: impl Into<Value>,
1672 ) -> Result<Self> {
1673 let left = left
1674 .into()
1675 .coerce(context, ty.left_type())
1676 .context("failed to coerce pair's left value")?;
1677 let right = right
1678 .into()
1679 .coerce(context, ty.right_type())
1680 .context("failed to coerce pair's right value")?;
1681 Ok(Self::new_unchecked(ty, left, right))
1682 }
1683
1684 pub(crate) fn new_unchecked(ty: impl Into<Type>, left: Value, right: Value) -> Self {
1687 let ty = ty.into();
1688 assert!(ty.as_pair().is_some());
1689 Self(Arc::new(PairInner {
1690 ty: ty.require(),
1691 left,
1692 right,
1693 }))
1694 }
1695
1696 pub fn ty(&self) -> Type {
1698 self.0.ty.clone()
1699 }
1700
1701 pub fn left(&self) -> &Value {
1703 &self.0.left
1704 }
1705
1706 pub fn right(&self) -> &Value {
1708 &self.0.right
1709 }
1710}
1711
1712impl fmt::Display for Pair {
1713 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1714 write!(
1715 f,
1716 "({left}, {right})",
1717 left = self.0.left,
1718 right = self.0.right
1719 )
1720 }
1721}
1722
1723#[derive(Debug)]
1725struct ArrayInner {
1726 ty: Type,
1728 elements: Vec<Value>,
1730}
1731
1732#[derive(Debug, Clone)]
1736pub struct Array(Arc<ArrayInner>);
1737
1738impl Array {
1739 pub fn new<V>(ty: ArrayType, elements: impl IntoIterator<Item = V>) -> Result<Self>
1744 where
1745 V: Into<Value>,
1746 {
1747 Self::new_with_context(None, ty, elements)
1748 }
1749
1750 pub(crate) fn new_with_context<V>(
1756 context: Option<&dyn EvaluationContext>,
1757 ty: ArrayType,
1758 elements: impl IntoIterator<Item = V>,
1759 ) -> Result<Self>
1760 where
1761 V: Into<Value>,
1762 {
1763 let element_type = ty.element_type();
1764 let elements = elements
1765 .into_iter()
1766 .enumerate()
1767 .map(|(i, v)| {
1768 let v = v.into();
1769 v.coerce(context, element_type)
1770 .with_context(|| format!("failed to coerce array element at index {i}"))
1771 })
1772 .collect::<Result<Vec<_>>>()?;
1773
1774 Ok(Self::new_unchecked(ty, elements))
1775 }
1776
1777 pub(crate) fn new_unchecked(ty: impl Into<Type>, elements: Vec<Value>) -> Self {
1784 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty.into() {
1785 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1786 } else {
1787 panic!("type is not an array type");
1788 };
1789
1790 Self(Arc::new(ArrayInner { ty, elements }))
1791 }
1792
1793 pub fn ty(&self) -> Type {
1795 self.0.ty.clone()
1796 }
1797
1798 pub fn as_slice(&self) -> &[Value] {
1800 &self.0.elements
1801 }
1802
1803 pub fn len(&self) -> usize {
1805 self.0.elements.len()
1806 }
1807
1808 pub fn is_empty(&self) -> bool {
1810 self.0.elements.is_empty()
1811 }
1812}
1813
1814impl fmt::Display for Array {
1815 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1816 write!(f, "[")?;
1817
1818 for (i, element) in self.0.elements.iter().enumerate() {
1819 if i > 0 {
1820 write!(f, ", ")?;
1821 }
1822
1823 write!(f, "{element}")?;
1824 }
1825
1826 write!(f, "]")
1827 }
1828}
1829
1830#[derive(Debug)]
1832struct MapInner {
1833 ty: Type,
1835 elements: IndexMap<PrimitiveValue, Value>,
1837}
1838
1839#[derive(Debug, Clone)]
1843pub struct Map(Arc<MapInner>);
1844
1845impl Map {
1846 pub fn new<K, V>(ty: MapType, elements: impl IntoIterator<Item = (K, V)>) -> Result<Self>
1851 where
1852 K: Into<PrimitiveValue>,
1853 V: Into<Value>,
1854 {
1855 Self::new_with_context(None, ty, elements)
1856 }
1857
1858 pub(crate) fn new_with_context<K, V>(
1863 context: Option<&dyn EvaluationContext>,
1864 ty: MapType,
1865 elements: impl IntoIterator<Item = (K, V)>,
1866 ) -> Result<Self>
1867 where
1868 K: Into<PrimitiveValue>,
1869 V: Into<Value>,
1870 {
1871 let key_type = ty.key_type();
1872 let value_type = ty.value_type();
1873
1874 let elements = elements
1875 .into_iter()
1876 .enumerate()
1877 .map(|(i, (k, v))| {
1878 let k = k.into();
1879 let v = v.into();
1880 Ok((
1881 k.coerce(context, key_type).with_context(|| {
1882 format!("failed to coerce map key for element at index {i}")
1883 })?,
1884 v.coerce(context, value_type).with_context(|| {
1885 format!("failed to coerce map value for element at index {i}")
1886 })?,
1887 ))
1888 })
1889 .collect::<Result<_>>()?;
1890
1891 Ok(Self::new_unchecked(ty, elements))
1892 }
1893
1894 pub(crate) fn new_unchecked(
1901 ty: impl Into<Type>,
1902 elements: IndexMap<PrimitiveValue, Value>,
1903 ) -> Self {
1904 let ty = ty.into();
1905 assert!(ty.as_map().is_some());
1906 Self(Arc::new(MapInner {
1907 ty: ty.require(),
1908 elements,
1909 }))
1910 }
1911
1912 pub fn ty(&self) -> Type {
1914 self.0.ty.clone()
1915 }
1916
1917 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&PrimitiveValue, &Value)> {
1919 self.0.elements.iter()
1920 }
1921
1922 pub fn keys(&self) -> impl ExactSizeIterator<Item = &PrimitiveValue> {
1924 self.0.elements.keys()
1925 }
1926
1927 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1929 self.0.elements.values()
1930 }
1931
1932 pub fn contains_key(&self, key: &PrimitiveValue) -> bool {
1934 self.0.elements.contains_key(key)
1935 }
1936
1937 pub fn get(&self, key: &PrimitiveValue) -> Option<&Value> {
1939 self.0.elements.get(key)
1940 }
1941
1942 pub fn len(&self) -> usize {
1944 self.0.elements.len()
1945 }
1946
1947 pub fn is_empty(&self) -> bool {
1949 self.0.elements.is_empty()
1950 }
1951}
1952
1953impl fmt::Display for Map {
1954 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1955 write!(f, "{{")?;
1956
1957 for (i, (k, v)) in self.iter().enumerate() {
1958 if i > 0 {
1959 write!(f, ", ")?;
1960 }
1961
1962 write!(f, "{k}: {v}")?;
1963 }
1964
1965 write!(f, "}}")
1966 }
1967}
1968
1969#[derive(Debug, Clone)]
1973pub struct Object {
1974 pub(crate) members: Arc<IndexMap<String, Value>>,
1976}
1977
1978impl Object {
1979 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1983 Self {
1984 members: Arc::new(members),
1985 }
1986 }
1987
1988 pub fn empty() -> Self {
1990 Self::new(IndexMap::default())
1991 }
1992
1993 pub fn from_v1_metadata<N: TreeNode>(
1995 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1996 ) -> Self {
1997 Object::new(
1998 items
1999 .map(|i| {
2000 (
2001 i.name().text().to_string(),
2002 Value::from_v1_metadata(&i.value()),
2003 )
2004 })
2005 .collect::<IndexMap<_, _>>(),
2006 )
2007 }
2008
2009 pub fn ty(&self) -> Type {
2011 Type::Object
2012 }
2013
2014 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2016 self.members.iter().map(|(k, v)| (k.as_str(), v))
2017 }
2018
2019 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2021 self.members.keys().map(|k| k.as_str())
2022 }
2023
2024 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2026 self.members.values()
2027 }
2028
2029 pub fn contains_key(&self, key: &str) -> bool {
2031 self.members.contains_key(key)
2032 }
2033
2034 pub fn get(&self, key: &str) -> Option<&Value> {
2036 self.members.get(key)
2037 }
2038
2039 pub fn len(&self) -> usize {
2041 self.members.len()
2042 }
2043
2044 pub fn is_empty(&self) -> bool {
2046 self.members.is_empty()
2047 }
2048}
2049
2050impl fmt::Display for Object {
2051 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2052 write!(f, "object {{")?;
2053
2054 for (i, (k, v)) in self.iter().enumerate() {
2055 if i > 0 {
2056 write!(f, ", ")?;
2057 }
2058
2059 write!(f, "{k}: {v}")?;
2060 }
2061
2062 write!(f, "}}")
2063 }
2064}
2065
2066#[derive(Debug)]
2068struct StructInner {
2069 ty: Type,
2071 name: Arc<String>,
2077 members: IndexMap<String, Value>,
2079}
2080
2081#[derive(Debug, Clone)]
2085pub struct Struct(Arc<StructInner>);
2086
2087impl Struct {
2088 pub fn new<S, V>(ty: StructType, members: impl IntoIterator<Item = (S, V)>) -> Result<Self>
2093 where
2094 S: Into<String>,
2095 V: Into<Value>,
2096 {
2097 Self::new_with_context(None, ty, members)
2098 }
2099
2100 pub(crate) fn new_with_context<S, V>(
2105 context: Option<&dyn EvaluationContext>,
2106 ty: StructType,
2107 members: impl IntoIterator<Item = (S, V)>,
2108 ) -> Result<Self>
2109 where
2110 S: Into<String>,
2111 V: Into<Value>,
2112 {
2113 let mut members = members
2114 .into_iter()
2115 .map(|(n, v)| {
2116 let n = n.into();
2117 let v = v.into();
2118 let v = v
2119 .coerce(
2120 context,
2121 ty.members().get(&n).ok_or_else(|| {
2122 anyhow!("struct does not contain a member named `{n}`")
2123 })?,
2124 )
2125 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
2126 Ok((n, v))
2127 })
2128 .collect::<Result<IndexMap<_, _>>>()?;
2129
2130 for (name, ty) in ty.members().iter() {
2131 if ty.is_optional() {
2133 if !members.contains_key(name) {
2134 members.insert(name.clone(), Value::new_none(ty.clone()));
2135 }
2136 } else {
2137 if !members.contains_key(name) {
2139 bail!("missing a value for struct member `{name}`");
2140 }
2141 }
2142 }
2143
2144 let name = Arc::new(ty.name().to_string());
2145 Ok(Self::new_unchecked(ty, name, members))
2146 }
2147
2148 pub(crate) fn new_unchecked(
2155 ty: impl Into<Type>,
2156 name: Arc<String>,
2157 members: impl Into<IndexMap<String, Value>>,
2158 ) -> Self {
2159 let ty = ty.into();
2160 assert!(ty.as_struct().is_some());
2161 Self(Arc::new(StructInner {
2162 ty: ty.require(),
2163 name,
2164 members: members.into(),
2165 }))
2166 }
2167
2168 pub fn ty(&self) -> Type {
2170 self.0.ty.clone()
2171 }
2172
2173 pub fn name(&self) -> &Arc<String> {
2175 &self.0.name
2176 }
2177
2178 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2180 self.0.members.iter().map(|(k, v)| (k.as_str(), v))
2181 }
2182
2183 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2185 self.0.members.keys().map(|k| k.as_str())
2186 }
2187
2188 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2190 self.0.members.values()
2191 }
2192
2193 pub fn contains_key(&self, key: &str) -> bool {
2195 self.0.members.contains_key(key)
2196 }
2197
2198 pub fn get(&self, key: &str) -> Option<&Value> {
2200 self.0.members.get(key)
2201 }
2202}
2203
2204impl fmt::Display for Struct {
2205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2206 write!(f, "{name} {{", name = self.0.name)?;
2207
2208 for (i, (k, v)) in self.0.members.iter().enumerate() {
2209 if i > 0 {
2210 write!(f, ", ")?;
2211 }
2212
2213 write!(f, "{k}: {v}")?;
2214 }
2215
2216 write!(f, "}}")
2217 }
2218}
2219
2220#[derive(Debug)]
2222struct EnumVariantInner {
2223 enum_ty: EnumType,
2225 variant_index: usize,
2227 value: Value,
2229}
2230
2231#[derive(Debug, Clone)]
2238pub struct EnumVariant(Arc<EnumVariantInner>);
2239
2240impl PartialEq for EnumVariant {
2241 fn eq(&self, other: &Self) -> bool {
2242 self.0.enum_ty == other.0.enum_ty && self.0.variant_index == other.0.variant_index
2243 }
2244}
2245
2246impl EnumVariant {
2247 pub fn new(enum_ty: impl Into<EnumType>, name: &str, value: impl Into<Value>) -> Self {
2253 let enum_ty = enum_ty.into();
2254
2255 let variant_index = enum_ty
2256 .variants()
2257 .iter()
2258 .position(|v| v == name)
2259 .expect("variant name must exist in enum type");
2260
2261 Self(Arc::new(EnumVariantInner {
2262 enum_ty,
2263 variant_index,
2264 value: value.into(),
2265 }))
2266 }
2267
2268 pub fn enum_ty(&self) -> EnumType {
2270 self.0.enum_ty.clone()
2271 }
2272
2273 pub fn name(&self) -> &str {
2275 &self.0.enum_ty.variants()[self.0.variant_index]
2276 }
2277
2278 pub fn value(&self) -> &Value {
2280 &self.0.value
2281 }
2282}
2283
2284impl fmt::Display for EnumVariant {
2309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2310 write!(f, "{}", self.name())
2311 }
2312}
2313
2314#[derive(Debug, Clone)]
2318pub enum CompoundValue {
2319 Pair(Pair),
2321 Array(Array),
2323 Map(Map),
2325 Object(Object),
2327 Struct(Struct),
2329 EnumVariant(EnumVariant),
2331}
2332
2333impl CompoundValue {
2334 pub fn ty(&self) -> Type {
2336 match self {
2337 CompoundValue::Pair(v) => v.ty(),
2338 CompoundValue::Array(v) => v.ty(),
2339 CompoundValue::Map(v) => v.ty(),
2340 CompoundValue::Object(v) => v.ty(),
2341 CompoundValue::Struct(v) => v.ty(),
2342 CompoundValue::EnumVariant(v) => v.enum_ty().into(),
2343 }
2344 }
2345
2346 pub fn as_pair(&self) -> Option<&Pair> {
2350 match self {
2351 Self::Pair(v) => Some(v),
2352 _ => None,
2353 }
2354 }
2355
2356 pub fn unwrap_pair(self) -> Pair {
2362 match self {
2363 Self::Pair(v) => v,
2364 _ => panic!("value is not a pair"),
2365 }
2366 }
2367
2368 pub fn as_array(&self) -> Option<&Array> {
2372 match self {
2373 Self::Array(v) => Some(v),
2374 _ => None,
2375 }
2376 }
2377
2378 pub fn unwrap_array(self) -> Array {
2384 match self {
2385 Self::Array(v) => v,
2386 _ => panic!("value is not an array"),
2387 }
2388 }
2389
2390 pub fn as_map(&self) -> Option<&Map> {
2394 match self {
2395 Self::Map(v) => Some(v),
2396 _ => None,
2397 }
2398 }
2399
2400 pub fn unwrap_map(self) -> Map {
2406 match self {
2407 Self::Map(v) => v,
2408 _ => panic!("value is not a map"),
2409 }
2410 }
2411
2412 pub fn as_object(&self) -> Option<&Object> {
2416 match self {
2417 Self::Object(v) => Some(v),
2418 _ => None,
2419 }
2420 }
2421
2422 pub fn unwrap_object(self) -> Object {
2428 match self {
2429 Self::Object(v) => v,
2430 _ => panic!("value is not an object"),
2431 }
2432 }
2433
2434 pub fn as_struct(&self) -> Option<&Struct> {
2438 match self {
2439 Self::Struct(v) => Some(v),
2440 _ => None,
2441 }
2442 }
2443
2444 pub fn unwrap_struct(self) -> Struct {
2450 match self {
2451 Self::Struct(v) => v,
2452 _ => panic!("value is not a struct"),
2453 }
2454 }
2455
2456 pub fn as_enum_variant(&self) -> Option<&EnumVariant> {
2460 match self {
2461 Self::EnumVariant(v) => Some(v),
2462 _ => None,
2463 }
2464 }
2465
2466 pub fn unwrap_enum_variant(self) -> EnumVariant {
2472 match self {
2473 Self::EnumVariant(v) => v,
2474 _ => panic!("value is not an enum"),
2475 }
2476 }
2477
2478 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2484 if left.ty() != right.ty() {
2487 return None;
2488 }
2489
2490 match (left, right) {
2491 (Self::Pair(left), Self::Pair(right)) => Some(
2492 Value::equals(left.left(), right.left())?
2493 && Value::equals(left.right(), right.right())?,
2494 ),
2495 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2496 left.len() == right.len()
2497 && left
2498 .as_slice()
2499 .iter()
2500 .zip(right.as_slice())
2501 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2502 ),
2503 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2504 left.len() == right.len()
2505 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2507 lk == rk && Value::equals(lv, rv).unwrap_or(false)
2508 }),
2509 ),
2510 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2511 left.len() == right.len()
2512 && left.iter().all(|(k, left)| match right.get(k) {
2513 Some(right) => Value::equals(left, right).unwrap_or(false),
2514 None => false,
2515 }),
2516 ),
2517 (CompoundValue::Struct(left), CompoundValue::Struct(right)) => Some(
2518 left.0.members.len() == right.0.members.len()
2519 && left
2520 .0
2521 .members
2522 .iter()
2523 .all(|(k, lv)| match right.0.members.get(k) {
2524 Some(rv) => Value::equals(lv, rv).unwrap_or(false),
2525 None => false,
2526 }),
2527 ),
2528 (CompoundValue::EnumVariant(left), CompoundValue::EnumVariant(right)) => {
2529 Some(left.enum_ty() == right.enum_ty() && left.name() == right.name())
2530 }
2531 _ => None,
2532 }
2533 }
2534
2535 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2540 where
2541 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2542 {
2543 match self {
2544 Self::Pair(pair) => {
2545 pair.left().visit_paths(cb)?;
2546 pair.right().visit_paths(cb)?;
2547 }
2548 Self::Array(array) => {
2549 for v in array.as_slice() {
2550 v.visit_paths(cb)?;
2551 }
2552 }
2553 Self::Map(map) => {
2554 for (k, v) in map.iter() {
2555 match k {
2556 PrimitiveValue::File(path) => cb(true, path)?,
2557 PrimitiveValue::Directory(path) => cb(false, path)?,
2558 _ => {}
2559 }
2560
2561 v.visit_paths(cb)?;
2562 }
2563 }
2564 Self::Object(object) => {
2565 for v in object.values() {
2566 v.visit_paths(cb)?;
2567 }
2568 }
2569 Self::Struct(s) => {
2570 for v in s.values() {
2571 v.visit_paths(cb)?;
2572 }
2573 }
2574 Self::EnumVariant(e) => {
2575 e.value().visit_paths(cb)?;
2576 }
2577 }
2578
2579 Ok(())
2580 }
2581
2582 fn resolve_paths<'a, F>(
2585 &'a self,
2586 base_dir: Option<&'a Path>,
2587 transferer: Option<&'a dyn Transferer>,
2588 translate: &'a F,
2589 ) -> BoxFuture<'a, Result<Self>>
2590 where
2591 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
2592 {
2593 async move {
2594 match self {
2595 Self::Pair(pair) => {
2596 let ty = pair.0.ty.as_pair().expect("should be a pair type");
2597 let (left_optional, right_optional) =
2598 (ty.left_type().is_optional(), ty.right_type().is_optional());
2599 let fst = pair
2600 .0
2601 .left
2602 .resolve_paths(left_optional, base_dir, transferer, translate)
2603 .await?;
2604 let snd = pair
2605 .0
2606 .right
2607 .resolve_paths(right_optional, base_dir, transferer, translate)
2608 .await?;
2609 Ok(Self::Pair(Pair::new_unchecked(ty.clone(), fst, snd)))
2610 }
2611 Self::Array(array) => {
2612 let ty = array.0.ty.as_array().expect("should be an array type");
2613 let optional = ty.element_type().is_optional();
2614 if !array.0.elements.is_empty() {
2615 let resolved_elements = futures::stream::iter(array.0.elements.iter())
2616 .then(|v| v.resolve_paths(optional, base_dir, transferer, translate))
2617 .try_collect::<Vec<Value>>()
2618 .await?;
2619 Ok(Self::Array(Array::new_unchecked(
2620 ty.clone(),
2621 resolved_elements,
2622 )))
2623 } else {
2624 Ok(self.clone())
2625 }
2626 }
2627 Self::Map(map) => {
2628 let ty = map.0.ty.as_map().expect("should be a map type").clone();
2629 let (key_optional, value_optional) =
2630 (ty.key_type().is_optional(), ty.value_type().is_optional());
2631 if !map.0.elements.is_empty() {
2632 let resolved_elements = futures::stream::iter(map.0.elements.iter())
2633 .then(async |(k, v)| {
2634 let resolved_key = Value::from(k.clone())
2635 .resolve_paths(key_optional, base_dir, transferer, translate)
2636 .await?
2637 .as_primitive()
2638 .cloned()
2639 .expect("key should be primitive");
2640 let resolved_value = v
2641 .resolve_paths(value_optional, base_dir, transferer, translate)
2642 .await?;
2643 Ok::<_, anyhow::Error>((resolved_key, resolved_value))
2644 })
2645 .try_collect()
2646 .await?;
2647 Ok(Self::Map(Map::new_unchecked(ty, resolved_elements)))
2648 } else {
2649 Ok(Self::Map(Map::new_unchecked(ty, IndexMap::new())))
2650 }
2651 }
2652 Self::Object(object) => {
2653 if object.is_empty() {
2654 Ok(self.clone())
2655 } else {
2656 let resolved_members = futures::stream::iter(object.iter())
2657 .then(async |(n, v)| {
2658 let resolved = v
2659 .resolve_paths(false, base_dir, transferer, translate)
2660 .await?;
2661 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2662 })
2663 .try_collect()
2664 .await?;
2665 Ok(Self::Object(Object::new(resolved_members)))
2666 }
2667 }
2668 Self::Struct(s) => {
2669 let ty = s.0.ty.as_struct().expect("should be a struct type");
2670 let name = s.name().clone();
2671 let resolved_members: IndexMap<String, Value> = futures::stream::iter(s.iter())
2672 .then(async |(n, v)| {
2673 let resolved = v
2674 .resolve_paths(
2675 ty.members()[n].is_optional(),
2676 base_dir,
2677 transferer,
2678 translate,
2679 )
2680 .await?;
2681 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2682 })
2683 .try_collect()
2684 .await?;
2685 Ok(Self::Struct(Struct::new_unchecked(
2686 ty.clone(),
2687 name,
2688 resolved_members,
2689 )))
2690 }
2691 Self::EnumVariant(e) => {
2692 let optional = e.enum_ty().inner_value_type().is_optional();
2693 let value =
2694 e.0.value
2695 .resolve_paths(optional, base_dir, transferer, translate)
2696 .await?;
2697 Ok(Self::EnumVariant(EnumVariant::new(
2698 e.0.enum_ty.clone(),
2699 e.name(),
2700 value,
2701 )))
2702 }
2703 }
2704 }
2705 .boxed()
2706 }
2707}
2708
2709impl fmt::Display for CompoundValue {
2710 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2711 match self {
2712 Self::Pair(v) => v.fmt(f),
2713 Self::Array(v) => v.fmt(f),
2714 Self::Map(v) => v.fmt(f),
2715 Self::Object(v) => v.fmt(f),
2716 Self::Struct(v) => v.fmt(f),
2717 Self::EnumVariant(v) => v.fmt(f),
2718 }
2719 }
2720}
2721
2722impl Coercible for CompoundValue {
2723 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2724 if target.is_union() || target.is_none() || self.ty().eq(target) {
2725 return Ok(self.clone());
2726 }
2727
2728 if let Type::Compound(target_ty, _) = target {
2729 match (self, target_ty) {
2730 (Self::Array(v), CompoundType::Array(target_ty)) => {
2732 if v.is_empty() && target_ty.is_non_empty() {
2735 bail!("cannot coerce empty array value to non-empty array {target:#}");
2736 }
2737
2738 return Ok(Self::Array(Array::new_with_context(
2739 context,
2740 target_ty.clone(),
2741 v.as_slice().iter().cloned(),
2742 )?));
2743 }
2744 (Self::Map(v), CompoundType::Map(target_ty)) => {
2746 return Ok(Self::Map(Map::new_with_context(
2747 context,
2748 target_ty.clone(),
2749 v.iter().map(|(k, v)| (k.clone(), v.clone())),
2750 )?));
2751 }
2752 (Self::Pair(v), CompoundType::Pair(target_ty)) => {
2754 return Ok(Self::Pair(Pair::new_with_context(
2755 context,
2756 target_ty.clone(),
2757 v.0.left.clone(),
2758 v.0.right.clone(),
2759 )?));
2760 }
2761 (Self::Map(v), CompoundType::Custom(CustomType::Struct(target_ty))) => {
2763 let len = v.len();
2764 let expected_len = target_ty.members().len();
2765
2766 if len != expected_len {
2767 bail!(
2768 "cannot coerce a map of {len} element{s1} to {target:#} as the struct \
2769 has {expected_len} member{s2}",
2770 s1 = if len == 1 { "" } else { "s" },
2771 s2 = if expected_len == 1 { "" } else { "s" }
2772 );
2773 }
2774
2775 return Ok(Self::Struct(Struct::new_unchecked(
2776 target.clone(),
2777 target_ty.name().clone(),
2778 v.iter()
2779 .map(|(k, v)| {
2780 let k = k
2781 .coerce(context, &PrimitiveType::String.into())
2782 .with_context(|| {
2783 format!(
2784 "cannot coerce a map of {map_type:#} to {target:#} as \
2785 the key type cannot coerce to type `String`",
2786 map_type = v.ty()
2787 )
2788 })?
2789 .unwrap_string();
2790 let ty =
2791 target_ty.members().get(k.as_ref()).with_context(|| {
2792 format!(
2793 "cannot coerce a map with key `{k}` to {target:#} as \
2794 the struct does not contain a member with that name"
2795 )
2796 })?;
2797 let v = v.coerce(context, ty).with_context(|| {
2798 format!("failed to coerce value of map key `{k}")
2799 })?;
2800 Ok((k.to_string(), v))
2801 })
2802 .collect::<Result<IndexMap<_, _>>>()?,
2803 )));
2804 }
2805 (Self::Struct(s), CompoundType::Map(map_ty)) => {
2807 let key_ty = map_ty.key_type();
2808 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2809 bail!(
2810 "cannot coerce a struct to {target:#} as key {key_ty:#} cannot be \
2811 coerced from type `String`"
2812 );
2813 }
2814
2815 let value_ty = map_ty.value_type();
2816 return Ok(Self::Map(Map::new_unchecked(
2817 target.clone(),
2818 s.0.members
2819 .iter()
2820 .map(|(n, v)| {
2821 let v = v
2822 .coerce(context, value_ty)
2823 .with_context(|| format!("failed to coerce member `{n}`"))?;
2824 Ok((
2825 PrimitiveValue::new_string(n)
2826 .coerce(context, key_ty)
2827 .expect("should coerce"),
2828 v,
2829 ))
2830 })
2831 .collect::<Result<_>>()?,
2832 )));
2833 }
2834 (Self::Object(object), CompoundType::Map(map_ty)) => {
2836 let key_ty = map_ty.key_type();
2837 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2838 bail!(
2839 "cannot coerce an object to {target:#} as key {key_ty:#} cannot be \
2840 coerced from type `String`"
2841 );
2842 }
2843
2844 let value_ty = map_ty.value_type();
2845 return Ok(Self::Map(Map::new_unchecked(
2846 target.clone(),
2847 object
2848 .iter()
2849 .map(|(n, v)| {
2850 let v = v
2851 .coerce(context, value_ty)
2852 .with_context(|| format!("failed to coerce member `{n}`"))?;
2853 Ok((
2854 PrimitiveValue::new_string(n)
2855 .coerce(context, key_ty)
2856 .expect("should coerce"),
2857 v,
2858 ))
2859 })
2860 .collect::<Result<_>>()?,
2861 )));
2862 }
2863 (Self::Object(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2865 return Ok(Self::Struct(Struct::new_with_context(
2866 context,
2867 struct_ty.clone(),
2868 v.iter().map(|(k, v)| (k, v.clone())),
2869 )?));
2870 }
2871 (Self::Struct(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2873 let len = v.0.members.len();
2874 let expected_len = struct_ty.members().len();
2875
2876 if len != expected_len {
2877 bail!(
2878 "cannot coerce a struct of {len} members{s1} to struct type \
2879 `{target:#}` as the target struct has {expected_len} member{s2}",
2880 s1 = if len == 1 { "" } else { "s" },
2881 s2 = if expected_len == 1 { "" } else { "s" }
2882 );
2883 }
2884
2885 return Ok(Self::Struct(Struct::new_unchecked(
2886 target.clone(),
2887 struct_ty.name().clone(),
2888 v.0.members
2889 .iter()
2890 .map(|(k, v)| {
2891 let ty = struct_ty.members().get(k).ok_or_else(|| {
2892 anyhow!(
2893 "cannot coerce a struct with member `{k}` to struct type \
2894 `{target:#}` as the target struct does not contain a \
2895 member with that name",
2896 )
2897 })?;
2898 let v = v
2899 .coerce(context, ty)
2900 .with_context(|| format!("failed to coerce member `{k}`"))?;
2901 Ok((k.clone(), v))
2902 })
2903 .collect::<Result<IndexMap<_, _>>>()?,
2904 )));
2905 }
2906 _ => {}
2907 }
2908 }
2909
2910 if let Type::Object = target {
2911 match self {
2912 Self::Map(v) => {
2914 return Ok(Self::Object(Object::new(
2915 v.iter()
2916 .map(|(k, v)| {
2917 let k = k
2918 .coerce(context, &PrimitiveType::String.into())
2919 .with_context(|| {
2920 format!(
2921 "cannot coerce a map of {map_type:#} to type `Object` \
2922 as the key type cannot coerce to type `String`",
2923 map_type = v.ty()
2924 )
2925 })?
2926 .unwrap_string();
2927 Ok((k.to_string(), v.clone()))
2928 })
2929 .collect::<Result<IndexMap<_, _>>>()?,
2930 )));
2931 }
2932 Self::Struct(v) => {
2934 return Ok(Self::Object(Object {
2935 members: Arc::new(v.0.members.clone()),
2936 }));
2937 }
2938 _ => {}
2939 };
2940 }
2941
2942 bail!(
2943 "cannot coerce a value of {ty:#} to {target:#}",
2944 ty = self.ty()
2945 );
2946 }
2947}
2948
2949impl From<Pair> for CompoundValue {
2950 fn from(value: Pair) -> Self {
2951 Self::Pair(value)
2952 }
2953}
2954
2955impl From<Array> for CompoundValue {
2956 fn from(value: Array) -> Self {
2957 Self::Array(value)
2958 }
2959}
2960
2961impl From<Map> for CompoundValue {
2962 fn from(value: Map) -> Self {
2963 Self::Map(value)
2964 }
2965}
2966
2967impl From<Object> for CompoundValue {
2968 fn from(value: Object) -> Self {
2969 Self::Object(value)
2970 }
2971}
2972
2973impl From<Struct> for CompoundValue {
2974 fn from(value: Struct) -> Self {
2975 Self::Struct(value)
2976 }
2977}
2978
2979#[derive(Debug, Clone)]
2983pub enum HiddenValue {
2984 Hints(HintsValue),
2988 Input(InputValue),
2992 Output(OutputValue),
2996 TaskPreEvaluation(TaskPreEvaluationValue),
3001 TaskPostEvaluation(TaskPostEvaluationValue),
3006 PreviousTaskData(PreviousTaskDataValue),
3011}
3012
3013impl HiddenValue {
3014 pub fn ty(&self) -> Type {
3016 match self {
3017 Self::Hints(_) => Type::Hidden(HiddenType::Hints),
3018 Self::Input(_) => Type::Hidden(HiddenType::Input),
3019 Self::Output(_) => Type::Hidden(HiddenType::Output),
3020 Self::TaskPreEvaluation(_) => Type::Hidden(HiddenType::TaskPreEvaluation),
3021 Self::TaskPostEvaluation(_) => Type::Hidden(HiddenType::TaskPostEvaluation),
3022 Self::PreviousTaskData(_) => Type::Hidden(HiddenType::PreviousTaskData),
3023 }
3024 }
3025}
3026
3027impl fmt::Display for HiddenValue {
3028 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3029 match self {
3030 Self::Hints(v) => v.fmt(f),
3031 Self::Input(v) => v.fmt(f),
3032 Self::Output(v) => v.fmt(f),
3033 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => write!(f, "task"),
3034 Self::PreviousTaskData(_) => write!(f, "task.previous"),
3035 }
3036 }
3037}
3038
3039impl Coercible for HiddenValue {
3040 fn coerce(&self, _: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
3041 match self {
3042 Self::Hints(_) => {
3043 if matches!(target, Type::Hidden(HiddenType::Hints)) {
3044 return Ok(self.clone());
3045 }
3046
3047 bail!("hints values cannot be coerced to any other type");
3048 }
3049 Self::Input(_) => {
3050 if matches!(target, Type::Hidden(HiddenType::Input)) {
3051 return Ok(self.clone());
3052 }
3053
3054 bail!("input values cannot be coerced to any other type");
3055 }
3056 Self::Output(_) => {
3057 if matches!(target, Type::Hidden(HiddenType::Output)) {
3058 return Ok(self.clone());
3059 }
3060
3061 bail!("output values cannot be coerced to any other type");
3062 }
3063 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => {
3064 if matches!(
3065 target,
3066 Type::Hidden(HiddenType::TaskPreEvaluation)
3067 | Type::Hidden(HiddenType::TaskPostEvaluation)
3068 ) {
3069 return Ok(self.clone());
3070 }
3071
3072 bail!("task variables cannot be coerced to any other type");
3073 }
3074 Self::PreviousTaskData(_) => {
3075 if matches!(target, Type::Hidden(HiddenType::PreviousTaskData)) {
3076 return Ok(self.clone());
3077 }
3078
3079 bail!("previous task data values cannot be coerced to any other type");
3080 }
3081 }
3082 }
3083}
3084
3085#[derive(Debug, Clone)]
3089pub(crate) struct TaskPostEvaluationData {
3090 container: Option<Arc<String>>,
3093 cpu: f64,
3095 memory: i64,
3097 gpu: Array,
3102 fpga: Array,
3107 disks: Map,
3114 max_retries: i64,
3116}
3117
3118#[derive(Debug, Clone)]
3122pub struct PreviousTaskDataValue(Option<Arc<TaskPostEvaluationData>>);
3123
3124impl PreviousTaskDataValue {
3125 pub(crate) fn new(data: Arc<TaskPostEvaluationData>) -> Self {
3127 Self(Some(data))
3128 }
3129
3130 pub(crate) fn empty() -> Self {
3132 Self(None)
3133 }
3134
3135 pub fn field(&self, name: &str) -> Option<Value> {
3142 match name {
3143 TASK_FIELD_MEMORY => Some(
3144 self.0
3145 .as_ref()
3146 .map(|data| Value::from(data.memory))
3147 .unwrap_or_else(|| {
3148 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3149 }),
3150 ),
3151 TASK_FIELD_CPU => Some(
3152 self.0
3153 .as_ref()
3154 .map(|data| Value::from(data.cpu))
3155 .unwrap_or_else(|| {
3156 Value::new_none(Type::from(PrimitiveType::Float).optional())
3157 }),
3158 ),
3159 TASK_FIELD_CONTAINER => Some(
3160 self.0
3161 .as_ref()
3162 .and_then(|data| {
3163 data.container
3164 .as_ref()
3165 .map(|c| PrimitiveValue::String(c.clone()).into())
3166 })
3167 .unwrap_or_else(|| {
3168 Value::new_none(Type::from(PrimitiveType::String).optional())
3169 }),
3170 ),
3171 TASK_FIELD_GPU => Some(
3172 self.0
3173 .as_ref()
3174 .map(|data| Value::from(data.gpu.clone()))
3175 .unwrap_or_else(|| {
3176 Value::new_none(Type::Compound(
3177 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3178 true,
3179 ))
3180 }),
3181 ),
3182 TASK_FIELD_FPGA => Some(
3183 self.0
3184 .as_ref()
3185 .map(|data| Value::from(data.fpga.clone()))
3186 .unwrap_or_else(|| {
3187 Value::new_none(Type::Compound(
3188 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3189 true,
3190 ))
3191 }),
3192 ),
3193 TASK_FIELD_DISKS => Some(
3194 self.0
3195 .as_ref()
3196 .map(|data| Value::from(data.disks.clone()))
3197 .unwrap_or_else(|| {
3198 Value::new_none(Type::Compound(
3199 MapType::new(PrimitiveType::String, PrimitiveType::Integer).into(),
3200 true,
3201 ))
3202 }),
3203 ),
3204 TASK_FIELD_MAX_RETRIES => Some(
3205 self.0
3206 .as_ref()
3207 .map(|data| Value::from(data.max_retries))
3208 .unwrap_or_else(|| {
3209 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3210 }),
3211 ),
3212 _ => None,
3213 }
3214 }
3215}
3216
3217#[derive(Debug, Clone)]
3219struct TaskPreEvaluationInner {
3220 name: Arc<String>,
3222 id: Arc<String>,
3224 attempt: i64,
3229 meta: Object,
3231 parameter_meta: Object,
3233 ext: Object,
3235 previous: PreviousTaskDataValue,
3241}
3242
3243#[derive(Debug, Clone)]
3250pub struct TaskPreEvaluationValue(Arc<TaskPreEvaluationInner>);
3251
3252impl TaskPreEvaluationValue {
3253 pub(crate) fn new(
3256 name: impl Into<String>,
3257 id: impl Into<String>,
3258 attempt: i64,
3259 meta: Object,
3260 parameter_meta: Object,
3261 ext: Object,
3262 ) -> Self {
3263 Self(Arc::new(TaskPreEvaluationInner {
3264 name: Arc::new(name.into()),
3265 id: Arc::new(id.into()),
3266 meta,
3267 parameter_meta,
3268 ext,
3269 attempt,
3270 previous: PreviousTaskDataValue::empty(),
3271 }))
3272 }
3273
3274 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3276 Arc::get_mut(&mut self.0)
3277 .expect("task value must be uniquely owned to mutate")
3278 .previous = PreviousTaskDataValue::new(data);
3279 }
3280
3281 pub fn name(&self) -> &Arc<String> {
3283 &self.0.name
3284 }
3285
3286 pub fn id(&self) -> &Arc<String> {
3288 &self.0.id
3289 }
3290
3291 pub fn attempt(&self) -> i64 {
3293 self.0.attempt
3294 }
3295
3296 pub fn field(&self, name: &str) -> Option<Value> {
3300 match name {
3301 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.0.name.clone()).into()),
3302 TASK_FIELD_ID => Some(PrimitiveValue::String(self.0.id.clone()).into()),
3303 TASK_FIELD_ATTEMPT => Some(self.0.attempt.into()),
3304 TASK_FIELD_META => Some(self.0.meta.clone().into()),
3305 TASK_FIELD_PARAMETER_META => Some(self.0.parameter_meta.clone().into()),
3306 TASK_FIELD_EXT => Some(self.0.ext.clone().into()),
3307 TASK_FIELD_PREVIOUS => {
3308 Some(HiddenValue::PreviousTaskData(self.0.previous.clone()).into())
3309 }
3310 _ => None,
3311 }
3312 }
3313}
3314
3315#[derive(Debug, Clone)]
3317struct TaskPostEvaluationInner {
3318 data: Arc<TaskPostEvaluationData>,
3320 name: Arc<String>,
3322 id: Arc<String>,
3324 attempt: i64,
3329 meta: Object,
3331 parameter_meta: Object,
3333 ext: Object,
3335 return_code: Option<i64>,
3339 end_time: Option<i64>,
3343 previous: PreviousTaskDataValue,
3349}
3350
3351#[derive(Debug, Clone)]
3357pub struct TaskPostEvaluationValue(Arc<TaskPostEvaluationInner>);
3358
3359impl TaskPostEvaluationValue {
3360 #[allow(clippy::too_many_arguments)]
3363 pub(crate) fn new(
3364 name: impl Into<String>,
3365 id: impl Into<String>,
3366 constraints: &TaskExecutionConstraints,
3367 max_retries: i64,
3368 attempt: i64,
3369 meta: Object,
3370 parameter_meta: Object,
3371 ext: Object,
3372 ) -> Self {
3373 Self(Arc::new(TaskPostEvaluationInner {
3374 name: Arc::new(name.into()),
3375 id: Arc::new(id.into()),
3376 data: Arc::new(TaskPostEvaluationData {
3377 container: None,
3382 cpu: constraints.cpu,
3383 memory: constraints
3384 .memory
3385 .try_into()
3386 .expect("memory exceeds a valid WDL value"),
3387 gpu: Array::new_unchecked(
3388 ANALYSIS_STDLIB.array_string_type().clone(),
3389 constraints
3390 .gpu
3391 .iter()
3392 .map(|v| PrimitiveValue::new_string(v).into())
3393 .collect(),
3394 ),
3395 fpga: Array::new_unchecked(
3396 ANALYSIS_STDLIB.array_string_type().clone(),
3397 constraints
3398 .fpga
3399 .iter()
3400 .map(|v| PrimitiveValue::new_string(v).into())
3401 .collect(),
3402 ),
3403 disks: Map::new_unchecked(
3404 ANALYSIS_STDLIB.map_string_int_type().clone(),
3405 constraints
3406 .disks
3407 .iter()
3408 .map(|(k, v)| (PrimitiveValue::new_string(k), (*v).into()))
3409 .collect(),
3410 ),
3411 max_retries,
3412 }),
3413 attempt,
3414 meta,
3415 parameter_meta,
3416 ext,
3417 return_code: None,
3418 end_time: None,
3419 previous: PreviousTaskDataValue::empty(),
3420 }))
3421 }
3422
3423 pub fn name(&self) -> &Arc<String> {
3425 &self.0.name
3426 }
3427
3428 pub fn id(&self) -> &Arc<String> {
3430 &self.0.id
3431 }
3432
3433 pub fn container(&self) -> Option<&Arc<String>> {
3435 self.0.data.container.as_ref()
3436 }
3437
3438 pub fn cpu(&self) -> f64 {
3440 self.0.data.cpu
3441 }
3442
3443 pub fn memory(&self) -> i64 {
3445 self.0.data.memory
3446 }
3447
3448 pub fn gpu(&self) -> &Array {
3453 &self.0.data.gpu
3454 }
3455
3456 pub fn fpga(&self) -> &Array {
3461 &self.0.data.fpga
3462 }
3463
3464 pub fn disks(&self) -> &Map {
3471 &self.0.data.disks
3472 }
3473
3474 pub fn attempt(&self) -> i64 {
3479 self.0.attempt
3480 }
3481
3482 pub fn end_time(&self) -> Option<i64> {
3486 self.0.end_time
3487 }
3488
3489 pub fn return_code(&self) -> Option<i64> {
3493 self.0.return_code
3494 }
3495
3496 pub fn meta(&self) -> &Object {
3498 &self.0.meta
3499 }
3500
3501 pub fn parameter_meta(&self) -> &Object {
3503 &self.0.parameter_meta
3504 }
3505
3506 pub fn ext(&self) -> &Object {
3508 &self.0.ext
3509 }
3510
3511 pub(crate) fn set_container(&mut self, container: String) {
3513 let inner = Arc::get_mut(&mut self.0).expect("task value must be uniquely owned to mutate");
3514 Arc::make_mut(&mut inner.data).container = Some(Arc::new(container));
3515 }
3516
3517 pub(crate) fn set_return_code(&mut self, code: i32) {
3519 Arc::get_mut(&mut self.0)
3520 .expect("task value must be uniquely owned to mutate")
3521 .return_code = Some(code as i64);
3522 }
3523
3524 pub(crate) fn set_attempt(&mut self, attempt: i64) {
3526 Arc::get_mut(&mut self.0)
3527 .expect("task value must be uniquely owned to mutate")
3528 .attempt = attempt;
3529 }
3530
3531 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3533 Arc::get_mut(&mut self.0)
3534 .expect("task value must be uniquely owned to mutate")
3535 .previous = PreviousTaskDataValue::new(data);
3536 }
3537
3538 pub(crate) fn data(&self) -> &Arc<TaskPostEvaluationData> {
3540 &self.0.data
3541 }
3542
3543 pub fn field(&self, version: SupportedVersion, name: &str) -> Option<Value> {
3547 match name {
3548 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.0.name.clone()).into()),
3549 TASK_FIELD_ID => Some(PrimitiveValue::String(self.0.id.clone()).into()),
3550 TASK_FIELD_ATTEMPT => Some(self.0.attempt.into()),
3551 TASK_FIELD_CONTAINER => Some(
3552 self.0
3553 .data
3554 .container
3555 .clone()
3556 .map(|c| PrimitiveValue::String(c).into())
3557 .unwrap_or_else(|| {
3558 Value::new_none(
3559 task_member_type_post_evaluation(version, TASK_FIELD_CONTAINER)
3560 .expect("failed to get task field type"),
3561 )
3562 }),
3563 ),
3564 TASK_FIELD_CPU => Some(self.0.data.cpu.into()),
3565 TASK_FIELD_MEMORY => Some(self.0.data.memory.into()),
3566 TASK_FIELD_GPU => Some(self.0.data.gpu.clone().into()),
3567 TASK_FIELD_FPGA => Some(self.0.data.fpga.clone().into()),
3568 TASK_FIELD_DISKS => Some(self.0.data.disks.clone().into()),
3569 TASK_FIELD_END_TIME => Some(self.0.end_time.map(Into::into).unwrap_or_else(|| {
3570 Value::new_none(
3571 task_member_type_post_evaluation(version, TASK_FIELD_END_TIME)
3572 .expect("failed to get task field type"),
3573 )
3574 })),
3575 TASK_FIELD_RETURN_CODE => {
3576 Some(self.0.return_code.map(Into::into).unwrap_or_else(|| {
3577 Value::new_none(
3578 task_member_type_post_evaluation(version, TASK_FIELD_RETURN_CODE)
3579 .expect("failed to get task field type"),
3580 )
3581 }))
3582 }
3583 TASK_FIELD_META => Some(self.0.meta.clone().into()),
3584 TASK_FIELD_PARAMETER_META => Some(self.0.parameter_meta.clone().into()),
3585 TASK_FIELD_EXT => Some(self.0.ext.clone().into()),
3586 TASK_FIELD_MAX_RETRIES if version >= SupportedVersion::V1(V1::Three) => {
3587 Some(self.0.data.max_retries.into())
3588 }
3589 TASK_FIELD_PREVIOUS if version >= SupportedVersion::V1(V1::Three) => {
3590 Some(HiddenValue::PreviousTaskData(self.0.previous.clone()).into())
3591 }
3592 _ => None,
3593 }
3594 }
3595}
3596
3597#[derive(Debug, Clone)]
3601pub struct HintsValue(Object);
3602
3603impl HintsValue {
3604 pub fn new(members: IndexMap<String, Value>) -> Self {
3606 Self(Object::new(members))
3607 }
3608
3609 pub fn as_object(&self) -> &Object {
3611 &self.0
3612 }
3613}
3614
3615impl From<HintsValue> for Value {
3616 fn from(value: HintsValue) -> Self {
3617 Self::Hidden(HiddenValue::Hints(value))
3618 }
3619}
3620
3621impl fmt::Display for HintsValue {
3622 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3623 write!(f, "hints {{")?;
3624
3625 for (i, (k, v)) in self.0.iter().enumerate() {
3626 if i > 0 {
3627 write!(f, ", ")?;
3628 }
3629
3630 write!(f, "{k}: {v}")?;
3631 }
3632
3633 write!(f, "}}")
3634 }
3635}
3636
3637impl From<Object> for HintsValue {
3638 fn from(value: Object) -> Self {
3639 Self(value)
3640 }
3641}
3642
3643#[derive(Debug, Clone)]
3647pub struct InputValue(Object);
3648
3649impl InputValue {
3650 pub fn new(members: IndexMap<String, Value>) -> Self {
3652 Self(Object::new(members))
3653 }
3654
3655 pub fn as_object(&self) -> &Object {
3657 &self.0
3658 }
3659}
3660
3661impl From<InputValue> for Value {
3662 fn from(value: InputValue) -> Self {
3663 Self::Hidden(HiddenValue::Input(value))
3664 }
3665}
3666
3667impl fmt::Display for InputValue {
3668 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3669 write!(f, "input {{")?;
3670
3671 for (i, (k, v)) in self.0.iter().enumerate() {
3672 if i > 0 {
3673 write!(f, ", ")?;
3674 }
3675
3676 write!(f, "{k}: {v}")?;
3677 }
3678
3679 write!(f, "}}")
3680 }
3681}
3682
3683impl From<Object> for InputValue {
3684 fn from(value: Object) -> Self {
3685 Self(value)
3686 }
3687}
3688
3689#[derive(Debug, Clone)]
3693pub struct OutputValue(Object);
3694
3695impl OutputValue {
3696 pub fn new(members: IndexMap<String, Value>) -> Self {
3698 Self(Object::new(members))
3699 }
3700
3701 pub fn as_object(&self) -> &Object {
3703 &self.0
3704 }
3705}
3706
3707impl From<OutputValue> for Value {
3708 fn from(value: OutputValue) -> Self {
3709 Self::Hidden(HiddenValue::Output(value))
3710 }
3711}
3712
3713impl fmt::Display for OutputValue {
3714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3715 write!(f, "output {{")?;
3716
3717 for (i, (k, v)) in self.0.iter().enumerate() {
3718 if i > 0 {
3719 write!(f, ", ")?;
3720 }
3721
3722 write!(f, "{k}: {v}")?;
3723 }
3724
3725 write!(f, "}}")
3726 }
3727}
3728
3729impl From<Object> for OutputValue {
3730 fn from(value: Object) -> Self {
3731 Self(value)
3732 }
3733}
3734
3735#[derive(Debug, Clone)]
3739pub struct CallValue {
3740 ty: Arc<CallType>,
3742 outputs: Arc<Outputs>,
3744}
3745
3746impl CallValue {
3747 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3750 Self {
3751 ty: Arc::new(ty),
3752 outputs,
3753 }
3754 }
3755
3756 pub fn ty(&self) -> &CallType {
3758 &self.ty
3759 }
3760
3761 pub fn outputs(&self) -> &Outputs {
3763 self.outputs.as_ref()
3764 }
3765}
3766
3767impl fmt::Display for CallValue {
3768 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3769 write!(f, "call output {{")?;
3770
3771 for (i, (k, v)) in self.outputs.iter().enumerate() {
3772 if i > 0 {
3773 write!(f, ", ")?;
3774 }
3775
3776 write!(f, "{k}: {v}")?;
3777 }
3778
3779 write!(f, "}}")
3780 }
3781}
3782
3783pub(crate) struct ValueSerializer<'a> {
3785 context: Option<&'a dyn EvaluationContext>,
3787 value: &'a Value,
3789 allow_pairs: bool,
3792}
3793
3794impl<'a> ValueSerializer<'a> {
3795 pub fn new(
3801 context: Option<&'a dyn EvaluationContext>,
3802 value: &'a Value,
3803 allow_pairs: bool,
3804 ) -> Self {
3805 Self {
3806 context,
3807 value,
3808 allow_pairs,
3809 }
3810 }
3811}
3812
3813impl serde::Serialize for ValueSerializer<'_> {
3814 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3815 where
3816 S: serde::Serializer,
3817 {
3818 use serde::ser::Error;
3819
3820 match &self.value {
3821 Value::None(_) => serializer.serialize_none(),
3822 Value::Primitive(v) => {
3823 PrimitiveValueSerializer::new(self.context, v).serialize(serializer)
3824 }
3825 Value::Compound(v) => CompoundValueSerializer::new(self.context, v, self.allow_pairs)
3826 .serialize(serializer),
3827 Value::Call(_) | Value::Hidden(_) | Value::TypeNameRef(_) => {
3828 Err(S::Error::custom("value cannot be serialized"))
3829 }
3830 }
3831 }
3832}
3833
3834pub(crate) struct PrimitiveValueSerializer<'a> {
3836 context: Option<&'a dyn EvaluationContext>,
3838 value: &'a PrimitiveValue,
3840}
3841
3842impl<'a> PrimitiveValueSerializer<'a> {
3843 pub fn new(context: Option<&'a dyn EvaluationContext>, value: &'a PrimitiveValue) -> Self {
3849 Self { context, value }
3850 }
3851}
3852
3853impl serde::Serialize for PrimitiveValueSerializer<'_> {
3854 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3855 where
3856 S: serde::Serializer,
3857 {
3858 match self.value {
3859 PrimitiveValue::Boolean(v) => v.serialize(serializer),
3860 PrimitiveValue::Integer(v) => v.serialize(serializer),
3861 PrimitiveValue::Float(v) => v.serialize(serializer),
3862 PrimitiveValue::String(s) => s.serialize(serializer),
3863 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3864 let path = self
3865 .context
3866 .and_then(|c| c.guest_path(p).map(|p| Cow::Owned(p.0)))
3867 .unwrap_or(Cow::Borrowed(&p.0));
3868
3869 path.serialize(serializer)
3870 }
3871 }
3872 }
3873}
3874
3875pub(crate) struct CompoundValueSerializer<'a> {
3877 context: Option<&'a dyn EvaluationContext>,
3879 value: &'a CompoundValue,
3881 allow_pairs: bool,
3884}
3885
3886impl<'a> CompoundValueSerializer<'a> {
3887 pub fn new(
3893 context: Option<&'a dyn EvaluationContext>,
3894 value: &'a CompoundValue,
3895 allow_pairs: bool,
3896 ) -> Self {
3897 Self {
3898 context,
3899 value,
3900 allow_pairs,
3901 }
3902 }
3903}
3904
3905impl serde::Serialize for CompoundValueSerializer<'_> {
3906 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3907 where
3908 S: serde::Serializer,
3909 {
3910 use serde::ser::Error;
3911
3912 match &self.value {
3913 CompoundValue::Pair(pair) if self.allow_pairs => {
3914 let mut map = serializer.serialize_map(Some(2))?;
3915 let left = ValueSerializer::new(self.context, pair.left(), self.allow_pairs);
3916 let right = ValueSerializer::new(self.context, pair.right(), self.allow_pairs);
3917 map.serialize_entry("left", &left)?;
3918 map.serialize_entry("right", &right)?;
3919 map.end()
3920 }
3921 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3922 CompoundValue::Array(v) => {
3923 let mut seq = serializer.serialize_seq(Some(v.len()))?;
3924 for v in v.as_slice() {
3925 seq.serialize_element(&ValueSerializer::new(
3926 self.context,
3927 v,
3928 self.allow_pairs,
3929 ))?;
3930 }
3931
3932 seq.end()
3933 }
3934 CompoundValue::Map(v) => {
3935 let mut map = serializer.serialize_map(Some(v.len()))?;
3936 for (k, v) in v.iter() {
3937 match k {
3938 PrimitiveValue::String(s) => {
3939 map.serialize_entry(
3940 s.as_str(),
3941 &ValueSerializer::new(self.context, v, self.allow_pairs),
3942 )?;
3943 }
3944 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3945 map.serialize_entry(
3946 p.as_str(),
3947 &ValueSerializer::new(self.context, v, self.allow_pairs),
3948 )?;
3949 }
3950 _ => {
3951 map.serialize_entry(
3953 &k.raw(None).to_string(),
3954 &ValueSerializer::new(self.context, v, self.allow_pairs),
3955 )?;
3956 }
3957 }
3958 }
3959
3960 map.end()
3961 }
3962 CompoundValue::Object(object) => {
3963 let mut map = serializer.serialize_map(Some(object.len()))?;
3964 for (k, v) in object.iter() {
3965 map.serialize_entry(
3966 k,
3967 &ValueSerializer::new(self.context, v, self.allow_pairs),
3968 )?;
3969 }
3970
3971 map.end()
3972 }
3973 CompoundValue::Struct(s) => {
3974 let mut map = serializer.serialize_map(Some(s.0.members.len()))?;
3975 for (k, v) in s.0.members.iter() {
3976 map.serialize_entry(
3977 k,
3978 &ValueSerializer::new(self.context, v, self.allow_pairs),
3979 )?;
3980 }
3981
3982 map.end()
3983 }
3984 CompoundValue::EnumVariant(e) => serializer.serialize_str(e.name()),
3985 }
3986 }
3987}
3988
3989#[cfg(test)]
3990mod test {
3991 use std::iter::empty;
3992
3993 use approx::assert_relative_eq;
3994 use pretty_assertions::assert_eq;
3995 use wdl_analysis::types::ArrayType;
3996 use wdl_analysis::types::MapType;
3997 use wdl_analysis::types::PairType;
3998 use wdl_analysis::types::StructType;
3999 use wdl_ast::Diagnostic;
4000 use wdl_ast::Span;
4001 use wdl_ast::SupportedVersion;
4002
4003 use super::*;
4004 use crate::EvaluationPath;
4005 use crate::http::Transferer;
4006
4007 #[test]
4008 fn boolean_coercion() {
4009 assert_eq!(
4011 Value::from(false)
4012 .coerce(None, &PrimitiveType::Boolean.into())
4013 .expect("should coerce")
4014 .unwrap_boolean(),
4015 Value::from(false).unwrap_boolean()
4016 );
4017 assert_eq!(
4019 format!(
4020 "{e:#}",
4021 e = Value::from(true)
4022 .coerce(None, &PrimitiveType::String.into())
4023 .unwrap_err()
4024 ),
4025 "cannot coerce type `Boolean` to type `String`"
4026 );
4027 }
4028
4029 #[test]
4030 fn boolean_display() {
4031 assert_eq!(Value::from(false).to_string(), "false");
4032 assert_eq!(Value::from(true).to_string(), "true");
4033 }
4034
4035 #[test]
4036 fn integer_coercion() {
4037 assert_eq!(
4039 Value::from(12345)
4040 .coerce(None, &PrimitiveType::Integer.into())
4041 .expect("should coerce")
4042 .unwrap_integer(),
4043 Value::from(12345).unwrap_integer()
4044 );
4045 assert_relative_eq!(
4047 Value::from(12345)
4048 .coerce(None, &PrimitiveType::Float.into())
4049 .expect("should coerce")
4050 .unwrap_float(),
4051 Value::from(12345.0).unwrap_float()
4052 );
4053 assert_eq!(
4055 format!(
4056 "{e:#}",
4057 e = Value::from(12345)
4058 .coerce(None, &PrimitiveType::Boolean.into())
4059 .unwrap_err()
4060 ),
4061 "cannot coerce type `Int` to type `Boolean`"
4062 );
4063 }
4064
4065 #[test]
4066 fn integer_display() {
4067 assert_eq!(Value::from(12345).to_string(), "12345");
4068 assert_eq!(Value::from(-12345).to_string(), "-12345");
4069 }
4070
4071 #[test]
4072 fn float_coercion() {
4073 assert_relative_eq!(
4075 Value::from(12345.0)
4076 .coerce(None, &PrimitiveType::Float.into())
4077 .expect("should coerce")
4078 .unwrap_float(),
4079 Value::from(12345.0).unwrap_float()
4080 );
4081 assert_eq!(
4083 format!(
4084 "{e:#}",
4085 e = Value::from(12345.0)
4086 .coerce(None, &PrimitiveType::Integer.into())
4087 .unwrap_err()
4088 ),
4089 "cannot coerce type `Float` to type `Int`"
4090 );
4091 }
4092
4093 #[test]
4094 fn float_display() {
4095 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
4096 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
4097 }
4098
4099 #[test]
4100 fn string_coercion() {
4101 let value = PrimitiveValue::new_string("foo");
4102 assert_eq!(
4104 value
4105 .coerce(None, &PrimitiveType::String.into())
4106 .expect("should coerce"),
4107 value
4108 );
4109 assert_eq!(
4111 value
4112 .coerce(None, &PrimitiveType::File.into())
4113 .expect("should coerce"),
4114 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
4115 );
4116 assert_eq!(
4118 value
4119 .coerce(None, &PrimitiveType::Directory.into())
4120 .expect("should coerce"),
4121 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
4122 );
4123 assert_eq!(
4125 format!(
4126 "{e:#}",
4127 e = value
4128 .coerce(None, &PrimitiveType::Boolean.into())
4129 .unwrap_err()
4130 ),
4131 "cannot coerce type `String` to type `Boolean`"
4132 );
4133
4134 struct Context;
4135
4136 impl EvaluationContext for Context {
4137 fn version(&self) -> SupportedVersion {
4138 unimplemented!()
4139 }
4140
4141 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4142 unimplemented!()
4143 }
4144
4145 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4146 unimplemented!()
4147 }
4148
4149 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4150 unimplemented!()
4151 }
4152
4153 fn base_dir(&self) -> &EvaluationPath {
4154 unimplemented!()
4155 }
4156
4157 fn temp_dir(&self) -> &Path {
4158 unimplemented!()
4159 }
4160
4161 fn transferer(&self) -> &dyn Transferer {
4162 unimplemented!()
4163 }
4164
4165 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
4166 if path.as_str() == "/mnt/task/input/0/path" {
4167 Some(HostPath::new("/some/host/path"))
4168 } else {
4169 None
4170 }
4171 }
4172 }
4173
4174 assert_eq!(
4176 PrimitiveValue::new_string("/mnt/task/input/0/path")
4177 .coerce(Some(&Context), &PrimitiveType::File.into())
4178 .expect("should coerce")
4179 .unwrap_file()
4180 .as_str(),
4181 "/some/host/path"
4182 );
4183
4184 assert_eq!(
4186 value
4187 .coerce(Some(&Context), &PrimitiveType::File.into())
4188 .expect("should coerce")
4189 .unwrap_file()
4190 .as_str(),
4191 "foo"
4192 );
4193
4194 assert_eq!(
4196 PrimitiveValue::new_string("/mnt/task/input/0/path")
4197 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4198 .expect("should coerce")
4199 .unwrap_directory()
4200 .as_str(),
4201 "/some/host/path"
4202 );
4203
4204 assert_eq!(
4206 value
4207 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4208 .expect("should coerce")
4209 .unwrap_directory()
4210 .as_str(),
4211 "foo"
4212 );
4213 }
4214
4215 #[test]
4216 fn string_display() {
4217 let value = PrimitiveValue::new_string("hello world!");
4218 assert_eq!(value.to_string(), "\"hello world!\"");
4219 }
4220
4221 #[test]
4222 fn string_display_escapes_special_characters() {
4223 let value = PrimitiveValue::new_string(
4224 "\u{1b}[31m${name} ~{color} \"quoted\" \\\\ tab\tline\ncarriage\r$HOME ~user",
4225 );
4226 assert_eq!(
4227 value.to_string(),
4228 r#""\x1B[31m\${name} \~{color} \"quoted\" \\\\ tab\tline\ncarriage\r$HOME ~user""#
4229 );
4230 }
4231
4232 #[test]
4233 fn file_coercion() {
4234 let value = PrimitiveValue::new_file("foo");
4235
4236 assert_eq!(
4238 value
4239 .coerce(None, &PrimitiveType::File.into())
4240 .expect("should coerce"),
4241 value
4242 );
4243 assert_eq!(
4245 value
4246 .coerce(None, &PrimitiveType::String.into())
4247 .expect("should coerce"),
4248 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
4249 );
4250 assert_eq!(
4252 format!(
4253 "{e:#}",
4254 e = value
4255 .coerce(None, &PrimitiveType::Directory.into())
4256 .unwrap_err()
4257 ),
4258 "cannot coerce type `File` to type `Directory`"
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 assert_eq!(
4303 PrimitiveValue::new_file("/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 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 file_display() {
4324 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
4325 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
4326 }
4327
4328 #[test]
4329 fn directory_coercion() {
4330 let value = PrimitiveValue::new_directory("foo");
4331
4332 assert_eq!(
4334 value
4335 .coerce(None, &PrimitiveType::Directory.into())
4336 .expect("should coerce"),
4337 value
4338 );
4339 assert_eq!(
4341 value
4342 .coerce(None, &PrimitiveType::String.into())
4343 .expect("should coerce"),
4344 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
4345 );
4346 assert_eq!(
4348 format!(
4349 "{e:#}",
4350 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
4351 ),
4352 "cannot coerce type `Directory` to type `File`"
4353 );
4354
4355 struct Context;
4356
4357 impl EvaluationContext for Context {
4358 fn version(&self) -> SupportedVersion {
4359 unimplemented!()
4360 }
4361
4362 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4363 unimplemented!()
4364 }
4365
4366 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4367 unimplemented!()
4368 }
4369
4370 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4371 unimplemented!()
4372 }
4373
4374 fn base_dir(&self) -> &EvaluationPath {
4375 unimplemented!()
4376 }
4377
4378 fn temp_dir(&self) -> &Path {
4379 unimplemented!()
4380 }
4381
4382 fn transferer(&self) -> &dyn Transferer {
4383 unimplemented!()
4384 }
4385
4386 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4387 if path.as_str() == "/some/host/path" {
4388 Some(GuestPath::new("/mnt/task/input/0/path"))
4389 } else {
4390 None
4391 }
4392 }
4393 }
4394
4395 assert_eq!(
4397 PrimitiveValue::new_directory("/some/host/path")
4398 .coerce(Some(&Context), &PrimitiveType::String.into())
4399 .expect("should coerce")
4400 .unwrap_string()
4401 .as_str(),
4402 "/mnt/task/input/0/path"
4403 );
4404
4405 assert_eq!(
4407 value
4408 .coerce(Some(&Context), &PrimitiveType::String.into())
4409 .expect("should coerce")
4410 .unwrap_string()
4411 .as_str(),
4412 "foo"
4413 );
4414 }
4415
4416 #[test]
4417 fn directory_display() {
4418 let value = PrimitiveValue::new_directory("/foo/bar/baz");
4419 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
4420 }
4421
4422 #[test]
4423 fn none_coercion() {
4424 assert!(
4426 Value::new_none(Type::None)
4427 .coerce(None, &Type::from(PrimitiveType::String).optional())
4428 .expect("should coerce")
4429 .is_none(),
4430 );
4431
4432 assert_eq!(
4434 format!(
4435 "{e:#}",
4436 e = Value::new_none(Type::None)
4437 .coerce(None, &PrimitiveType::String.into())
4438 .unwrap_err()
4439 ),
4440 "cannot coerce `None` to non-optional type `String`"
4441 );
4442 }
4443
4444 #[test]
4445 fn none_display() {
4446 assert_eq!(Value::new_none(Type::None).to_string(), "None");
4447 }
4448
4449 #[test]
4450 fn array_coercion() {
4451 let src_ty = ArrayType::new(PrimitiveType::Integer);
4452 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
4453
4454 let src: CompoundValue = Array::new(src_ty, [1, 2, 3])
4456 .expect("should create array value")
4457 .into();
4458 let target = src.coerce(None, &target_ty).expect("should coerce");
4459 assert_eq!(
4460 target.unwrap_array().to_string(),
4461 "[1.000000, 2.000000, 3.000000]"
4462 );
4463
4464 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
4466 assert_eq!(
4467 format!("{e:#}", e = src.coerce(None, &target_ty).unwrap_err()),
4468 "failed to coerce array element at index 0: cannot coerce type `Int` to type `String`"
4469 );
4470 }
4471
4472 #[test]
4473 fn non_empty_array_coercion() {
4474 let ty = ArrayType::new(PrimitiveType::String);
4475 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
4476
4477 let string = PrimitiveValue::new_string("foo");
4479 let value: Value = Array::new(ty.clone(), [string])
4480 .expect("should create array")
4481 .into();
4482 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
4483
4484 let value: Value = Array::new::<Value>(ty, [])
4486 .expect("should create array")
4487 .into();
4488 assert_eq!(
4489 format!("{e:#}", e = value.coerce(None, &target_ty).unwrap_err()),
4490 "cannot coerce empty array value to non-empty array type `Array[String]+`"
4491 );
4492 }
4493
4494 #[test]
4495 fn array_display() {
4496 let ty = ArrayType::new(PrimitiveType::Integer);
4497 let value: Value = Array::new(ty, [1, 2, 3])
4498 .expect("should create array")
4499 .into();
4500
4501 assert_eq!(value.to_string(), "[1, 2, 3]");
4502 }
4503
4504 #[test]
4505 fn map_coerce() {
4506 let key1 = PrimitiveValue::new_file("foo");
4507 let value1 = PrimitiveValue::new_string("bar");
4508 let key2 = PrimitiveValue::new_file("baz");
4509 let value2 = PrimitiveValue::new_string("qux");
4510
4511 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
4512 let file_to_string: Value = Map::new(ty, [(key1, value1), (key2, value2)])
4513 .expect("should create map value")
4514 .into();
4515
4516 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
4518 let string_to_file = file_to_string
4519 .coerce(None, &ty)
4520 .expect("value should coerce");
4521 assert_eq!(
4522 string_to_file.to_string(),
4523 r#"{"foo": "bar", "baz": "qux"}"#
4524 );
4525
4526 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
4528 assert_eq!(
4529 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4530 "failed to coerce map key for element at index 0: cannot coerce type `String` to type \
4531 `Int`"
4532 );
4533
4534 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
4536 assert_eq!(
4537 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4538 "failed to coerce map value for element at index 0: cannot coerce type `File` to type \
4539 `Int`"
4540 );
4541
4542 let ty = StructType::new(
4544 "Foo",
4545 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
4546 )
4547 .into();
4548 let struct_value = string_to_file
4549 .coerce(None, &ty)
4550 .expect("value should coerce");
4551 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4552
4553 let ty = StructType::new(
4555 "Foo",
4556 [
4557 ("foo", PrimitiveType::String),
4558 ("baz", PrimitiveType::String),
4559 ],
4560 )
4561 .into();
4562 let struct_value = file_to_string
4563 .coerce(None, &ty)
4564 .expect("value should coerce");
4565 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4566
4567 let ty = StructType::new(
4569 "Foo",
4570 [
4571 ("foo", PrimitiveType::File),
4572 ("baz", PrimitiveType::File),
4573 ("qux", PrimitiveType::File),
4574 ],
4575 )
4576 .into();
4577 assert_eq!(
4578 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4579 "cannot coerce a map of 2 elements to an instance of struct `Foo` as the struct has 3 \
4580 members"
4581 );
4582
4583 let object_value = string_to_file
4585 .coerce(None, &Type::Object)
4586 .expect("value should coerce");
4587 assert_eq!(
4588 object_value.to_string(),
4589 r#"object {foo: "bar", baz: "qux"}"#
4590 );
4591
4592 let object_value = file_to_string
4594 .coerce(None, &Type::Object)
4595 .expect("value should coerce");
4596 assert_eq!(
4597 object_value.to_string(),
4598 r#"object {foo: "bar", baz: "qux"}"#
4599 );
4600 }
4601
4602 #[test]
4603 fn map_display() {
4604 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4605 let value: Value = Map::new(ty, [(1, true), (2, false)])
4606 .expect("should create map value")
4607 .into();
4608 assert_eq!(value.to_string(), "{1: true, 2: false}");
4609 }
4610
4611 #[test]
4612 fn pair_coercion() {
4613 let left = PrimitiveValue::new_file("foo");
4614 let right = PrimitiveValue::new_string("bar");
4615
4616 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4617 let value: Value = Pair::new(ty, left, right)
4618 .expect("should create pair value")
4619 .into();
4620
4621 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
4623 let value = value.coerce(None, &ty).expect("value should coerce");
4624 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
4625
4626 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
4628 assert_eq!(
4629 format!("{e:#}", e = value.coerce(None, &ty).unwrap_err()),
4630 "failed to coerce pair's left value: cannot coerce type `String` to type `Int`"
4631 );
4632 }
4633
4634 #[test]
4635 fn pair_display() {
4636 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4637 let value: Value = Pair::new(ty, 12345, false)
4638 .expect("should create pair value")
4639 .into();
4640 assert_eq!(value.to_string(), "(12345, false)");
4641 }
4642
4643 #[test]
4644 fn struct_coercion() {
4645 let ty = StructType::new(
4646 "Foo",
4647 [
4648 ("foo", PrimitiveType::Float),
4649 ("bar", PrimitiveType::Float),
4650 ("baz", PrimitiveType::Float),
4651 ],
4652 );
4653 let value: Value = Struct::new(ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
4654 .expect("should create map value")
4655 .into();
4656
4657 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
4659 let map_value = value.coerce(None, &ty).expect("value should coerce");
4660 assert_eq!(
4661 map_value.to_string(),
4662 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4663 );
4664
4665 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
4667 let map_value = value.coerce(None, &ty).expect("value should coerce");
4668 assert_eq!(
4669 map_value.to_string(),
4670 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4671 );
4672
4673 let ty = StructType::new(
4675 "Bar",
4676 [
4677 ("foo", PrimitiveType::Float),
4678 ("bar", PrimitiveType::Float),
4679 ("baz", PrimitiveType::Float),
4680 ],
4681 )
4682 .into();
4683 let struct_value = value.coerce(None, &ty).expect("value should coerce");
4684 assert_eq!(
4685 struct_value.to_string(),
4686 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4687 );
4688
4689 let object_value = value
4691 .coerce(None, &Type::Object)
4692 .expect("value should coerce");
4693 assert_eq!(
4694 object_value.to_string(),
4695 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4696 );
4697 }
4698
4699 #[test]
4700 fn struct_display() {
4701 let ty = StructType::new(
4702 "Foo",
4703 [
4704 ("foo", PrimitiveType::Float),
4705 ("bar", PrimitiveType::String),
4706 ("baz", PrimitiveType::Integer),
4707 ],
4708 );
4709 let value: Value = Struct::new(
4710 ty,
4711 [
4712 ("foo", Value::from(1.101)),
4713 ("bar", PrimitiveValue::new_string("foo").into()),
4714 ("baz", 1234.into()),
4715 ],
4716 )
4717 .expect("should create map value")
4718 .into();
4719 assert_eq!(
4720 value.to_string(),
4721 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
4722 );
4723 }
4724
4725 #[test]
4726 fn pair_serialization() {
4727 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4728 let pair: Value = Pair::new(
4729 pair_ty,
4730 PrimitiveValue::new_file("foo"),
4731 PrimitiveValue::new_string("bar"),
4732 )
4733 .expect("should create pair value")
4734 .into();
4735 let value_serializer = ValueSerializer::new(None, &pair, true);
4737 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4738 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
4739
4740 let value_serializer = ValueSerializer::new(None, &pair, false);
4742 assert!(serde_json::to_string(&value_serializer).is_err());
4743
4744 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
4745 let array: Value = Array::new(array_ty, [pair])
4746 .expect("should create array value")
4747 .into();
4748
4749 let value_serializer = ValueSerializer::new(None, &array, true);
4751 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4752 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
4753 }
4754
4755 #[test]
4756 fn type_name_ref_equality() {
4757 use wdl_analysis::types::EnumType;
4758
4759 let enum_type = Type::Compound(
4760 CompoundType::Custom(CustomType::Enum(
4761 EnumType::new(
4762 "MyEnum",
4763 Span::new(0, 0),
4764 Type::Primitive(PrimitiveType::Integer, false),
4765 Vec::<(String, Type)>::new(),
4766 &[],
4767 )
4768 .expect("should create enum type"),
4769 )),
4770 false,
4771 );
4772
4773 let value1 = Value::TypeNameRef(TypeNameRefValue::new(enum_type.clone()));
4774 let value2 = Value::TypeNameRef(TypeNameRefValue::new(enum_type.clone()));
4775
4776 assert_eq!(value1.ty(), value2.ty());
4777 }
4778
4779 #[test]
4780 fn type_name_ref_ty() {
4781 let struct_type = Type::Compound(
4782 CompoundType::Custom(CustomType::Struct(StructType::new(
4783 "MyStruct",
4784 empty::<(&str, Type)>(),
4785 ))),
4786 false,
4787 );
4788
4789 let value = Value::TypeNameRef(TypeNameRefValue::new(struct_type.clone()));
4790 assert_eq!(value.ty(), struct_type);
4791 }
4792
4793 #[test]
4794 fn type_name_ref_display() {
4795 use wdl_analysis::types::EnumType;
4796
4797 let enum_type = Type::Compound(
4798 CompoundType::Custom(CustomType::Enum(
4799 EnumType::new(
4800 "Color",
4801 Span::new(0, 0),
4802 Type::Primitive(PrimitiveType::Integer, false),
4803 Vec::<(String, Type)>::new(),
4804 &[],
4805 )
4806 .expect("should create enum type"),
4807 )),
4808 false,
4809 );
4810
4811 let value = Value::TypeNameRef(TypeNameRefValue::new(enum_type));
4812 assert_eq!(value.to_string(), "Color");
4813 }
4814}