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