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() => {
904 let enum_ty = target.as_enum().unwrap();
906
907 if enum_ty
908 .choices()
909 .iter()
910 .any(|choice_name| choice_name == s.as_str())
911 {
912 if let Some(context) = context {
913 if let Ok(value) = context.enum_choice_value(enum_ty.name(), s) {
914 return Ok(Value::Compound(CompoundValue::EnumChoice(
915 EnumChoice::new(enum_ty.clone(), s.as_str(), value),
916 )));
917 } else {
918 bail!(
919 "enum choice value lookup failed for choice `{s}` in enum `{}`",
920 enum_ty.name()
921 );
922 }
923 } else {
924 bail!(
925 "context does not exist when creating enum choice value `{s}` in enum \
926 `{}`",
927 enum_ty.name()
928 );
929 }
930 }
931
932 let choices = if enum_ty.choices().is_empty() {
933 None
934 } else {
935 let mut choice_names = enum_ty.choices().to_vec();
936 choice_names.sort();
937 Some(format!(" (choices: `{}`)", choice_names.join("`, `")))
938 }
939 .unwrap_or_default();
940
941 bail!(
942 "cannot coerce type `String` to {target:#}: choice `{s}` not found in enum \
943 `{}`{choices}",
944 enum_ty.name()
945 );
946 }
947 Self::Compound(CompoundValue::EnumChoice(e))
948 if target
949 .as_primitive()
950 .map(|t| matches!(t, PrimitiveType::String))
951 .unwrap_or(false) =>
952 {
953 Ok(Value::Primitive(PrimitiveValue::new_string(e.name())))
954 }
955 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
956 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
957 Self::Hidden(v) => v.coerce(context, target).map(Self::Hidden),
958 Self::Call(_) => {
959 bail!("call values cannot be coerced to any other type");
960 }
961 Self::TypeNameRef(_) => {
962 bail!("type name references cannot be coerced to any other type");
963 }
964 }
965 }
966}
967
968impl From<NoneValue> for Value {
969 fn from(value: NoneValue) -> Self {
970 Self::None(value)
971 }
972}
973
974impl From<bool> for Value {
975 fn from(value: bool) -> Self {
976 Self::Primitive(value.into())
977 }
978}
979
980impl From<i64> for Value {
981 fn from(value: i64) -> Self {
982 Self::Primitive(value.into())
983 }
984}
985
986impl TryFrom<u64> for Value {
987 type Error = std::num::TryFromIntError;
988
989 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
990 let value: i64 = value.try_into()?;
991 Ok(value.into())
992 }
993}
994
995impl From<f64> for Value {
996 fn from(value: f64) -> Self {
997 Self::Primitive(value.into())
998 }
999}
1000
1001impl From<String> for Value {
1002 fn from(value: String) -> Self {
1003 Self::Primitive(value.into())
1004 }
1005}
1006
1007impl From<PrimitiveValue> for Value {
1008 fn from(value: PrimitiveValue) -> Self {
1009 Self::Primitive(value)
1010 }
1011}
1012
1013impl From<Option<PrimitiveValue>> for Value {
1014 fn from(value: Option<PrimitiveValue>) -> Self {
1015 match value {
1016 Some(v) => v.into(),
1017 None => Self::new_none(Type::None),
1018 }
1019 }
1020}
1021
1022impl From<CompoundValue> for Value {
1023 fn from(value: CompoundValue) -> Self {
1024 Self::Compound(value)
1025 }
1026}
1027
1028impl From<HiddenValue> for Value {
1029 fn from(value: HiddenValue) -> Self {
1030 Self::Hidden(value)
1031 }
1032}
1033
1034impl From<Pair> for Value {
1035 fn from(value: Pair) -> Self {
1036 Self::Compound(value.into())
1037 }
1038}
1039
1040impl From<Array> for Value {
1041 fn from(value: Array) -> Self {
1042 Self::Compound(value.into())
1043 }
1044}
1045
1046impl From<Map> for Value {
1047 fn from(value: Map) -> Self {
1048 Self::Compound(value.into())
1049 }
1050}
1051
1052impl From<Object> for Value {
1053 fn from(value: Object) -> Self {
1054 Self::Compound(value.into())
1055 }
1056}
1057
1058impl From<Struct> for Value {
1059 fn from(value: Struct) -> Self {
1060 Self::Compound(value.into())
1061 }
1062}
1063
1064impl From<CallValue> for Value {
1065 fn from(value: CallValue) -> Self {
1066 Self::Call(value)
1067 }
1068}
1069
1070impl<'de> serde::Deserialize<'de> for Value {
1071 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
1072 where
1073 D: serde::Deserializer<'de>,
1074 {
1075 use serde::Deserialize as _;
1076
1077 struct Visitor;
1079
1080 impl<'de> serde::de::Visitor<'de> for Visitor {
1081 type Value = Value;
1082
1083 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
1084 where
1085 E: serde::de::Error,
1086 {
1087 Ok(Value::new_none(Type::None))
1088 }
1089
1090 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
1091 where
1092 E: serde::de::Error,
1093 {
1094 Ok(Value::new_none(Type::None))
1095 }
1096
1097 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
1098 where
1099 D: serde::Deserializer<'de>,
1100 {
1101 Value::deserialize(deserializer)
1102 }
1103
1104 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
1105 where
1106 E: serde::de::Error,
1107 {
1108 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
1109 }
1110
1111 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
1112 where
1113 E: serde::de::Error,
1114 {
1115 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
1116 }
1117
1118 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
1119 where
1120 E: serde::de::Error,
1121 {
1122 Ok(Value::Primitive(PrimitiveValue::Integer(
1123 v.try_into().map_err(|_| {
1124 E::custom("integer not in range for a 64-bit signed integer")
1125 })?,
1126 )))
1127 }
1128
1129 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
1130 where
1131 E: serde::de::Error,
1132 {
1133 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
1134 }
1135
1136 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
1137 where
1138 E: serde::de::Error,
1139 {
1140 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1141 }
1142
1143 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
1144 where
1145 E: serde::de::Error,
1146 {
1147 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1148 }
1149
1150 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
1151 where
1152 A: serde::de::SeqAccess<'de>,
1153 {
1154 use serde::de::Error as _;
1155
1156 let mut elements = vec![];
1157 while let Some(element) = seq.next_element::<Value>()? {
1158 elements.push(element);
1159 }
1160
1161 let mut candidate_ty = None;
1163 for element in elements.iter() {
1164 let new_candidate_ty = element.ty();
1165 let old_candidate_ty =
1166 candidate_ty.get_or_insert_with(|| new_candidate_ty.clone());
1167 let Some(new_common_ty) = old_candidate_ty.common_type(&new_candidate_ty)
1168 else {
1169 return Err(A::Error::custom(format!(
1170 "a common element type does not exist between {old_candidate_ty:#} \
1171 and {new_candidate_ty:#}"
1172 )));
1173 };
1174 candidate_ty = Some(new_common_ty);
1175 }
1176 let array_ty = ArrayType::new(candidate_ty.unwrap_or(Type::Union));
1178 Ok(Array::new(array_ty.clone(), elements)
1179 .map_err(|e| {
1180 A::Error::custom(format!("cannot coerce value to {array_ty:#}: {e:#}"))
1181 })?
1182 .into())
1183 }
1184
1185 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
1186 where
1187 A: serde::de::MapAccess<'de>,
1188 {
1189 let mut members = IndexMap::new();
1190 while let Some(key) = map.next_key::<String>()? {
1191 members.insert(key, map.next_value()?);
1192 }
1193
1194 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
1195 }
1196
1197 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1198 write!(f, "a WDL value")
1199 }
1200 }
1201
1202 deserializer.deserialize_any(Visitor)
1203 }
1204}
1205
1206#[derive(Debug, Clone)]
1210pub enum PrimitiveValue {
1211 Boolean(bool),
1213 Integer(i64),
1215 Float(OrderedFloat<f64>),
1217 String(Arc<String>),
1219 File(HostPath),
1221 Directory(HostPath),
1223}
1224
1225impl PrimitiveValue {
1226 pub fn new_string(s: impl Into<String>) -> Self {
1228 Self::String(Arc::new(s.into()))
1229 }
1230
1231 pub fn new_file(path: impl Into<HostPath>) -> Self {
1233 Self::File(path.into())
1234 }
1235
1236 pub fn new_directory(path: impl Into<HostPath>) -> Self {
1238 Self::Directory(path.into())
1239 }
1240
1241 pub fn ty(&self) -> Type {
1243 match self {
1244 Self::Boolean(_) => PrimitiveType::Boolean.into(),
1245 Self::Integer(_) => PrimitiveType::Integer.into(),
1246 Self::Float(_) => PrimitiveType::Float.into(),
1247 Self::String(_) => PrimitiveType::String.into(),
1248 Self::File(_) => PrimitiveType::File.into(),
1249 Self::Directory(_) => PrimitiveType::Directory.into(),
1250 }
1251 }
1252
1253 pub fn as_boolean(&self) -> Option<bool> {
1257 match self {
1258 Self::Boolean(v) => Some(*v),
1259 _ => None,
1260 }
1261 }
1262
1263 pub fn unwrap_boolean(self) -> bool {
1269 match self {
1270 Self::Boolean(v) => v,
1271 _ => panic!("value is not a boolean"),
1272 }
1273 }
1274
1275 pub fn as_integer(&self) -> Option<i64> {
1279 match self {
1280 Self::Integer(v) => Some(*v),
1281 _ => None,
1282 }
1283 }
1284
1285 pub fn unwrap_integer(self) -> i64 {
1291 match self {
1292 Self::Integer(v) => v,
1293 _ => panic!("value is not an integer"),
1294 }
1295 }
1296
1297 pub fn as_float(&self) -> Option<f64> {
1301 match self {
1302 Self::Float(v) => Some((*v).into()),
1303 _ => None,
1304 }
1305 }
1306
1307 pub fn unwrap_float(self) -> f64 {
1313 match self {
1314 Self::Float(v) => v.into(),
1315 _ => panic!("value is not a float"),
1316 }
1317 }
1318
1319 pub fn as_string(&self) -> Option<&Arc<String>> {
1323 match self {
1324 Self::String(s) => Some(s),
1325 _ => None,
1326 }
1327 }
1328
1329 pub fn unwrap_string(self) -> Arc<String> {
1335 match self {
1336 Self::String(s) => s,
1337 _ => panic!("value is not a string"),
1338 }
1339 }
1340
1341 pub fn as_file(&self) -> Option<&HostPath> {
1345 match self {
1346 Self::File(p) => Some(p),
1347 _ => None,
1348 }
1349 }
1350
1351 pub fn unwrap_file(self) -> HostPath {
1357 match self {
1358 Self::File(p) => p,
1359 _ => panic!("value is not a file"),
1360 }
1361 }
1362
1363 pub fn as_directory(&self) -> Option<&HostPath> {
1367 match self {
1368 Self::Directory(p) => Some(p),
1369 _ => None,
1370 }
1371 }
1372
1373 pub fn unwrap_directory(self) -> HostPath {
1379 match self {
1380 Self::Directory(p) => p,
1381 _ => panic!("value is not a directory"),
1382 }
1383 }
1384
1385 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1392 match (left, right) {
1393 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1394 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1395 (Self::Integer(left), Self::Float(right)) => {
1396 Some(OrderedFloat(*left as f64).cmp(right))
1397 }
1398 (Self::Float(left), Self::Integer(right)) => {
1399 Some(left.cmp(&OrderedFloat(*right as f64)))
1400 }
1401 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1402 (Self::String(left), Self::String(right))
1403 | (Self::String(left), Self::File(HostPath(right)))
1404 | (Self::String(left), Self::Directory(HostPath(right)))
1405 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1406 | (Self::File(HostPath(left)), Self::String(right))
1407 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1408 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1409 _ => None,
1410 }
1411 }
1412
1413 pub(crate) fn raw<'a>(
1421 &'a self,
1422 context: Option<&'a dyn EvaluationContext>,
1423 ) -> impl fmt::Display + use<'a> {
1424 struct Display<'a> {
1426 value: &'a PrimitiveValue,
1428 context: Option<&'a dyn EvaluationContext>,
1430 }
1431
1432 impl fmt::Display for Display<'_> {
1433 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1434 match self.value {
1435 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1436 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1437 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1438 PrimitiveValue::String(v) => write!(f, "{v}"),
1439 PrimitiveValue::File(v) => {
1440 write!(
1441 f,
1442 "{v}",
1443 v = self
1444 .context
1445 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1446 .unwrap_or(Cow::Borrowed(&v.0))
1447 )
1448 }
1449 PrimitiveValue::Directory(v) => {
1450 write!(
1451 f,
1452 "{v}",
1453 v = self
1454 .context
1455 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1456 .unwrap_or(Cow::Borrowed(&v.0))
1457 )
1458 }
1459 }
1460 }
1461 }
1462
1463 Display {
1464 value: self,
1465 context,
1466 }
1467 }
1468}
1469
1470impl fmt::Display for PrimitiveValue {
1471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1472 match self {
1473 Self::Boolean(v) => write!(f, "{v}"),
1474 Self::Integer(v) => write!(f, "{v}"),
1475 Self::Float(v) => write!(f, "{v:.6?}"),
1476 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1477 f.write_str("\"")?;
1478 write_escaped_wdl_string(f, s.as_str())?;
1479 f.write_str("\"")
1480 }
1481 }
1482 }
1483}
1484
1485impl PartialEq for PrimitiveValue {
1486 fn eq(&self, other: &Self) -> bool {
1487 Self::compare(self, other) == Some(Ordering::Equal)
1488 }
1489}
1490
1491impl Eq for PrimitiveValue {}
1492
1493impl Hash for PrimitiveValue {
1494 fn hash<H: Hasher>(&self, state: &mut H) {
1495 match self {
1496 Self::Boolean(v) => {
1497 0.hash(state);
1498 v.hash(state);
1499 }
1500 Self::Integer(v) => {
1501 1.hash(state);
1502 v.hash(state);
1503 }
1504 Self::Float(v) => {
1505 1.hash(state);
1508 v.hash(state);
1509 }
1510 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1511 2.hash(state);
1514 v.hash(state);
1515 }
1516 }
1517 }
1518}
1519
1520impl From<bool> for PrimitiveValue {
1521 fn from(value: bool) -> Self {
1522 Self::Boolean(value)
1523 }
1524}
1525
1526impl From<i64> for PrimitiveValue {
1527 fn from(value: i64) -> Self {
1528 Self::Integer(value)
1529 }
1530}
1531
1532impl From<f64> for PrimitiveValue {
1533 fn from(value: f64) -> Self {
1534 Self::Float(value.into())
1535 }
1536}
1537
1538impl From<String> for PrimitiveValue {
1539 fn from(value: String) -> Self {
1540 Self::String(value.into())
1541 }
1542}
1543
1544impl Coercible for PrimitiveValue {
1545 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1546 if target.is_union() || target.is_none() || self.ty().eq(target) {
1547 return Ok(self.clone());
1548 }
1549
1550 match self {
1551 Self::Boolean(v) => {
1552 target
1553 .as_primitive()
1554 .and_then(|ty| match ty {
1555 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1557 _ => None,
1558 })
1559 .with_context(|| format!("cannot coerce type `Boolean` to {target:#}"))
1560 }
1561 Self::Integer(v) => {
1562 target
1563 .as_primitive()
1564 .and_then(|ty| match ty {
1565 PrimitiveType::Integer => Some(Self::Integer(*v)),
1567 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1569 _ => None,
1570 })
1571 .with_context(|| format!("cannot coerce type `Int` to {target:#}"))
1572 }
1573 Self::Float(v) => {
1574 target
1575 .as_primitive()
1576 .and_then(|ty| match ty {
1577 PrimitiveType::Float => Some(Self::Float(*v)),
1579 _ => None,
1580 })
1581 .with_context(|| format!("cannot coerce type `Float` to {target:#}"))
1582 }
1583 Self::String(s) => {
1584 target
1585 .as_primitive()
1586 .and_then(|ty| match ty {
1587 PrimitiveType::String => Some(Self::String(s.clone())),
1589 PrimitiveType::File => Some(Self::File(
1591 context
1592 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1593 .unwrap_or_else(|| s.clone().into()),
1594 )),
1595 PrimitiveType::Directory => Some(Self::Directory(
1597 context
1598 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1599 .unwrap_or_else(|| s.clone().into()),
1600 )),
1601 _ => None,
1602 })
1603 .with_context(|| format!("cannot coerce type `String` to {target:#}"))
1604 }
1605 Self::File(p) => {
1606 target
1607 .as_primitive()
1608 .and_then(|ty| match ty {
1609 PrimitiveType::File => Some(Self::File(p.clone())),
1611 PrimitiveType::String => Some(Self::String(
1613 context
1614 .and_then(|c| c.guest_path(p).map(Into::into))
1615 .unwrap_or_else(|| p.clone().into()),
1616 )),
1617 _ => None,
1618 })
1619 .with_context(|| format!("cannot coerce type `File` to {target:#}"))
1620 }
1621 Self::Directory(p) => {
1622 target
1623 .as_primitive()
1624 .and_then(|ty| match ty {
1625 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1627 PrimitiveType::String => Some(Self::String(
1629 context
1630 .and_then(|c| c.guest_path(p).map(Into::into))
1631 .unwrap_or_else(|| p.clone().into()),
1632 )),
1633 _ => None,
1634 })
1635 .with_context(|| format!("cannot coerce type `Directory` to {target:#}"))
1636 }
1637 }
1638 }
1639}
1640
1641#[derive(Debug)]
1643struct PairInner {
1644 ty: Type,
1646 left: Value,
1648 right: Value,
1650}
1651
1652#[derive(Debug, Clone)]
1656pub struct Pair(Arc<PairInner>);
1657
1658impl Pair {
1659 pub fn new(ty: PairType, left: impl Into<Value>, right: impl Into<Value>) -> Result<Self> {
1664 Self::new_with_context(None, ty, left, right)
1665 }
1666
1667 pub(crate) fn new_with_context(
1672 context: Option<&dyn EvaluationContext>,
1673 ty: PairType,
1674 left: impl Into<Value>,
1675 right: impl Into<Value>,
1676 ) -> Result<Self> {
1677 let left = left
1678 .into()
1679 .coerce(context, ty.left_type())
1680 .context("failed to coerce pair's left value")?;
1681 let right = right
1682 .into()
1683 .coerce(context, ty.right_type())
1684 .context("failed to coerce pair's right value")?;
1685 Ok(Self::new_unchecked(ty, left, right))
1686 }
1687
1688 pub(crate) fn new_unchecked(ty: impl Into<Type>, left: Value, right: Value) -> Self {
1691 let ty = ty.into();
1692 assert!(ty.as_pair().is_some());
1693 Self(Arc::new(PairInner {
1694 ty: ty.require(),
1695 left,
1696 right,
1697 }))
1698 }
1699
1700 pub fn ty(&self) -> Type {
1702 self.0.ty.clone()
1703 }
1704
1705 pub fn left(&self) -> &Value {
1707 &self.0.left
1708 }
1709
1710 pub fn right(&self) -> &Value {
1712 &self.0.right
1713 }
1714}
1715
1716impl fmt::Display for Pair {
1717 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1718 write!(
1719 f,
1720 "({left}, {right})",
1721 left = self.0.left,
1722 right = self.0.right
1723 )
1724 }
1725}
1726
1727#[derive(Debug)]
1729struct ArrayInner {
1730 ty: Type,
1732 elements: Vec<Value>,
1734}
1735
1736#[derive(Debug, Clone)]
1740pub struct Array(Arc<ArrayInner>);
1741
1742impl Array {
1743 pub fn new<V>(ty: ArrayType, elements: impl IntoIterator<Item = V>) -> Result<Self>
1748 where
1749 V: Into<Value>,
1750 {
1751 Self::new_with_context(None, ty, elements)
1752 }
1753
1754 pub(crate) fn new_with_context<V>(
1760 context: Option<&dyn EvaluationContext>,
1761 ty: ArrayType,
1762 elements: impl IntoIterator<Item = V>,
1763 ) -> Result<Self>
1764 where
1765 V: Into<Value>,
1766 {
1767 let element_type = ty.element_type();
1768 let elements = elements
1769 .into_iter()
1770 .enumerate()
1771 .map(|(i, v)| {
1772 let v = v.into();
1773 v.coerce(context, element_type)
1774 .with_context(|| format!("failed to coerce array element at index {i}"))
1775 })
1776 .collect::<Result<Vec<_>>>()?;
1777
1778 Ok(Self::new_unchecked(ty, elements))
1779 }
1780
1781 pub(crate) fn new_unchecked(ty: impl Into<Type>, elements: Vec<Value>) -> Self {
1788 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty.into() {
1789 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1790 } else {
1791 panic!("type is not an array type");
1792 };
1793
1794 Self(Arc::new(ArrayInner { ty, elements }))
1795 }
1796
1797 pub fn ty(&self) -> Type {
1799 self.0.ty.clone()
1800 }
1801
1802 pub fn as_slice(&self) -> &[Value] {
1804 &self.0.elements
1805 }
1806
1807 pub fn len(&self) -> usize {
1809 self.0.elements.len()
1810 }
1811
1812 pub fn is_empty(&self) -> bool {
1814 self.0.elements.is_empty()
1815 }
1816}
1817
1818impl fmt::Display for Array {
1819 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1820 write!(f, "[")?;
1821
1822 for (i, element) in self.0.elements.iter().enumerate() {
1823 if i > 0 {
1824 write!(f, ", ")?;
1825 }
1826
1827 write!(f, "{element}")?;
1828 }
1829
1830 write!(f, "]")
1831 }
1832}
1833
1834#[derive(Debug)]
1836struct MapInner {
1837 ty: Type,
1839 elements: IndexMap<PrimitiveValue, Value>,
1841}
1842
1843#[derive(Debug, Clone)]
1847pub struct Map(Arc<MapInner>);
1848
1849impl Map {
1850 pub fn new<K, V>(ty: MapType, elements: impl IntoIterator<Item = (K, V)>) -> Result<Self>
1855 where
1856 K: Into<PrimitiveValue>,
1857 V: Into<Value>,
1858 {
1859 Self::new_with_context(None, ty, elements)
1860 }
1861
1862 pub(crate) fn new_with_context<K, V>(
1867 context: Option<&dyn EvaluationContext>,
1868 ty: MapType,
1869 elements: impl IntoIterator<Item = (K, V)>,
1870 ) -> Result<Self>
1871 where
1872 K: Into<PrimitiveValue>,
1873 V: Into<Value>,
1874 {
1875 let key_type = ty.key_type();
1876 let value_type = ty.value_type();
1877
1878 let elements = elements
1879 .into_iter()
1880 .enumerate()
1881 .map(|(i, (k, v))| {
1882 let k = k.into();
1883 let v = v.into();
1884 Ok((
1885 k.coerce(context, key_type).with_context(|| {
1886 format!("failed to coerce map key for element at index {i}")
1887 })?,
1888 v.coerce(context, value_type).with_context(|| {
1889 format!("failed to coerce map value for element at index {i}")
1890 })?,
1891 ))
1892 })
1893 .collect::<Result<_>>()?;
1894
1895 Ok(Self::new_unchecked(ty, elements))
1896 }
1897
1898 pub(crate) fn new_unchecked(
1905 ty: impl Into<Type>,
1906 elements: IndexMap<PrimitiveValue, Value>,
1907 ) -> Self {
1908 let ty = ty.into();
1909 assert!(ty.as_map().is_some());
1910 Self(Arc::new(MapInner {
1911 ty: ty.require(),
1912 elements,
1913 }))
1914 }
1915
1916 pub fn ty(&self) -> Type {
1918 self.0.ty.clone()
1919 }
1920
1921 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&PrimitiveValue, &Value)> {
1923 self.0.elements.iter()
1924 }
1925
1926 pub fn keys(&self) -> impl ExactSizeIterator<Item = &PrimitiveValue> {
1928 self.0.elements.keys()
1929 }
1930
1931 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1933 self.0.elements.values()
1934 }
1935
1936 pub fn contains_key(&self, key: &PrimitiveValue) -> bool {
1938 self.0.elements.contains_key(key)
1939 }
1940
1941 pub fn get(&self, key: &PrimitiveValue) -> Option<&Value> {
1943 self.0.elements.get(key)
1944 }
1945
1946 pub fn len(&self) -> usize {
1948 self.0.elements.len()
1949 }
1950
1951 pub fn is_empty(&self) -> bool {
1953 self.0.elements.is_empty()
1954 }
1955}
1956
1957impl fmt::Display for Map {
1958 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1959 write!(f, "{{")?;
1960
1961 for (i, (k, v)) in self.iter().enumerate() {
1962 if i > 0 {
1963 write!(f, ", ")?;
1964 }
1965
1966 write!(f, "{k}: {v}")?;
1967 }
1968
1969 write!(f, "}}")
1970 }
1971}
1972
1973#[derive(Debug, Clone, Default)]
1977pub struct Object {
1978 pub(crate) members: Arc<IndexMap<String, Value>>,
1980}
1981
1982impl Object {
1983 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1987 Self {
1988 members: Arc::new(members),
1989 }
1990 }
1991
1992 pub fn empty() -> Self {
1994 Self::default()
1995 }
1996
1997 pub fn from_v1_metadata<N: TreeNode>(
1999 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
2000 ) -> Self {
2001 Object::new(
2002 items
2003 .map(|i| {
2004 (
2005 i.name().text().to_string(),
2006 Value::from_v1_metadata(&i.value()),
2007 )
2008 })
2009 .collect::<IndexMap<_, _>>(),
2010 )
2011 }
2012
2013 pub fn ty(&self) -> Type {
2015 Type::Object
2016 }
2017
2018 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2020 self.members.iter().map(|(k, v)| (k.as_str(), v))
2021 }
2022
2023 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2025 self.members.keys().map(|k| k.as_str())
2026 }
2027
2028 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2030 self.members.values()
2031 }
2032
2033 pub fn contains_key(&self, key: &str) -> bool {
2035 self.members.contains_key(key)
2036 }
2037
2038 pub fn get(&self, key: &str) -> Option<&Value> {
2040 self.members.get(key)
2041 }
2042
2043 pub fn len(&self) -> usize {
2045 self.members.len()
2046 }
2047
2048 pub fn is_empty(&self) -> bool {
2050 self.members.is_empty()
2051 }
2052}
2053
2054impl fmt::Display for Object {
2055 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2056 write!(f, "object {{")?;
2057
2058 for (i, (k, v)) in self.iter().enumerate() {
2059 if i > 0 {
2060 write!(f, ", ")?;
2061 }
2062
2063 write!(f, "{k}: {v}")?;
2064 }
2065
2066 write!(f, "}}")
2067 }
2068}
2069
2070#[derive(Debug)]
2072struct StructInner {
2073 ty: Type,
2075 name: Arc<String>,
2081 members: IndexMap<String, Value>,
2083}
2084
2085#[derive(Debug, Clone)]
2089pub struct Struct(Arc<StructInner>);
2090
2091impl Struct {
2092 pub fn new<S, V>(ty: StructType, members: impl IntoIterator<Item = (S, V)>) -> Result<Self>
2097 where
2098 S: Into<String>,
2099 V: Into<Value>,
2100 {
2101 Self::new_with_context(None, ty, members)
2102 }
2103
2104 pub(crate) fn new_with_context<S, V>(
2109 context: Option<&dyn EvaluationContext>,
2110 ty: StructType,
2111 members: impl IntoIterator<Item = (S, V)>,
2112 ) -> Result<Self>
2113 where
2114 S: Into<String>,
2115 V: Into<Value>,
2116 {
2117 let mut members = members
2118 .into_iter()
2119 .map(|(n, v)| {
2120 let n = n.into();
2121 let v = v.into();
2122 let v = v
2123 .coerce(
2124 context,
2125 ty.members().get(&n).ok_or_else(|| {
2126 anyhow!("struct does not contain a member named `{n}`")
2127 })?,
2128 )
2129 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
2130 Ok((n, v))
2131 })
2132 .collect::<Result<IndexMap<_, _>>>()?;
2133
2134 for (name, ty) in ty.members().iter() {
2135 if ty.is_optional() {
2137 if !members.contains_key(name) {
2138 members.insert(name.clone(), Value::new_none(ty.clone()));
2139 }
2140 } else {
2141 if !members.contains_key(name) {
2143 bail!("missing a value for struct member `{name}`");
2144 }
2145 }
2146 }
2147
2148 let name = Arc::new(ty.name().to_string());
2149 Ok(Self::new_unchecked(ty, name, members))
2150 }
2151
2152 pub(crate) fn new_unchecked(
2159 ty: impl Into<Type>,
2160 name: Arc<String>,
2161 members: impl Into<IndexMap<String, Value>>,
2162 ) -> Self {
2163 let ty = ty.into();
2164 assert!(ty.as_struct().is_some());
2165 Self(Arc::new(StructInner {
2166 ty: ty.require(),
2167 name,
2168 members: members.into(),
2169 }))
2170 }
2171
2172 pub fn ty(&self) -> Type {
2174 self.0.ty.clone()
2175 }
2176
2177 pub fn name(&self) -> &Arc<String> {
2179 &self.0.name
2180 }
2181
2182 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2184 self.0.members.iter().map(|(k, v)| (k.as_str(), v))
2185 }
2186
2187 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2189 self.0.members.keys().map(|k| k.as_str())
2190 }
2191
2192 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2194 self.0.members.values()
2195 }
2196
2197 pub fn contains_key(&self, key: &str) -> bool {
2199 self.0.members.contains_key(key)
2200 }
2201
2202 pub fn get(&self, key: &str) -> Option<&Value> {
2204 self.0.members.get(key)
2205 }
2206}
2207
2208impl fmt::Display for Struct {
2209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2210 write!(f, "{name} {{", name = self.0.name)?;
2211
2212 for (i, (k, v)) in self.0.members.iter().enumerate() {
2213 if i > 0 {
2214 write!(f, ", ")?;
2215 }
2216
2217 write!(f, "{k}: {v}")?;
2218 }
2219
2220 write!(f, "}}")
2221 }
2222}
2223
2224#[derive(Debug)]
2226struct EnumChoiceInner {
2227 enum_ty: EnumType,
2229 choice_index: usize,
2231 value: Value,
2233}
2234
2235#[derive(Debug, Clone)]
2241pub struct EnumChoice(Arc<EnumChoiceInner>);
2242
2243impl PartialEq for EnumChoice {
2244 fn eq(&self, other: &Self) -> bool {
2245 self.0.enum_ty == other.0.enum_ty && self.0.choice_index == other.0.choice_index
2246 }
2247}
2248
2249impl EnumChoice {
2250 pub fn new(enum_ty: impl Into<EnumType>, name: &str, value: impl Into<Value>) -> Self {
2256 let enum_ty = enum_ty.into();
2257
2258 let choice_index = enum_ty
2259 .choices()
2260 .iter()
2261 .position(|v| v == name)
2262 .expect("choice name must exist in enum type");
2263
2264 Self(Arc::new(EnumChoiceInner {
2265 enum_ty,
2266 choice_index,
2267 value: value.into(),
2268 }))
2269 }
2270
2271 pub fn enum_ty(&self) -> EnumType {
2273 self.0.enum_ty.clone()
2274 }
2275
2276 pub fn name(&self) -> &str {
2278 &self.0.enum_ty.choices()[self.0.choice_index]
2279 }
2280
2281 pub fn value(&self) -> &Value {
2283 &self.0.value
2284 }
2285}
2286
2287impl fmt::Display for EnumChoice {
2312 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2313 write!(f, "{}", self.name())
2314 }
2315}
2316
2317#[derive(Debug, Clone)]
2321pub enum CompoundValue {
2322 Pair(Pair),
2324 Array(Array),
2326 Map(Map),
2328 Object(Object),
2330 Struct(Struct),
2332 EnumChoice(EnumChoice),
2334}
2335
2336impl CompoundValue {
2337 pub fn ty(&self) -> Type {
2339 match self {
2340 CompoundValue::Pair(v) => v.ty(),
2341 CompoundValue::Array(v) => v.ty(),
2342 CompoundValue::Map(v) => v.ty(),
2343 CompoundValue::Object(v) => v.ty(),
2344 CompoundValue::Struct(v) => v.ty(),
2345 CompoundValue::EnumChoice(v) => v.enum_ty().into(),
2346 }
2347 }
2348
2349 pub fn as_pair(&self) -> Option<&Pair> {
2353 match self {
2354 Self::Pair(v) => Some(v),
2355 _ => None,
2356 }
2357 }
2358
2359 pub fn unwrap_pair(self) -> Pair {
2365 match self {
2366 Self::Pair(v) => v,
2367 _ => panic!("value is not a pair"),
2368 }
2369 }
2370
2371 pub fn as_array(&self) -> Option<&Array> {
2375 match self {
2376 Self::Array(v) => Some(v),
2377 _ => None,
2378 }
2379 }
2380
2381 pub fn unwrap_array(self) -> Array {
2387 match self {
2388 Self::Array(v) => v,
2389 _ => panic!("value is not an array"),
2390 }
2391 }
2392
2393 pub fn as_map(&self) -> Option<&Map> {
2397 match self {
2398 Self::Map(v) => Some(v),
2399 _ => None,
2400 }
2401 }
2402
2403 pub fn unwrap_map(self) -> Map {
2409 match self {
2410 Self::Map(v) => v,
2411 _ => panic!("value is not a map"),
2412 }
2413 }
2414
2415 pub fn as_object(&self) -> Option<&Object> {
2419 match self {
2420 Self::Object(v) => Some(v),
2421 _ => None,
2422 }
2423 }
2424
2425 pub fn unwrap_object(self) -> Object {
2431 match self {
2432 Self::Object(v) => v,
2433 _ => panic!("value is not an object"),
2434 }
2435 }
2436
2437 pub fn as_struct(&self) -> Option<&Struct> {
2441 match self {
2442 Self::Struct(v) => Some(v),
2443 _ => None,
2444 }
2445 }
2446
2447 pub fn unwrap_struct(self) -> Struct {
2453 match self {
2454 Self::Struct(v) => v,
2455 _ => panic!("value is not a struct"),
2456 }
2457 }
2458
2459 pub fn as_enum_choice(&self) -> Option<&EnumChoice> {
2463 match self {
2464 Self::EnumChoice(v) => Some(v),
2465 _ => None,
2466 }
2467 }
2468
2469 pub fn unwrap_enum_choice(self) -> EnumChoice {
2475 match self {
2476 Self::EnumChoice(v) => v,
2477 _ => panic!("value is not an enum choice"),
2478 }
2479 }
2480
2481 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2487 if left.ty() != right.ty() {
2490 return None;
2491 }
2492
2493 match (left, right) {
2494 (Self::Pair(left), Self::Pair(right)) => Some(
2495 Value::equals(left.left(), right.left())?
2496 && Value::equals(left.right(), right.right())?,
2497 ),
2498 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2499 left.len() == right.len()
2500 && left
2501 .as_slice()
2502 .iter()
2503 .zip(right.as_slice())
2504 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2505 ),
2506 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2507 left.len() == right.len()
2508 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2510 lk == rk && Value::equals(lv, rv).unwrap_or(false)
2511 }),
2512 ),
2513 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2514 left.len() == right.len()
2515 && left.iter().all(|(k, left)| match right.get(k) {
2516 Some(right) => Value::equals(left, right).unwrap_or(false),
2517 None => false,
2518 }),
2519 ),
2520 (CompoundValue::Struct(left), CompoundValue::Struct(right)) => Some(
2521 left.0.members.len() == right.0.members.len()
2522 && left
2523 .0
2524 .members
2525 .iter()
2526 .all(|(k, lv)| match right.0.members.get(k) {
2527 Some(rv) => Value::equals(lv, rv).unwrap_or(false),
2528 None => false,
2529 }),
2530 ),
2531 (CompoundValue::EnumChoice(left), CompoundValue::EnumChoice(right)) => {
2532 Some(left.enum_ty() == right.enum_ty() && left.name() == right.name())
2533 }
2534 _ => None,
2535 }
2536 }
2537
2538 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2543 where
2544 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2545 {
2546 match self {
2547 Self::Pair(pair) => {
2548 pair.left().visit_paths(cb)?;
2549 pair.right().visit_paths(cb)?;
2550 }
2551 Self::Array(array) => {
2552 for v in array.as_slice() {
2553 v.visit_paths(cb)?;
2554 }
2555 }
2556 Self::Map(map) => {
2557 for (k, v) in map.iter() {
2558 match k {
2559 PrimitiveValue::File(path) => cb(true, path)?,
2560 PrimitiveValue::Directory(path) => cb(false, path)?,
2561 _ => {}
2562 }
2563
2564 v.visit_paths(cb)?;
2565 }
2566 }
2567 Self::Object(object) => {
2568 for v in object.values() {
2569 v.visit_paths(cb)?;
2570 }
2571 }
2572 Self::Struct(s) => {
2573 for v in s.values() {
2574 v.visit_paths(cb)?;
2575 }
2576 }
2577 Self::EnumChoice(e) => {
2578 e.value().visit_paths(cb)?;
2579 }
2580 }
2581
2582 Ok(())
2583 }
2584
2585 fn resolve_paths<'a, F>(
2588 &'a self,
2589 base_dir: Option<&'a Path>,
2590 transferer: Option<&'a dyn Transferer>,
2591 translate: &'a F,
2592 ) -> BoxFuture<'a, Result<Self>>
2593 where
2594 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
2595 {
2596 async move {
2597 match self {
2598 Self::Pair(pair) => {
2599 let ty = pair.0.ty.as_pair().expect("should be a pair type");
2600 let (left_optional, right_optional) =
2601 (ty.left_type().is_optional(), ty.right_type().is_optional());
2602 let fst = pair
2603 .0
2604 .left
2605 .resolve_paths(left_optional, base_dir, transferer, translate)
2606 .await?;
2607 let snd = pair
2608 .0
2609 .right
2610 .resolve_paths(right_optional, base_dir, transferer, translate)
2611 .await?;
2612 Ok(Self::Pair(Pair::new_unchecked(ty.clone(), fst, snd)))
2613 }
2614 Self::Array(array) => {
2615 let ty = array.0.ty.as_array().expect("should be an array type");
2616 let optional = ty.element_type().is_optional();
2617 if !array.0.elements.is_empty() {
2618 let resolved_elements = futures::stream::iter(array.0.elements.iter())
2619 .then(|v| v.resolve_paths(optional, base_dir, transferer, translate))
2620 .try_collect::<Vec<Value>>()
2621 .await?;
2622 Ok(Self::Array(Array::new_unchecked(
2623 ty.clone(),
2624 resolved_elements,
2625 )))
2626 } else {
2627 Ok(self.clone())
2628 }
2629 }
2630 Self::Map(map) => {
2631 let ty = map.0.ty.as_map().expect("should be a map type").clone();
2632 let (key_optional, value_optional) =
2633 (ty.key_type().is_optional(), ty.value_type().is_optional());
2634 if !map.0.elements.is_empty() {
2635 let resolved_elements = futures::stream::iter(map.0.elements.iter())
2636 .then(async |(k, v)| {
2637 let resolved_key = Value::from(k.clone())
2638 .resolve_paths(key_optional, base_dir, transferer, translate)
2639 .await?
2640 .as_primitive()
2641 .cloned()
2642 .expect("key should be primitive");
2643 let resolved_value = v
2644 .resolve_paths(value_optional, base_dir, transferer, translate)
2645 .await?;
2646 Ok::<_, anyhow::Error>((resolved_key, resolved_value))
2647 })
2648 .try_collect()
2649 .await?;
2650 Ok(Self::Map(Map::new_unchecked(ty, resolved_elements)))
2651 } else {
2652 Ok(Self::Map(Map::new_unchecked(ty, IndexMap::new())))
2653 }
2654 }
2655 Self::Object(object) => {
2656 if object.is_empty() {
2657 Ok(self.clone())
2658 } else {
2659 let resolved_members = futures::stream::iter(object.iter())
2660 .then(async |(n, v)| {
2661 let resolved = v
2662 .resolve_paths(false, base_dir, transferer, translate)
2663 .await?;
2664 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2665 })
2666 .try_collect()
2667 .await?;
2668 Ok(Self::Object(Object::new(resolved_members)))
2669 }
2670 }
2671 Self::Struct(s) => {
2672 let ty = s.0.ty.as_struct().expect("should be a struct type");
2673 let name = s.name().clone();
2674 let resolved_members: IndexMap<String, Value> = futures::stream::iter(s.iter())
2675 .then(async |(n, v)| {
2676 let resolved = v
2677 .resolve_paths(
2678 ty.members()[n].is_optional(),
2679 base_dir,
2680 transferer,
2681 translate,
2682 )
2683 .await?;
2684 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2685 })
2686 .try_collect()
2687 .await?;
2688 Ok(Self::Struct(Struct::new_unchecked(
2689 ty.clone(),
2690 name,
2691 resolved_members,
2692 )))
2693 }
2694 Self::EnumChoice(e) => {
2695 let optional = e.enum_ty().inner_value_type().is_optional();
2696 let value =
2697 e.0.value
2698 .resolve_paths(optional, base_dir, transferer, translate)
2699 .await?;
2700
2701 Ok(Self::EnumChoice(EnumChoice::new(
2702 e.0.enum_ty.clone(),
2703 e.name(),
2704 value,
2705 )))
2706 }
2707 }
2708 }
2709 .boxed()
2710 }
2711}
2712
2713impl fmt::Display for CompoundValue {
2714 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2715 match self {
2716 Self::Pair(v) => v.fmt(f),
2717 Self::Array(v) => v.fmt(f),
2718 Self::Map(v) => v.fmt(f),
2719 Self::Object(v) => v.fmt(f),
2720 Self::Struct(v) => v.fmt(f),
2721 Self::EnumChoice(v) => v.fmt(f),
2722 }
2723 }
2724}
2725
2726impl Coercible for CompoundValue {
2727 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2728 if target.is_union() || target.is_none() || self.ty().eq(target) {
2729 return Ok(self.clone());
2730 }
2731
2732 if let Type::Compound(target_ty, _) = target {
2733 match (self, target_ty) {
2734 (Self::Array(v), CompoundType::Array(target_ty)) => {
2736 if v.is_empty() && target_ty.is_non_empty() {
2739 bail!("cannot coerce empty array value to non-empty array {target:#}");
2740 }
2741
2742 return Ok(Self::Array(Array::new_with_context(
2743 context,
2744 target_ty.clone(),
2745 v.as_slice().iter().cloned(),
2746 )?));
2747 }
2748 (Self::Map(v), CompoundType::Map(target_ty)) => {
2750 return Ok(Self::Map(Map::new_with_context(
2751 context,
2752 target_ty.clone(),
2753 v.iter().map(|(k, v)| (k.clone(), v.clone())),
2754 )?));
2755 }
2756 (Self::Pair(v), CompoundType::Pair(target_ty)) => {
2758 return Ok(Self::Pair(Pair::new_with_context(
2759 context,
2760 target_ty.clone(),
2761 v.0.left.clone(),
2762 v.0.right.clone(),
2763 )?));
2764 }
2765 (Self::Map(v), CompoundType::Custom(CustomType::Struct(target_ty))) => {
2767 let len = v.len();
2768 let expected_len = target_ty.members().len();
2769
2770 if len != expected_len {
2771 bail!(
2772 "cannot coerce a map of {len} element{s1} to {target:#} as the struct \
2773 has {expected_len} member{s2}",
2774 s1 = if len == 1 { "" } else { "s" },
2775 s2 = if expected_len == 1 { "" } else { "s" }
2776 );
2777 }
2778
2779 return Ok(Self::Struct(Struct::new_unchecked(
2780 target.clone(),
2781 target_ty.name().clone(),
2782 v.iter()
2783 .map(|(k, v)| {
2784 let k = k
2785 .coerce(context, &PrimitiveType::String.into())
2786 .with_context(|| {
2787 format!(
2788 "cannot coerce a map of {map_type:#} to {target:#} as \
2789 the key type cannot coerce to type `String`",
2790 map_type = v.ty()
2791 )
2792 })?
2793 .unwrap_string();
2794 let ty =
2795 target_ty.members().get(k.as_ref()).with_context(|| {
2796 format!(
2797 "cannot coerce a map with key `{k}` to {target:#} as \
2798 the struct does not contain a member with that name"
2799 )
2800 })?;
2801 let v = v.coerce(context, ty).with_context(|| {
2802 format!("failed to coerce value of map key `{k}")
2803 })?;
2804 Ok((k.to_string(), v))
2805 })
2806 .collect::<Result<IndexMap<_, _>>>()?,
2807 )));
2808 }
2809 (Self::Struct(s), CompoundType::Map(map_ty)) => {
2811 let key_ty = map_ty.key_type();
2812 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2813 bail!(
2814 "cannot coerce a struct to {target:#} as key {key_ty:#} cannot be \
2815 coerced from type `String`"
2816 );
2817 }
2818
2819 let value_ty = map_ty.value_type();
2820 return Ok(Self::Map(Map::new_unchecked(
2821 target.clone(),
2822 s.0.members
2823 .iter()
2824 .map(|(n, v)| {
2825 let v = v
2826 .coerce(context, value_ty)
2827 .with_context(|| format!("failed to coerce member `{n}`"))?;
2828 Ok((
2829 PrimitiveValue::new_string(n)
2830 .coerce(context, key_ty)
2831 .expect("should coerce"),
2832 v,
2833 ))
2834 })
2835 .collect::<Result<_>>()?,
2836 )));
2837 }
2838 (Self::Object(object), CompoundType::Map(map_ty)) => {
2840 let key_ty = map_ty.key_type();
2841 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2842 bail!(
2843 "cannot coerce an object to {target:#} as key {key_ty:#} cannot be \
2844 coerced from type `String`"
2845 );
2846 }
2847
2848 let value_ty = map_ty.value_type();
2849 return Ok(Self::Map(Map::new_unchecked(
2850 target.clone(),
2851 object
2852 .iter()
2853 .map(|(n, v)| {
2854 let v = v
2855 .coerce(context, value_ty)
2856 .with_context(|| format!("failed to coerce member `{n}`"))?;
2857 Ok((
2858 PrimitiveValue::new_string(n)
2859 .coerce(context, key_ty)
2860 .expect("should coerce"),
2861 v,
2862 ))
2863 })
2864 .collect::<Result<_>>()?,
2865 )));
2866 }
2867 (Self::Object(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2869 return Ok(Self::Struct(Struct::new_with_context(
2870 context,
2871 struct_ty.clone(),
2872 v.iter().map(|(k, v)| (k, v.clone())),
2873 )?));
2874 }
2875 (Self::Struct(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2877 let len = v.0.members.len();
2878 let expected_len = struct_ty.members().len();
2879
2880 if len != expected_len {
2881 bail!(
2882 "cannot coerce a struct of {len} members{s1} to struct type \
2883 `{target:#}` as the target struct has {expected_len} member{s2}",
2884 s1 = if len == 1 { "" } else { "s" },
2885 s2 = if expected_len == 1 { "" } else { "s" }
2886 );
2887 }
2888
2889 return Ok(Self::Struct(Struct::new_unchecked(
2890 target.clone(),
2891 struct_ty.name().clone(),
2892 v.0.members
2893 .iter()
2894 .map(|(k, v)| {
2895 let ty = struct_ty.members().get(k).ok_or_else(|| {
2896 anyhow!(
2897 "cannot coerce a struct with member `{k}` to struct type \
2898 `{target:#}` as the target struct does not contain a \
2899 member with that name",
2900 )
2901 })?;
2902 let v = v
2903 .coerce(context, ty)
2904 .with_context(|| format!("failed to coerce member `{k}`"))?;
2905 Ok((k.clone(), v))
2906 })
2907 .collect::<Result<IndexMap<_, _>>>()?,
2908 )));
2909 }
2910 _ => {}
2911 }
2912 }
2913
2914 if let Type::Object = target {
2915 match self {
2916 Self::Map(v) => {
2918 return Ok(Self::Object(Object::new(
2919 v.iter()
2920 .map(|(k, v)| {
2921 let k = k
2922 .coerce(context, &PrimitiveType::String.into())
2923 .with_context(|| {
2924 format!(
2925 "cannot coerce a map of {map_type:#} to type `Object` \
2926 as the key type cannot coerce to type `String`",
2927 map_type = v.ty()
2928 )
2929 })?
2930 .unwrap_string();
2931 Ok((k.to_string(), v.clone()))
2932 })
2933 .collect::<Result<IndexMap<_, _>>>()?,
2934 )));
2935 }
2936 Self::Struct(v) => {
2938 return Ok(Self::Object(Object {
2939 members: Arc::new(v.0.members.clone()),
2940 }));
2941 }
2942 _ => {}
2943 };
2944 }
2945
2946 bail!(
2947 "cannot coerce a value of {ty:#} to {target:#}",
2948 ty = self.ty()
2949 );
2950 }
2951}
2952
2953impl From<Pair> for CompoundValue {
2954 fn from(value: Pair) -> Self {
2955 Self::Pair(value)
2956 }
2957}
2958
2959impl From<Array> for CompoundValue {
2960 fn from(value: Array) -> Self {
2961 Self::Array(value)
2962 }
2963}
2964
2965impl From<Map> for CompoundValue {
2966 fn from(value: Map) -> Self {
2967 Self::Map(value)
2968 }
2969}
2970
2971impl From<Object> for CompoundValue {
2972 fn from(value: Object) -> Self {
2973 Self::Object(value)
2974 }
2975}
2976
2977impl From<Struct> for CompoundValue {
2978 fn from(value: Struct) -> Self {
2979 Self::Struct(value)
2980 }
2981}
2982
2983#[derive(Debug, Clone)]
2987pub enum HiddenValue {
2988 Hints(HintsValue),
2992 Input(InputValue),
2996 Output(OutputValue),
3000 TaskPreEvaluation(TaskPreEvaluationValue),
3005 TaskPostEvaluation(TaskPostEvaluationValue),
3010 PreviousTaskData(PreviousTaskDataValue),
3015}
3016
3017impl HiddenValue {
3018 pub fn ty(&self) -> Type {
3020 match self {
3021 Self::Hints(_) => Type::Hidden(HiddenType::Hints),
3022 Self::Input(_) => Type::Hidden(HiddenType::Input),
3023 Self::Output(_) => Type::Hidden(HiddenType::Output),
3024 Self::TaskPreEvaluation(_) => Type::Hidden(HiddenType::TaskPreEvaluation),
3025 Self::TaskPostEvaluation(_) => Type::Hidden(HiddenType::TaskPostEvaluation),
3026 Self::PreviousTaskData(_) => Type::Hidden(HiddenType::PreviousTaskData),
3027 }
3028 }
3029}
3030
3031impl fmt::Display for HiddenValue {
3032 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3033 match self {
3034 Self::Hints(v) => v.fmt(f),
3035 Self::Input(v) => v.fmt(f),
3036 Self::Output(v) => v.fmt(f),
3037 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => write!(f, "task"),
3038 Self::PreviousTaskData(_) => write!(f, "task.previous"),
3039 }
3040 }
3041}
3042
3043impl Coercible for HiddenValue {
3044 fn coerce(&self, _: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
3045 match self {
3046 Self::Hints(_) => {
3047 if matches!(target, Type::Hidden(HiddenType::Hints)) {
3048 return Ok(self.clone());
3049 }
3050
3051 bail!("hints values cannot be coerced to any other type");
3052 }
3053 Self::Input(_) => {
3054 if matches!(target, Type::Hidden(HiddenType::Input)) {
3055 return Ok(self.clone());
3056 }
3057
3058 bail!("input values cannot be coerced to any other type");
3059 }
3060 Self::Output(_) => {
3061 if matches!(target, Type::Hidden(HiddenType::Output)) {
3062 return Ok(self.clone());
3063 }
3064
3065 bail!("output values cannot be coerced to any other type");
3066 }
3067 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => {
3068 if matches!(
3069 target,
3070 Type::Hidden(HiddenType::TaskPreEvaluation)
3071 | Type::Hidden(HiddenType::TaskPostEvaluation)
3072 ) {
3073 return Ok(self.clone());
3074 }
3075
3076 bail!("task variables cannot be coerced to any other type");
3077 }
3078 Self::PreviousTaskData(_) => {
3079 if matches!(target, Type::Hidden(HiddenType::PreviousTaskData)) {
3080 return Ok(self.clone());
3081 }
3082
3083 bail!("previous task data values cannot be coerced to any other type");
3084 }
3085 }
3086 }
3087}
3088
3089#[derive(Debug, Clone)]
3093pub(crate) struct TaskPostEvaluationData {
3094 container: Option<Arc<String>>,
3097 cpu: f64,
3099 memory: i64,
3101 gpu: Array,
3106 fpga: Array,
3111 disks: Map,
3118 max_retries: i64,
3120}
3121
3122#[derive(Debug, Clone)]
3126pub struct PreviousTaskDataValue(Option<Arc<TaskPostEvaluationData>>);
3127
3128impl PreviousTaskDataValue {
3129 pub(crate) fn new(data: Arc<TaskPostEvaluationData>) -> Self {
3131 Self(Some(data))
3132 }
3133
3134 pub(crate) fn empty() -> Self {
3136 Self(None)
3137 }
3138
3139 pub fn field(&self, name: &str) -> Option<Value> {
3146 match name {
3147 TASK_FIELD_MEMORY => Some(
3148 self.0
3149 .as_ref()
3150 .map(|data| Value::from(data.memory))
3151 .unwrap_or_else(|| {
3152 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3153 }),
3154 ),
3155 TASK_FIELD_CPU => Some(
3156 self.0
3157 .as_ref()
3158 .map(|data| Value::from(data.cpu))
3159 .unwrap_or_else(|| {
3160 Value::new_none(Type::from(PrimitiveType::Float).optional())
3161 }),
3162 ),
3163 TASK_FIELD_CONTAINER => Some(
3164 self.0
3165 .as_ref()
3166 .and_then(|data| {
3167 data.container
3168 .as_ref()
3169 .map(|c| PrimitiveValue::String(c.clone()).into())
3170 })
3171 .unwrap_or_else(|| {
3172 Value::new_none(Type::from(PrimitiveType::String).optional())
3173 }),
3174 ),
3175 TASK_FIELD_GPU => Some(
3176 self.0
3177 .as_ref()
3178 .map(|data| Value::from(data.gpu.clone()))
3179 .unwrap_or_else(|| {
3180 Value::new_none(Type::Compound(
3181 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3182 true,
3183 ))
3184 }),
3185 ),
3186 TASK_FIELD_FPGA => Some(
3187 self.0
3188 .as_ref()
3189 .map(|data| Value::from(data.fpga.clone()))
3190 .unwrap_or_else(|| {
3191 Value::new_none(Type::Compound(
3192 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3193 true,
3194 ))
3195 }),
3196 ),
3197 TASK_FIELD_DISKS => Some(
3198 self.0
3199 .as_ref()
3200 .map(|data| Value::from(data.disks.clone()))
3201 .unwrap_or_else(|| {
3202 Value::new_none(Type::Compound(
3203 MapType::new(PrimitiveType::String, PrimitiveType::Integer).into(),
3204 true,
3205 ))
3206 }),
3207 ),
3208 TASK_FIELD_MAX_RETRIES => Some(
3209 self.0
3210 .as_ref()
3211 .map(|data| Value::from(data.max_retries))
3212 .unwrap_or_else(|| {
3213 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3214 }),
3215 ),
3216 _ => None,
3217 }
3218 }
3219}
3220
3221#[derive(Debug, Clone)]
3223struct TaskPreEvaluationInner {
3224 name: Arc<String>,
3226 id: Arc<String>,
3228 attempt: i64,
3233 meta: Object,
3235 parameter_meta: Object,
3237 ext: Object,
3239 previous: PreviousTaskDataValue,
3245}
3246
3247#[derive(Debug, Clone)]
3254pub struct TaskPreEvaluationValue(Arc<TaskPreEvaluationInner>);
3255
3256impl TaskPreEvaluationValue {
3257 pub(crate) fn new(
3260 name: impl Into<String>,
3261 id: impl Into<String>,
3262 attempt: i64,
3263 meta: Object,
3264 parameter_meta: Object,
3265 ext: Object,
3266 ) -> Self {
3267 Self(Arc::new(TaskPreEvaluationInner {
3268 name: Arc::new(name.into()),
3269 id: Arc::new(id.into()),
3270 meta,
3271 parameter_meta,
3272 ext,
3273 attempt,
3274 previous: PreviousTaskDataValue::empty(),
3275 }))
3276 }
3277
3278 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3280 Arc::get_mut(&mut self.0)
3281 .expect("task value must be uniquely owned to mutate")
3282 .previous = PreviousTaskDataValue::new(data);
3283 }
3284
3285 pub fn name(&self) -> &Arc<String> {
3287 &self.0.name
3288 }
3289
3290 pub fn id(&self) -> &Arc<String> {
3292 &self.0.id
3293 }
3294
3295 pub fn attempt(&self) -> i64 {
3297 self.0.attempt
3298 }
3299
3300 pub fn field(&self, name: &str) -> Option<Value> {
3304 match name {
3305 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.0.name.clone()).into()),
3306 TASK_FIELD_ID => Some(PrimitiveValue::String(self.0.id.clone()).into()),
3307 TASK_FIELD_ATTEMPT => Some(self.0.attempt.into()),
3308 TASK_FIELD_META => Some(self.0.meta.clone().into()),
3309 TASK_FIELD_PARAMETER_META => Some(self.0.parameter_meta.clone().into()),
3310 TASK_FIELD_EXT => Some(self.0.ext.clone().into()),
3311 TASK_FIELD_PREVIOUS => {
3312 Some(HiddenValue::PreviousTaskData(self.0.previous.clone()).into())
3313 }
3314 _ => None,
3315 }
3316 }
3317}
3318
3319#[derive(Debug, Clone)]
3321struct TaskPostEvaluationInner {
3322 data: Arc<TaskPostEvaluationData>,
3324 name: Arc<String>,
3326 id: Arc<String>,
3328 attempt: i64,
3333 meta: Object,
3335 parameter_meta: Object,
3337 ext: Object,
3339 return_code: Option<i64>,
3343 end_time: Option<i64>,
3347 previous: PreviousTaskDataValue,
3353}
3354
3355#[derive(Debug, Clone)]
3361pub struct TaskPostEvaluationValue(Arc<TaskPostEvaluationInner>);
3362
3363impl TaskPostEvaluationValue {
3364 #[allow(clippy::too_many_arguments)]
3367 pub(crate) fn new(
3368 name: impl Into<String>,
3369 id: impl Into<String>,
3370 constraints: &TaskExecutionConstraints,
3371 max_retries: i64,
3372 attempt: i64,
3373 meta: Object,
3374 parameter_meta: Object,
3375 ext: Object,
3376 ) -> Self {
3377 Self(Arc::new(TaskPostEvaluationInner {
3378 name: Arc::new(name.into()),
3379 id: Arc::new(id.into()),
3380 data: Arc::new(TaskPostEvaluationData {
3381 container: None,
3386 cpu: constraints.cpu,
3387 memory: constraints
3388 .memory
3389 .try_into()
3390 .expect("memory exceeds a valid WDL value"),
3391 gpu: Array::new_unchecked(
3392 ANALYSIS_STDLIB.array_string_type().clone(),
3393 constraints
3394 .gpu
3395 .iter()
3396 .map(|v| PrimitiveValue::new_string(v).into())
3397 .collect(),
3398 ),
3399 fpga: Array::new_unchecked(
3400 ANALYSIS_STDLIB.array_string_type().clone(),
3401 constraints
3402 .fpga
3403 .iter()
3404 .map(|v| PrimitiveValue::new_string(v).into())
3405 .collect(),
3406 ),
3407 disks: Map::new_unchecked(
3408 ANALYSIS_STDLIB.map_string_int_type().clone(),
3409 constraints
3410 .disks
3411 .iter()
3412 .map(|(k, v)| (PrimitiveValue::new_string(k), (*v).into()))
3413 .collect(),
3414 ),
3415 max_retries,
3416 }),
3417 attempt,
3418 meta,
3419 parameter_meta,
3420 ext,
3421 return_code: None,
3422 end_time: None,
3423 previous: PreviousTaskDataValue::empty(),
3424 }))
3425 }
3426
3427 pub fn name(&self) -> &Arc<String> {
3429 &self.0.name
3430 }
3431
3432 pub fn id(&self) -> &Arc<String> {
3434 &self.0.id
3435 }
3436
3437 pub fn container(&self) -> Option<&Arc<String>> {
3439 self.0.data.container.as_ref()
3440 }
3441
3442 pub fn cpu(&self) -> f64 {
3444 self.0.data.cpu
3445 }
3446
3447 pub fn memory(&self) -> i64 {
3449 self.0.data.memory
3450 }
3451
3452 pub fn gpu(&self) -> &Array {
3457 &self.0.data.gpu
3458 }
3459
3460 pub fn fpga(&self) -> &Array {
3465 &self.0.data.fpga
3466 }
3467
3468 pub fn disks(&self) -> &Map {
3475 &self.0.data.disks
3476 }
3477
3478 pub fn attempt(&self) -> i64 {
3483 self.0.attempt
3484 }
3485
3486 pub fn end_time(&self) -> Option<i64> {
3490 self.0.end_time
3491 }
3492
3493 pub fn return_code(&self) -> Option<i64> {
3497 self.0.return_code
3498 }
3499
3500 pub fn meta(&self) -> &Object {
3502 &self.0.meta
3503 }
3504
3505 pub fn parameter_meta(&self) -> &Object {
3507 &self.0.parameter_meta
3508 }
3509
3510 pub fn ext(&self) -> &Object {
3512 &self.0.ext
3513 }
3514
3515 pub(crate) fn set_container(&mut self, container: String) {
3517 let inner = Arc::get_mut(&mut self.0).expect("task value must be uniquely owned to mutate");
3518 Arc::make_mut(&mut inner.data).container = Some(Arc::new(container));
3519 }
3520
3521 pub(crate) fn set_return_code(&mut self, code: i32) {
3523 Arc::get_mut(&mut self.0)
3524 .expect("task value must be uniquely owned to mutate")
3525 .return_code = Some(code as i64);
3526 }
3527
3528 pub(crate) fn set_attempt(&mut self, attempt: i64) {
3530 Arc::get_mut(&mut self.0)
3531 .expect("task value must be uniquely owned to mutate")
3532 .attempt = attempt;
3533 }
3534
3535 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3537 Arc::get_mut(&mut self.0)
3538 .expect("task value must be uniquely owned to mutate")
3539 .previous = PreviousTaskDataValue::new(data);
3540 }
3541
3542 pub(crate) fn data(&self) -> &Arc<TaskPostEvaluationData> {
3544 &self.0.data
3545 }
3546
3547 pub fn field(&self, version: SupportedVersion, name: &str) -> Option<Value> {
3551 match name {
3552 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.0.name.clone()).into()),
3553 TASK_FIELD_ID => Some(PrimitiveValue::String(self.0.id.clone()).into()),
3554 TASK_FIELD_ATTEMPT => Some(self.0.attempt.into()),
3555 TASK_FIELD_CONTAINER => Some(
3556 self.0
3557 .data
3558 .container
3559 .clone()
3560 .map(|c| PrimitiveValue::String(c).into())
3561 .unwrap_or_else(|| {
3562 Value::new_none(
3563 task_member_type_post_evaluation(version, TASK_FIELD_CONTAINER)
3564 .expect("failed to get task field type"),
3565 )
3566 }),
3567 ),
3568 TASK_FIELD_CPU => Some(self.0.data.cpu.into()),
3569 TASK_FIELD_MEMORY => Some(self.0.data.memory.into()),
3570 TASK_FIELD_GPU => Some(self.0.data.gpu.clone().into()),
3571 TASK_FIELD_FPGA => Some(self.0.data.fpga.clone().into()),
3572 TASK_FIELD_DISKS => Some(self.0.data.disks.clone().into()),
3573 TASK_FIELD_END_TIME => Some(self.0.end_time.map(Into::into).unwrap_or_else(|| {
3574 Value::new_none(
3575 task_member_type_post_evaluation(version, TASK_FIELD_END_TIME)
3576 .expect("failed to get task field type"),
3577 )
3578 })),
3579 TASK_FIELD_RETURN_CODE => Some(
3580 self.0
3581 .return_code
3582 .map(Into::into)
3583 .expect("task return codes can only be accessed after being set"),
3584 ),
3585 TASK_FIELD_META => Some(self.0.meta.clone().into()),
3586 TASK_FIELD_PARAMETER_META => Some(self.0.parameter_meta.clone().into()),
3587 TASK_FIELD_EXT => Some(self.0.ext.clone().into()),
3588 TASK_FIELD_MAX_RETRIES if version >= SupportedVersion::V1(V1::Three) => {
3589 Some(self.0.data.max_retries.into())
3590 }
3591 TASK_FIELD_PREVIOUS if version >= SupportedVersion::V1(V1::Three) => {
3592 Some(HiddenValue::PreviousTaskData(self.0.previous.clone()).into())
3593 }
3594 _ => None,
3595 }
3596 }
3597}
3598
3599#[derive(Debug, Clone)]
3603pub struct HintsValue(Object);
3604
3605impl HintsValue {
3606 pub fn new(members: IndexMap<String, Value>) -> Self {
3608 Self(Object::new(members))
3609 }
3610
3611 pub fn as_object(&self) -> &Object {
3613 &self.0
3614 }
3615}
3616
3617impl From<HintsValue> for Value {
3618 fn from(value: HintsValue) -> Self {
3619 Self::Hidden(HiddenValue::Hints(value))
3620 }
3621}
3622
3623impl fmt::Display for HintsValue {
3624 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3625 write!(f, "hints {{")?;
3626
3627 for (i, (k, v)) in self.0.iter().enumerate() {
3628 if i > 0 {
3629 write!(f, ", ")?;
3630 }
3631
3632 write!(f, "{k}: {v}")?;
3633 }
3634
3635 write!(f, "}}")
3636 }
3637}
3638
3639impl From<Object> for HintsValue {
3640 fn from(value: Object) -> Self {
3641 Self(value)
3642 }
3643}
3644
3645#[derive(Debug, Clone)]
3649pub struct InputValue(Object);
3650
3651impl InputValue {
3652 pub fn new(members: IndexMap<String, Value>) -> Self {
3654 Self(Object::new(members))
3655 }
3656
3657 pub fn as_object(&self) -> &Object {
3659 &self.0
3660 }
3661}
3662
3663impl From<InputValue> for Value {
3664 fn from(value: InputValue) -> Self {
3665 Self::Hidden(HiddenValue::Input(value))
3666 }
3667}
3668
3669impl fmt::Display for InputValue {
3670 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3671 write!(f, "input {{")?;
3672
3673 for (i, (k, v)) in self.0.iter().enumerate() {
3674 if i > 0 {
3675 write!(f, ", ")?;
3676 }
3677
3678 write!(f, "{k}: {v}")?;
3679 }
3680
3681 write!(f, "}}")
3682 }
3683}
3684
3685impl From<Object> for InputValue {
3686 fn from(value: Object) -> Self {
3687 Self(value)
3688 }
3689}
3690
3691#[derive(Debug, Clone)]
3695pub struct OutputValue(Object);
3696
3697impl OutputValue {
3698 pub fn new(members: IndexMap<String, Value>) -> Self {
3700 Self(Object::new(members))
3701 }
3702
3703 pub fn as_object(&self) -> &Object {
3705 &self.0
3706 }
3707}
3708
3709impl From<OutputValue> for Value {
3710 fn from(value: OutputValue) -> Self {
3711 Self::Hidden(HiddenValue::Output(value))
3712 }
3713}
3714
3715impl fmt::Display for OutputValue {
3716 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3717 write!(f, "output {{")?;
3718
3719 for (i, (k, v)) in self.0.iter().enumerate() {
3720 if i > 0 {
3721 write!(f, ", ")?;
3722 }
3723
3724 write!(f, "{k}: {v}")?;
3725 }
3726
3727 write!(f, "}}")
3728 }
3729}
3730
3731impl From<Object> for OutputValue {
3732 fn from(value: Object) -> Self {
3733 Self(value)
3734 }
3735}
3736
3737#[derive(Debug, Clone)]
3741pub struct CallValue {
3742 ty: Arc<CallType>,
3744 outputs: Arc<Outputs>,
3746}
3747
3748impl CallValue {
3749 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3752 Self {
3753 ty: Arc::new(ty),
3754 outputs,
3755 }
3756 }
3757
3758 pub fn ty(&self) -> &CallType {
3760 &self.ty
3761 }
3762
3763 pub fn outputs(&self) -> &Outputs {
3765 self.outputs.as_ref()
3766 }
3767}
3768
3769impl fmt::Display for CallValue {
3770 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3771 write!(f, "call output {{")?;
3772
3773 for (i, (k, v)) in self.outputs.iter().enumerate() {
3774 if i > 0 {
3775 write!(f, ", ")?;
3776 }
3777
3778 write!(f, "{k}: {v}")?;
3779 }
3780
3781 write!(f, "}}")
3782 }
3783}
3784
3785pub(crate) struct ValueSerializer<'a> {
3787 context: Option<&'a dyn EvaluationContext>,
3789 value: &'a Value,
3791 allow_pairs: bool,
3794}
3795
3796impl<'a> ValueSerializer<'a> {
3797 pub fn new(
3803 context: Option<&'a dyn EvaluationContext>,
3804 value: &'a Value,
3805 allow_pairs: bool,
3806 ) -> Self {
3807 Self {
3808 context,
3809 value,
3810 allow_pairs,
3811 }
3812 }
3813}
3814
3815impl serde::Serialize for ValueSerializer<'_> {
3816 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3817 where
3818 S: serde::Serializer,
3819 {
3820 use serde::ser::Error;
3821
3822 match &self.value {
3823 Value::None(_) => serializer.serialize_none(),
3824 Value::Primitive(v) => {
3825 PrimitiveValueSerializer::new(self.context, v).serialize(serializer)
3826 }
3827 Value::Compound(v) => CompoundValueSerializer::new(self.context, v, self.allow_pairs)
3828 .serialize(serializer),
3829 Value::Call(_) | Value::Hidden(_) | Value::TypeNameRef(_) => {
3830 Err(S::Error::custom("value cannot be serialized"))
3831 }
3832 }
3833 }
3834}
3835
3836pub(crate) struct PrimitiveValueSerializer<'a> {
3838 context: Option<&'a dyn EvaluationContext>,
3840 value: &'a PrimitiveValue,
3842}
3843
3844impl<'a> PrimitiveValueSerializer<'a> {
3845 pub fn new(context: Option<&'a dyn EvaluationContext>, value: &'a PrimitiveValue) -> Self {
3851 Self { context, value }
3852 }
3853}
3854
3855impl serde::Serialize for PrimitiveValueSerializer<'_> {
3856 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3857 where
3858 S: serde::Serializer,
3859 {
3860 match self.value {
3861 PrimitiveValue::Boolean(v) => v.serialize(serializer),
3862 PrimitiveValue::Integer(v) => v.serialize(serializer),
3863 PrimitiveValue::Float(v) => v.serialize(serializer),
3864 PrimitiveValue::String(s) => s.serialize(serializer),
3865 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3866 let path = self
3867 .context
3868 .and_then(|c| c.guest_path(p).map(|p| Cow::Owned(p.0)))
3869 .unwrap_or(Cow::Borrowed(&p.0));
3870
3871 path.serialize(serializer)
3872 }
3873 }
3874 }
3875}
3876
3877pub(crate) struct CompoundValueSerializer<'a> {
3879 context: Option<&'a dyn EvaluationContext>,
3881 value: &'a CompoundValue,
3883 allow_pairs: bool,
3886}
3887
3888impl<'a> CompoundValueSerializer<'a> {
3889 pub fn new(
3895 context: Option<&'a dyn EvaluationContext>,
3896 value: &'a CompoundValue,
3897 allow_pairs: bool,
3898 ) -> Self {
3899 Self {
3900 context,
3901 value,
3902 allow_pairs,
3903 }
3904 }
3905}
3906
3907impl serde::Serialize for CompoundValueSerializer<'_> {
3908 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3909 where
3910 S: serde::Serializer,
3911 {
3912 use serde::ser::Error;
3913
3914 match &self.value {
3915 CompoundValue::Pair(pair) if self.allow_pairs => {
3916 let mut map = serializer.serialize_map(Some(2))?;
3917 let left = ValueSerializer::new(self.context, pair.left(), self.allow_pairs);
3918 let right = ValueSerializer::new(self.context, pair.right(), self.allow_pairs);
3919 map.serialize_entry("left", &left)?;
3920 map.serialize_entry("right", &right)?;
3921 map.end()
3922 }
3923 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3924 CompoundValue::Array(v) => {
3925 let mut seq = serializer.serialize_seq(Some(v.len()))?;
3926 for v in v.as_slice() {
3927 seq.serialize_element(&ValueSerializer::new(
3928 self.context,
3929 v,
3930 self.allow_pairs,
3931 ))?;
3932 }
3933
3934 seq.end()
3935 }
3936 CompoundValue::Map(v) => {
3937 let mut map = serializer.serialize_map(Some(v.len()))?;
3938 for (k, v) in v.iter() {
3939 match k {
3940 PrimitiveValue::String(s) => {
3941 map.serialize_entry(
3942 s.as_str(),
3943 &ValueSerializer::new(self.context, v, self.allow_pairs),
3944 )?;
3945 }
3946 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3947 map.serialize_entry(
3948 p.as_str(),
3949 &ValueSerializer::new(self.context, v, self.allow_pairs),
3950 )?;
3951 }
3952 _ => {
3953 map.serialize_entry(
3955 &k.raw(None).to_string(),
3956 &ValueSerializer::new(self.context, v, self.allow_pairs),
3957 )?;
3958 }
3959 }
3960 }
3961
3962 map.end()
3963 }
3964 CompoundValue::Object(object) => {
3965 let mut map = serializer.serialize_map(Some(object.len()))?;
3966 for (k, v) in object.iter() {
3967 map.serialize_entry(
3968 k,
3969 &ValueSerializer::new(self.context, v, self.allow_pairs),
3970 )?;
3971 }
3972
3973 map.end()
3974 }
3975 CompoundValue::Struct(s) => {
3976 let mut map = serializer.serialize_map(Some(s.0.members.len()))?;
3977 for (k, v) in s.0.members.iter() {
3978 map.serialize_entry(
3979 k,
3980 &ValueSerializer::new(self.context, v, self.allow_pairs),
3981 )?;
3982 }
3983
3984 map.end()
3985 }
3986 CompoundValue::EnumChoice(e) => serializer.serialize_str(e.name()),
3987 }
3988 }
3989}
3990
3991#[cfg(test)]
3992mod test {
3993 use std::iter::empty;
3994
3995 use approx::assert_relative_eq;
3996 use pretty_assertions::assert_eq;
3997 use wdl_analysis::types::ArrayType;
3998 use wdl_analysis::types::MapType;
3999 use wdl_analysis::types::PairType;
4000 use wdl_analysis::types::StructType;
4001 use wdl_ast::Diagnostic;
4002 use wdl_ast::Span;
4003 use wdl_ast::SupportedVersion;
4004
4005 use super::*;
4006 use crate::EvaluationPath;
4007 use crate::http::Transferer;
4008
4009 #[test]
4010 fn boolean_coercion() {
4011 assert_eq!(
4013 Value::from(false)
4014 .coerce(None, &PrimitiveType::Boolean.into())
4015 .expect("should coerce")
4016 .unwrap_boolean(),
4017 Value::from(false).unwrap_boolean()
4018 );
4019 assert_eq!(
4021 format!(
4022 "{e:#}",
4023 e = Value::from(true)
4024 .coerce(None, &PrimitiveType::String.into())
4025 .unwrap_err()
4026 ),
4027 "cannot coerce type `Boolean` to type `String`"
4028 );
4029 }
4030
4031 #[test]
4032 fn boolean_display() {
4033 assert_eq!(Value::from(false).to_string(), "false");
4034 assert_eq!(Value::from(true).to_string(), "true");
4035 }
4036
4037 #[test]
4038 fn integer_coercion() {
4039 assert_eq!(
4041 Value::from(12345)
4042 .coerce(None, &PrimitiveType::Integer.into())
4043 .expect("should coerce")
4044 .unwrap_integer(),
4045 Value::from(12345).unwrap_integer()
4046 );
4047 assert_relative_eq!(
4049 Value::from(12345)
4050 .coerce(None, &PrimitiveType::Float.into())
4051 .expect("should coerce")
4052 .unwrap_float(),
4053 Value::from(12345.0).unwrap_float()
4054 );
4055 assert_eq!(
4057 format!(
4058 "{e:#}",
4059 e = Value::from(12345)
4060 .coerce(None, &PrimitiveType::Boolean.into())
4061 .unwrap_err()
4062 ),
4063 "cannot coerce type `Int` to type `Boolean`"
4064 );
4065 }
4066
4067 #[test]
4068 fn integer_display() {
4069 assert_eq!(Value::from(12345).to_string(), "12345");
4070 assert_eq!(Value::from(-12345).to_string(), "-12345");
4071 }
4072
4073 #[test]
4074 fn float_coercion() {
4075 assert_relative_eq!(
4077 Value::from(12345.0)
4078 .coerce(None, &PrimitiveType::Float.into())
4079 .expect("should coerce")
4080 .unwrap_float(),
4081 Value::from(12345.0).unwrap_float()
4082 );
4083 assert_eq!(
4085 format!(
4086 "{e:#}",
4087 e = Value::from(12345.0)
4088 .coerce(None, &PrimitiveType::Integer.into())
4089 .unwrap_err()
4090 ),
4091 "cannot coerce type `Float` to type `Int`"
4092 );
4093 }
4094
4095 #[test]
4096 fn float_display() {
4097 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
4098 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
4099 }
4100
4101 #[test]
4102 fn string_coercion() {
4103 let value = PrimitiveValue::new_string("foo");
4104 assert_eq!(
4106 value
4107 .coerce(None, &PrimitiveType::String.into())
4108 .expect("should coerce"),
4109 value
4110 );
4111 assert_eq!(
4113 value
4114 .coerce(None, &PrimitiveType::File.into())
4115 .expect("should coerce"),
4116 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
4117 );
4118 assert_eq!(
4120 value
4121 .coerce(None, &PrimitiveType::Directory.into())
4122 .expect("should coerce"),
4123 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
4124 );
4125 assert_eq!(
4127 format!(
4128 "{e:#}",
4129 e = value
4130 .coerce(None, &PrimitiveType::Boolean.into())
4131 .unwrap_err()
4132 ),
4133 "cannot coerce type `String` to type `Boolean`"
4134 );
4135
4136 struct Context;
4137
4138 impl EvaluationContext for Context {
4139 fn version(&self) -> SupportedVersion {
4140 unimplemented!()
4141 }
4142
4143 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4144 unimplemented!()
4145 }
4146
4147 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4148 unimplemented!()
4149 }
4150
4151 fn enum_choice_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4152 unimplemented!()
4153 }
4154
4155 fn base_dir(&self) -> &EvaluationPath {
4156 unimplemented!()
4157 }
4158
4159 fn temp_dir(&self) -> &Path {
4160 unimplemented!()
4161 }
4162
4163 fn transferer(&self) -> &dyn Transferer {
4164 unimplemented!()
4165 }
4166
4167 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
4168 if path.as_str() == "/mnt/task/input/0/path" {
4169 Some(HostPath::new("/some/host/path"))
4170 } else {
4171 None
4172 }
4173 }
4174 }
4175
4176 assert_eq!(
4178 PrimitiveValue::new_string("/mnt/task/input/0/path")
4179 .coerce(Some(&Context), &PrimitiveType::File.into())
4180 .expect("should coerce")
4181 .unwrap_file()
4182 .as_str(),
4183 "/some/host/path"
4184 );
4185
4186 assert_eq!(
4188 value
4189 .coerce(Some(&Context), &PrimitiveType::File.into())
4190 .expect("should coerce")
4191 .unwrap_file()
4192 .as_str(),
4193 "foo"
4194 );
4195
4196 assert_eq!(
4198 PrimitiveValue::new_string("/mnt/task/input/0/path")
4199 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4200 .expect("should coerce")
4201 .unwrap_directory()
4202 .as_str(),
4203 "/some/host/path"
4204 );
4205
4206 assert_eq!(
4208 value
4209 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4210 .expect("should coerce")
4211 .unwrap_directory()
4212 .as_str(),
4213 "foo"
4214 );
4215 }
4216
4217 #[test]
4218 fn string_display() {
4219 let value = PrimitiveValue::new_string("hello world!");
4220 assert_eq!(value.to_string(), "\"hello world!\"");
4221 }
4222
4223 #[test]
4224 fn string_display_escapes_special_characters() {
4225 let value = PrimitiveValue::new_string(
4226 "\u{1b}[31m${name} ~{color} \"quoted\" \\\\ tab\tline\ncarriage\r$HOME ~user",
4227 );
4228 assert_eq!(
4229 value.to_string(),
4230 r#""\x1B[31m\${name} \~{color} \"quoted\" \\\\ tab\tline\ncarriage\r$HOME ~user""#
4231 );
4232 }
4233
4234 #[test]
4235 fn file_coercion() {
4236 let value = PrimitiveValue::new_file("foo");
4237
4238 assert_eq!(
4240 value
4241 .coerce(None, &PrimitiveType::File.into())
4242 .expect("should coerce"),
4243 value
4244 );
4245 assert_eq!(
4247 value
4248 .coerce(None, &PrimitiveType::String.into())
4249 .expect("should coerce"),
4250 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
4251 );
4252 assert_eq!(
4254 format!(
4255 "{e:#}",
4256 e = value
4257 .coerce(None, &PrimitiveType::Directory.into())
4258 .unwrap_err()
4259 ),
4260 "cannot coerce type `File` to type `Directory`"
4261 );
4262
4263 struct Context;
4264
4265 impl EvaluationContext for Context {
4266 fn version(&self) -> SupportedVersion {
4267 unimplemented!()
4268 }
4269
4270 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4271 unimplemented!()
4272 }
4273
4274 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4275 unimplemented!()
4276 }
4277
4278 fn enum_choice_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4279 unimplemented!()
4280 }
4281
4282 fn base_dir(&self) -> &EvaluationPath {
4283 unimplemented!()
4284 }
4285
4286 fn temp_dir(&self) -> &Path {
4287 unimplemented!()
4288 }
4289
4290 fn transferer(&self) -> &dyn Transferer {
4291 unimplemented!()
4292 }
4293
4294 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4295 if path.as_str() == "/some/host/path" {
4296 Some(GuestPath::new("/mnt/task/input/0/path"))
4297 } else {
4298 None
4299 }
4300 }
4301 }
4302
4303 assert_eq!(
4305 PrimitiveValue::new_file("/some/host/path")
4306 .coerce(Some(&Context), &PrimitiveType::String.into())
4307 .expect("should coerce")
4308 .unwrap_string()
4309 .as_str(),
4310 "/mnt/task/input/0/path"
4311 );
4312
4313 assert_eq!(
4315 value
4316 .coerce(Some(&Context), &PrimitiveType::String.into())
4317 .expect("should coerce")
4318 .unwrap_string()
4319 .as_str(),
4320 "foo"
4321 );
4322 }
4323
4324 #[test]
4325 fn file_display() {
4326 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
4327 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
4328 }
4329
4330 #[test]
4331 fn directory_coercion() {
4332 let value = PrimitiveValue::new_directory("foo");
4333
4334 assert_eq!(
4336 value
4337 .coerce(None, &PrimitiveType::Directory.into())
4338 .expect("should coerce"),
4339 value
4340 );
4341 assert_eq!(
4343 value
4344 .coerce(None, &PrimitiveType::String.into())
4345 .expect("should coerce"),
4346 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
4347 );
4348 assert_eq!(
4350 format!(
4351 "{e:#}",
4352 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
4353 ),
4354 "cannot coerce type `Directory` to type `File`"
4355 );
4356
4357 struct Context;
4358
4359 impl EvaluationContext for Context {
4360 fn version(&self) -> SupportedVersion {
4361 unimplemented!()
4362 }
4363
4364 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4365 unimplemented!()
4366 }
4367
4368 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4369 unimplemented!()
4370 }
4371
4372 fn enum_choice_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4373 unimplemented!()
4374 }
4375
4376 fn base_dir(&self) -> &EvaluationPath {
4377 unimplemented!()
4378 }
4379
4380 fn temp_dir(&self) -> &Path {
4381 unimplemented!()
4382 }
4383
4384 fn transferer(&self) -> &dyn Transferer {
4385 unimplemented!()
4386 }
4387
4388 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4389 if path.as_str() == "/some/host/path" {
4390 Some(GuestPath::new("/mnt/task/input/0/path"))
4391 } else {
4392 None
4393 }
4394 }
4395 }
4396
4397 assert_eq!(
4399 PrimitiveValue::new_directory("/some/host/path")
4400 .coerce(Some(&Context), &PrimitiveType::String.into())
4401 .expect("should coerce")
4402 .unwrap_string()
4403 .as_str(),
4404 "/mnt/task/input/0/path"
4405 );
4406
4407 assert_eq!(
4409 value
4410 .coerce(Some(&Context), &PrimitiveType::String.into())
4411 .expect("should coerce")
4412 .unwrap_string()
4413 .as_str(),
4414 "foo"
4415 );
4416 }
4417
4418 #[test]
4419 fn directory_display() {
4420 let value = PrimitiveValue::new_directory("/foo/bar/baz");
4421 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
4422 }
4423
4424 #[test]
4425 fn none_coercion() {
4426 assert!(
4428 Value::new_none(Type::None)
4429 .coerce(None, &Type::from(PrimitiveType::String).optional())
4430 .expect("should coerce")
4431 .is_none(),
4432 );
4433
4434 assert_eq!(
4436 format!(
4437 "{e:#}",
4438 e = Value::new_none(Type::None)
4439 .coerce(None, &PrimitiveType::String.into())
4440 .unwrap_err()
4441 ),
4442 "cannot coerce `None` to non-optional type `String`"
4443 );
4444 }
4445
4446 #[test]
4447 fn none_display() {
4448 assert_eq!(Value::new_none(Type::None).to_string(), "None");
4449 }
4450
4451 #[test]
4452 fn array_coercion() {
4453 let src_ty = ArrayType::new(PrimitiveType::Integer);
4454 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
4455
4456 let src: CompoundValue = Array::new(src_ty, [1, 2, 3])
4458 .expect("should create array value")
4459 .into();
4460 let target = src.coerce(None, &target_ty).expect("should coerce");
4461 assert_eq!(
4462 target.unwrap_array().to_string(),
4463 "[1.000000, 2.000000, 3.000000]"
4464 );
4465
4466 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
4468 assert_eq!(
4469 format!("{e:#}", e = src.coerce(None, &target_ty).unwrap_err()),
4470 "failed to coerce array element at index 0: cannot coerce type `Int` to type `String`"
4471 );
4472 }
4473
4474 #[test]
4475 fn non_empty_array_coercion() {
4476 let ty = ArrayType::new(PrimitiveType::String);
4477 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
4478
4479 let string = PrimitiveValue::new_string("foo");
4481 let value: Value = Array::new(ty.clone(), [string])
4482 .expect("should create array")
4483 .into();
4484 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
4485
4486 let value: Value = Array::new::<Value>(ty, [])
4488 .expect("should create array")
4489 .into();
4490 assert_eq!(
4491 format!("{e:#}", e = value.coerce(None, &target_ty).unwrap_err()),
4492 "cannot coerce empty array value to non-empty array type `Array[String]+`"
4493 );
4494 }
4495
4496 #[test]
4497 fn array_display() {
4498 let ty = ArrayType::new(PrimitiveType::Integer);
4499 let value: Value = Array::new(ty, [1, 2, 3])
4500 .expect("should create array")
4501 .into();
4502
4503 assert_eq!(value.to_string(), "[1, 2, 3]");
4504 }
4505
4506 #[test]
4507 fn map_coerce() {
4508 let key1 = PrimitiveValue::new_file("foo");
4509 let value1 = PrimitiveValue::new_string("bar");
4510 let key2 = PrimitiveValue::new_file("baz");
4511 let value2 = PrimitiveValue::new_string("qux");
4512
4513 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
4514 let file_to_string: Value = Map::new(ty, [(key1, value1), (key2, value2)])
4515 .expect("should create map value")
4516 .into();
4517
4518 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
4520 let string_to_file = file_to_string
4521 .coerce(None, &ty)
4522 .expect("value should coerce");
4523 assert_eq!(
4524 string_to_file.to_string(),
4525 r#"{"foo": "bar", "baz": "qux"}"#
4526 );
4527
4528 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
4530 assert_eq!(
4531 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4532 "failed to coerce map key for element at index 0: cannot coerce type `String` to type \
4533 `Int`"
4534 );
4535
4536 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
4538 assert_eq!(
4539 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4540 "failed to coerce map value for element at index 0: cannot coerce type `File` to type \
4541 `Int`"
4542 );
4543
4544 let ty = StructType::new(
4546 "Foo",
4547 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
4548 )
4549 .into();
4550 let struct_value = string_to_file
4551 .coerce(None, &ty)
4552 .expect("value should coerce");
4553 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4554
4555 let ty = StructType::new(
4557 "Foo",
4558 [
4559 ("foo", PrimitiveType::String),
4560 ("baz", PrimitiveType::String),
4561 ],
4562 )
4563 .into();
4564 let struct_value = file_to_string
4565 .coerce(None, &ty)
4566 .expect("value should coerce");
4567 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4568
4569 let ty = StructType::new(
4571 "Foo",
4572 [
4573 ("foo", PrimitiveType::File),
4574 ("baz", PrimitiveType::File),
4575 ("qux", PrimitiveType::File),
4576 ],
4577 )
4578 .into();
4579 assert_eq!(
4580 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4581 "cannot coerce a map of 2 elements to an instance of struct `Foo` as the struct has 3 \
4582 members"
4583 );
4584
4585 let object_value = string_to_file
4587 .coerce(None, &Type::Object)
4588 .expect("value should coerce");
4589 assert_eq!(
4590 object_value.to_string(),
4591 r#"object {foo: "bar", baz: "qux"}"#
4592 );
4593
4594 let object_value = file_to_string
4596 .coerce(None, &Type::Object)
4597 .expect("value should coerce");
4598 assert_eq!(
4599 object_value.to_string(),
4600 r#"object {foo: "bar", baz: "qux"}"#
4601 );
4602 }
4603
4604 #[test]
4605 fn map_display() {
4606 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4607 let value: Value = Map::new(ty, [(1, true), (2, false)])
4608 .expect("should create map value")
4609 .into();
4610 assert_eq!(value.to_string(), "{1: true, 2: false}");
4611 }
4612
4613 #[test]
4614 fn pair_coercion() {
4615 let left = PrimitiveValue::new_file("foo");
4616 let right = PrimitiveValue::new_string("bar");
4617
4618 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4619 let value: Value = Pair::new(ty, left, right)
4620 .expect("should create pair value")
4621 .into();
4622
4623 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
4625 let value = value.coerce(None, &ty).expect("value should coerce");
4626 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
4627
4628 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
4630 assert_eq!(
4631 format!("{e:#}", e = value.coerce(None, &ty).unwrap_err()),
4632 "failed to coerce pair's left value: cannot coerce type `String` to type `Int`"
4633 );
4634 }
4635
4636 #[test]
4637 fn pair_display() {
4638 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4639 let value: Value = Pair::new(ty, 12345, false)
4640 .expect("should create pair value")
4641 .into();
4642 assert_eq!(value.to_string(), "(12345, false)");
4643 }
4644
4645 #[test]
4646 fn struct_coercion() {
4647 let ty = StructType::new(
4648 "Foo",
4649 [
4650 ("foo", PrimitiveType::Float),
4651 ("bar", PrimitiveType::Float),
4652 ("baz", PrimitiveType::Float),
4653 ],
4654 );
4655 let value: Value = Struct::new(ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
4656 .expect("should create map value")
4657 .into();
4658
4659 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
4661 let map_value = value.coerce(None, &ty).expect("value should coerce");
4662 assert_eq!(
4663 map_value.to_string(),
4664 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4665 );
4666
4667 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
4669 let map_value = value.coerce(None, &ty).expect("value should coerce");
4670 assert_eq!(
4671 map_value.to_string(),
4672 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4673 );
4674
4675 let ty = StructType::new(
4677 "Bar",
4678 [
4679 ("foo", PrimitiveType::Float),
4680 ("bar", PrimitiveType::Float),
4681 ("baz", PrimitiveType::Float),
4682 ],
4683 )
4684 .into();
4685 let struct_value = value.coerce(None, &ty).expect("value should coerce");
4686 assert_eq!(
4687 struct_value.to_string(),
4688 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4689 );
4690
4691 let object_value = value
4693 .coerce(None, &Type::Object)
4694 .expect("value should coerce");
4695 assert_eq!(
4696 object_value.to_string(),
4697 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4698 );
4699 }
4700
4701 #[test]
4702 fn struct_display() {
4703 let ty = StructType::new(
4704 "Foo",
4705 [
4706 ("foo", PrimitiveType::Float),
4707 ("bar", PrimitiveType::String),
4708 ("baz", PrimitiveType::Integer),
4709 ],
4710 );
4711 let value: Value = Struct::new(
4712 ty,
4713 [
4714 ("foo", Value::from(1.101)),
4715 ("bar", PrimitiveValue::new_string("foo").into()),
4716 ("baz", 1234.into()),
4717 ],
4718 )
4719 .expect("should create map value")
4720 .into();
4721 assert_eq!(
4722 value.to_string(),
4723 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
4724 );
4725 }
4726
4727 #[test]
4728 fn pair_serialization() {
4729 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4730 let pair: Value = Pair::new(
4731 pair_ty,
4732 PrimitiveValue::new_file("foo"),
4733 PrimitiveValue::new_string("bar"),
4734 )
4735 .expect("should create pair value")
4736 .into();
4737 let value_serializer = ValueSerializer::new(None, &pair, true);
4739 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4740 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
4741
4742 let value_serializer = ValueSerializer::new(None, &pair, false);
4744 assert!(serde_json::to_string(&value_serializer).is_err());
4745
4746 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
4747 let array: Value = Array::new(array_ty, [pair])
4748 .expect("should create array value")
4749 .into();
4750
4751 let value_serializer = ValueSerializer::new(None, &array, true);
4753 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4754 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
4755 }
4756
4757 #[test]
4758 fn type_name_ref_equality() {
4759 use wdl_analysis::types::EnumType;
4760
4761 let enum_type = Type::Compound(
4762 CompoundType::Custom(CustomType::Enum(
4763 EnumType::new(
4764 "MyEnum",
4765 Span::new(0, 0),
4766 Type::Primitive(PrimitiveType::Integer, false),
4767 Vec::<(String, Type)>::new(),
4768 &[],
4769 )
4770 .expect("should create enum type"),
4771 )),
4772 false,
4773 );
4774
4775 let value1 = Value::TypeNameRef(TypeNameRefValue::new(enum_type.clone()));
4776 let value2 = Value::TypeNameRef(TypeNameRefValue::new(enum_type.clone()));
4777
4778 assert_eq!(value1.ty(), value2.ty());
4779 }
4780
4781 #[test]
4782 fn type_name_ref_ty() {
4783 let struct_type = Type::Compound(
4784 CompoundType::Custom(CustomType::Struct(StructType::new(
4785 "MyStruct",
4786 empty::<(&str, Type)>(),
4787 ))),
4788 false,
4789 );
4790
4791 let value = Value::TypeNameRef(TypeNameRefValue::new(struct_type.clone()));
4792 assert_eq!(value.ty(), struct_type);
4793 }
4794
4795 #[test]
4796 fn type_name_ref_display() {
4797 use wdl_analysis::types::EnumType;
4798
4799 let enum_type = Type::Compound(
4800 CompoundType::Custom(CustomType::Enum(
4801 EnumType::new(
4802 "Color",
4803 Span::new(0, 0),
4804 Type::Primitive(PrimitiveType::Integer, false),
4805 Vec::<(String, Type)>::new(),
4806 &[],
4807 )
4808 .expect("should create enum type"),
4809 )),
4810 false,
4811 );
4812
4813 let value = Value::TypeNameRef(TypeNameRefValue::new(enum_type));
4814 assert_eq!(value.to_string(), "Color");
4815 }
4816}