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::sync::Arc;
10
11use anyhow::Context;
12use anyhow::Result;
13use anyhow::anyhow;
14use anyhow::bail;
15use futures::FutureExt;
16use futures::StreamExt as _;
17use futures::TryStreamExt as _;
18use futures::future::BoxFuture;
19use indexmap::IndexMap;
20use itertools::Either;
21use ordered_float::OrderedFloat;
22use serde::ser::SerializeMap;
23use serde::ser::SerializeSeq;
24use url::Url;
25use wdl_analysis::stdlib::STDLIB as ANALYSIS_STDLIB;
26use wdl_analysis::types::ArrayType;
27use wdl_analysis::types::CallType;
28use wdl_analysis::types::Coercible as _;
29use wdl_analysis::types::CompoundType;
30use wdl_analysis::types::CustomType;
31use wdl_analysis::types::EnumType;
32use wdl_analysis::types::HiddenType;
33use wdl_analysis::types::MapType;
34use wdl_analysis::types::Optional;
35use wdl_analysis::types::PairType;
36use wdl_analysis::types::PrimitiveType;
37use wdl_analysis::types::StructType;
38use wdl_analysis::types::Type;
39use wdl_analysis::types::v1::task_member_type_post_evaluation;
40use wdl_ast::AstToken;
41use wdl_ast::SupportedVersion;
42use wdl_ast::TreeNode;
43use wdl_ast::v1;
44use wdl_ast::v1::TASK_FIELD_ATTEMPT;
45use wdl_ast::v1::TASK_FIELD_CONTAINER;
46use wdl_ast::v1::TASK_FIELD_CPU;
47use wdl_ast::v1::TASK_FIELD_DISKS;
48use wdl_ast::v1::TASK_FIELD_END_TIME;
49use wdl_ast::v1::TASK_FIELD_EXT;
50use wdl_ast::v1::TASK_FIELD_FPGA;
51use wdl_ast::v1::TASK_FIELD_GPU;
52use wdl_ast::v1::TASK_FIELD_ID;
53use wdl_ast::v1::TASK_FIELD_MAX_RETRIES;
54use wdl_ast::v1::TASK_FIELD_MEMORY;
55use wdl_ast::v1::TASK_FIELD_META;
56use wdl_ast::v1::TASK_FIELD_NAME;
57use wdl_ast::v1::TASK_FIELD_PARAMETER_META;
58use wdl_ast::v1::TASK_FIELD_PREVIOUS;
59use wdl_ast::v1::TASK_FIELD_RETURN_CODE;
60use wdl_ast::version::V1;
61
62use crate::EvaluationContext;
63use crate::EvaluationPath;
64use crate::Outputs;
65use crate::backend::TaskExecutionConstraints;
66use crate::http::Transferer;
67use crate::path;
68
69#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
74pub struct HostPath(pub Arc<String>);
75
76impl HostPath {
77 pub fn new(path: impl Into<String>) -> Self {
79 Self(Arc::new(path.into()))
80 }
81
82 pub fn as_str(&self) -> &str {
84 &self.0
85 }
86
87 pub fn expand(&self, base_dir: &EvaluationPath) -> Result<Self> {
91 let shell_expanded = shellexpand::full(self.as_str()).with_context(|| {
93 format!("failed to shell-expand path `{path}`", path = self.as_str())
94 })?;
95
96 if path::is_supported_url(&shell_expanded) {
98 Ok(Self::new(shell_expanded))
99 } else {
100 Ok(Self::new(base_dir.join(&shell_expanded)?.to_string()))
102 }
103 }
104
105 pub fn is_relative(&self) -> bool {
107 !path::is_supported_url(&self.0) && Path::new(self.0.as_str()).is_relative()
108 }
109}
110
111impl fmt::Display for HostPath {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 self.0.fmt(f)
114 }
115}
116
117impl From<Arc<String>> for HostPath {
118 fn from(path: Arc<String>) -> Self {
119 Self(path)
120 }
121}
122
123impl From<HostPath> for Arc<String> {
124 fn from(path: HostPath) -> Self {
125 path.0
126 }
127}
128
129impl From<String> for HostPath {
130 fn from(s: String) -> Self {
131 Arc::new(s).into()
132 }
133}
134
135impl<'a> From<&'a str> for HostPath {
136 fn from(s: &'a str) -> Self {
137 s.to_string().into()
138 }
139}
140
141impl From<url::Url> for HostPath {
142 fn from(url: url::Url) -> Self {
143 url.as_str().into()
144 }
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
151pub struct GuestPath(pub Arc<String>);
152
153impl GuestPath {
154 pub fn new(path: impl Into<String>) -> Self {
156 Self(Arc::new(path.into()))
157 }
158
159 pub fn as_str(&self) -> &str {
161 &self.0
162 }
163}
164
165impl fmt::Display for GuestPath {
166 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
167 self.0.fmt(f)
168 }
169}
170
171impl From<Arc<String>> for GuestPath {
172 fn from(path: Arc<String>) -> Self {
173 Self(path)
174 }
175}
176
177impl From<GuestPath> for Arc<String> {
178 fn from(path: GuestPath) -> Self {
179 path.0
180 }
181}
182
183pub(crate) trait Coercible: Sized {
185 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self>;
193}
194
195#[derive(Debug, Clone)]
199pub enum Value {
200 None(Type),
204 Primitive(PrimitiveValue),
206 Compound(CompoundValue),
208 Hidden(HiddenValue),
213 Call(CallValue),
215 TypeNameRef(Type),
217}
218
219impl Value {
220 pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
226 match value {
227 v1::MetadataValue::Boolean(v) => v.value().into(),
228 v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
229 v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
230 v1::MetadataValue::String(v) => PrimitiveValue::new_string(
231 v.text()
232 .expect("metadata strings shouldn't have placeholders")
233 .text(),
234 )
235 .into(),
236 v1::MetadataValue::Null(_) => Self::new_none(Type::None),
237 v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
238 v1::MetadataValue::Array(a) => Array::new_unchecked(
239 ANALYSIS_STDLIB.array_object_type().clone(),
240 a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
241 )
242 .into(),
243 }
244 }
245
246 pub fn new_none(ty: Type) -> Self {
252 assert!(ty.is_optional(), "the provided `None` type is not optional");
253 Self::None(ty)
254 }
255
256 pub fn ty(&self) -> Type {
258 match self {
259 Self::None(ty) => ty.clone(),
260 Self::Primitive(v) => v.ty(),
261 Self::Compound(v) => v.ty(),
262 Self::Hidden(v) => v.ty(),
263 Self::Call(v) => Type::Call(v.ty.clone()),
264 Self::TypeNameRef(ty) => ty.clone(),
265 }
266 }
267
268 pub fn is_none(&self) -> bool {
270 matches!(self, Self::None(_))
271 }
272
273 pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
277 match self {
278 Self::Primitive(v) => Some(v),
279 _ => None,
280 }
281 }
282
283 pub fn as_compound(&self) -> Option<&CompoundValue> {
287 match self {
288 Self::Compound(v) => Some(v),
289 _ => None,
290 }
291 }
292
293 pub fn as_boolean(&self) -> Option<bool> {
297 match self {
298 Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
299 _ => None,
300 }
301 }
302
303 pub fn unwrap_boolean(self) -> bool {
309 match self {
310 Self::Primitive(PrimitiveValue::Boolean(v)) => v,
311 _ => panic!("value is not a boolean"),
312 }
313 }
314
315 pub fn as_integer(&self) -> Option<i64> {
319 match self {
320 Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
321 _ => None,
322 }
323 }
324
325 pub fn unwrap_integer(self) -> i64 {
331 match self {
332 Self::Primitive(PrimitiveValue::Integer(v)) => v,
333 _ => panic!("value is not an integer"),
334 }
335 }
336
337 pub fn as_float(&self) -> Option<f64> {
341 match self {
342 Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
343 _ => None,
344 }
345 }
346
347 pub fn unwrap_float(self) -> f64 {
353 match self {
354 Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
355 _ => panic!("value is not a float"),
356 }
357 }
358
359 pub fn as_string(&self) -> Option<&Arc<String>> {
363 match self {
364 Self::Primitive(PrimitiveValue::String(s)) => Some(s),
365 _ => None,
366 }
367 }
368
369 pub fn unwrap_string(self) -> Arc<String> {
375 match self {
376 Self::Primitive(PrimitiveValue::String(s)) => s,
377 _ => panic!("value is not a string"),
378 }
379 }
380
381 pub fn as_file(&self) -> Option<&HostPath> {
385 match self {
386 Self::Primitive(PrimitiveValue::File(p)) => Some(p),
387 _ => None,
388 }
389 }
390
391 pub fn unwrap_file(self) -> HostPath {
397 match self {
398 Self::Primitive(PrimitiveValue::File(p)) => p,
399 _ => panic!("value is not a file"),
400 }
401 }
402
403 pub fn as_directory(&self) -> Option<&HostPath> {
407 match self {
408 Self::Primitive(PrimitiveValue::Directory(p)) => Some(p),
409 _ => None,
410 }
411 }
412
413 pub fn unwrap_directory(self) -> HostPath {
419 match self {
420 Self::Primitive(PrimitiveValue::Directory(p)) => p,
421 _ => panic!("value is not a directory"),
422 }
423 }
424
425 pub fn as_pair(&self) -> Option<&Pair> {
429 match self {
430 Self::Compound(CompoundValue::Pair(v)) => Some(v),
431 _ => None,
432 }
433 }
434
435 pub fn unwrap_pair(self) -> Pair {
441 match self {
442 Self::Compound(CompoundValue::Pair(v)) => v,
443 _ => panic!("value is not a pair"),
444 }
445 }
446
447 pub fn as_array(&self) -> Option<&Array> {
451 match self {
452 Self::Compound(CompoundValue::Array(v)) => Some(v),
453 _ => None,
454 }
455 }
456
457 pub fn unwrap_array(self) -> Array {
463 match self {
464 Self::Compound(CompoundValue::Array(v)) => v,
465 _ => panic!("value is not an array"),
466 }
467 }
468
469 pub fn as_map(&self) -> Option<&Map> {
473 match self {
474 Self::Compound(CompoundValue::Map(v)) => Some(v),
475 _ => None,
476 }
477 }
478
479 pub fn unwrap_map(self) -> Map {
485 match self {
486 Self::Compound(CompoundValue::Map(v)) => v,
487 _ => panic!("value is not a map"),
488 }
489 }
490
491 pub fn as_object(&self) -> Option<&Object> {
495 match self {
496 Self::Compound(CompoundValue::Object(v)) => Some(v),
497 _ => None,
498 }
499 }
500
501 pub fn unwrap_object(self) -> Object {
507 match self {
508 Self::Compound(CompoundValue::Object(v)) => v,
509 _ => panic!("value is not an object"),
510 }
511 }
512
513 pub fn as_struct(&self) -> Option<&Struct> {
517 match self {
518 Self::Compound(CompoundValue::Struct(v)) => Some(v),
519 _ => None,
520 }
521 }
522
523 pub fn unwrap_struct(self) -> Struct {
529 match self {
530 Self::Compound(CompoundValue::Struct(v)) => v,
531 _ => panic!("value is not a struct"),
532 }
533 }
534
535 pub fn as_task_pre_evaluation(&self) -> Option<&TaskPreEvaluationValue> {
539 match self {
540 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => Some(v),
541 _ => None,
542 }
543 }
544
545 pub fn unwrap_task_pre_evaluation(self) -> TaskPreEvaluationValue {
551 match self {
552 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => v,
553 _ => panic!("value is not a pre-evaluation task"),
554 }
555 }
556
557 pub fn as_task_post_evaluation(&self) -> Option<&TaskPostEvaluationValue> {
561 match self {
562 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
563 _ => None,
564 }
565 }
566
567 pub(crate) fn as_task_post_evaluation_mut(&mut self) -> Option<&mut TaskPostEvaluationValue> {
571 match self {
572 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
573 _ => None,
574 }
575 }
576
577 pub fn unwrap_task_post_evaluation(self) -> TaskPostEvaluationValue {
583 match self {
584 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => v,
585 _ => panic!("value is not a post-evaluation task"),
586 }
587 }
588
589 pub fn as_hints(&self) -> Option<&HintsValue> {
593 match self {
594 Self::Hidden(HiddenValue::Hints(v)) => Some(v),
595 _ => None,
596 }
597 }
598
599 pub fn unwrap_hints(self) -> HintsValue {
605 match self {
606 Self::Hidden(HiddenValue::Hints(v)) => v,
607 _ => panic!("value is not a hints value"),
608 }
609 }
610
611 pub fn as_call(&self) -> Option<&CallValue> {
615 match self {
616 Self::Call(v) => Some(v),
617 _ => None,
618 }
619 }
620
621 pub fn unwrap_call(self) -> CallValue {
627 match self {
628 Self::Call(v) => v,
629 _ => panic!("value is not a call value"),
630 }
631 }
632
633 pub(crate) fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
638 where
639 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
640 {
641 match self {
642 Self::Primitive(PrimitiveValue::File(path)) => cb(true, path),
643 Self::Primitive(PrimitiveValue::Directory(path)) => cb(false, path),
644 Self::Compound(v) => v.visit_paths(cb),
645 _ => Ok(()),
646 }
647 }
648
649 pub(crate) async fn resolve_paths<F>(
668 &self,
669 optional: bool,
670 base_dir: Option<&Path>,
671 transferer: Option<&dyn Transferer>,
672 translate: &F,
673 ) -> Result<Self>
674 where
675 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
676 {
677 fn new_file_or_directory(is_file: bool, path: impl Into<HostPath>) -> PrimitiveValue {
678 if is_file {
679 PrimitiveValue::File(path.into())
680 } else {
681 PrimitiveValue::Directory(path.into())
682 }
683 }
684
685 match self {
686 Self::Primitive(v @ PrimitiveValue::File(path))
687 | Self::Primitive(v @ PrimitiveValue::Directory(path)) => {
688 let is_file = v.as_file().is_some();
691 let path = translate(path)?;
692
693 if path::is_file_url(path.as_str()) {
694 let exists = path
697 .as_str()
698 .parse::<Url>()
699 .ok()
700 .and_then(|url| url.to_file_path().ok())
701 .map(|p| p.exists())
702 .unwrap_or(false);
703 if exists {
704 let v = new_file_or_directory(is_file, path);
705 return Ok(Self::Primitive(v));
706 }
707
708 if optional && !exists {
709 return Ok(Value::new_none(self.ty().optional()));
710 }
711
712 bail!("path `{path}` does not exist");
713 } else if path::is_supported_url(path.as_str()) {
714 match transferer {
715 Some(transferer) => {
716 let exists = transferer
717 .exists(
718 &path
719 .as_str()
720 .parse()
721 .with_context(|| format!("invalid URL `{path}`"))?,
722 )
723 .await?;
724 if exists {
725 let v = new_file_or_directory(is_file, path);
726 return Ok(Self::Primitive(v));
727 }
728
729 if optional && !exists {
730 return Ok(Value::new_none(self.ty().optional()));
731 }
732
733 bail!("URL `{path}` does not exist");
734 }
735 None => {
736 let v = new_file_or_directory(is_file, path);
738 return Ok(Self::Primitive(v));
739 }
740 }
741 }
742
743 let exists_path: Cow<'_, Path> = base_dir
745 .map(|d| d.join(path.as_str()).into())
746 .unwrap_or_else(|| Path::new(path.as_str()).into());
747 if is_file && !exists_path.is_file() {
748 if optional {
749 return Ok(Value::new_none(self.ty().optional()));
750 } else {
751 bail!("file `{}` does not exist", exists_path.display());
752 }
753 } else if !is_file && !exists_path.is_dir() {
754 if optional {
755 return Ok(Value::new_none(self.ty().optional()));
756 } else {
757 bail!("directory `{}` does not exist", exists_path.display())
758 }
759 }
760
761 let v = new_file_or_directory(is_file, path);
762 Ok(Self::Primitive(v))
763 }
764 Self::Compound(v) => Ok(Self::Compound(
765 v.resolve_paths(base_dir, transferer, translate)
766 .boxed()
767 .await?,
768 )),
769 v => Ok(v.clone()),
770 }
771 }
772
773 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
778 match (left, right) {
779 (Value::None(_), Value::None(_)) => Some(true),
780 (Value::None(_), _) | (_, Value::None(_)) => Some(false),
781 (Value::Primitive(left), Value::Primitive(right)) => {
782 Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
783 }
784 (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
785 _ => None,
786 }
787 }
788}
789
790impl fmt::Display for Value {
791 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
792 match self {
793 Self::None(_) => write!(f, "None"),
794 Self::Primitive(v) => v.fmt(f),
795 Self::Compound(v) => v.fmt(f),
796 Self::Hidden(v) => v.fmt(f),
797 Self::Call(c) => c.fmt(f),
798 Self::TypeNameRef(ty) => ty.fmt(f),
799 }
800 }
801}
802
803impl Coercible for Value {
804 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
805 if target.is_union() || target.is_none() || self.ty().eq(target) {
806 return Ok(self.clone());
807 }
808
809 match self {
810 Self::None(_) => {
811 if target.is_optional() {
812 Ok(Self::new_none(target.clone()))
813 } else {
814 bail!("cannot coerce `None` to non-optional type `{target}`");
815 }
816 }
817 Self::Primitive(PrimitiveValue::String(s)) if target.as_enum().is_some() => {
819 let enum_ty = target.as_enum().unwrap();
821
822 if enum_ty
823 .variants()
824 .iter()
825 .any(|variant_name| variant_name == s.as_str())
826 {
827 if let Some(context) = context {
828 if let Ok(value) = context.enum_variant_value(enum_ty.name(), s) {
829 return Ok(Value::Compound(CompoundValue::EnumVariant(
830 EnumVariant::new(enum_ty.clone(), s.as_str(), value),
831 )));
832 } else {
833 bail!(
834 "enum variant value lookup failed for variant `{s}` in enum `{}`",
835 enum_ty.name()
836 );
837 }
838 } else {
839 bail!(
840 "context does not exist when creating enum variant value `{s}` in \
841 enum `{}`",
842 enum_ty.name()
843 );
844 }
845 }
846
847 let variants = if enum_ty.variants().is_empty() {
848 None
849 } else {
850 let mut variant_names = enum_ty.variants().to_vec();
851 variant_names.sort();
852 Some(format!(" (variants: `{}`)", variant_names.join("`, `")))
853 }
854 .unwrap_or_default();
855
856 bail!(
857 "cannot coerce type `String` to type `{target}`: variant `{s}` not found in \
858 enum `{}`{variants}",
859 enum_ty.name()
860 );
861 }
862 Self::Compound(CompoundValue::EnumVariant(e))
864 if target
865 .as_primitive()
866 .map(|t| matches!(t, PrimitiveType::String))
867 .unwrap_or(false) =>
868 {
869 Ok(Value::Primitive(PrimitiveValue::new_string(e.name())))
870 }
871 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
872 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
873 Self::Hidden(v) => v.coerce(context, target).map(Self::Hidden),
874 Self::Call(_) => {
875 bail!("call values cannot be coerced to any other type");
876 }
877 Self::TypeNameRef(_) => {
878 bail!("type name references cannot be coerced to any other type");
879 }
880 }
881 }
882}
883
884impl From<bool> for Value {
885 fn from(value: bool) -> Self {
886 Self::Primitive(value.into())
887 }
888}
889
890impl From<i64> for Value {
891 fn from(value: i64) -> Self {
892 Self::Primitive(value.into())
893 }
894}
895
896impl TryFrom<u64> for Value {
897 type Error = std::num::TryFromIntError;
898
899 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
900 let value: i64 = value.try_into()?;
901 Ok(value.into())
902 }
903}
904
905impl From<f64> for Value {
906 fn from(value: f64) -> Self {
907 Self::Primitive(value.into())
908 }
909}
910
911impl From<String> for Value {
912 fn from(value: String) -> Self {
913 Self::Primitive(value.into())
914 }
915}
916
917impl From<PrimitiveValue> for Value {
918 fn from(value: PrimitiveValue) -> Self {
919 Self::Primitive(value)
920 }
921}
922
923impl From<Option<PrimitiveValue>> for Value {
924 fn from(value: Option<PrimitiveValue>) -> Self {
925 match value {
926 Some(v) => v.into(),
927 None => Self::new_none(Type::None),
928 }
929 }
930}
931
932impl From<CompoundValue> for Value {
933 fn from(value: CompoundValue) -> Self {
934 Self::Compound(value)
935 }
936}
937
938impl From<HiddenValue> for Value {
939 fn from(value: HiddenValue) -> Self {
940 Self::Hidden(value)
941 }
942}
943
944impl From<Pair> for Value {
945 fn from(value: Pair) -> Self {
946 Self::Compound(value.into())
947 }
948}
949
950impl From<Array> for Value {
951 fn from(value: Array) -> Self {
952 Self::Compound(value.into())
953 }
954}
955
956impl From<Map> for Value {
957 fn from(value: Map) -> Self {
958 Self::Compound(value.into())
959 }
960}
961
962impl From<Object> for Value {
963 fn from(value: Object) -> Self {
964 Self::Compound(value.into())
965 }
966}
967
968impl From<Struct> for Value {
969 fn from(value: Struct) -> Self {
970 Self::Compound(value.into())
971 }
972}
973
974impl From<CallValue> for Value {
975 fn from(value: CallValue) -> Self {
976 Self::Call(value)
977 }
978}
979
980impl<'de> serde::Deserialize<'de> for Value {
981 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
982 where
983 D: serde::Deserializer<'de>,
984 {
985 use serde::Deserialize as _;
986
987 struct Visitor;
989
990 impl<'de> serde::de::Visitor<'de> for Visitor {
991 type Value = Value;
992
993 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
994 where
995 E: serde::de::Error,
996 {
997 Ok(Value::new_none(Type::None))
998 }
999
1000 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
1001 where
1002 E: serde::de::Error,
1003 {
1004 Ok(Value::new_none(Type::None))
1005 }
1006
1007 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
1008 where
1009 D: serde::Deserializer<'de>,
1010 {
1011 Value::deserialize(deserializer)
1012 }
1013
1014 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
1015 where
1016 E: serde::de::Error,
1017 {
1018 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
1019 }
1020
1021 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
1022 where
1023 E: serde::de::Error,
1024 {
1025 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
1026 }
1027
1028 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
1029 where
1030 E: serde::de::Error,
1031 {
1032 Ok(Value::Primitive(PrimitiveValue::Integer(
1033 v.try_into().map_err(|_| {
1034 E::custom("integer not in range for a 64-bit signed integer")
1035 })?,
1036 )))
1037 }
1038
1039 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
1040 where
1041 E: serde::de::Error,
1042 {
1043 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
1044 }
1045
1046 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
1047 where
1048 E: serde::de::Error,
1049 {
1050 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1051 }
1052
1053 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
1054 where
1055 E: serde::de::Error,
1056 {
1057 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
1058 }
1059
1060 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
1061 where
1062 A: serde::de::SeqAccess<'de>,
1063 {
1064 use serde::de::Error as _;
1065
1066 let mut elements = vec![];
1067 while let Some(element) = seq.next_element::<Value>()? {
1068 elements.push(element);
1069 }
1070
1071 let mut candidate_ty = None;
1073 for element in elements.iter() {
1074 let new_candidate_ty = element.ty();
1075 let old_candidate_ty =
1076 candidate_ty.get_or_insert_with(|| new_candidate_ty.clone());
1077 let Some(new_common_ty) = old_candidate_ty.common_type(&new_candidate_ty)
1078 else {
1079 return Err(A::Error::custom(format!(
1080 "a common element type does not exist between `{old_candidate_ty}` \
1081 and `{new_candidate_ty}`"
1082 )));
1083 };
1084 candidate_ty = Some(new_common_ty);
1085 }
1086 let array_ty = ArrayType::new(candidate_ty.unwrap_or(Type::Union));
1088 Ok(Array::new(array_ty.clone(), elements)
1089 .map_err(|e| {
1090 A::Error::custom(format!("cannot coerce value to `{array_ty}`: {e:#}"))
1091 })?
1092 .into())
1093 }
1094
1095 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
1096 where
1097 A: serde::de::MapAccess<'de>,
1098 {
1099 let mut members = IndexMap::new();
1100 while let Some(key) = map.next_key::<String>()? {
1101 members.insert(key, map.next_value()?);
1102 }
1103
1104 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
1105 }
1106
1107 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1108 write!(f, "a WDL value")
1109 }
1110 }
1111
1112 deserializer.deserialize_any(Visitor)
1113 }
1114}
1115
1116#[derive(Debug, Clone)]
1120pub enum PrimitiveValue {
1121 Boolean(bool),
1123 Integer(i64),
1125 Float(OrderedFloat<f64>),
1127 String(Arc<String>),
1129 File(HostPath),
1131 Directory(HostPath),
1133}
1134
1135impl PrimitiveValue {
1136 pub fn new_string(s: impl Into<String>) -> Self {
1138 Self::String(Arc::new(s.into()))
1139 }
1140
1141 pub fn new_file(path: impl Into<HostPath>) -> Self {
1143 Self::File(path.into())
1144 }
1145
1146 pub fn new_directory(path: impl Into<HostPath>) -> Self {
1148 Self::Directory(path.into())
1149 }
1150
1151 pub fn ty(&self) -> Type {
1153 match self {
1154 Self::Boolean(_) => PrimitiveType::Boolean.into(),
1155 Self::Integer(_) => PrimitiveType::Integer.into(),
1156 Self::Float(_) => PrimitiveType::Float.into(),
1157 Self::String(_) => PrimitiveType::String.into(),
1158 Self::File(_) => PrimitiveType::File.into(),
1159 Self::Directory(_) => PrimitiveType::Directory.into(),
1160 }
1161 }
1162
1163 pub fn as_boolean(&self) -> Option<bool> {
1167 match self {
1168 Self::Boolean(v) => Some(*v),
1169 _ => None,
1170 }
1171 }
1172
1173 pub fn unwrap_boolean(self) -> bool {
1179 match self {
1180 Self::Boolean(v) => v,
1181 _ => panic!("value is not a boolean"),
1182 }
1183 }
1184
1185 pub fn as_integer(&self) -> Option<i64> {
1189 match self {
1190 Self::Integer(v) => Some(*v),
1191 _ => None,
1192 }
1193 }
1194
1195 pub fn unwrap_integer(self) -> i64 {
1201 match self {
1202 Self::Integer(v) => v,
1203 _ => panic!("value is not an integer"),
1204 }
1205 }
1206
1207 pub fn as_float(&self) -> Option<f64> {
1211 match self {
1212 Self::Float(v) => Some((*v).into()),
1213 _ => None,
1214 }
1215 }
1216
1217 pub fn unwrap_float(self) -> f64 {
1223 match self {
1224 Self::Float(v) => v.into(),
1225 _ => panic!("value is not a float"),
1226 }
1227 }
1228
1229 pub fn as_string(&self) -> Option<&Arc<String>> {
1233 match self {
1234 Self::String(s) => Some(s),
1235 _ => None,
1236 }
1237 }
1238
1239 pub fn unwrap_string(self) -> Arc<String> {
1245 match self {
1246 Self::String(s) => s,
1247 _ => panic!("value is not a string"),
1248 }
1249 }
1250
1251 pub fn as_file(&self) -> Option<&HostPath> {
1255 match self {
1256 Self::File(p) => Some(p),
1257 _ => None,
1258 }
1259 }
1260
1261 pub fn unwrap_file(self) -> HostPath {
1267 match self {
1268 Self::File(p) => p,
1269 _ => panic!("value is not a file"),
1270 }
1271 }
1272
1273 pub fn as_directory(&self) -> Option<&HostPath> {
1277 match self {
1278 Self::Directory(p) => Some(p),
1279 _ => None,
1280 }
1281 }
1282
1283 pub fn unwrap_directory(self) -> HostPath {
1289 match self {
1290 Self::Directory(p) => p,
1291 _ => panic!("value is not a directory"),
1292 }
1293 }
1294
1295 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1302 match (left, right) {
1303 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1304 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1305 (Self::Integer(left), Self::Float(right)) => {
1306 Some(OrderedFloat(*left as f64).cmp(right))
1307 }
1308 (Self::Float(left), Self::Integer(right)) => {
1309 Some(left.cmp(&OrderedFloat(*right as f64)))
1310 }
1311 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1312 (Self::String(left), Self::String(right))
1313 | (Self::String(left), Self::File(HostPath(right)))
1314 | (Self::String(left), Self::Directory(HostPath(right)))
1315 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1316 | (Self::File(HostPath(left)), Self::String(right))
1317 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1318 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1319 _ => None,
1320 }
1321 }
1322
1323 pub(crate) fn raw<'a>(
1331 &'a self,
1332 context: Option<&'a dyn EvaluationContext>,
1333 ) -> impl fmt::Display + use<'a> {
1334 struct Display<'a> {
1336 value: &'a PrimitiveValue,
1338 context: Option<&'a dyn EvaluationContext>,
1340 }
1341
1342 impl fmt::Display for Display<'_> {
1343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1344 match self.value {
1345 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1346 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1347 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1348 PrimitiveValue::String(v) => write!(f, "{v}"),
1349 PrimitiveValue::File(v) => {
1350 write!(
1351 f,
1352 "{v}",
1353 v = self
1354 .context
1355 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1356 .unwrap_or(Cow::Borrowed(&v.0))
1357 )
1358 }
1359 PrimitiveValue::Directory(v) => {
1360 write!(
1361 f,
1362 "{v}",
1363 v = self
1364 .context
1365 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1366 .unwrap_or(Cow::Borrowed(&v.0))
1367 )
1368 }
1369 }
1370 }
1371 }
1372
1373 Display {
1374 value: self,
1375 context,
1376 }
1377 }
1378}
1379
1380impl fmt::Display for PrimitiveValue {
1381 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1382 match self {
1383 Self::Boolean(v) => write!(f, "{v}"),
1384 Self::Integer(v) => write!(f, "{v}"),
1385 Self::Float(v) => write!(f, "{v:.6?}"),
1386 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1387 write!(f, "\"{s}\"")
1389 }
1390 }
1391 }
1392}
1393
1394impl PartialEq for PrimitiveValue {
1395 fn eq(&self, other: &Self) -> bool {
1396 Self::compare(self, other) == Some(Ordering::Equal)
1397 }
1398}
1399
1400impl Eq for PrimitiveValue {}
1401
1402impl Hash for PrimitiveValue {
1403 fn hash<H: Hasher>(&self, state: &mut H) {
1404 match self {
1405 Self::Boolean(v) => {
1406 0.hash(state);
1407 v.hash(state);
1408 }
1409 Self::Integer(v) => {
1410 1.hash(state);
1411 v.hash(state);
1412 }
1413 Self::Float(v) => {
1414 1.hash(state);
1417 v.hash(state);
1418 }
1419 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1420 2.hash(state);
1423 v.hash(state);
1424 }
1425 }
1426 }
1427}
1428
1429impl From<bool> for PrimitiveValue {
1430 fn from(value: bool) -> Self {
1431 Self::Boolean(value)
1432 }
1433}
1434
1435impl From<i64> for PrimitiveValue {
1436 fn from(value: i64) -> Self {
1437 Self::Integer(value)
1438 }
1439}
1440
1441impl From<f64> for PrimitiveValue {
1442 fn from(value: f64) -> Self {
1443 Self::Float(value.into())
1444 }
1445}
1446
1447impl From<String> for PrimitiveValue {
1448 fn from(value: String) -> Self {
1449 Self::String(value.into())
1450 }
1451}
1452
1453impl Coercible for PrimitiveValue {
1454 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1455 if target.is_union() || target.is_none() || self.ty().eq(target) {
1456 return Ok(self.clone());
1457 }
1458
1459 match self {
1460 Self::Boolean(v) => {
1461 target
1462 .as_primitive()
1463 .and_then(|ty| match ty {
1464 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1466 _ => None,
1467 })
1468 .with_context(|| format!("cannot coerce type `Boolean` to type `{target}`"))
1469 }
1470 Self::Integer(v) => {
1471 target
1472 .as_primitive()
1473 .and_then(|ty| match ty {
1474 PrimitiveType::Integer => Some(Self::Integer(*v)),
1476 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1478 _ => None,
1479 })
1480 .with_context(|| format!("cannot coerce type `Int` to type `{target}`"))
1481 }
1482 Self::Float(v) => {
1483 target
1484 .as_primitive()
1485 .and_then(|ty| match ty {
1486 PrimitiveType::Float => Some(Self::Float(*v)),
1488 _ => None,
1489 })
1490 .with_context(|| format!("cannot coerce type `Float` to type `{target}`"))
1491 }
1492 Self::String(s) => {
1493 target
1494 .as_primitive()
1495 .and_then(|ty| match ty {
1496 PrimitiveType::String => Some(Self::String(s.clone())),
1498 PrimitiveType::File => Some(Self::File(
1500 context
1501 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1502 .unwrap_or_else(|| s.clone().into()),
1503 )),
1504 PrimitiveType::Directory => Some(Self::Directory(
1506 context
1507 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1508 .unwrap_or_else(|| s.clone().into()),
1509 )),
1510 _ => None,
1511 })
1512 .with_context(|| format!("cannot coerce type `String` to type `{target}`"))
1513 }
1514 Self::File(p) => {
1515 target
1516 .as_primitive()
1517 .and_then(|ty| match ty {
1518 PrimitiveType::File => Some(Self::File(p.clone())),
1520 PrimitiveType::String => Some(Self::String(
1522 context
1523 .and_then(|c| c.guest_path(p).map(Into::into))
1524 .unwrap_or_else(|| p.clone().into()),
1525 )),
1526 _ => None,
1527 })
1528 .with_context(|| format!("cannot coerce type `File` to type `{target}`"))
1529 }
1530 Self::Directory(p) => {
1531 target
1532 .as_primitive()
1533 .and_then(|ty| match ty {
1534 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1536 PrimitiveType::String => Some(Self::String(
1538 context
1539 .and_then(|c| c.guest_path(p).map(Into::into))
1540 .unwrap_or_else(|| p.clone().into()),
1541 )),
1542 _ => None,
1543 })
1544 .with_context(|| format!("cannot coerce type `Directory` to type `{target}`"))
1545 }
1546 }
1547 }
1548}
1549
1550#[derive(Debug, Clone)]
1554pub struct Pair {
1555 ty: Type,
1557 values: Arc<(Value, Value)>,
1559}
1560
1561impl Pair {
1562 pub fn new(ty: PairType, left: impl Into<Value>, right: impl Into<Value>) -> Result<Self> {
1567 Self::new_with_context(None, ty, left, right)
1568 }
1569
1570 pub(crate) fn new_with_context(
1575 context: Option<&dyn EvaluationContext>,
1576 ty: PairType,
1577 left: impl Into<Value>,
1578 right: impl Into<Value>,
1579 ) -> Result<Self> {
1580 let left = left
1581 .into()
1582 .coerce(context, ty.left_type())
1583 .context("failed to coerce pair's left value")?;
1584 let right = right
1585 .into()
1586 .coerce(context, ty.right_type())
1587 .context("failed to coerce pair's right value")?;
1588 Ok(Self::new_unchecked(ty, left, right))
1589 }
1590
1591 pub(crate) fn new_unchecked(ty: impl Into<Type>, left: Value, right: Value) -> Self {
1594 let ty = ty.into();
1595 assert!(ty.as_pair().is_some());
1596 Self {
1597 ty: ty.require(),
1598 values: Arc::new((left, right)),
1599 }
1600 }
1601
1602 pub fn ty(&self) -> Type {
1604 self.ty.clone()
1605 }
1606
1607 pub fn left(&self) -> &Value {
1609 &self.values.0
1610 }
1611
1612 pub fn right(&self) -> &Value {
1614 &self.values.1
1615 }
1616}
1617
1618impl fmt::Display for Pair {
1619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1620 write!(
1621 f,
1622 "({left}, {right})",
1623 left = self.values.0,
1624 right = self.values.1
1625 )
1626 }
1627}
1628
1629#[derive(Debug, Clone)]
1633pub struct Array {
1634 ty: Type,
1636 elements: Option<Arc<Vec<Value>>>,
1640}
1641
1642impl Array {
1643 pub fn new<V>(ty: ArrayType, elements: impl IntoIterator<Item = V>) -> Result<Self>
1648 where
1649 V: Into<Value>,
1650 {
1651 Self::new_with_context(None, ty, elements)
1652 }
1653
1654 pub(crate) fn new_with_context<V>(
1660 context: Option<&dyn EvaluationContext>,
1661 ty: ArrayType,
1662 elements: impl IntoIterator<Item = V>,
1663 ) -> Result<Self>
1664 where
1665 V: Into<Value>,
1666 {
1667 let element_type = ty.element_type();
1668 let elements = elements
1669 .into_iter()
1670 .enumerate()
1671 .map(|(i, v)| {
1672 let v = v.into();
1673 v.coerce(context, element_type)
1674 .with_context(|| format!("failed to coerce array element at index {i}"))
1675 })
1676 .collect::<Result<Vec<_>>>()?;
1677
1678 Ok(Self::new_unchecked(ty, elements))
1679 }
1680
1681 pub(crate) fn new_unchecked(ty: impl Into<Type>, elements: Vec<Value>) -> Self {
1688 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty.into() {
1689 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1690 } else {
1691 panic!("type is not an array type");
1692 };
1693
1694 Self {
1695 ty,
1696 elements: if elements.is_empty() {
1697 None
1698 } else {
1699 Some(Arc::new(elements))
1700 },
1701 }
1702 }
1703
1704 pub fn ty(&self) -> Type {
1706 self.ty.clone()
1707 }
1708
1709 pub fn as_slice(&self) -> &[Value] {
1711 self.elements.as_ref().map(|v| v.as_slice()).unwrap_or(&[])
1712 }
1713
1714 pub fn len(&self) -> usize {
1716 self.elements.as_ref().map(|v| v.len()).unwrap_or(0)
1717 }
1718
1719 pub fn is_empty(&self) -> bool {
1721 self.len() == 0
1722 }
1723}
1724
1725impl fmt::Display for Array {
1726 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1727 write!(f, "[")?;
1728
1729 if let Some(elements) = &self.elements {
1730 for (i, element) in elements.iter().enumerate() {
1731 if i > 0 {
1732 write!(f, ", ")?;
1733 }
1734
1735 write!(f, "{element}")?;
1736 }
1737 }
1738
1739 write!(f, "]")
1740 }
1741}
1742
1743#[derive(Debug, Clone)]
1747pub struct Map {
1748 ty: Type,
1750 elements: Option<Arc<IndexMap<Option<PrimitiveValue>, Value>>>,
1754}
1755
1756impl Map {
1757 pub fn new<K, V>(ty: MapType, elements: impl IntoIterator<Item = (K, V)>) -> Result<Self>
1762 where
1763 K: Into<Value>,
1764 V: Into<Value>,
1765 {
1766 Self::new_with_context(None, ty, elements)
1767 }
1768
1769 pub(crate) fn new_with_context<K, V>(
1774 context: Option<&dyn EvaluationContext>,
1775 ty: MapType,
1776 elements: impl IntoIterator<Item = (K, V)>,
1777 ) -> Result<Self>
1778 where
1779 K: Into<Value>,
1780 V: Into<Value>,
1781 {
1782 let key_type = ty.key_type();
1783 let value_type = ty.value_type();
1784
1785 let elements = elements
1786 .into_iter()
1787 .enumerate()
1788 .map(|(i, (k, v))| {
1789 let k = k.into();
1790 let v = v.into();
1791 Ok((
1792 if k.is_none() {
1793 None
1794 } else {
1795 match k.coerce(context, key_type).with_context(|| {
1796 format!("failed to coerce map key for element at index {i}")
1797 })? {
1798 Value::None(_) => None,
1799 Value::Primitive(v) => Some(v),
1800 _ => {
1801 bail!("not all key values are primitive")
1802 }
1803 }
1804 },
1805 v.coerce(context, value_type).with_context(|| {
1806 format!("failed to coerce map value for element at index {i}")
1807 })?,
1808 ))
1809 })
1810 .collect::<Result<_>>()?;
1811
1812 Ok(Self::new_unchecked(ty, elements))
1813 }
1814
1815 pub(crate) fn new_unchecked(
1822 ty: impl Into<Type>,
1823 elements: IndexMap<Option<PrimitiveValue>, Value>,
1824 ) -> Self {
1825 let ty = ty.into();
1826 assert!(ty.as_map().is_some());
1827 Self {
1828 ty: ty.require(),
1829 elements: if elements.is_empty() {
1830 None
1831 } else {
1832 Some(Arc::new(elements))
1833 },
1834 }
1835 }
1836
1837 pub fn ty(&self) -> Type {
1839 self.ty.clone()
1840 }
1841
1842 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&Option<PrimitiveValue>, &Value)> {
1844 self.elements
1845 .as_ref()
1846 .map(|m| Either::Left(m.iter()))
1847 .unwrap_or(Either::Right(std::iter::empty()))
1848 }
1849
1850 pub fn keys(&self) -> impl ExactSizeIterator<Item = &Option<PrimitiveValue>> {
1852 self.elements
1853 .as_ref()
1854 .map(|m| Either::Left(m.keys()))
1855 .unwrap_or(Either::Right(std::iter::empty()))
1856 }
1857
1858 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1860 self.elements
1861 .as_ref()
1862 .map(|m| Either::Left(m.values()))
1863 .unwrap_or(Either::Right(std::iter::empty()))
1864 }
1865
1866 pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1868 self.elements
1869 .as_ref()
1870 .map(|m| m.contains_key(key))
1871 .unwrap_or(false)
1872 }
1873
1874 pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1876 self.elements.as_ref().and_then(|m| m.get(key))
1877 }
1878
1879 pub fn len(&self) -> usize {
1881 self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1882 }
1883
1884 pub fn is_empty(&self) -> bool {
1886 self.len() == 0
1887 }
1888}
1889
1890impl fmt::Display for Map {
1891 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1892 write!(f, "{{")?;
1893
1894 for (i, (k, v)) in self.iter().enumerate() {
1895 if i > 0 {
1896 write!(f, ", ")?;
1897 }
1898
1899 match k {
1900 Some(k) => write!(f, "{k}: {v}")?,
1901 None => write!(f, "None: {v}")?,
1902 }
1903 }
1904
1905 write!(f, "}}")
1906 }
1907}
1908
1909#[derive(Debug, Clone)]
1913pub struct Object {
1914 pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1918}
1919
1920impl Object {
1921 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1925 Self {
1926 members: if members.is_empty() {
1927 None
1928 } else {
1929 Some(Arc::new(members))
1930 },
1931 }
1932 }
1933
1934 pub fn empty() -> Self {
1936 Self::new(IndexMap::default())
1937 }
1938
1939 pub fn from_v1_metadata<N: TreeNode>(
1941 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1942 ) -> Self {
1943 Object::new(
1944 items
1945 .map(|i| {
1946 (
1947 i.name().text().to_string(),
1948 Value::from_v1_metadata(&i.value()),
1949 )
1950 })
1951 .collect::<IndexMap<_, _>>(),
1952 )
1953 }
1954
1955 pub fn ty(&self) -> Type {
1957 Type::Object
1958 }
1959
1960 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
1962 self.members
1963 .as_ref()
1964 .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1965 .unwrap_or(Either::Right(std::iter::empty()))
1966 }
1967
1968 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
1970 self.members
1971 .as_ref()
1972 .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1973 .unwrap_or(Either::Right(std::iter::empty()))
1974 }
1975
1976 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1978 self.members
1979 .as_ref()
1980 .map(|m| Either::Left(m.values()))
1981 .unwrap_or(Either::Right(std::iter::empty()))
1982 }
1983
1984 pub fn contains_key(&self, key: &str) -> bool {
1986 self.members
1987 .as_ref()
1988 .map(|m| m.contains_key(key))
1989 .unwrap_or(false)
1990 }
1991
1992 pub fn get(&self, key: &str) -> Option<&Value> {
1994 self.members.as_ref().and_then(|m| m.get(key))
1995 }
1996
1997 pub fn len(&self) -> usize {
1999 self.members.as_ref().map(|m| m.len()).unwrap_or(0)
2000 }
2001
2002 pub fn is_empty(&self) -> bool {
2004 self.len() == 0
2005 }
2006}
2007
2008impl fmt::Display for Object {
2009 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2010 write!(f, "object {{")?;
2011
2012 for (i, (k, v)) in self.iter().enumerate() {
2013 if i > 0 {
2014 write!(f, ", ")?;
2015 }
2016
2017 write!(f, "{k}: {v}")?;
2018 }
2019
2020 write!(f, "}}")
2021 }
2022}
2023
2024#[derive(Debug, Clone)]
2028pub struct Struct {
2029 ty: Type,
2031 name: Arc<String>,
2033 pub(crate) members: Arc<IndexMap<String, Value>>,
2035}
2036
2037impl Struct {
2038 pub fn new<S, V>(ty: StructType, members: impl IntoIterator<Item = (S, V)>) -> Result<Self>
2043 where
2044 S: Into<String>,
2045 V: Into<Value>,
2046 {
2047 Self::new_with_context(None, ty, members)
2048 }
2049
2050 pub(crate) fn new_with_context<S, V>(
2055 context: Option<&dyn EvaluationContext>,
2056 ty: StructType,
2057 members: impl IntoIterator<Item = (S, V)>,
2058 ) -> Result<Self>
2059 where
2060 S: Into<String>,
2061 V: Into<Value>,
2062 {
2063 let mut members = members
2064 .into_iter()
2065 .map(|(n, v)| {
2066 let n = n.into();
2067 let v = v.into();
2068 let v = v
2069 .coerce(
2070 context,
2071 ty.members().get(&n).ok_or_else(|| {
2072 anyhow!("struct does not contain a member named `{n}`")
2073 })?,
2074 )
2075 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
2076 Ok((n, v))
2077 })
2078 .collect::<Result<IndexMap<_, _>>>()?;
2079
2080 for (name, ty) in ty.members().iter() {
2081 if ty.is_optional() {
2083 if !members.contains_key(name) {
2084 members.insert(name.clone(), Value::new_none(ty.clone()));
2085 }
2086 } else {
2087 if !members.contains_key(name) {
2089 bail!("missing a value for struct member `{name}`");
2090 }
2091 }
2092 }
2093
2094 let name = ty.name().to_string();
2095 Ok(Self::new_unchecked(ty, name.into(), members.into()))
2096 }
2097
2098 pub(crate) fn new_unchecked(
2105 ty: impl Into<Type>,
2106 name: Arc<String>,
2107 members: Arc<IndexMap<String, Value>>,
2108 ) -> Self {
2109 let ty = ty.into();
2110 assert!(ty.as_struct().is_some());
2111 Self {
2112 ty: ty.require(),
2113 name,
2114 members,
2115 }
2116 }
2117
2118 pub fn ty(&self) -> Type {
2120 self.ty.clone()
2121 }
2122
2123 pub fn name(&self) -> &Arc<String> {
2125 &self.name
2126 }
2127
2128 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2130 self.members.iter().map(|(k, v)| (k.as_str(), v))
2131 }
2132
2133 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2135 self.members.keys().map(|k| k.as_str())
2136 }
2137
2138 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2140 self.members.values()
2141 }
2142
2143 pub fn contains_key(&self, key: &str) -> bool {
2145 self.members.contains_key(key)
2146 }
2147
2148 pub fn get(&self, key: &str) -> Option<&Value> {
2150 self.members.get(key)
2151 }
2152}
2153
2154impl fmt::Display for Struct {
2155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2156 write!(f, "{name} {{", name = self.name)?;
2157
2158 for (i, (k, v)) in self.members.iter().enumerate() {
2159 if i > 0 {
2160 write!(f, ", ")?;
2161 }
2162
2163 write!(f, "{k}: {v}")?;
2164 }
2165
2166 write!(f, "}}")
2167 }
2168}
2169
2170#[derive(Debug, Clone)]
2177pub struct EnumVariant {
2178 enum_ty: EnumType,
2180 variant_index: usize,
2182 value: Arc<Value>,
2184}
2185
2186impl PartialEq for EnumVariant {
2187 fn eq(&self, other: &Self) -> bool {
2188 self.enum_ty == other.enum_ty && self.variant_index == other.variant_index
2189 }
2190}
2191
2192impl EnumVariant {
2193 pub fn new(enum_ty: impl Into<EnumType>, name: &str, value: impl Into<Value>) -> Self {
2199 let enum_ty = enum_ty.into();
2200 let value = Arc::new(value.into());
2201
2202 let variant_index = enum_ty
2203 .variants()
2204 .iter()
2205 .position(|v| v == name)
2206 .expect("variant name must exist in enum type");
2207
2208 Self {
2209 enum_ty,
2210 variant_index,
2211 value,
2212 }
2213 }
2214
2215 pub fn enum_ty(&self) -> EnumType {
2217 self.enum_ty.clone()
2218 }
2219
2220 pub fn name(&self) -> &str {
2222 &self.enum_ty.variants()[self.variant_index]
2223 }
2224
2225 pub fn value(&self) -> &Value {
2227 &self.value
2228 }
2229}
2230
2231impl fmt::Display for EnumVariant {
2256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2257 write!(f, "{}", self.name())
2258 }
2259}
2260
2261#[derive(Debug, Clone)]
2265pub enum CompoundValue {
2266 Pair(Pair),
2268 Array(Array),
2270 Map(Map),
2272 Object(Object),
2274 Struct(Struct),
2276 EnumVariant(EnumVariant),
2278}
2279
2280impl CompoundValue {
2281 pub fn ty(&self) -> Type {
2283 match self {
2284 CompoundValue::Pair(v) => v.ty(),
2285 CompoundValue::Array(v) => v.ty(),
2286 CompoundValue::Map(v) => v.ty(),
2287 CompoundValue::Object(v) => v.ty(),
2288 CompoundValue::Struct(v) => v.ty(),
2289 CompoundValue::EnumVariant(v) => v.enum_ty().into(),
2290 }
2291 }
2292
2293 pub fn as_pair(&self) -> Option<&Pair> {
2297 match self {
2298 Self::Pair(v) => Some(v),
2299 _ => None,
2300 }
2301 }
2302
2303 pub fn unwrap_pair(self) -> Pair {
2309 match self {
2310 Self::Pair(v) => v,
2311 _ => panic!("value is not a pair"),
2312 }
2313 }
2314
2315 pub fn as_array(&self) -> Option<&Array> {
2319 match self {
2320 Self::Array(v) => Some(v),
2321 _ => None,
2322 }
2323 }
2324
2325 pub fn unwrap_array(self) -> Array {
2331 match self {
2332 Self::Array(v) => v,
2333 _ => panic!("value is not an array"),
2334 }
2335 }
2336
2337 pub fn as_map(&self) -> Option<&Map> {
2341 match self {
2342 Self::Map(v) => Some(v),
2343 _ => None,
2344 }
2345 }
2346
2347 pub fn unwrap_map(self) -> Map {
2353 match self {
2354 Self::Map(v) => v,
2355 _ => panic!("value is not a map"),
2356 }
2357 }
2358
2359 pub fn as_object(&self) -> Option<&Object> {
2363 match self {
2364 Self::Object(v) => Some(v),
2365 _ => None,
2366 }
2367 }
2368
2369 pub fn unwrap_object(self) -> Object {
2375 match self {
2376 Self::Object(v) => v,
2377 _ => panic!("value is not an object"),
2378 }
2379 }
2380
2381 pub fn as_struct(&self) -> Option<&Struct> {
2385 match self {
2386 Self::Struct(v) => Some(v),
2387 _ => None,
2388 }
2389 }
2390
2391 pub fn unwrap_struct(self) -> Struct {
2397 match self {
2398 Self::Struct(v) => v,
2399 _ => panic!("value is not a struct"),
2400 }
2401 }
2402
2403 pub fn as_enum_variant(&self) -> Option<&EnumVariant> {
2407 match self {
2408 Self::EnumVariant(v) => Some(v),
2409 _ => None,
2410 }
2411 }
2412
2413 pub fn unwrap_enum_variant(self) -> EnumVariant {
2419 match self {
2420 Self::EnumVariant(v) => v,
2421 _ => panic!("value is not an enum"),
2422 }
2423 }
2424
2425 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2431 if left.ty() != right.ty() {
2434 return None;
2435 }
2436
2437 match (left, right) {
2438 (Self::Pair(left), Self::Pair(right)) => Some(
2439 Value::equals(left.left(), right.left())?
2440 && Value::equals(left.right(), right.right())?,
2441 ),
2442 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2443 left.len() == right.len()
2444 && left
2445 .as_slice()
2446 .iter()
2447 .zip(right.as_slice())
2448 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2449 ),
2450 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2451 left.len() == right.len()
2452 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2454 match (lk, rk) {
2455 (None, None) => {},
2456 (Some(lk), Some(rk)) if lk == rk => {},
2457 _ => return false
2458 }
2459
2460 Value::equals(lv, rv).unwrap_or(false)
2461 }),
2462 ),
2463 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2464 left.len() == right.len()
2465 && left.iter().all(|(k, left)| match right.get(k) {
2466 Some(right) => Value::equals(left, right).unwrap_or(false),
2467 None => false,
2468 }),
2469 ),
2470 (
2471 CompoundValue::Struct(Struct { members: left, .. }),
2472 CompoundValue::Struct(Struct { members: right, .. }),
2473 ) => Some(
2474 left.len() == right.len()
2475 && left.iter().all(|(k, left)| match right.get(k) {
2476 Some(right) => Value::equals(left, right).unwrap_or(false),
2477 None => false,
2478 }),
2479 ),
2480 (CompoundValue::EnumVariant(left), CompoundValue::EnumVariant(right)) => {
2481 Some(left.enum_ty() == right.enum_ty() && left.name() == right.name())
2482 }
2483 _ => None,
2484 }
2485 }
2486
2487 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2492 where
2493 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2494 {
2495 match self {
2496 Self::Pair(pair) => {
2497 pair.left().visit_paths(cb)?;
2498 pair.right().visit_paths(cb)?;
2499 }
2500 Self::Array(array) => {
2501 for v in array.as_slice() {
2502 v.visit_paths(cb)?;
2503 }
2504 }
2505 Self::Map(map) => {
2506 for (k, v) in map.iter() {
2507 match k {
2508 Some(PrimitiveValue::File(path)) => cb(true, path)?,
2509 Some(PrimitiveValue::Directory(path)) => cb(false, path)?,
2510 _ => {}
2511 }
2512
2513 v.visit_paths(cb)?;
2514 }
2515 }
2516 Self::Object(object) => {
2517 for v in object.values() {
2518 v.visit_paths(cb)?;
2519 }
2520 }
2521 Self::Struct(s) => {
2522 for v in s.values() {
2523 v.visit_paths(cb)?;
2524 }
2525 }
2526 Self::EnumVariant(e) => {
2527 e.value().visit_paths(cb)?;
2528 }
2529 }
2530
2531 Ok(())
2532 }
2533
2534 fn resolve_paths<'a, F>(
2537 &'a self,
2538 base_dir: Option<&'a Path>,
2539 transferer: Option<&'a dyn Transferer>,
2540 translate: &'a F,
2541 ) -> BoxFuture<'a, Result<Self>>
2542 where
2543 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
2544 {
2545 async move {
2546 match self {
2547 Self::Pair(pair) => {
2548 let ty = pair.ty.as_pair().expect("should be a pair type");
2549 let (left_optional, right_optional) =
2550 (ty.left_type().is_optional(), ty.right_type().is_optional());
2551 let (fst, snd) = pair.values.as_ref();
2552 let fst = fst
2553 .resolve_paths(left_optional, base_dir, transferer, translate)
2554 .await?;
2555 let snd = snd
2556 .resolve_paths(right_optional, base_dir, transferer, translate)
2557 .await?;
2558 Ok(Self::Pair(Pair::new_unchecked(ty.clone(), fst, snd)))
2559 }
2560 Self::Array(array) => {
2561 let ty = array.ty.as_array().expect("should be an array type");
2562 let optional = ty.element_type().is_optional();
2563 if let Some(elements) = &array.elements {
2564 let resolved_elements = futures::stream::iter(elements.iter())
2565 .then(|v| v.resolve_paths(optional, base_dir, transferer, translate))
2566 .try_collect()
2567 .await?;
2568 Ok(Self::Array(Array::new_unchecked(
2569 ty.clone(),
2570 resolved_elements,
2571 )))
2572 } else {
2573 Ok(self.clone())
2574 }
2575 }
2576 Self::Map(map) => {
2577 let ty = map.ty.as_map().expect("should be a map type").clone();
2578 let (key_optional, value_optional) =
2579 (ty.key_type().is_optional(), ty.value_type().is_optional());
2580 if let Some(elements) = &map.elements {
2581 let resolved_elements = futures::stream::iter(elements.iter())
2582 .then(async |(k, v)| {
2583 let resolved_key = if let Some(k) = k {
2584 Value::from(k.clone())
2585 .resolve_paths(
2586 key_optional,
2587 base_dir,
2588 transferer,
2589 translate,
2590 )
2591 .await?
2592 .as_primitive()
2593 .cloned()
2594 } else {
2595 None
2596 };
2597 let resolved_value = v
2598 .resolve_paths(value_optional, base_dir, transferer, translate)
2599 .await?;
2600 Ok::<_, anyhow::Error>((resolved_key, resolved_value))
2601 })
2602 .try_collect()
2603 .await?;
2604 Ok(Self::Map(Map::new_unchecked(ty, resolved_elements)))
2605 } else {
2606 Ok(Self::Map(Map::new_unchecked(ty, IndexMap::new())))
2607 }
2608 }
2609 Self::Object(object) => {
2610 if let Some(members) = &object.members {
2611 let resolved_members = futures::stream::iter(members.iter())
2612 .then(async |(n, v)| {
2613 let resolved = v
2614 .resolve_paths(false, base_dir, transferer, translate)
2615 .await?;
2616 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2617 })
2618 .try_collect()
2619 .await?;
2620 Ok(Self::Object(Object::new(resolved_members)))
2621 } else {
2622 Ok(self.clone())
2623 }
2624 }
2625 Self::Struct(s) => {
2626 let ty = s.ty.as_struct().expect("should be a struct type");
2627 let name = s.name();
2628 let resolved_members = futures::stream::iter(s.iter())
2629 .then(async |(n, v)| {
2630 let resolved = v
2631 .resolve_paths(
2632 ty.members()[n].is_optional(),
2633 base_dir,
2634 transferer,
2635 translate,
2636 )
2637 .await?;
2638 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2639 })
2640 .try_collect()
2641 .await?;
2642 Ok(Self::Struct(Struct::new_unchecked(
2643 ty.clone(),
2644 name.clone(),
2645 Arc::new(resolved_members),
2646 )))
2647 }
2648 Self::EnumVariant(e) => {
2649 let optional = e.enum_ty().inner_value_type().is_optional();
2650 let value = e
2651 .value
2652 .resolve_paths(optional, base_dir, transferer, translate)
2653 .await?;
2654 Ok(Self::EnumVariant(EnumVariant::new(
2655 e.enum_ty.clone(),
2656 e.name(),
2657 value,
2658 )))
2659 }
2660 }
2661 }
2662 .boxed()
2663 }
2664}
2665
2666impl fmt::Display for CompoundValue {
2667 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2668 match self {
2669 Self::Pair(v) => v.fmt(f),
2670 Self::Array(v) => v.fmt(f),
2671 Self::Map(v) => v.fmt(f),
2672 Self::Object(v) => v.fmt(f),
2673 Self::Struct(v) => v.fmt(f),
2674 Self::EnumVariant(v) => v.fmt(f),
2675 }
2676 }
2677}
2678
2679impl Coercible for CompoundValue {
2680 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2681 if target.is_union() || target.is_none() || self.ty().eq(target) {
2682 return Ok(self.clone());
2683 }
2684
2685 if let Type::Compound(target_ty, _) = target {
2686 match (self, target_ty) {
2687 (Self::Array(v), CompoundType::Array(target_ty)) => {
2689 if v.is_empty() && target_ty.is_non_empty() {
2692 bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2693 }
2694
2695 return Ok(Self::Array(Array::new_with_context(
2696 context,
2697 target_ty.clone(),
2698 v.as_slice().iter().cloned(),
2699 )?));
2700 }
2701 (Self::Map(v), CompoundType::Map(target_ty)) => {
2703 return Ok(Self::Map(Map::new_with_context(
2704 context,
2705 target_ty.clone(),
2706 v.iter().map(|(k, v)| {
2707 (
2708 k.clone()
2709 .map(Into::into)
2710 .unwrap_or(Value::new_none(target_ty.key_type().optional())),
2711 v.clone(),
2712 )
2713 }),
2714 )?));
2715 }
2716 (Self::Pair(v), CompoundType::Pair(target_ty)) => {
2718 return Ok(Self::Pair(Pair::new_with_context(
2719 context,
2720 target_ty.clone(),
2721 v.values.0.clone(),
2722 v.values.1.clone(),
2723 )?));
2724 }
2725 (Self::Map(v), CompoundType::Custom(CustomType::Struct(target_ty))) => {
2727 let len = v.len();
2728 let expected_len = target_ty.members().len();
2729
2730 if len != expected_len {
2731 bail!(
2732 "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2733 as the struct has {expected_len} member{s2}",
2734 s1 = if len == 1 { "" } else { "s" },
2735 s2 = if expected_len == 1 { "" } else { "s" }
2736 );
2737 }
2738
2739 return Ok(Self::Struct(Struct {
2740 ty: target.clone(),
2741 name: target_ty.name().clone(),
2742 members: Arc::new(
2743 v.iter()
2744 .map(|(k, v)| {
2745 let k = k
2746 .as_ref()
2747 .and_then(|k| {
2748 k.coerce(context, &PrimitiveType::String.into()).ok()
2749 })
2750 .with_context(|| {
2751 format!(
2752 "cannot coerce a map of type `{map_type}` to \
2753 struct type `{target}` as the key type cannot \
2754 coerce to `String`",
2755 map_type = v.ty()
2756 )
2757 })?
2758 .unwrap_string();
2759 let ty =
2760 target_ty.members().get(k.as_ref()).with_context(|| {
2761 format!(
2762 "cannot coerce a map with key `{k}` to struct \
2763 type `{target}` as the struct does not contain a \
2764 member with that name"
2765 )
2766 })?;
2767 let v = v.coerce(context, ty).with_context(|| {
2768 format!("failed to coerce value of map key `{k}")
2769 })?;
2770 Ok((k.to_string(), v))
2771 })
2772 .collect::<Result<_>>()?,
2773 ),
2774 }));
2775 }
2776 (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2778 let key_ty = map_ty.key_type();
2779 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2780 bail!(
2781 "cannot coerce a struct to type `{target}` as key type `{key_ty}` \
2782 cannot be coerced from `String`"
2783 );
2784 }
2785
2786 let value_ty = map_ty.value_type();
2787 return Ok(Self::Map(Map::new_unchecked(
2788 target.clone(),
2789 members
2790 .iter()
2791 .map(|(n, v)| {
2792 let v = v
2793 .coerce(context, value_ty)
2794 .with_context(|| format!("failed to coerce member `{n}`"))?;
2795 Ok((
2796 PrimitiveValue::new_string(n)
2797 .coerce(context, key_ty)
2798 .expect("should coerce")
2799 .into(),
2800 v,
2801 ))
2802 })
2803 .collect::<Result<_>>()?,
2804 )));
2805 }
2806 (Self::Object(object), CompoundType::Map(map_ty)) => {
2808 let key_ty = map_ty.key_type();
2809 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2810 bail!(
2811 "cannot coerce an object to type `{target}` as key type `{key_ty}` \
2812 cannot be coerced from `String`"
2813 );
2814 }
2815
2816 let value_ty = map_ty.value_type();
2817 return Ok(Self::Map(Map::new_unchecked(
2818 target.clone(),
2819 object
2820 .iter()
2821 .map(|(n, v)| {
2822 let v = v
2823 .coerce(context, value_ty)
2824 .with_context(|| format!("failed to coerce member `{n}`"))?;
2825 Ok((
2826 PrimitiveValue::new_string(n)
2827 .coerce(context, key_ty)
2828 .expect("should coerce")
2829 .into(),
2830 v,
2831 ))
2832 })
2833 .collect::<Result<_>>()?,
2834 )));
2835 }
2836 (Self::Object(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2838 return Ok(Self::Struct(Struct::new_with_context(
2839 context,
2840 struct_ty.clone(),
2841 v.iter().map(|(k, v)| (k, v.clone())),
2842 )?));
2843 }
2844 (Self::Struct(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2846 let len = v.members.len();
2847 let expected_len = struct_ty.members().len();
2848
2849 if len != expected_len {
2850 bail!(
2851 "cannot coerce a struct of {len} members{s1} to struct type \
2852 `{target}` as the target struct has {expected_len} member{s2}",
2853 s1 = if len == 1 { "" } else { "s" },
2854 s2 = if expected_len == 1 { "" } else { "s" }
2855 );
2856 }
2857
2858 return Ok(Self::Struct(Struct {
2859 ty: target.clone(),
2860 name: struct_ty.name().clone(),
2861 members: Arc::new(
2862 v.members
2863 .iter()
2864 .map(|(k, v)| {
2865 let ty = struct_ty.members().get(k).ok_or_else(|| {
2866 anyhow!(
2867 "cannot coerce a struct with member `{k}` to struct \
2868 type `{target}` as the target struct does not \
2869 contain a member with that name",
2870 )
2871 })?;
2872 let v = v.coerce(context, ty).with_context(|| {
2873 format!("failed to coerce member `{k}`")
2874 })?;
2875 Ok((k.clone(), v))
2876 })
2877 .collect::<Result<_>>()?,
2878 ),
2879 }));
2880 }
2881 _ => {}
2882 }
2883 }
2884
2885 if let Type::Object = target {
2886 match self {
2887 Self::Map(v) => {
2889 return Ok(Self::Object(Object::new(
2890 v.iter()
2891 .map(|(k, v)| {
2892 let k = k
2893 .as_ref()
2894 .and_then(|k| {
2895 k.coerce(context, &PrimitiveType::String.into()).ok()
2896 })
2897 .with_context(|| {
2898 format!(
2899 "cannot coerce a map of type `{map_type}` to `Object` \
2900 as the key type cannot coerce to `String`",
2901 map_type = v.ty()
2902 )
2903 })?
2904 .unwrap_string();
2905 Ok((k.to_string(), v.clone()))
2906 })
2907 .collect::<Result<IndexMap<_, _>>>()?,
2908 )));
2909 }
2910 Self::Struct(v) => {
2912 return Ok(Self::Object(Object {
2913 members: Some(v.members.clone()),
2914 }));
2915 }
2916 _ => {}
2917 };
2918 }
2919
2920 bail!(
2921 "cannot coerce a value of type `{ty}` to type `{target}`",
2922 ty = self.ty()
2923 );
2924 }
2925}
2926
2927impl From<Pair> for CompoundValue {
2928 fn from(value: Pair) -> Self {
2929 Self::Pair(value)
2930 }
2931}
2932
2933impl From<Array> for CompoundValue {
2934 fn from(value: Array) -> Self {
2935 Self::Array(value)
2936 }
2937}
2938
2939impl From<Map> for CompoundValue {
2940 fn from(value: Map) -> Self {
2941 Self::Map(value)
2942 }
2943}
2944
2945impl From<Object> for CompoundValue {
2946 fn from(value: Object) -> Self {
2947 Self::Object(value)
2948 }
2949}
2950
2951impl From<Struct> for CompoundValue {
2952 fn from(value: Struct) -> Self {
2953 Self::Struct(value)
2954 }
2955}
2956
2957#[derive(Debug, Clone)]
2961pub enum HiddenValue {
2962 Hints(HintsValue),
2966 Input(InputValue),
2970 Output(OutputValue),
2974 TaskPreEvaluation(TaskPreEvaluationValue),
2979 TaskPostEvaluation(TaskPostEvaluationValue),
2984 PreviousTaskData(PreviousTaskDataValue),
2989}
2990
2991impl HiddenValue {
2992 pub fn ty(&self) -> Type {
2994 match self {
2995 Self::Hints(_) => Type::Hidden(HiddenType::Hints),
2996 Self::Input(_) => Type::Hidden(HiddenType::Input),
2997 Self::Output(_) => Type::Hidden(HiddenType::Output),
2998 Self::TaskPreEvaluation(_) => Type::Hidden(HiddenType::TaskPreEvaluation),
2999 Self::TaskPostEvaluation(_) => Type::Hidden(HiddenType::TaskPostEvaluation),
3000 Self::PreviousTaskData(_) => Type::Hidden(HiddenType::PreviousTaskData),
3001 }
3002 }
3003}
3004
3005impl fmt::Display for HiddenValue {
3006 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3007 match self {
3008 Self::Hints(v) => v.fmt(f),
3009 Self::Input(v) => v.fmt(f),
3010 Self::Output(v) => v.fmt(f),
3011 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => write!(f, "task"),
3012 Self::PreviousTaskData(_) => write!(f, "task.previous"),
3013 }
3014 }
3015}
3016
3017impl Coercible for HiddenValue {
3018 fn coerce(&self, _: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
3019 match self {
3020 Self::Hints(_) => {
3021 if matches!(target, Type::Hidden(HiddenType::Hints)) {
3022 return Ok(self.clone());
3023 }
3024
3025 bail!("hints values cannot be coerced to any other type");
3026 }
3027 Self::Input(_) => {
3028 if matches!(target, Type::Hidden(HiddenType::Input)) {
3029 return Ok(self.clone());
3030 }
3031
3032 bail!("input values cannot be coerced to any other type");
3033 }
3034 Self::Output(_) => {
3035 if matches!(target, Type::Hidden(HiddenType::Output)) {
3036 return Ok(self.clone());
3037 }
3038
3039 bail!("output values cannot be coerced to any other type");
3040 }
3041 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => {
3042 if matches!(
3043 target,
3044 Type::Hidden(HiddenType::TaskPreEvaluation)
3045 | Type::Hidden(HiddenType::TaskPostEvaluation)
3046 ) {
3047 return Ok(self.clone());
3048 }
3049
3050 bail!("task variables cannot be coerced to any other type");
3051 }
3052 Self::PreviousTaskData(_) => {
3053 if matches!(target, Type::Hidden(HiddenType::PreviousTaskData)) {
3054 return Ok(self.clone());
3055 }
3056
3057 bail!("previous task data values cannot be coerced to any other type");
3058 }
3059 }
3060 }
3061}
3062
3063#[derive(Debug, Clone)]
3067pub(crate) struct TaskPostEvaluationData {
3068 container: Option<Arc<String>>,
3070 cpu: f64,
3072 memory: i64,
3074 gpu: Array,
3079 fpga: Array,
3084 disks: Map,
3091 max_retries: i64,
3093}
3094
3095#[derive(Debug, Clone)]
3099pub struct PreviousTaskDataValue(Option<Arc<TaskPostEvaluationData>>);
3100
3101impl PreviousTaskDataValue {
3102 pub(crate) fn new(data: Arc<TaskPostEvaluationData>) -> Self {
3104 Self(Some(data))
3105 }
3106
3107 pub(crate) fn empty() -> Self {
3109 Self(None)
3110 }
3111
3112 pub fn field(&self, name: &str) -> Option<Value> {
3119 match name {
3120 TASK_FIELD_MEMORY => Some(
3121 self.0
3122 .as_ref()
3123 .map(|data| Value::from(data.memory))
3124 .unwrap_or_else(|| {
3125 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3126 }),
3127 ),
3128 TASK_FIELD_CPU => Some(
3129 self.0
3130 .as_ref()
3131 .map(|data| Value::from(data.cpu))
3132 .unwrap_or_else(|| {
3133 Value::new_none(Type::from(PrimitiveType::Float).optional())
3134 }),
3135 ),
3136 TASK_FIELD_CONTAINER => Some(
3137 self.0
3138 .as_ref()
3139 .and_then(|data| {
3140 data.container
3141 .as_ref()
3142 .map(|c| PrimitiveValue::String(c.clone()).into())
3143 })
3144 .unwrap_or_else(|| {
3145 Value::new_none(Type::from(PrimitiveType::String).optional())
3146 }),
3147 ),
3148 TASK_FIELD_GPU => Some(
3149 self.0
3150 .as_ref()
3151 .map(|data| Value::from(data.gpu.clone()))
3152 .unwrap_or_else(|| {
3153 Value::new_none(Type::Compound(
3154 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3155 true,
3156 ))
3157 }),
3158 ),
3159 TASK_FIELD_FPGA => Some(
3160 self.0
3161 .as_ref()
3162 .map(|data| Value::from(data.fpga.clone()))
3163 .unwrap_or_else(|| {
3164 Value::new_none(Type::Compound(
3165 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3166 true,
3167 ))
3168 }),
3169 ),
3170 TASK_FIELD_DISKS => Some(
3171 self.0
3172 .as_ref()
3173 .map(|data| Value::from(data.disks.clone()))
3174 .unwrap_or_else(|| {
3175 Value::new_none(Type::Compound(
3176 MapType::new(PrimitiveType::String, PrimitiveType::Integer).into(),
3177 true,
3178 ))
3179 }),
3180 ),
3181 TASK_FIELD_MAX_RETRIES => Some(
3182 self.0
3183 .as_ref()
3184 .map(|data| Value::from(data.max_retries))
3185 .unwrap_or_else(|| {
3186 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3187 }),
3188 ),
3189 _ => None,
3190 }
3191 }
3192}
3193
3194#[derive(Debug, Clone)]
3201pub struct TaskPreEvaluationValue {
3202 name: Arc<String>,
3204 id: Arc<String>,
3206 attempt: i64,
3211 meta: Object,
3213 parameter_meta: Object,
3215 ext: Object,
3217 previous: PreviousTaskDataValue,
3223}
3224
3225impl TaskPreEvaluationValue {
3226 pub(crate) fn new(
3229 name: impl Into<String>,
3230 id: impl Into<String>,
3231 attempt: i64,
3232 meta: Object,
3233 parameter_meta: Object,
3234 ext: Object,
3235 ) -> Self {
3236 Self {
3237 name: Arc::new(name.into()),
3238 id: Arc::new(id.into()),
3239 meta,
3240 parameter_meta,
3241 ext,
3242 attempt,
3243 previous: PreviousTaskDataValue::empty(),
3244 }
3245 }
3246
3247 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3249 self.previous = PreviousTaskDataValue::new(data);
3250 }
3251
3252 pub fn name(&self) -> &Arc<String> {
3254 &self.name
3255 }
3256
3257 pub fn id(&self) -> &Arc<String> {
3259 &self.id
3260 }
3261
3262 pub fn attempt(&self) -> i64 {
3264 self.attempt
3265 }
3266
3267 pub fn field(&self, name: &str) -> Option<Value> {
3271 match name {
3272 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
3273 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
3274 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
3275 TASK_FIELD_META => Some(self.meta.clone().into()),
3276 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
3277 TASK_FIELD_EXT => Some(self.ext.clone().into()),
3278 TASK_FIELD_PREVIOUS => {
3279 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
3280 }
3281 _ => None,
3282 }
3283 }
3284}
3285
3286#[derive(Debug, Clone)]
3292pub struct TaskPostEvaluationValue {
3293 data: Arc<TaskPostEvaluationData>,
3295 name: Arc<String>,
3297 id: Arc<String>,
3299 attempt: i64,
3304 meta: Object,
3306 parameter_meta: Object,
3308 ext: Object,
3310 return_code: Option<i64>,
3314 end_time: Option<i64>,
3318 previous: PreviousTaskDataValue,
3324}
3325
3326impl TaskPostEvaluationValue {
3327 #[allow(clippy::too_many_arguments)]
3330 pub(crate) fn new(
3331 name: impl Into<String>,
3332 id: impl Into<String>,
3333 constraints: &TaskExecutionConstraints,
3334 max_retries: i64,
3335 attempt: i64,
3336 meta: Object,
3337 parameter_meta: Object,
3338 ext: Object,
3339 ) -> Self {
3340 Self {
3341 name: Arc::new(name.into()),
3342 id: Arc::new(id.into()),
3343 data: Arc::new(TaskPostEvaluationData {
3344 container: constraints
3345 .container
3346 .as_ref()
3347 .map(|c| Arc::new(c.to_string())),
3348 cpu: constraints.cpu,
3349 memory: constraints
3350 .memory
3351 .try_into()
3352 .expect("memory exceeds a valid WDL value"),
3353 gpu: Array::new_unchecked(
3354 ANALYSIS_STDLIB.array_string_type().clone(),
3355 constraints
3356 .gpu
3357 .iter()
3358 .map(|v| PrimitiveValue::new_string(v).into())
3359 .collect(),
3360 ),
3361 fpga: Array::new_unchecked(
3362 ANALYSIS_STDLIB.array_string_type().clone(),
3363 constraints
3364 .fpga
3365 .iter()
3366 .map(|v| PrimitiveValue::new_string(v).into())
3367 .collect(),
3368 ),
3369 disks: Map::new_unchecked(
3370 ANALYSIS_STDLIB.map_string_int_type().clone(),
3371 constraints
3372 .disks
3373 .iter()
3374 .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), (*v).into()))
3375 .collect(),
3376 ),
3377 max_retries,
3378 }),
3379 attempt,
3380 meta,
3381 parameter_meta,
3382 ext,
3383 return_code: None,
3384 end_time: None,
3385 previous: PreviousTaskDataValue::empty(),
3386 }
3387 }
3388
3389 pub fn name(&self) -> &Arc<String> {
3391 &self.name
3392 }
3393
3394 pub fn id(&self) -> &Arc<String> {
3396 &self.id
3397 }
3398
3399 pub fn container(&self) -> Option<&Arc<String>> {
3401 self.data.container.as_ref()
3402 }
3403
3404 pub fn cpu(&self) -> f64 {
3406 self.data.cpu
3407 }
3408
3409 pub fn memory(&self) -> i64 {
3411 self.data.memory
3412 }
3413
3414 pub fn gpu(&self) -> &Array {
3419 &self.data.gpu
3420 }
3421
3422 pub fn fpga(&self) -> &Array {
3427 &self.data.fpga
3428 }
3429
3430 pub fn disks(&self) -> &Map {
3437 &self.data.disks
3438 }
3439
3440 pub fn attempt(&self) -> i64 {
3445 self.attempt
3446 }
3447
3448 pub fn end_time(&self) -> Option<i64> {
3452 self.end_time
3453 }
3454
3455 pub fn return_code(&self) -> Option<i64> {
3459 self.return_code
3460 }
3461
3462 pub fn meta(&self) -> &Object {
3464 &self.meta
3465 }
3466
3467 pub fn parameter_meta(&self) -> &Object {
3469 &self.parameter_meta
3470 }
3471
3472 pub fn ext(&self) -> &Object {
3474 &self.ext
3475 }
3476
3477 pub(crate) fn set_return_code(&mut self, code: i32) {
3479 self.return_code = Some(code as i64);
3480 }
3481
3482 pub(crate) fn set_attempt(&mut self, attempt: i64) {
3484 self.attempt = attempt;
3485 }
3486
3487 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3489 self.previous = PreviousTaskDataValue::new(data);
3490 }
3491
3492 pub(crate) fn data(&self) -> &Arc<TaskPostEvaluationData> {
3494 &self.data
3495 }
3496
3497 pub fn field(&self, version: SupportedVersion, name: &str) -> Option<Value> {
3501 match name {
3502 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
3503 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
3504 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
3505 TASK_FIELD_CONTAINER => Some(
3506 self.data
3507 .container
3508 .clone()
3509 .map(|c| PrimitiveValue::String(c).into())
3510 .unwrap_or_else(|| {
3511 Value::new_none(
3512 task_member_type_post_evaluation(version, TASK_FIELD_CONTAINER)
3513 .expect("failed to get task field type"),
3514 )
3515 }),
3516 ),
3517 TASK_FIELD_CPU => Some(self.data.cpu.into()),
3518 TASK_FIELD_MEMORY => Some(self.data.memory.into()),
3519 TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
3520 TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
3521 TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
3522 TASK_FIELD_END_TIME => Some(self.end_time.map(Into::into).unwrap_or_else(|| {
3523 Value::new_none(
3524 task_member_type_post_evaluation(version, TASK_FIELD_END_TIME)
3525 .expect("failed to get task field type"),
3526 )
3527 })),
3528 TASK_FIELD_RETURN_CODE => Some(self.return_code.map(Into::into).unwrap_or_else(|| {
3529 Value::new_none(
3530 task_member_type_post_evaluation(version, TASK_FIELD_RETURN_CODE)
3531 .expect("failed to get task field type"),
3532 )
3533 })),
3534 TASK_FIELD_META => Some(self.meta.clone().into()),
3535 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
3536 TASK_FIELD_EXT => Some(self.ext.clone().into()),
3537 TASK_FIELD_MAX_RETRIES if version >= SupportedVersion::V1(V1::Three) => {
3538 Some(self.data.max_retries.into())
3539 }
3540 TASK_FIELD_PREVIOUS if version >= SupportedVersion::V1(V1::Three) => {
3541 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
3542 }
3543 _ => None,
3544 }
3545 }
3546}
3547
3548#[derive(Debug, Clone)]
3552pub struct HintsValue(Object);
3553
3554impl HintsValue {
3555 pub fn new(members: IndexMap<String, Value>) -> Self {
3557 Self(Object::new(members))
3558 }
3559
3560 pub fn as_object(&self) -> &Object {
3562 &self.0
3563 }
3564}
3565
3566impl From<HintsValue> for Value {
3567 fn from(value: HintsValue) -> Self {
3568 Self::Hidden(HiddenValue::Hints(value))
3569 }
3570}
3571
3572impl fmt::Display for HintsValue {
3573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3574 write!(f, "hints {{")?;
3575
3576 for (i, (k, v)) in self.0.iter().enumerate() {
3577 if i > 0 {
3578 write!(f, ", ")?;
3579 }
3580
3581 write!(f, "{k}: {v}")?;
3582 }
3583
3584 write!(f, "}}")
3585 }
3586}
3587
3588impl From<Object> for HintsValue {
3589 fn from(value: Object) -> Self {
3590 Self(value)
3591 }
3592}
3593
3594#[derive(Debug, Clone)]
3598pub struct InputValue(Object);
3599
3600impl InputValue {
3601 pub fn new(members: IndexMap<String, Value>) -> Self {
3603 Self(Object::new(members))
3604 }
3605
3606 pub fn as_object(&self) -> &Object {
3608 &self.0
3609 }
3610}
3611
3612impl From<InputValue> for Value {
3613 fn from(value: InputValue) -> Self {
3614 Self::Hidden(HiddenValue::Input(value))
3615 }
3616}
3617
3618impl fmt::Display for InputValue {
3619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3620 write!(f, "input {{")?;
3621
3622 for (i, (k, v)) in self.0.iter().enumerate() {
3623 if i > 0 {
3624 write!(f, ", ")?;
3625 }
3626
3627 write!(f, "{k}: {v}")?;
3628 }
3629
3630 write!(f, "}}")
3631 }
3632}
3633
3634impl From<Object> for InputValue {
3635 fn from(value: Object) -> Self {
3636 Self(value)
3637 }
3638}
3639
3640#[derive(Debug, Clone)]
3644pub struct OutputValue(Object);
3645
3646impl OutputValue {
3647 pub fn new(members: IndexMap<String, Value>) -> Self {
3649 Self(Object::new(members))
3650 }
3651
3652 pub fn as_object(&self) -> &Object {
3654 &self.0
3655 }
3656}
3657
3658impl From<OutputValue> for Value {
3659 fn from(value: OutputValue) -> Self {
3660 Self::Hidden(HiddenValue::Output(value))
3661 }
3662}
3663
3664impl fmt::Display for OutputValue {
3665 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3666 write!(f, "output {{")?;
3667
3668 for (i, (k, v)) in self.0.iter().enumerate() {
3669 if i > 0 {
3670 write!(f, ", ")?;
3671 }
3672
3673 write!(f, "{k}: {v}")?;
3674 }
3675
3676 write!(f, "}}")
3677 }
3678}
3679
3680impl From<Object> for OutputValue {
3681 fn from(value: Object) -> Self {
3682 Self(value)
3683 }
3684}
3685
3686#[derive(Debug, Clone)]
3690pub struct CallValue {
3691 ty: CallType,
3693 outputs: Arc<Outputs>,
3695}
3696
3697impl CallValue {
3698 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3701 Self { ty, outputs }
3702 }
3703
3704 pub fn ty(&self) -> &CallType {
3706 &self.ty
3707 }
3708
3709 pub fn outputs(&self) -> &Outputs {
3711 self.outputs.as_ref()
3712 }
3713}
3714
3715impl fmt::Display for CallValue {
3716 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3717 write!(f, "call output {{")?;
3718
3719 for (i, (k, v)) in self.outputs.iter().enumerate() {
3720 if i > 0 {
3721 write!(f, ", ")?;
3722 }
3723
3724 write!(f, "{k}: {v}")?;
3725 }
3726
3727 write!(f, "}}")
3728 }
3729}
3730
3731pub(crate) struct ValueSerializer<'a> {
3733 context: Option<&'a dyn EvaluationContext>,
3735 value: &'a Value,
3737 allow_pairs: bool,
3740}
3741
3742impl<'a> ValueSerializer<'a> {
3743 pub fn new(
3749 context: Option<&'a dyn EvaluationContext>,
3750 value: &'a Value,
3751 allow_pairs: bool,
3752 ) -> Self {
3753 Self {
3754 context,
3755 value,
3756 allow_pairs,
3757 }
3758 }
3759}
3760
3761impl serde::Serialize for ValueSerializer<'_> {
3762 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3763 where
3764 S: serde::Serializer,
3765 {
3766 use serde::ser::Error;
3767
3768 match &self.value {
3769 Value::None(_) => serializer.serialize_none(),
3770 Value::Primitive(v) => {
3771 PrimitiveValueSerializer::new(self.context, v).serialize(serializer)
3772 }
3773 Value::Compound(v) => CompoundValueSerializer::new(self.context, v, self.allow_pairs)
3774 .serialize(serializer),
3775 Value::Call(_) | Value::Hidden(_) | Value::TypeNameRef(_) => {
3776 Err(S::Error::custom("value cannot be serialized"))
3777 }
3778 }
3779 }
3780}
3781
3782pub(crate) struct PrimitiveValueSerializer<'a> {
3784 context: Option<&'a dyn EvaluationContext>,
3786 value: &'a PrimitiveValue,
3788}
3789
3790impl<'a> PrimitiveValueSerializer<'a> {
3791 pub fn new(context: Option<&'a dyn EvaluationContext>, value: &'a PrimitiveValue) -> Self {
3797 Self { context, value }
3798 }
3799}
3800
3801impl serde::Serialize for PrimitiveValueSerializer<'_> {
3802 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3803 where
3804 S: serde::Serializer,
3805 {
3806 match self.value {
3807 PrimitiveValue::Boolean(v) => v.serialize(serializer),
3808 PrimitiveValue::Integer(v) => v.serialize(serializer),
3809 PrimitiveValue::Float(v) => v.serialize(serializer),
3810 PrimitiveValue::String(s) => s.serialize(serializer),
3811 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3812 let path = self
3813 .context
3814 .and_then(|c| c.guest_path(p).map(|p| Cow::Owned(p.0)))
3815 .unwrap_or(Cow::Borrowed(&p.0));
3816
3817 path.serialize(serializer)
3818 }
3819 }
3820 }
3821}
3822
3823pub(crate) struct CompoundValueSerializer<'a> {
3825 context: Option<&'a dyn EvaluationContext>,
3827 value: &'a CompoundValue,
3829 allow_pairs: bool,
3832}
3833
3834impl<'a> CompoundValueSerializer<'a> {
3835 pub fn new(
3841 context: Option<&'a dyn EvaluationContext>,
3842 value: &'a CompoundValue,
3843 allow_pairs: bool,
3844 ) -> Self {
3845 Self {
3846 context,
3847 value,
3848 allow_pairs,
3849 }
3850 }
3851}
3852
3853impl serde::Serialize for CompoundValueSerializer<'_> {
3854 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3855 where
3856 S: serde::Serializer,
3857 {
3858 use serde::ser::Error;
3859
3860 match &self.value {
3861 CompoundValue::Pair(pair) if self.allow_pairs => {
3862 let mut state = serializer.serialize_map(Some(2))?;
3863 let left = ValueSerializer::new(self.context, pair.left(), self.allow_pairs);
3864 let right = ValueSerializer::new(self.context, pair.right(), self.allow_pairs);
3865 state.serialize_entry("left", &left)?;
3866 state.serialize_entry("right", &right)?;
3867 state.end()
3868 }
3869 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3870 CompoundValue::Array(v) => {
3871 let mut s = serializer.serialize_seq(Some(v.len()))?;
3872 for v in v.as_slice() {
3873 s.serialize_element(&ValueSerializer::new(self.context, v, self.allow_pairs))?;
3874 }
3875
3876 s.end()
3877 }
3878 CompoundValue::Map(v) => {
3879 let ty = v.ty();
3880 let map_type = ty.as_map().expect("type should be a map");
3881 if !map_type
3882 .key_type()
3883 .is_coercible_to(&PrimitiveType::String.into())
3884 {
3885 return Err(S::Error::custom(format!(
3886 "cannot serialize a map of type `{ty}` as the key type cannot be coerced \
3887 to `String`",
3888 )));
3889 }
3890
3891 let mut s = serializer.serialize_map(Some(v.len()))?;
3892 for (k, v) in v.iter() {
3893 s.serialize_entry(
3894 &k.as_ref()
3895 .map(|k| PrimitiveValueSerializer::new(self.context, k)),
3896 &ValueSerializer::new(self.context, v, self.allow_pairs),
3897 )?;
3898 }
3899
3900 s.end()
3901 }
3902 CompoundValue::Object(object) => {
3903 let mut s = serializer.serialize_map(Some(object.len()))?;
3904 for (k, v) in object.iter() {
3905 s.serialize_entry(k, &ValueSerializer::new(self.context, v, self.allow_pairs))?;
3906 }
3907
3908 s.end()
3909 }
3910 CompoundValue::Struct(Struct { members, .. }) => {
3911 let mut s = serializer.serialize_map(Some(members.len()))?;
3912 for (k, v) in members.iter() {
3913 s.serialize_entry(k, &ValueSerializer::new(self.context, v, self.allow_pairs))?;
3914 }
3915
3916 s.end()
3917 }
3918 CompoundValue::EnumVariant(e) => serializer.serialize_str(e.name()),
3919 }
3920 }
3921}
3922
3923#[cfg(test)]
3924mod test {
3925 use std::iter::empty;
3926
3927 use approx::assert_relative_eq;
3928 use pretty_assertions::assert_eq;
3929 use wdl_analysis::types::ArrayType;
3930 use wdl_analysis::types::MapType;
3931 use wdl_analysis::types::PairType;
3932 use wdl_analysis::types::StructType;
3933 use wdl_ast::Diagnostic;
3934 use wdl_ast::Span;
3935 use wdl_ast::SupportedVersion;
3936
3937 use super::*;
3938 use crate::EvaluationPath;
3939 use crate::http::Transferer;
3940
3941 #[test]
3942 fn boolean_coercion() {
3943 assert_eq!(
3945 Value::from(false)
3946 .coerce(None, &PrimitiveType::Boolean.into())
3947 .expect("should coerce")
3948 .unwrap_boolean(),
3949 Value::from(false).unwrap_boolean()
3950 );
3951 assert_eq!(
3953 format!(
3954 "{e:?}",
3955 e = Value::from(true)
3956 .coerce(None, &PrimitiveType::String.into())
3957 .unwrap_err()
3958 ),
3959 "cannot coerce type `Boolean` to type `String`"
3960 );
3961 }
3962
3963 #[test]
3964 fn boolean_display() {
3965 assert_eq!(Value::from(false).to_string(), "false");
3966 assert_eq!(Value::from(true).to_string(), "true");
3967 }
3968
3969 #[test]
3970 fn integer_coercion() {
3971 assert_eq!(
3973 Value::from(12345)
3974 .coerce(None, &PrimitiveType::Integer.into())
3975 .expect("should coerce")
3976 .unwrap_integer(),
3977 Value::from(12345).unwrap_integer()
3978 );
3979 assert_relative_eq!(
3981 Value::from(12345)
3982 .coerce(None, &PrimitiveType::Float.into())
3983 .expect("should coerce")
3984 .unwrap_float(),
3985 Value::from(12345.0).unwrap_float()
3986 );
3987 assert_eq!(
3989 format!(
3990 "{e:?}",
3991 e = Value::from(12345)
3992 .coerce(None, &PrimitiveType::Boolean.into())
3993 .unwrap_err()
3994 ),
3995 "cannot coerce type `Int` to type `Boolean`"
3996 );
3997 }
3998
3999 #[test]
4000 fn integer_display() {
4001 assert_eq!(Value::from(12345).to_string(), "12345");
4002 assert_eq!(Value::from(-12345).to_string(), "-12345");
4003 }
4004
4005 #[test]
4006 fn float_coercion() {
4007 assert_relative_eq!(
4009 Value::from(12345.0)
4010 .coerce(None, &PrimitiveType::Float.into())
4011 .expect("should coerce")
4012 .unwrap_float(),
4013 Value::from(12345.0).unwrap_float()
4014 );
4015 assert_eq!(
4017 format!(
4018 "{e:?}",
4019 e = Value::from(12345.0)
4020 .coerce(None, &PrimitiveType::Integer.into())
4021 .unwrap_err()
4022 ),
4023 "cannot coerce type `Float` to type `Int`"
4024 );
4025 }
4026
4027 #[test]
4028 fn float_display() {
4029 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
4030 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
4031 }
4032
4033 #[test]
4034 fn string_coercion() {
4035 let value = PrimitiveValue::new_string("foo");
4036 assert_eq!(
4038 value
4039 .coerce(None, &PrimitiveType::String.into())
4040 .expect("should coerce"),
4041 value
4042 );
4043 assert_eq!(
4045 value
4046 .coerce(None, &PrimitiveType::File.into())
4047 .expect("should coerce"),
4048 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
4049 );
4050 assert_eq!(
4052 value
4053 .coerce(None, &PrimitiveType::Directory.into())
4054 .expect("should coerce"),
4055 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
4056 );
4057 assert_eq!(
4059 format!(
4060 "{e:?}",
4061 e = value
4062 .coerce(None, &PrimitiveType::Boolean.into())
4063 .unwrap_err()
4064 ),
4065 "cannot coerce type `String` to type `Boolean`"
4066 );
4067
4068 struct Context;
4069
4070 impl EvaluationContext for Context {
4071 fn version(&self) -> SupportedVersion {
4072 unimplemented!()
4073 }
4074
4075 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4076 unimplemented!()
4077 }
4078
4079 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4080 unimplemented!()
4081 }
4082
4083 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4084 unimplemented!()
4085 }
4086
4087 fn base_dir(&self) -> &EvaluationPath {
4088 unimplemented!()
4089 }
4090
4091 fn temp_dir(&self) -> &Path {
4092 unimplemented!()
4093 }
4094
4095 fn transferer(&self) -> &dyn Transferer {
4096 unimplemented!()
4097 }
4098
4099 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
4100 if path.as_str() == "/mnt/task/input/0/path" {
4101 Some(HostPath::new("/some/host/path"))
4102 } else {
4103 None
4104 }
4105 }
4106 }
4107
4108 assert_eq!(
4110 PrimitiveValue::new_string("/mnt/task/input/0/path")
4111 .coerce(Some(&Context), &PrimitiveType::File.into())
4112 .expect("should coerce")
4113 .unwrap_file()
4114 .as_str(),
4115 "/some/host/path"
4116 );
4117
4118 assert_eq!(
4120 value
4121 .coerce(Some(&Context), &PrimitiveType::File.into())
4122 .expect("should coerce")
4123 .unwrap_file()
4124 .as_str(),
4125 "foo"
4126 );
4127
4128 assert_eq!(
4130 PrimitiveValue::new_string("/mnt/task/input/0/path")
4131 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4132 .expect("should coerce")
4133 .unwrap_directory()
4134 .as_str(),
4135 "/some/host/path"
4136 );
4137
4138 assert_eq!(
4140 value
4141 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4142 .expect("should coerce")
4143 .unwrap_directory()
4144 .as_str(),
4145 "foo"
4146 );
4147 }
4148
4149 #[test]
4150 fn string_display() {
4151 let value = PrimitiveValue::new_string("hello world!");
4152 assert_eq!(value.to_string(), "\"hello world!\"");
4153 }
4154
4155 #[test]
4156 fn file_coercion() {
4157 let value = PrimitiveValue::new_file("foo");
4158
4159 assert_eq!(
4161 value
4162 .coerce(None, &PrimitiveType::File.into())
4163 .expect("should coerce"),
4164 value
4165 );
4166 assert_eq!(
4168 value
4169 .coerce(None, &PrimitiveType::String.into())
4170 .expect("should coerce"),
4171 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
4172 );
4173 assert_eq!(
4175 format!(
4176 "{e:?}",
4177 e = value
4178 .coerce(None, &PrimitiveType::Directory.into())
4179 .unwrap_err()
4180 ),
4181 "cannot coerce type `File` to type `Directory`"
4182 );
4183
4184 struct Context;
4185
4186 impl EvaluationContext for Context {
4187 fn version(&self) -> SupportedVersion {
4188 unimplemented!()
4189 }
4190
4191 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4192 unimplemented!()
4193 }
4194
4195 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4196 unimplemented!()
4197 }
4198
4199 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4200 unimplemented!()
4201 }
4202
4203 fn base_dir(&self) -> &EvaluationPath {
4204 unimplemented!()
4205 }
4206
4207 fn temp_dir(&self) -> &Path {
4208 unimplemented!()
4209 }
4210
4211 fn transferer(&self) -> &dyn Transferer {
4212 unimplemented!()
4213 }
4214
4215 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4216 if path.as_str() == "/some/host/path" {
4217 Some(GuestPath::new("/mnt/task/input/0/path"))
4218 } else {
4219 None
4220 }
4221 }
4222 }
4223
4224 assert_eq!(
4226 PrimitiveValue::new_file("/some/host/path")
4227 .coerce(Some(&Context), &PrimitiveType::String.into())
4228 .expect("should coerce")
4229 .unwrap_string()
4230 .as_str(),
4231 "/mnt/task/input/0/path"
4232 );
4233
4234 assert_eq!(
4236 value
4237 .coerce(Some(&Context), &PrimitiveType::String.into())
4238 .expect("should coerce")
4239 .unwrap_string()
4240 .as_str(),
4241 "foo"
4242 );
4243 }
4244
4245 #[test]
4246 fn file_display() {
4247 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
4248 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
4249 }
4250
4251 #[test]
4252 fn directory_coercion() {
4253 let value = PrimitiveValue::new_directory("foo");
4254
4255 assert_eq!(
4257 value
4258 .coerce(None, &PrimitiveType::Directory.into())
4259 .expect("should coerce"),
4260 value
4261 );
4262 assert_eq!(
4264 value
4265 .coerce(None, &PrimitiveType::String.into())
4266 .expect("should coerce"),
4267 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
4268 );
4269 assert_eq!(
4271 format!(
4272 "{e:?}",
4273 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
4274 ),
4275 "cannot coerce type `Directory` to type `File`"
4276 );
4277
4278 struct Context;
4279
4280 impl EvaluationContext for Context {
4281 fn version(&self) -> SupportedVersion {
4282 unimplemented!()
4283 }
4284
4285 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4286 unimplemented!()
4287 }
4288
4289 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4290 unimplemented!()
4291 }
4292
4293 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4294 unimplemented!()
4295 }
4296
4297 fn base_dir(&self) -> &EvaluationPath {
4298 unimplemented!()
4299 }
4300
4301 fn temp_dir(&self) -> &Path {
4302 unimplemented!()
4303 }
4304
4305 fn transferer(&self) -> &dyn Transferer {
4306 unimplemented!()
4307 }
4308
4309 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4310 if path.as_str() == "/some/host/path" {
4311 Some(GuestPath::new("/mnt/task/input/0/path"))
4312 } else {
4313 None
4314 }
4315 }
4316 }
4317
4318 assert_eq!(
4320 PrimitiveValue::new_directory("/some/host/path")
4321 .coerce(Some(&Context), &PrimitiveType::String.into())
4322 .expect("should coerce")
4323 .unwrap_string()
4324 .as_str(),
4325 "/mnt/task/input/0/path"
4326 );
4327
4328 assert_eq!(
4330 value
4331 .coerce(Some(&Context), &PrimitiveType::String.into())
4332 .expect("should coerce")
4333 .unwrap_string()
4334 .as_str(),
4335 "foo"
4336 );
4337 }
4338
4339 #[test]
4340 fn directory_display() {
4341 let value = PrimitiveValue::new_directory("/foo/bar/baz");
4342 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
4343 }
4344
4345 #[test]
4346 fn none_coercion() {
4347 assert!(
4349 Value::new_none(Type::None)
4350 .coerce(None, &Type::from(PrimitiveType::String).optional())
4351 .expect("should coerce")
4352 .is_none(),
4353 );
4354
4355 assert_eq!(
4357 format!(
4358 "{e:?}",
4359 e = Value::new_none(Type::None)
4360 .coerce(None, &PrimitiveType::String.into())
4361 .unwrap_err()
4362 ),
4363 "cannot coerce `None` to non-optional type `String`"
4364 );
4365 }
4366
4367 #[test]
4368 fn none_display() {
4369 assert_eq!(Value::new_none(Type::None).to_string(), "None");
4370 }
4371
4372 #[test]
4373 fn array_coercion() {
4374 let src_ty = ArrayType::new(PrimitiveType::Integer);
4375 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
4376
4377 let src: CompoundValue = Array::new(src_ty, [1, 2, 3])
4379 .expect("should create array value")
4380 .into();
4381 let target = src.coerce(None, &target_ty).expect("should coerce");
4382 assert_eq!(
4383 target.unwrap_array().to_string(),
4384 "[1.000000, 2.000000, 3.000000]"
4385 );
4386
4387 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
4389 assert_eq!(
4390 format!("{e:?}", e = src.coerce(None, &target_ty).unwrap_err()),
4391 r#"failed to coerce array element at index 0
4392
4393Caused by:
4394 cannot coerce type `Int` to type `String`"#
4395 );
4396 }
4397
4398 #[test]
4399 fn non_empty_array_coercion() {
4400 let ty = ArrayType::new(PrimitiveType::String);
4401 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
4402
4403 let string = PrimitiveValue::new_string("foo");
4405 let value: Value = Array::new(ty.clone(), [string])
4406 .expect("should create array")
4407 .into();
4408 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
4409
4410 let value: Value = Array::new::<Value>(ty, [])
4412 .expect("should create array")
4413 .into();
4414 assert_eq!(
4415 format!("{e:?}", e = value.coerce(None, &target_ty).unwrap_err()),
4416 "cannot coerce empty array value to non-empty array type `Array[String]+`"
4417 );
4418 }
4419
4420 #[test]
4421 fn array_display() {
4422 let ty = ArrayType::new(PrimitiveType::Integer);
4423 let value: Value = Array::new(ty, [1, 2, 3])
4424 .expect("should create array")
4425 .into();
4426
4427 assert_eq!(value.to_string(), "[1, 2, 3]");
4428 }
4429
4430 #[test]
4431 fn map_coerce() {
4432 let key1 = PrimitiveValue::new_file("foo");
4433 let value1 = PrimitiveValue::new_string("bar");
4434 let key2 = PrimitiveValue::new_file("baz");
4435 let value2 = PrimitiveValue::new_string("qux");
4436
4437 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
4438 let file_to_string: Value = Map::new(ty, [(key1, value1), (key2, value2)])
4439 .expect("should create map value")
4440 .into();
4441
4442 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
4444 let string_to_file = file_to_string
4445 .coerce(None, &ty)
4446 .expect("value should coerce");
4447 assert_eq!(
4448 string_to_file.to_string(),
4449 r#"{"foo": "bar", "baz": "qux"}"#
4450 );
4451
4452 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
4454 assert_eq!(
4455 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4456 r#"failed to coerce map key for element at index 0
4457
4458Caused by:
4459 cannot coerce type `String` to type `Int`"#
4460 );
4461
4462 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
4464 assert_eq!(
4465 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4466 r#"failed to coerce map value for element at index 0
4467
4468Caused by:
4469 cannot coerce type `File` to type `Int`"#
4470 );
4471
4472 let ty = StructType::new(
4474 "Foo",
4475 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
4476 )
4477 .into();
4478 let struct_value = string_to_file
4479 .coerce(None, &ty)
4480 .expect("value should coerce");
4481 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4482
4483 let ty = StructType::new(
4485 "Foo",
4486 [
4487 ("foo", PrimitiveType::String),
4488 ("baz", PrimitiveType::String),
4489 ],
4490 )
4491 .into();
4492 let struct_value = file_to_string
4493 .coerce(None, &ty)
4494 .expect("value should coerce");
4495 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4496
4497 let ty = StructType::new(
4499 "Foo",
4500 [
4501 ("foo", PrimitiveType::File),
4502 ("baz", PrimitiveType::File),
4503 ("qux", PrimitiveType::File),
4504 ],
4505 )
4506 .into();
4507 assert_eq!(
4508 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4509 "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
4510 );
4511
4512 let object_value = string_to_file
4514 .coerce(None, &Type::Object)
4515 .expect("value should coerce");
4516 assert_eq!(
4517 object_value.to_string(),
4518 r#"object {foo: "bar", baz: "qux"}"#
4519 );
4520
4521 let object_value = file_to_string
4523 .coerce(None, &Type::Object)
4524 .expect("value should coerce");
4525 assert_eq!(
4526 object_value.to_string(),
4527 r#"object {foo: "bar", baz: "qux"}"#
4528 );
4529 }
4530
4531 #[test]
4532 fn map_display() {
4533 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4534 let value: Value = Map::new(ty, [(1, true), (2, false)])
4535 .expect("should create map value")
4536 .into();
4537 assert_eq!(value.to_string(), "{1: true, 2: false}");
4538 }
4539
4540 #[test]
4541 fn pair_coercion() {
4542 let left = PrimitiveValue::new_file("foo");
4543 let right = PrimitiveValue::new_string("bar");
4544
4545 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4546 let value: Value = Pair::new(ty, left, right)
4547 .expect("should create pair value")
4548 .into();
4549
4550 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
4552 let value = value.coerce(None, &ty).expect("value should coerce");
4553 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
4554
4555 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
4557 assert_eq!(
4558 format!("{e:?}", e = value.coerce(None, &ty).unwrap_err()),
4559 r#"failed to coerce pair's left value
4560
4561Caused by:
4562 cannot coerce type `String` to type `Int`"#
4563 );
4564 }
4565
4566 #[test]
4567 fn pair_display() {
4568 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4569 let value: Value = Pair::new(ty, 12345, false)
4570 .expect("should create pair value")
4571 .into();
4572 assert_eq!(value.to_string(), "(12345, false)");
4573 }
4574
4575 #[test]
4576 fn struct_coercion() {
4577 let ty = StructType::new(
4578 "Foo",
4579 [
4580 ("foo", PrimitiveType::Float),
4581 ("bar", PrimitiveType::Float),
4582 ("baz", PrimitiveType::Float),
4583 ],
4584 );
4585 let value: Value = Struct::new(ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
4586 .expect("should create map value")
4587 .into();
4588
4589 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
4591 let map_value = value.coerce(None, &ty).expect("value should coerce");
4592 assert_eq!(
4593 map_value.to_string(),
4594 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4595 );
4596
4597 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
4599 let map_value = value.coerce(None, &ty).expect("value should coerce");
4600 assert_eq!(
4601 map_value.to_string(),
4602 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4603 );
4604
4605 let ty = StructType::new(
4607 "Bar",
4608 [
4609 ("foo", PrimitiveType::Float),
4610 ("bar", PrimitiveType::Float),
4611 ("baz", PrimitiveType::Float),
4612 ],
4613 )
4614 .into();
4615 let struct_value = value.coerce(None, &ty).expect("value should coerce");
4616 assert_eq!(
4617 struct_value.to_string(),
4618 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4619 );
4620
4621 let object_value = value
4623 .coerce(None, &Type::Object)
4624 .expect("value should coerce");
4625 assert_eq!(
4626 object_value.to_string(),
4627 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4628 );
4629 }
4630
4631 #[test]
4632 fn struct_display() {
4633 let ty = StructType::new(
4634 "Foo",
4635 [
4636 ("foo", PrimitiveType::Float),
4637 ("bar", PrimitiveType::String),
4638 ("baz", PrimitiveType::Integer),
4639 ],
4640 );
4641 let value: Value = Struct::new(
4642 ty,
4643 [
4644 ("foo", Value::from(1.101)),
4645 ("bar", PrimitiveValue::new_string("foo").into()),
4646 ("baz", 1234.into()),
4647 ],
4648 )
4649 .expect("should create map value")
4650 .into();
4651 assert_eq!(
4652 value.to_string(),
4653 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
4654 );
4655 }
4656
4657 #[test]
4658 fn pair_serialization() {
4659 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4660 let pair: Value = Pair::new(
4661 pair_ty,
4662 PrimitiveValue::new_file("foo"),
4663 PrimitiveValue::new_string("bar"),
4664 )
4665 .expect("should create pair value")
4666 .into();
4667 let value_serializer = ValueSerializer::new(None, &pair, true);
4669 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4670 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
4671
4672 let value_serializer = ValueSerializer::new(None, &pair, false);
4674 assert!(serde_json::to_string(&value_serializer).is_err());
4675
4676 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
4677 let array: Value = Array::new(array_ty, [pair])
4678 .expect("should create array value")
4679 .into();
4680
4681 let value_serializer = ValueSerializer::new(None, &array, true);
4683 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4684 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
4685 }
4686
4687 #[test]
4688 fn type_name_ref_equality() {
4689 use wdl_analysis::types::EnumType;
4690
4691 let enum_type = Type::Compound(
4692 CompoundType::Custom(CustomType::Enum(
4693 EnumType::new(
4694 "MyEnum",
4695 Span::new(0, 0),
4696 Type::Primitive(PrimitiveType::Integer, false),
4697 Vec::<(String, Type)>::new(),
4698 &[],
4699 )
4700 .expect("should create enum type"),
4701 )),
4702 false,
4703 );
4704
4705 let value1 = Value::TypeNameRef(enum_type.clone());
4706 let value2 = Value::TypeNameRef(enum_type.clone());
4707
4708 assert_eq!(value1.ty(), value2.ty());
4709 }
4710
4711 #[test]
4712 fn type_name_ref_ty() {
4713 let struct_type = Type::Compound(
4714 CompoundType::Custom(CustomType::Struct(StructType::new(
4715 "MyStruct",
4716 empty::<(&str, Type)>(),
4717 ))),
4718 false,
4719 );
4720
4721 let value = Value::TypeNameRef(struct_type.clone());
4722 assert_eq!(value.ty(), struct_type);
4723 }
4724
4725 #[test]
4726 fn type_name_ref_display() {
4727 use wdl_analysis::types::EnumType;
4728
4729 let enum_type = Type::Compound(
4730 CompoundType::Custom(CustomType::Enum(
4731 EnumType::new(
4732 "Color",
4733 Span::new(0, 0),
4734 Type::Primitive(PrimitiveType::Integer, false),
4735 Vec::<(String, Type)>::new(),
4736 &[],
4737 )
4738 .expect("should create enum type"),
4739 )),
4740 false,
4741 );
4742
4743 let value = Value::TypeNameRef(enum_type);
4744 assert_eq!(value.to_string(), "Color");
4745 }
4746}