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