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<Option<PrimitiveValue>, Value>>>,
1767}
1768
1769impl Map {
1770 pub fn new<K, V>(ty: MapType, elements: impl IntoIterator<Item = (K, V)>) -> Result<Self>
1775 where
1776 K: Into<Value>,
1777 V: Into<Value>,
1778 {
1779 Self::new_with_context(None, ty, elements)
1780 }
1781
1782 pub(crate) fn new_with_context<K, V>(
1787 context: Option<&dyn EvaluationContext>,
1788 ty: MapType,
1789 elements: impl IntoIterator<Item = (K, V)>,
1790 ) -> Result<Self>
1791 where
1792 K: Into<Value>,
1793 V: Into<Value>,
1794 {
1795 let key_type = ty.key_type();
1796 let value_type = ty.value_type();
1797
1798 let elements = elements
1799 .into_iter()
1800 .enumerate()
1801 .map(|(i, (k, v))| {
1802 let k = k.into();
1803 let v = v.into();
1804 Ok((
1805 if k.is_none() {
1806 None
1807 } else {
1808 match k.coerce(context, key_type).with_context(|| {
1809 format!("failed to coerce map key for element at index {i}")
1810 })? {
1811 Value::None(_) => None,
1812 Value::Primitive(v) => Some(v),
1813 _ => {
1814 bail!("not all key values are primitive")
1815 }
1816 }
1817 },
1818 v.coerce(context, value_type).with_context(|| {
1819 format!("failed to coerce map value for element at index {i}")
1820 })?,
1821 ))
1822 })
1823 .collect::<Result<_>>()?;
1824
1825 Ok(Self::new_unchecked(ty, elements))
1826 }
1827
1828 pub(crate) fn new_unchecked(
1835 ty: impl Into<Type>,
1836 elements: IndexMap<Option<PrimitiveValue>, Value>,
1837 ) -> Self {
1838 let ty = ty.into();
1839 assert!(ty.as_map().is_some());
1840 Self {
1841 ty: ty.require(),
1842 elements: if elements.is_empty() {
1843 None
1844 } else {
1845 Some(Arc::new(elements))
1846 },
1847 }
1848 }
1849
1850 pub fn ty(&self) -> Type {
1852 self.ty.clone()
1853 }
1854
1855 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&Option<PrimitiveValue>, &Value)> {
1857 self.elements
1858 .as_ref()
1859 .map(|m| Either::Left(m.iter()))
1860 .unwrap_or(Either::Right(std::iter::empty()))
1861 }
1862
1863 pub fn keys(&self) -> impl ExactSizeIterator<Item = &Option<PrimitiveValue>> {
1865 self.elements
1866 .as_ref()
1867 .map(|m| Either::Left(m.keys()))
1868 .unwrap_or(Either::Right(std::iter::empty()))
1869 }
1870
1871 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1873 self.elements
1874 .as_ref()
1875 .map(|m| Either::Left(m.values()))
1876 .unwrap_or(Either::Right(std::iter::empty()))
1877 }
1878
1879 pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1881 self.elements
1882 .as_ref()
1883 .map(|m| m.contains_key(key))
1884 .unwrap_or(false)
1885 }
1886
1887 pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1889 self.elements.as_ref().and_then(|m| m.get(key))
1890 }
1891
1892 pub fn len(&self) -> usize {
1894 self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1895 }
1896
1897 pub fn is_empty(&self) -> bool {
1899 self.len() == 0
1900 }
1901}
1902
1903impl fmt::Display for Map {
1904 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1905 write!(f, "{{")?;
1906
1907 for (i, (k, v)) in self.iter().enumerate() {
1908 if i > 0 {
1909 write!(f, ", ")?;
1910 }
1911
1912 match k {
1913 Some(k) => write!(f, "{k}: {v}")?,
1914 None => write!(f, "None: {v}")?,
1915 }
1916 }
1917
1918 write!(f, "}}")
1919 }
1920}
1921
1922#[derive(Debug, Clone)]
1926pub struct Object {
1927 pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1931}
1932
1933impl Object {
1934 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1938 Self {
1939 members: if members.is_empty() {
1940 None
1941 } else {
1942 Some(Arc::new(members))
1943 },
1944 }
1945 }
1946
1947 pub fn empty() -> Self {
1949 Self::new(IndexMap::default())
1950 }
1951
1952 pub fn from_v1_metadata<N: TreeNode>(
1954 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1955 ) -> Self {
1956 Object::new(
1957 items
1958 .map(|i| {
1959 (
1960 i.name().text().to_string(),
1961 Value::from_v1_metadata(&i.value()),
1962 )
1963 })
1964 .collect::<IndexMap<_, _>>(),
1965 )
1966 }
1967
1968 pub fn ty(&self) -> Type {
1970 Type::Object
1971 }
1972
1973 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
1975 self.members
1976 .as_ref()
1977 .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1978 .unwrap_or(Either::Right(std::iter::empty()))
1979 }
1980
1981 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
1983 self.members
1984 .as_ref()
1985 .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1986 .unwrap_or(Either::Right(std::iter::empty()))
1987 }
1988
1989 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1991 self.members
1992 .as_ref()
1993 .map(|m| Either::Left(m.values()))
1994 .unwrap_or(Either::Right(std::iter::empty()))
1995 }
1996
1997 pub fn contains_key(&self, key: &str) -> bool {
1999 self.members
2000 .as_ref()
2001 .map(|m| m.contains_key(key))
2002 .unwrap_or(false)
2003 }
2004
2005 pub fn get(&self, key: &str) -> Option<&Value> {
2007 self.members.as_ref().and_then(|m| m.get(key))
2008 }
2009
2010 pub fn len(&self) -> usize {
2012 self.members.as_ref().map(|m| m.len()).unwrap_or(0)
2013 }
2014
2015 pub fn is_empty(&self) -> bool {
2017 self.len() == 0
2018 }
2019}
2020
2021impl fmt::Display for Object {
2022 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2023 write!(f, "object {{")?;
2024
2025 for (i, (k, v)) in self.iter().enumerate() {
2026 if i > 0 {
2027 write!(f, ", ")?;
2028 }
2029
2030 write!(f, "{k}: {v}")?;
2031 }
2032
2033 write!(f, "}}")
2034 }
2035}
2036
2037#[derive(Debug, Clone)]
2041pub struct Struct {
2042 ty: Type,
2044 name: Arc<String>,
2046 pub(crate) members: Arc<IndexMap<String, Value>>,
2048}
2049
2050impl Struct {
2051 pub fn new<S, V>(ty: StructType, members: impl IntoIterator<Item = (S, V)>) -> Result<Self>
2056 where
2057 S: Into<String>,
2058 V: Into<Value>,
2059 {
2060 Self::new_with_context(None, ty, members)
2061 }
2062
2063 pub(crate) fn new_with_context<S, V>(
2068 context: Option<&dyn EvaluationContext>,
2069 ty: StructType,
2070 members: impl IntoIterator<Item = (S, V)>,
2071 ) -> Result<Self>
2072 where
2073 S: Into<String>,
2074 V: Into<Value>,
2075 {
2076 let mut members = members
2077 .into_iter()
2078 .map(|(n, v)| {
2079 let n = n.into();
2080 let v = v.into();
2081 let v = v
2082 .coerce(
2083 context,
2084 ty.members().get(&n).ok_or_else(|| {
2085 anyhow!("struct does not contain a member named `{n}`")
2086 })?,
2087 )
2088 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
2089 Ok((n, v))
2090 })
2091 .collect::<Result<IndexMap<_, _>>>()?;
2092
2093 for (name, ty) in ty.members().iter() {
2094 if ty.is_optional() {
2096 if !members.contains_key(name) {
2097 members.insert(name.clone(), Value::new_none(ty.clone()));
2098 }
2099 } else {
2100 if !members.contains_key(name) {
2102 bail!("missing a value for struct member `{name}`");
2103 }
2104 }
2105 }
2106
2107 let name = ty.name().to_string();
2108 Ok(Self::new_unchecked(ty, name.into(), members.into()))
2109 }
2110
2111 pub(crate) fn new_unchecked(
2118 ty: impl Into<Type>,
2119 name: Arc<String>,
2120 members: Arc<IndexMap<String, Value>>,
2121 ) -> Self {
2122 let ty = ty.into();
2123 assert!(ty.as_struct().is_some());
2124 Self {
2125 ty: ty.require(),
2126 name,
2127 members,
2128 }
2129 }
2130
2131 pub fn ty(&self) -> Type {
2133 self.ty.clone()
2134 }
2135
2136 pub fn name(&self) -> &Arc<String> {
2138 &self.name
2139 }
2140
2141 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
2143 self.members.iter().map(|(k, v)| (k.as_str(), v))
2144 }
2145
2146 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
2148 self.members.keys().map(|k| k.as_str())
2149 }
2150
2151 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
2153 self.members.values()
2154 }
2155
2156 pub fn contains_key(&self, key: &str) -> bool {
2158 self.members.contains_key(key)
2159 }
2160
2161 pub fn get(&self, key: &str) -> Option<&Value> {
2163 self.members.get(key)
2164 }
2165}
2166
2167impl fmt::Display for Struct {
2168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2169 write!(f, "{name} {{", name = self.name)?;
2170
2171 for (i, (k, v)) in self.members.iter().enumerate() {
2172 if i > 0 {
2173 write!(f, ", ")?;
2174 }
2175
2176 write!(f, "{k}: {v}")?;
2177 }
2178
2179 write!(f, "}}")
2180 }
2181}
2182
2183#[derive(Debug, Clone)]
2190pub struct EnumVariant {
2191 enum_ty: EnumType,
2193 variant_index: usize,
2195 value: Arc<Value>,
2197}
2198
2199impl PartialEq for EnumVariant {
2200 fn eq(&self, other: &Self) -> bool {
2201 self.enum_ty == other.enum_ty && self.variant_index == other.variant_index
2202 }
2203}
2204
2205impl EnumVariant {
2206 pub fn new(enum_ty: impl Into<EnumType>, name: &str, value: impl Into<Value>) -> Self {
2212 let enum_ty = enum_ty.into();
2213 let value = Arc::new(value.into());
2214
2215 let variant_index = enum_ty
2216 .variants()
2217 .iter()
2218 .position(|v| v == name)
2219 .expect("variant name must exist in enum type");
2220
2221 Self {
2222 enum_ty,
2223 variant_index,
2224 value,
2225 }
2226 }
2227
2228 pub fn enum_ty(&self) -> EnumType {
2230 self.enum_ty.clone()
2231 }
2232
2233 pub fn name(&self) -> &str {
2235 &self.enum_ty.variants()[self.variant_index]
2236 }
2237
2238 pub fn value(&self) -> &Value {
2240 &self.value
2241 }
2242}
2243
2244impl fmt::Display for EnumVariant {
2269 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2270 write!(f, "{}", self.name())
2271 }
2272}
2273
2274#[derive(Debug, Clone)]
2278pub enum CompoundValue {
2279 Pair(Pair),
2281 Array(Array),
2283 Map(Map),
2285 Object(Object),
2287 Struct(Struct),
2289 EnumVariant(EnumVariant),
2291}
2292
2293impl CompoundValue {
2294 pub fn ty(&self) -> Type {
2296 match self {
2297 CompoundValue::Pair(v) => v.ty(),
2298 CompoundValue::Array(v) => v.ty(),
2299 CompoundValue::Map(v) => v.ty(),
2300 CompoundValue::Object(v) => v.ty(),
2301 CompoundValue::Struct(v) => v.ty(),
2302 CompoundValue::EnumVariant(v) => v.enum_ty().into(),
2303 }
2304 }
2305
2306 pub fn as_pair(&self) -> Option<&Pair> {
2310 match self {
2311 Self::Pair(v) => Some(v),
2312 _ => None,
2313 }
2314 }
2315
2316 pub fn unwrap_pair(self) -> Pair {
2322 match self {
2323 Self::Pair(v) => v,
2324 _ => panic!("value is not a pair"),
2325 }
2326 }
2327
2328 pub fn as_array(&self) -> Option<&Array> {
2332 match self {
2333 Self::Array(v) => Some(v),
2334 _ => None,
2335 }
2336 }
2337
2338 pub fn unwrap_array(self) -> Array {
2344 match self {
2345 Self::Array(v) => v,
2346 _ => panic!("value is not an array"),
2347 }
2348 }
2349
2350 pub fn as_map(&self) -> Option<&Map> {
2354 match self {
2355 Self::Map(v) => Some(v),
2356 _ => None,
2357 }
2358 }
2359
2360 pub fn unwrap_map(self) -> Map {
2366 match self {
2367 Self::Map(v) => v,
2368 _ => panic!("value is not a map"),
2369 }
2370 }
2371
2372 pub fn as_object(&self) -> Option<&Object> {
2376 match self {
2377 Self::Object(v) => Some(v),
2378 _ => None,
2379 }
2380 }
2381
2382 pub fn unwrap_object(self) -> Object {
2388 match self {
2389 Self::Object(v) => v,
2390 _ => panic!("value is not an object"),
2391 }
2392 }
2393
2394 pub fn as_struct(&self) -> Option<&Struct> {
2398 match self {
2399 Self::Struct(v) => Some(v),
2400 _ => None,
2401 }
2402 }
2403
2404 pub fn unwrap_struct(self) -> Struct {
2410 match self {
2411 Self::Struct(v) => v,
2412 _ => panic!("value is not a struct"),
2413 }
2414 }
2415
2416 pub fn as_enum_variant(&self) -> Option<&EnumVariant> {
2420 match self {
2421 Self::EnumVariant(v) => Some(v),
2422 _ => None,
2423 }
2424 }
2425
2426 pub fn unwrap_enum_variant(self) -> EnumVariant {
2432 match self {
2433 Self::EnumVariant(v) => v,
2434 _ => panic!("value is not an enum"),
2435 }
2436 }
2437
2438 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2444 if left.ty() != right.ty() {
2447 return None;
2448 }
2449
2450 match (left, right) {
2451 (Self::Pair(left), Self::Pair(right)) => Some(
2452 Value::equals(left.left(), right.left())?
2453 && Value::equals(left.right(), right.right())?,
2454 ),
2455 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2456 left.len() == right.len()
2457 && left
2458 .as_slice()
2459 .iter()
2460 .zip(right.as_slice())
2461 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2462 ),
2463 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2464 left.len() == right.len()
2465 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2467 match (lk, rk) {
2468 (None, None) => {},
2469 (Some(lk), Some(rk)) if lk == rk => {},
2470 _ => return false
2471 }
2472
2473 Value::equals(lv, rv).unwrap_or(false)
2474 }),
2475 ),
2476 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2477 left.len() == right.len()
2478 && left.iter().all(|(k, left)| match right.get(k) {
2479 Some(right) => Value::equals(left, right).unwrap_or(false),
2480 None => false,
2481 }),
2482 ),
2483 (
2484 CompoundValue::Struct(Struct { members: left, .. }),
2485 CompoundValue::Struct(Struct { members: right, .. }),
2486 ) => Some(
2487 left.len() == right.len()
2488 && left.iter().all(|(k, left)| match right.get(k) {
2489 Some(right) => Value::equals(left, right).unwrap_or(false),
2490 None => false,
2491 }),
2492 ),
2493 (CompoundValue::EnumVariant(left), CompoundValue::EnumVariant(right)) => {
2494 Some(left.enum_ty() == right.enum_ty() && left.name() == right.name())
2495 }
2496 _ => None,
2497 }
2498 }
2499
2500 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2505 where
2506 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2507 {
2508 match self {
2509 Self::Pair(pair) => {
2510 pair.left().visit_paths(cb)?;
2511 pair.right().visit_paths(cb)?;
2512 }
2513 Self::Array(array) => {
2514 for v in array.as_slice() {
2515 v.visit_paths(cb)?;
2516 }
2517 }
2518 Self::Map(map) => {
2519 for (k, v) in map.iter() {
2520 match k {
2521 Some(PrimitiveValue::File(path)) => cb(true, path)?,
2522 Some(PrimitiveValue::Directory(path)) => cb(false, path)?,
2523 _ => {}
2524 }
2525
2526 v.visit_paths(cb)?;
2527 }
2528 }
2529 Self::Object(object) => {
2530 for v in object.values() {
2531 v.visit_paths(cb)?;
2532 }
2533 }
2534 Self::Struct(s) => {
2535 for v in s.values() {
2536 v.visit_paths(cb)?;
2537 }
2538 }
2539 Self::EnumVariant(e) => {
2540 e.value().visit_paths(cb)?;
2541 }
2542 }
2543
2544 Ok(())
2545 }
2546
2547 fn resolve_paths<'a, F>(
2550 &'a self,
2551 base_dir: Option<&'a Path>,
2552 transferer: Option<&'a dyn Transferer>,
2553 translate: &'a F,
2554 ) -> BoxFuture<'a, Result<Self>>
2555 where
2556 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
2557 {
2558 async move {
2559 match self {
2560 Self::Pair(pair) => {
2561 let ty = pair.ty.as_pair().expect("should be a pair type");
2562 let (left_optional, right_optional) =
2563 (ty.left_type().is_optional(), ty.right_type().is_optional());
2564 let (fst, snd) = pair.values.as_ref();
2565 let fst = fst
2566 .resolve_paths(left_optional, base_dir, transferer, translate)
2567 .await?;
2568 let snd = snd
2569 .resolve_paths(right_optional, base_dir, transferer, translate)
2570 .await?;
2571 Ok(Self::Pair(Pair::new_unchecked(ty.clone(), fst, snd)))
2572 }
2573 Self::Array(array) => {
2574 let ty = array.ty.as_array().expect("should be an array type");
2575 let optional = ty.element_type().is_optional();
2576 if let Some(elements) = &array.elements {
2577 let resolved_elements = futures::stream::iter(elements.iter())
2578 .then(|v| v.resolve_paths(optional, base_dir, transferer, translate))
2579 .try_collect()
2580 .await?;
2581 Ok(Self::Array(Array::new_unchecked(
2582 ty.clone(),
2583 resolved_elements,
2584 )))
2585 } else {
2586 Ok(self.clone())
2587 }
2588 }
2589 Self::Map(map) => {
2590 let ty = map.ty.as_map().expect("should be a map type").clone();
2591 let (key_optional, value_optional) =
2592 (ty.key_type().is_optional(), ty.value_type().is_optional());
2593 if let Some(elements) = &map.elements {
2594 let resolved_elements = futures::stream::iter(elements.iter())
2595 .then(async |(k, v)| {
2596 let resolved_key = if let Some(k) = k {
2597 Value::from(k.clone())
2598 .resolve_paths(
2599 key_optional,
2600 base_dir,
2601 transferer,
2602 translate,
2603 )
2604 .await?
2605 .as_primitive()
2606 .cloned()
2607 } else {
2608 None
2609 };
2610 let resolved_value = v
2611 .resolve_paths(value_optional, base_dir, transferer, translate)
2612 .await?;
2613 Ok::<_, anyhow::Error>((resolved_key, resolved_value))
2614 })
2615 .try_collect()
2616 .await?;
2617 Ok(Self::Map(Map::new_unchecked(ty, resolved_elements)))
2618 } else {
2619 Ok(Self::Map(Map::new_unchecked(ty, IndexMap::new())))
2620 }
2621 }
2622 Self::Object(object) => {
2623 if let Some(members) = &object.members {
2624 let resolved_members = futures::stream::iter(members.iter())
2625 .then(async |(n, v)| {
2626 let resolved = v
2627 .resolve_paths(false, base_dir, transferer, translate)
2628 .await?;
2629 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2630 })
2631 .try_collect()
2632 .await?;
2633 Ok(Self::Object(Object::new(resolved_members)))
2634 } else {
2635 Ok(self.clone())
2636 }
2637 }
2638 Self::Struct(s) => {
2639 let ty = s.ty.as_struct().expect("should be a struct type");
2640 let name = s.name();
2641 let resolved_members = futures::stream::iter(s.iter())
2642 .then(async |(n, v)| {
2643 let resolved = v
2644 .resolve_paths(
2645 ty.members()[n].is_optional(),
2646 base_dir,
2647 transferer,
2648 translate,
2649 )
2650 .await?;
2651 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2652 })
2653 .try_collect()
2654 .await?;
2655 Ok(Self::Struct(Struct::new_unchecked(
2656 ty.clone(),
2657 name.clone(),
2658 Arc::new(resolved_members),
2659 )))
2660 }
2661 Self::EnumVariant(e) => {
2662 let optional = e.enum_ty().inner_value_type().is_optional();
2663 let value = e
2664 .value
2665 .resolve_paths(optional, base_dir, transferer, translate)
2666 .await?;
2667 Ok(Self::EnumVariant(EnumVariant::new(
2668 e.enum_ty.clone(),
2669 e.name(),
2670 value,
2671 )))
2672 }
2673 }
2674 }
2675 .boxed()
2676 }
2677}
2678
2679impl fmt::Display for CompoundValue {
2680 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2681 match self {
2682 Self::Pair(v) => v.fmt(f),
2683 Self::Array(v) => v.fmt(f),
2684 Self::Map(v) => v.fmt(f),
2685 Self::Object(v) => v.fmt(f),
2686 Self::Struct(v) => v.fmt(f),
2687 Self::EnumVariant(v) => v.fmt(f),
2688 }
2689 }
2690}
2691
2692impl Coercible for CompoundValue {
2693 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2694 if target.is_union() || target.is_none() || self.ty().eq(target) {
2695 return Ok(self.clone());
2696 }
2697
2698 if let Type::Compound(target_ty, _) = target {
2699 match (self, target_ty) {
2700 (Self::Array(v), CompoundType::Array(target_ty)) => {
2702 if v.is_empty() && target_ty.is_non_empty() {
2705 bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2706 }
2707
2708 return Ok(Self::Array(Array::new_with_context(
2709 context,
2710 target_ty.clone(),
2711 v.as_slice().iter().cloned(),
2712 )?));
2713 }
2714 (Self::Map(v), CompoundType::Map(target_ty)) => {
2716 return Ok(Self::Map(Map::new_with_context(
2717 context,
2718 target_ty.clone(),
2719 v.iter().map(|(k, v)| {
2720 (
2721 k.clone()
2722 .map(Into::into)
2723 .unwrap_or(Value::new_none(target_ty.key_type().optional())),
2724 v.clone(),
2725 )
2726 }),
2727 )?));
2728 }
2729 (Self::Pair(v), CompoundType::Pair(target_ty)) => {
2731 return Ok(Self::Pair(Pair::new_with_context(
2732 context,
2733 target_ty.clone(),
2734 v.values.0.clone(),
2735 v.values.1.clone(),
2736 )?));
2737 }
2738 (Self::Map(v), CompoundType::Custom(CustomType::Struct(target_ty))) => {
2740 let len = v.len();
2741 let expected_len = target_ty.members().len();
2742
2743 if len != expected_len {
2744 bail!(
2745 "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2746 as the struct has {expected_len} member{s2}",
2747 s1 = if len == 1 { "" } else { "s" },
2748 s2 = if expected_len == 1 { "" } else { "s" }
2749 );
2750 }
2751
2752 return Ok(Self::Struct(Struct {
2753 ty: target.clone(),
2754 name: target_ty.name().clone(),
2755 members: Arc::new(
2756 v.iter()
2757 .map(|(k, v)| {
2758 let k = k
2759 .as_ref()
2760 .and_then(|k| {
2761 k.coerce(context, &PrimitiveType::String.into()).ok()
2762 })
2763 .with_context(|| {
2764 format!(
2765 "cannot coerce a map of type `{map_type}` to \
2766 struct type `{target}` as the key type cannot \
2767 coerce to `String`",
2768 map_type = v.ty()
2769 )
2770 })?
2771 .unwrap_string();
2772 let ty =
2773 target_ty.members().get(k.as_ref()).with_context(|| {
2774 format!(
2775 "cannot coerce a map with key `{k}` to struct \
2776 type `{target}` as the struct does not contain a \
2777 member with that name"
2778 )
2779 })?;
2780 let v = v.coerce(context, ty).with_context(|| {
2781 format!("failed to coerce value of map key `{k}")
2782 })?;
2783 Ok((k.to_string(), v))
2784 })
2785 .collect::<Result<_>>()?,
2786 ),
2787 }));
2788 }
2789 (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2791 let key_ty = map_ty.key_type();
2792 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2793 bail!(
2794 "cannot coerce a struct to type `{target}` as key type `{key_ty}` \
2795 cannot be coerced from `String`"
2796 );
2797 }
2798
2799 let value_ty = map_ty.value_type();
2800 return Ok(Self::Map(Map::new_unchecked(
2801 target.clone(),
2802 members
2803 .iter()
2804 .map(|(n, v)| {
2805 let v = v
2806 .coerce(context, value_ty)
2807 .with_context(|| format!("failed to coerce member `{n}`"))?;
2808 Ok((
2809 PrimitiveValue::new_string(n)
2810 .coerce(context, key_ty)
2811 .expect("should coerce")
2812 .into(),
2813 v,
2814 ))
2815 })
2816 .collect::<Result<_>>()?,
2817 )));
2818 }
2819 (Self::Object(object), CompoundType::Map(map_ty)) => {
2821 let key_ty = map_ty.key_type();
2822 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2823 bail!(
2824 "cannot coerce an object to type `{target}` as key type `{key_ty}` \
2825 cannot be coerced from `String`"
2826 );
2827 }
2828
2829 let value_ty = map_ty.value_type();
2830 return Ok(Self::Map(Map::new_unchecked(
2831 target.clone(),
2832 object
2833 .iter()
2834 .map(|(n, v)| {
2835 let v = v
2836 .coerce(context, value_ty)
2837 .with_context(|| format!("failed to coerce member `{n}`"))?;
2838 Ok((
2839 PrimitiveValue::new_string(n)
2840 .coerce(context, key_ty)
2841 .expect("should coerce")
2842 .into(),
2843 v,
2844 ))
2845 })
2846 .collect::<Result<_>>()?,
2847 )));
2848 }
2849 (Self::Object(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2851 return Ok(Self::Struct(Struct::new_with_context(
2852 context,
2853 struct_ty.clone(),
2854 v.iter().map(|(k, v)| (k, v.clone())),
2855 )?));
2856 }
2857 (Self::Struct(v), CompoundType::Custom(CustomType::Struct(struct_ty))) => {
2859 let len = v.members.len();
2860 let expected_len = struct_ty.members().len();
2861
2862 if len != expected_len {
2863 bail!(
2864 "cannot coerce a struct of {len} members{s1} to struct type \
2865 `{target}` as the target struct has {expected_len} member{s2}",
2866 s1 = if len == 1 { "" } else { "s" },
2867 s2 = if expected_len == 1 { "" } else { "s" }
2868 );
2869 }
2870
2871 return Ok(Self::Struct(Struct {
2872 ty: target.clone(),
2873 name: struct_ty.name().clone(),
2874 members: Arc::new(
2875 v.members
2876 .iter()
2877 .map(|(k, v)| {
2878 let ty = struct_ty.members().get(k).ok_or_else(|| {
2879 anyhow!(
2880 "cannot coerce a struct with member `{k}` to struct \
2881 type `{target}` as the target struct does not \
2882 contain a member with that name",
2883 )
2884 })?;
2885 let v = v.coerce(context, ty).with_context(|| {
2886 format!("failed to coerce member `{k}`")
2887 })?;
2888 Ok((k.clone(), v))
2889 })
2890 .collect::<Result<_>>()?,
2891 ),
2892 }));
2893 }
2894 _ => {}
2895 }
2896 }
2897
2898 if let Type::Object = target {
2899 match self {
2900 Self::Map(v) => {
2902 return Ok(Self::Object(Object::new(
2903 v.iter()
2904 .map(|(k, v)| {
2905 let k = k
2906 .as_ref()
2907 .and_then(|k| {
2908 k.coerce(context, &PrimitiveType::String.into()).ok()
2909 })
2910 .with_context(|| {
2911 format!(
2912 "cannot coerce a map of type `{map_type}` to `Object` \
2913 as the key type cannot coerce to `String`",
2914 map_type = v.ty()
2915 )
2916 })?
2917 .unwrap_string();
2918 Ok((k.to_string(), v.clone()))
2919 })
2920 .collect::<Result<IndexMap<_, _>>>()?,
2921 )));
2922 }
2923 Self::Struct(v) => {
2925 return Ok(Self::Object(Object {
2926 members: Some(v.members.clone()),
2927 }));
2928 }
2929 _ => {}
2930 };
2931 }
2932
2933 bail!(
2934 "cannot coerce a value of type `{ty}` to type `{target}`",
2935 ty = self.ty()
2936 );
2937 }
2938}
2939
2940impl From<Pair> for CompoundValue {
2941 fn from(value: Pair) -> Self {
2942 Self::Pair(value)
2943 }
2944}
2945
2946impl From<Array> for CompoundValue {
2947 fn from(value: Array) -> Self {
2948 Self::Array(value)
2949 }
2950}
2951
2952impl From<Map> for CompoundValue {
2953 fn from(value: Map) -> Self {
2954 Self::Map(value)
2955 }
2956}
2957
2958impl From<Object> for CompoundValue {
2959 fn from(value: Object) -> Self {
2960 Self::Object(value)
2961 }
2962}
2963
2964impl From<Struct> for CompoundValue {
2965 fn from(value: Struct) -> Self {
2966 Self::Struct(value)
2967 }
2968}
2969
2970#[derive(Debug, Clone)]
2974pub enum HiddenValue {
2975 Hints(HintsValue),
2979 Input(InputValue),
2983 Output(OutputValue),
2987 TaskPreEvaluation(TaskPreEvaluationValue),
2992 TaskPostEvaluation(TaskPostEvaluationValue),
2997 PreviousTaskData(PreviousTaskDataValue),
3002}
3003
3004impl HiddenValue {
3005 pub fn ty(&self) -> Type {
3007 match self {
3008 Self::Hints(_) => Type::Hidden(HiddenType::Hints),
3009 Self::Input(_) => Type::Hidden(HiddenType::Input),
3010 Self::Output(_) => Type::Hidden(HiddenType::Output),
3011 Self::TaskPreEvaluation(_) => Type::Hidden(HiddenType::TaskPreEvaluation),
3012 Self::TaskPostEvaluation(_) => Type::Hidden(HiddenType::TaskPostEvaluation),
3013 Self::PreviousTaskData(_) => Type::Hidden(HiddenType::PreviousTaskData),
3014 }
3015 }
3016}
3017
3018impl fmt::Display for HiddenValue {
3019 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3020 match self {
3021 Self::Hints(v) => v.fmt(f),
3022 Self::Input(v) => v.fmt(f),
3023 Self::Output(v) => v.fmt(f),
3024 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => write!(f, "task"),
3025 Self::PreviousTaskData(_) => write!(f, "task.previous"),
3026 }
3027 }
3028}
3029
3030impl Coercible for HiddenValue {
3031 fn coerce(&self, _: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
3032 match self {
3033 Self::Hints(_) => {
3034 if matches!(target, Type::Hidden(HiddenType::Hints)) {
3035 return Ok(self.clone());
3036 }
3037
3038 bail!("hints values cannot be coerced to any other type");
3039 }
3040 Self::Input(_) => {
3041 if matches!(target, Type::Hidden(HiddenType::Input)) {
3042 return Ok(self.clone());
3043 }
3044
3045 bail!("input values cannot be coerced to any other type");
3046 }
3047 Self::Output(_) => {
3048 if matches!(target, Type::Hidden(HiddenType::Output)) {
3049 return Ok(self.clone());
3050 }
3051
3052 bail!("output values cannot be coerced to any other type");
3053 }
3054 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => {
3055 if matches!(
3056 target,
3057 Type::Hidden(HiddenType::TaskPreEvaluation)
3058 | Type::Hidden(HiddenType::TaskPostEvaluation)
3059 ) {
3060 return Ok(self.clone());
3061 }
3062
3063 bail!("task variables cannot be coerced to any other type");
3064 }
3065 Self::PreviousTaskData(_) => {
3066 if matches!(target, Type::Hidden(HiddenType::PreviousTaskData)) {
3067 return Ok(self.clone());
3068 }
3069
3070 bail!("previous task data values cannot be coerced to any other type");
3071 }
3072 }
3073 }
3074}
3075
3076#[derive(Debug, Clone)]
3080pub(crate) struct TaskPostEvaluationData {
3081 container: Option<Arc<String>>,
3083 cpu: f64,
3085 memory: i64,
3087 gpu: Array,
3092 fpga: Array,
3097 disks: Map,
3104 max_retries: i64,
3106}
3107
3108#[derive(Debug, Clone)]
3112pub struct PreviousTaskDataValue(Option<Arc<TaskPostEvaluationData>>);
3113
3114impl PreviousTaskDataValue {
3115 pub(crate) fn new(data: Arc<TaskPostEvaluationData>) -> Self {
3117 Self(Some(data))
3118 }
3119
3120 pub(crate) fn empty() -> Self {
3122 Self(None)
3123 }
3124
3125 pub fn field(&self, name: &str) -> Option<Value> {
3132 match name {
3133 TASK_FIELD_MEMORY => Some(
3134 self.0
3135 .as_ref()
3136 .map(|data| Value::from(data.memory))
3137 .unwrap_or_else(|| {
3138 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3139 }),
3140 ),
3141 TASK_FIELD_CPU => Some(
3142 self.0
3143 .as_ref()
3144 .map(|data| Value::from(data.cpu))
3145 .unwrap_or_else(|| {
3146 Value::new_none(Type::from(PrimitiveType::Float).optional())
3147 }),
3148 ),
3149 TASK_FIELD_CONTAINER => Some(
3150 self.0
3151 .as_ref()
3152 .and_then(|data| {
3153 data.container
3154 .as_ref()
3155 .map(|c| PrimitiveValue::String(c.clone()).into())
3156 })
3157 .unwrap_or_else(|| {
3158 Value::new_none(Type::from(PrimitiveType::String).optional())
3159 }),
3160 ),
3161 TASK_FIELD_GPU => Some(
3162 self.0
3163 .as_ref()
3164 .map(|data| Value::from(data.gpu.clone()))
3165 .unwrap_or_else(|| {
3166 Value::new_none(Type::Compound(
3167 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3168 true,
3169 ))
3170 }),
3171 ),
3172 TASK_FIELD_FPGA => Some(
3173 self.0
3174 .as_ref()
3175 .map(|data| Value::from(data.fpga.clone()))
3176 .unwrap_or_else(|| {
3177 Value::new_none(Type::Compound(
3178 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
3179 true,
3180 ))
3181 }),
3182 ),
3183 TASK_FIELD_DISKS => Some(
3184 self.0
3185 .as_ref()
3186 .map(|data| Value::from(data.disks.clone()))
3187 .unwrap_or_else(|| {
3188 Value::new_none(Type::Compound(
3189 MapType::new(PrimitiveType::String, PrimitiveType::Integer).into(),
3190 true,
3191 ))
3192 }),
3193 ),
3194 TASK_FIELD_MAX_RETRIES => Some(
3195 self.0
3196 .as_ref()
3197 .map(|data| Value::from(data.max_retries))
3198 .unwrap_or_else(|| {
3199 Value::new_none(Type::from(PrimitiveType::Integer).optional())
3200 }),
3201 ),
3202 _ => None,
3203 }
3204 }
3205}
3206
3207#[derive(Debug, Clone)]
3214pub struct TaskPreEvaluationValue {
3215 name: Arc<String>,
3217 id: Arc<String>,
3219 attempt: i64,
3224 meta: Object,
3226 parameter_meta: Object,
3228 ext: Object,
3230 previous: PreviousTaskDataValue,
3236}
3237
3238impl TaskPreEvaluationValue {
3239 pub(crate) fn new(
3242 name: impl Into<String>,
3243 id: impl Into<String>,
3244 attempt: i64,
3245 meta: Object,
3246 parameter_meta: Object,
3247 ext: Object,
3248 ) -> Self {
3249 Self {
3250 name: Arc::new(name.into()),
3251 id: Arc::new(id.into()),
3252 meta,
3253 parameter_meta,
3254 ext,
3255 attempt,
3256 previous: PreviousTaskDataValue::empty(),
3257 }
3258 }
3259
3260 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3262 self.previous = PreviousTaskDataValue::new(data);
3263 }
3264
3265 pub fn name(&self) -> &Arc<String> {
3267 &self.name
3268 }
3269
3270 pub fn id(&self) -> &Arc<String> {
3272 &self.id
3273 }
3274
3275 pub fn attempt(&self) -> i64 {
3277 self.attempt
3278 }
3279
3280 pub fn field(&self, name: &str) -> Option<Value> {
3284 match name {
3285 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
3286 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
3287 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
3288 TASK_FIELD_META => Some(self.meta.clone().into()),
3289 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
3290 TASK_FIELD_EXT => Some(self.ext.clone().into()),
3291 TASK_FIELD_PREVIOUS => {
3292 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
3293 }
3294 _ => None,
3295 }
3296 }
3297}
3298
3299#[derive(Debug, Clone)]
3305pub struct TaskPostEvaluationValue {
3306 data: Arc<TaskPostEvaluationData>,
3308 name: Arc<String>,
3310 id: Arc<String>,
3312 attempt: i64,
3317 meta: Object,
3319 parameter_meta: Object,
3321 ext: Object,
3323 return_code: Option<i64>,
3327 end_time: Option<i64>,
3331 previous: PreviousTaskDataValue,
3337}
3338
3339impl TaskPostEvaluationValue {
3340 #[allow(clippy::too_many_arguments)]
3343 pub(crate) fn new(
3344 name: impl Into<String>,
3345 id: impl Into<String>,
3346 constraints: &TaskExecutionConstraints,
3347 max_retries: i64,
3348 attempt: i64,
3349 meta: Object,
3350 parameter_meta: Object,
3351 ext: Object,
3352 ) -> Self {
3353 Self {
3354 name: Arc::new(name.into()),
3355 id: Arc::new(id.into()),
3356 data: Arc::new(TaskPostEvaluationData {
3357 container: constraints
3358 .container
3359 .as_ref()
3360 .map(|c| Arc::new(c.to_string())),
3361 cpu: constraints.cpu,
3362 memory: constraints
3363 .memory
3364 .try_into()
3365 .expect("memory exceeds a valid WDL value"),
3366 gpu: Array::new_unchecked(
3367 ANALYSIS_STDLIB.array_string_type().clone(),
3368 constraints
3369 .gpu
3370 .iter()
3371 .map(|v| PrimitiveValue::new_string(v).into())
3372 .collect(),
3373 ),
3374 fpga: Array::new_unchecked(
3375 ANALYSIS_STDLIB.array_string_type().clone(),
3376 constraints
3377 .fpga
3378 .iter()
3379 .map(|v| PrimitiveValue::new_string(v).into())
3380 .collect(),
3381 ),
3382 disks: Map::new_unchecked(
3383 ANALYSIS_STDLIB.map_string_int_type().clone(),
3384 constraints
3385 .disks
3386 .iter()
3387 .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), (*v).into()))
3388 .collect(),
3389 ),
3390 max_retries,
3391 }),
3392 attempt,
3393 meta,
3394 parameter_meta,
3395 ext,
3396 return_code: None,
3397 end_time: None,
3398 previous: PreviousTaskDataValue::empty(),
3399 }
3400 }
3401
3402 pub fn name(&self) -> &Arc<String> {
3404 &self.name
3405 }
3406
3407 pub fn id(&self) -> &Arc<String> {
3409 &self.id
3410 }
3411
3412 pub fn container(&self) -> Option<&Arc<String>> {
3414 self.data.container.as_ref()
3415 }
3416
3417 pub fn cpu(&self) -> f64 {
3419 self.data.cpu
3420 }
3421
3422 pub fn memory(&self) -> i64 {
3424 self.data.memory
3425 }
3426
3427 pub fn gpu(&self) -> &Array {
3432 &self.data.gpu
3433 }
3434
3435 pub fn fpga(&self) -> &Array {
3440 &self.data.fpga
3441 }
3442
3443 pub fn disks(&self) -> &Map {
3450 &self.data.disks
3451 }
3452
3453 pub fn attempt(&self) -> i64 {
3458 self.attempt
3459 }
3460
3461 pub fn end_time(&self) -> Option<i64> {
3465 self.end_time
3466 }
3467
3468 pub fn return_code(&self) -> Option<i64> {
3472 self.return_code
3473 }
3474
3475 pub fn meta(&self) -> &Object {
3477 &self.meta
3478 }
3479
3480 pub fn parameter_meta(&self) -> &Object {
3482 &self.parameter_meta
3483 }
3484
3485 pub fn ext(&self) -> &Object {
3487 &self.ext
3488 }
3489
3490 pub(crate) fn set_return_code(&mut self, code: i32) {
3492 self.return_code = Some(code as i64);
3493 }
3494
3495 pub(crate) fn set_attempt(&mut self, attempt: i64) {
3497 self.attempt = attempt;
3498 }
3499
3500 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3502 self.previous = PreviousTaskDataValue::new(data);
3503 }
3504
3505 pub(crate) fn data(&self) -> &Arc<TaskPostEvaluationData> {
3507 &self.data
3508 }
3509
3510 pub fn field(&self, version: SupportedVersion, name: &str) -> Option<Value> {
3514 match name {
3515 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
3516 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
3517 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
3518 TASK_FIELD_CONTAINER => Some(
3519 self.data
3520 .container
3521 .clone()
3522 .map(|c| PrimitiveValue::String(c).into())
3523 .unwrap_or_else(|| {
3524 Value::new_none(
3525 task_member_type_post_evaluation(version, TASK_FIELD_CONTAINER)
3526 .expect("failed to get task field type"),
3527 )
3528 }),
3529 ),
3530 TASK_FIELD_CPU => Some(self.data.cpu.into()),
3531 TASK_FIELD_MEMORY => Some(self.data.memory.into()),
3532 TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
3533 TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
3534 TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
3535 TASK_FIELD_END_TIME => Some(self.end_time.map(Into::into).unwrap_or_else(|| {
3536 Value::new_none(
3537 task_member_type_post_evaluation(version, TASK_FIELD_END_TIME)
3538 .expect("failed to get task field type"),
3539 )
3540 })),
3541 TASK_FIELD_RETURN_CODE => Some(self.return_code.map(Into::into).unwrap_or_else(|| {
3542 Value::new_none(
3543 task_member_type_post_evaluation(version, TASK_FIELD_RETURN_CODE)
3544 .expect("failed to get task field type"),
3545 )
3546 })),
3547 TASK_FIELD_META => Some(self.meta.clone().into()),
3548 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
3549 TASK_FIELD_EXT => Some(self.ext.clone().into()),
3550 TASK_FIELD_MAX_RETRIES if version >= SupportedVersion::V1(V1::Three) => {
3551 Some(self.data.max_retries.into())
3552 }
3553 TASK_FIELD_PREVIOUS if version >= SupportedVersion::V1(V1::Three) => {
3554 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
3555 }
3556 _ => None,
3557 }
3558 }
3559}
3560
3561#[derive(Debug, Clone)]
3565pub struct HintsValue(Object);
3566
3567impl HintsValue {
3568 pub fn new(members: IndexMap<String, Value>) -> Self {
3570 Self(Object::new(members))
3571 }
3572
3573 pub fn as_object(&self) -> &Object {
3575 &self.0
3576 }
3577}
3578
3579impl From<HintsValue> for Value {
3580 fn from(value: HintsValue) -> Self {
3581 Self::Hidden(HiddenValue::Hints(value))
3582 }
3583}
3584
3585impl fmt::Display for HintsValue {
3586 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3587 write!(f, "hints {{")?;
3588
3589 for (i, (k, v)) in self.0.iter().enumerate() {
3590 if i > 0 {
3591 write!(f, ", ")?;
3592 }
3593
3594 write!(f, "{k}: {v}")?;
3595 }
3596
3597 write!(f, "}}")
3598 }
3599}
3600
3601impl From<Object> for HintsValue {
3602 fn from(value: Object) -> Self {
3603 Self(value)
3604 }
3605}
3606
3607#[derive(Debug, Clone)]
3611pub struct InputValue(Object);
3612
3613impl InputValue {
3614 pub fn new(members: IndexMap<String, Value>) -> Self {
3616 Self(Object::new(members))
3617 }
3618
3619 pub fn as_object(&self) -> &Object {
3621 &self.0
3622 }
3623}
3624
3625impl From<InputValue> for Value {
3626 fn from(value: InputValue) -> Self {
3627 Self::Hidden(HiddenValue::Input(value))
3628 }
3629}
3630
3631impl fmt::Display for InputValue {
3632 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3633 write!(f, "input {{")?;
3634
3635 for (i, (k, v)) in self.0.iter().enumerate() {
3636 if i > 0 {
3637 write!(f, ", ")?;
3638 }
3639
3640 write!(f, "{k}: {v}")?;
3641 }
3642
3643 write!(f, "}}")
3644 }
3645}
3646
3647impl From<Object> for InputValue {
3648 fn from(value: Object) -> Self {
3649 Self(value)
3650 }
3651}
3652
3653#[derive(Debug, Clone)]
3657pub struct OutputValue(Object);
3658
3659impl OutputValue {
3660 pub fn new(members: IndexMap<String, Value>) -> Self {
3662 Self(Object::new(members))
3663 }
3664
3665 pub fn as_object(&self) -> &Object {
3667 &self.0
3668 }
3669}
3670
3671impl From<OutputValue> for Value {
3672 fn from(value: OutputValue) -> Self {
3673 Self::Hidden(HiddenValue::Output(value))
3674 }
3675}
3676
3677impl fmt::Display for OutputValue {
3678 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3679 write!(f, "output {{")?;
3680
3681 for (i, (k, v)) in self.0.iter().enumerate() {
3682 if i > 0 {
3683 write!(f, ", ")?;
3684 }
3685
3686 write!(f, "{k}: {v}")?;
3687 }
3688
3689 write!(f, "}}")
3690 }
3691}
3692
3693impl From<Object> for OutputValue {
3694 fn from(value: Object) -> Self {
3695 Self(value)
3696 }
3697}
3698
3699#[derive(Debug, Clone)]
3703pub struct CallValue {
3704 ty: CallType,
3706 outputs: Arc<Outputs>,
3708}
3709
3710impl CallValue {
3711 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3714 Self { ty, outputs }
3715 }
3716
3717 pub fn ty(&self) -> &CallType {
3719 &self.ty
3720 }
3721
3722 pub fn outputs(&self) -> &Outputs {
3724 self.outputs.as_ref()
3725 }
3726}
3727
3728impl fmt::Display for CallValue {
3729 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3730 write!(f, "call output {{")?;
3731
3732 for (i, (k, v)) in self.outputs.iter().enumerate() {
3733 if i > 0 {
3734 write!(f, ", ")?;
3735 }
3736
3737 write!(f, "{k}: {v}")?;
3738 }
3739
3740 write!(f, "}}")
3741 }
3742}
3743
3744pub(crate) struct ValueSerializer<'a> {
3746 context: Option<&'a dyn EvaluationContext>,
3748 value: &'a Value,
3750 allow_pairs: bool,
3753}
3754
3755impl<'a> ValueSerializer<'a> {
3756 pub fn new(
3762 context: Option<&'a dyn EvaluationContext>,
3763 value: &'a Value,
3764 allow_pairs: bool,
3765 ) -> Self {
3766 Self {
3767 context,
3768 value,
3769 allow_pairs,
3770 }
3771 }
3772}
3773
3774impl serde::Serialize for ValueSerializer<'_> {
3775 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3776 where
3777 S: serde::Serializer,
3778 {
3779 use serde::ser::Error;
3780
3781 match &self.value {
3782 Value::None(_) => serializer.serialize_none(),
3783 Value::Primitive(v) => {
3784 PrimitiveValueSerializer::new(self.context, v).serialize(serializer)
3785 }
3786 Value::Compound(v) => CompoundValueSerializer::new(self.context, v, self.allow_pairs)
3787 .serialize(serializer),
3788 Value::Call(_) | Value::Hidden(_) | Value::TypeNameRef(_) => {
3789 Err(S::Error::custom("value cannot be serialized"))
3790 }
3791 }
3792 }
3793}
3794
3795pub(crate) struct PrimitiveValueSerializer<'a> {
3797 context: Option<&'a dyn EvaluationContext>,
3799 value: &'a PrimitiveValue,
3801}
3802
3803impl<'a> PrimitiveValueSerializer<'a> {
3804 pub fn new(context: Option<&'a dyn EvaluationContext>, value: &'a PrimitiveValue) -> Self {
3810 Self { context, value }
3811 }
3812}
3813
3814impl serde::Serialize for PrimitiveValueSerializer<'_> {
3815 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3816 where
3817 S: serde::Serializer,
3818 {
3819 match self.value {
3820 PrimitiveValue::Boolean(v) => v.serialize(serializer),
3821 PrimitiveValue::Integer(v) => v.serialize(serializer),
3822 PrimitiveValue::Float(v) => v.serialize(serializer),
3823 PrimitiveValue::String(s) => s.serialize(serializer),
3824 PrimitiveValue::File(p) | PrimitiveValue::Directory(p) => {
3825 let path = self
3826 .context
3827 .and_then(|c| c.guest_path(p).map(|p| Cow::Owned(p.0)))
3828 .unwrap_or(Cow::Borrowed(&p.0));
3829
3830 path.serialize(serializer)
3831 }
3832 }
3833 }
3834}
3835
3836pub(crate) struct CompoundValueSerializer<'a> {
3838 context: Option<&'a dyn EvaluationContext>,
3840 value: &'a CompoundValue,
3842 allow_pairs: bool,
3845}
3846
3847impl<'a> CompoundValueSerializer<'a> {
3848 pub fn new(
3854 context: Option<&'a dyn EvaluationContext>,
3855 value: &'a CompoundValue,
3856 allow_pairs: bool,
3857 ) -> Self {
3858 Self {
3859 context,
3860 value,
3861 allow_pairs,
3862 }
3863 }
3864}
3865
3866impl serde::Serialize for CompoundValueSerializer<'_> {
3867 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3868 where
3869 S: serde::Serializer,
3870 {
3871 use serde::ser::Error;
3872
3873 match &self.value {
3874 CompoundValue::Pair(pair) if self.allow_pairs => {
3875 let mut state = serializer.serialize_map(Some(2))?;
3876 let left = ValueSerializer::new(self.context, pair.left(), self.allow_pairs);
3877 let right = ValueSerializer::new(self.context, pair.right(), self.allow_pairs);
3878 state.serialize_entry("left", &left)?;
3879 state.serialize_entry("right", &right)?;
3880 state.end()
3881 }
3882 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3883 CompoundValue::Array(v) => {
3884 let mut s = serializer.serialize_seq(Some(v.len()))?;
3885 for v in v.as_slice() {
3886 s.serialize_element(&ValueSerializer::new(self.context, v, self.allow_pairs))?;
3887 }
3888
3889 s.end()
3890 }
3891 CompoundValue::Map(v) => {
3892 let ty = v.ty();
3893 let map_type = ty.as_map().expect("type should be a map");
3894 if !map_type
3895 .key_type()
3896 .is_coercible_to(&PrimitiveType::String.into())
3897 {
3898 return Err(S::Error::custom(format!(
3899 "cannot serialize a map of type `{ty}` as the key type cannot be coerced \
3900 to `String`",
3901 )));
3902 }
3903
3904 let mut s = serializer.serialize_map(Some(v.len()))?;
3905 for (k, v) in v.iter() {
3906 s.serialize_entry(
3907 &k.as_ref()
3908 .map(|k| PrimitiveValueSerializer::new(self.context, k)),
3909 &ValueSerializer::new(self.context, v, self.allow_pairs),
3910 )?;
3911 }
3912
3913 s.end()
3914 }
3915 CompoundValue::Object(object) => {
3916 let mut s = serializer.serialize_map(Some(object.len()))?;
3917 for (k, v) in object.iter() {
3918 s.serialize_entry(k, &ValueSerializer::new(self.context, v, self.allow_pairs))?;
3919 }
3920
3921 s.end()
3922 }
3923 CompoundValue::Struct(Struct { members, .. }) => {
3924 let mut s = serializer.serialize_map(Some(members.len()))?;
3925 for (k, v) in members.iter() {
3926 s.serialize_entry(k, &ValueSerializer::new(self.context, v, self.allow_pairs))?;
3927 }
3928
3929 s.end()
3930 }
3931 CompoundValue::EnumVariant(e) => serializer.serialize_str(e.name()),
3932 }
3933 }
3934}
3935
3936#[cfg(test)]
3937mod test {
3938 use std::iter::empty;
3939
3940 use approx::assert_relative_eq;
3941 use pretty_assertions::assert_eq;
3942 use wdl_analysis::types::ArrayType;
3943 use wdl_analysis::types::MapType;
3944 use wdl_analysis::types::PairType;
3945 use wdl_analysis::types::StructType;
3946 use wdl_ast::Diagnostic;
3947 use wdl_ast::Span;
3948 use wdl_ast::SupportedVersion;
3949
3950 use super::*;
3951 use crate::EvaluationPath;
3952 use crate::http::Transferer;
3953
3954 #[test]
3955 fn boolean_coercion() {
3956 assert_eq!(
3958 Value::from(false)
3959 .coerce(None, &PrimitiveType::Boolean.into())
3960 .expect("should coerce")
3961 .unwrap_boolean(),
3962 Value::from(false).unwrap_boolean()
3963 );
3964 assert_eq!(
3966 format!(
3967 "{e:#}",
3968 e = Value::from(true)
3969 .coerce(None, &PrimitiveType::String.into())
3970 .unwrap_err()
3971 ),
3972 "cannot coerce type `Boolean` to type `String`"
3973 );
3974 }
3975
3976 #[test]
3977 fn boolean_display() {
3978 assert_eq!(Value::from(false).to_string(), "false");
3979 assert_eq!(Value::from(true).to_string(), "true");
3980 }
3981
3982 #[test]
3983 fn integer_coercion() {
3984 assert_eq!(
3986 Value::from(12345)
3987 .coerce(None, &PrimitiveType::Integer.into())
3988 .expect("should coerce")
3989 .unwrap_integer(),
3990 Value::from(12345).unwrap_integer()
3991 );
3992 assert_relative_eq!(
3994 Value::from(12345)
3995 .coerce(None, &PrimitiveType::Float.into())
3996 .expect("should coerce")
3997 .unwrap_float(),
3998 Value::from(12345.0).unwrap_float()
3999 );
4000 assert_eq!(
4002 format!(
4003 "{e:#}",
4004 e = Value::from(12345)
4005 .coerce(None, &PrimitiveType::Boolean.into())
4006 .unwrap_err()
4007 ),
4008 "cannot coerce type `Int` to type `Boolean`"
4009 );
4010 }
4011
4012 #[test]
4013 fn integer_display() {
4014 assert_eq!(Value::from(12345).to_string(), "12345");
4015 assert_eq!(Value::from(-12345).to_string(), "-12345");
4016 }
4017
4018 #[test]
4019 fn float_coercion() {
4020 assert_relative_eq!(
4022 Value::from(12345.0)
4023 .coerce(None, &PrimitiveType::Float.into())
4024 .expect("should coerce")
4025 .unwrap_float(),
4026 Value::from(12345.0).unwrap_float()
4027 );
4028 assert_eq!(
4030 format!(
4031 "{e:#}",
4032 e = Value::from(12345.0)
4033 .coerce(None, &PrimitiveType::Integer.into())
4034 .unwrap_err()
4035 ),
4036 "cannot coerce type `Float` to type `Int`"
4037 );
4038 }
4039
4040 #[test]
4041 fn float_display() {
4042 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
4043 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
4044 }
4045
4046 #[test]
4047 fn string_coercion() {
4048 let value = PrimitiveValue::new_string("foo");
4049 assert_eq!(
4051 value
4052 .coerce(None, &PrimitiveType::String.into())
4053 .expect("should coerce"),
4054 value
4055 );
4056 assert_eq!(
4058 value
4059 .coerce(None, &PrimitiveType::File.into())
4060 .expect("should coerce"),
4061 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
4062 );
4063 assert_eq!(
4065 value
4066 .coerce(None, &PrimitiveType::Directory.into())
4067 .expect("should coerce"),
4068 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
4069 );
4070 assert_eq!(
4072 format!(
4073 "{e:#}",
4074 e = value
4075 .coerce(None, &PrimitiveType::Boolean.into())
4076 .unwrap_err()
4077 ),
4078 "cannot coerce type `String` to type `Boolean`"
4079 );
4080
4081 struct Context;
4082
4083 impl EvaluationContext for Context {
4084 fn version(&self) -> SupportedVersion {
4085 unimplemented!()
4086 }
4087
4088 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4089 unimplemented!()
4090 }
4091
4092 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4093 unimplemented!()
4094 }
4095
4096 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4097 unimplemented!()
4098 }
4099
4100 fn base_dir(&self) -> &EvaluationPath {
4101 unimplemented!()
4102 }
4103
4104 fn temp_dir(&self) -> &Path {
4105 unimplemented!()
4106 }
4107
4108 fn transferer(&self) -> &dyn Transferer {
4109 unimplemented!()
4110 }
4111
4112 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
4113 if path.as_str() == "/mnt/task/input/0/path" {
4114 Some(HostPath::new("/some/host/path"))
4115 } else {
4116 None
4117 }
4118 }
4119 }
4120
4121 assert_eq!(
4123 PrimitiveValue::new_string("/mnt/task/input/0/path")
4124 .coerce(Some(&Context), &PrimitiveType::File.into())
4125 .expect("should coerce")
4126 .unwrap_file()
4127 .as_str(),
4128 "/some/host/path"
4129 );
4130
4131 assert_eq!(
4133 value
4134 .coerce(Some(&Context), &PrimitiveType::File.into())
4135 .expect("should coerce")
4136 .unwrap_file()
4137 .as_str(),
4138 "foo"
4139 );
4140
4141 assert_eq!(
4143 PrimitiveValue::new_string("/mnt/task/input/0/path")
4144 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4145 .expect("should coerce")
4146 .unwrap_directory()
4147 .as_str(),
4148 "/some/host/path"
4149 );
4150
4151 assert_eq!(
4153 value
4154 .coerce(Some(&Context), &PrimitiveType::Directory.into())
4155 .expect("should coerce")
4156 .unwrap_directory()
4157 .as_str(),
4158 "foo"
4159 );
4160 }
4161
4162 #[test]
4163 fn string_display() {
4164 let value = PrimitiveValue::new_string("hello world!");
4165 assert_eq!(value.to_string(), "\"hello world!\"");
4166 }
4167
4168 #[test]
4169 fn file_coercion() {
4170 let value = PrimitiveValue::new_file("foo");
4171
4172 assert_eq!(
4174 value
4175 .coerce(None, &PrimitiveType::File.into())
4176 .expect("should coerce"),
4177 value
4178 );
4179 assert_eq!(
4181 value
4182 .coerce(None, &PrimitiveType::String.into())
4183 .expect("should coerce"),
4184 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
4185 );
4186 assert_eq!(
4188 format!(
4189 "{e:#}",
4190 e = value
4191 .coerce(None, &PrimitiveType::Directory.into())
4192 .unwrap_err()
4193 ),
4194 "cannot coerce type `File` to type `Directory`"
4195 );
4196
4197 struct Context;
4198
4199 impl EvaluationContext for Context {
4200 fn version(&self) -> SupportedVersion {
4201 unimplemented!()
4202 }
4203
4204 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4205 unimplemented!()
4206 }
4207
4208 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4209 unimplemented!()
4210 }
4211
4212 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4213 unimplemented!()
4214 }
4215
4216 fn base_dir(&self) -> &EvaluationPath {
4217 unimplemented!()
4218 }
4219
4220 fn temp_dir(&self) -> &Path {
4221 unimplemented!()
4222 }
4223
4224 fn transferer(&self) -> &dyn Transferer {
4225 unimplemented!()
4226 }
4227
4228 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4229 if path.as_str() == "/some/host/path" {
4230 Some(GuestPath::new("/mnt/task/input/0/path"))
4231 } else {
4232 None
4233 }
4234 }
4235 }
4236
4237 assert_eq!(
4239 PrimitiveValue::new_file("/some/host/path")
4240 .coerce(Some(&Context), &PrimitiveType::String.into())
4241 .expect("should coerce")
4242 .unwrap_string()
4243 .as_str(),
4244 "/mnt/task/input/0/path"
4245 );
4246
4247 assert_eq!(
4249 value
4250 .coerce(Some(&Context), &PrimitiveType::String.into())
4251 .expect("should coerce")
4252 .unwrap_string()
4253 .as_str(),
4254 "foo"
4255 );
4256 }
4257
4258 #[test]
4259 fn file_display() {
4260 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
4261 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
4262 }
4263
4264 #[test]
4265 fn directory_coercion() {
4266 let value = PrimitiveValue::new_directory("foo");
4267
4268 assert_eq!(
4270 value
4271 .coerce(None, &PrimitiveType::Directory.into())
4272 .expect("should coerce"),
4273 value
4274 );
4275 assert_eq!(
4277 value
4278 .coerce(None, &PrimitiveType::String.into())
4279 .expect("should coerce"),
4280 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
4281 );
4282 assert_eq!(
4284 format!(
4285 "{e:#}",
4286 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
4287 ),
4288 "cannot coerce type `Directory` to type `File`"
4289 );
4290
4291 struct Context;
4292
4293 impl EvaluationContext for Context {
4294 fn version(&self) -> SupportedVersion {
4295 unimplemented!()
4296 }
4297
4298 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
4299 unimplemented!()
4300 }
4301
4302 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
4303 unimplemented!()
4304 }
4305
4306 fn enum_variant_value(&self, _: &str, _: &str) -> Result<Value, Diagnostic> {
4307 unimplemented!()
4308 }
4309
4310 fn base_dir(&self) -> &EvaluationPath {
4311 unimplemented!()
4312 }
4313
4314 fn temp_dir(&self) -> &Path {
4315 unimplemented!()
4316 }
4317
4318 fn transferer(&self) -> &dyn Transferer {
4319 unimplemented!()
4320 }
4321
4322 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
4323 if path.as_str() == "/some/host/path" {
4324 Some(GuestPath::new("/mnt/task/input/0/path"))
4325 } else {
4326 None
4327 }
4328 }
4329 }
4330
4331 assert_eq!(
4333 PrimitiveValue::new_directory("/some/host/path")
4334 .coerce(Some(&Context), &PrimitiveType::String.into())
4335 .expect("should coerce")
4336 .unwrap_string()
4337 .as_str(),
4338 "/mnt/task/input/0/path"
4339 );
4340
4341 assert_eq!(
4343 value
4344 .coerce(Some(&Context), &PrimitiveType::String.into())
4345 .expect("should coerce")
4346 .unwrap_string()
4347 .as_str(),
4348 "foo"
4349 );
4350 }
4351
4352 #[test]
4353 fn directory_display() {
4354 let value = PrimitiveValue::new_directory("/foo/bar/baz");
4355 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
4356 }
4357
4358 #[test]
4359 fn none_coercion() {
4360 assert!(
4362 Value::new_none(Type::None)
4363 .coerce(None, &Type::from(PrimitiveType::String).optional())
4364 .expect("should coerce")
4365 .is_none(),
4366 );
4367
4368 assert_eq!(
4370 format!(
4371 "{e:#}",
4372 e = Value::new_none(Type::None)
4373 .coerce(None, &PrimitiveType::String.into())
4374 .unwrap_err()
4375 ),
4376 "cannot coerce `None` to non-optional type `String`"
4377 );
4378 }
4379
4380 #[test]
4381 fn none_display() {
4382 assert_eq!(Value::new_none(Type::None).to_string(), "None");
4383 }
4384
4385 #[test]
4386 fn array_coercion() {
4387 let src_ty = ArrayType::new(PrimitiveType::Integer);
4388 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
4389
4390 let src: CompoundValue = Array::new(src_ty, [1, 2, 3])
4392 .expect("should create array value")
4393 .into();
4394 let target = src.coerce(None, &target_ty).expect("should coerce");
4395 assert_eq!(
4396 target.unwrap_array().to_string(),
4397 "[1.000000, 2.000000, 3.000000]"
4398 );
4399
4400 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
4402 assert_eq!(
4403 format!("{e:#}", e = src.coerce(None, &target_ty).unwrap_err()),
4404 "failed to coerce array element at index 0: cannot coerce type `Int` to type `String`"
4405 );
4406 }
4407
4408 #[test]
4409 fn non_empty_array_coercion() {
4410 let ty = ArrayType::new(PrimitiveType::String);
4411 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
4412
4413 let string = PrimitiveValue::new_string("foo");
4415 let value: Value = Array::new(ty.clone(), [string])
4416 .expect("should create array")
4417 .into();
4418 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
4419
4420 let value: Value = Array::new::<Value>(ty, [])
4422 .expect("should create array")
4423 .into();
4424 assert_eq!(
4425 format!("{e:#}", e = value.coerce(None, &target_ty).unwrap_err()),
4426 "cannot coerce empty array value to non-empty array type `Array[String]+`"
4427 );
4428 }
4429
4430 #[test]
4431 fn array_display() {
4432 let ty = ArrayType::new(PrimitiveType::Integer);
4433 let value: Value = Array::new(ty, [1, 2, 3])
4434 .expect("should create array")
4435 .into();
4436
4437 assert_eq!(value.to_string(), "[1, 2, 3]");
4438 }
4439
4440 #[test]
4441 fn map_coerce() {
4442 let key1 = PrimitiveValue::new_file("foo");
4443 let value1 = PrimitiveValue::new_string("bar");
4444 let key2 = PrimitiveValue::new_file("baz");
4445 let value2 = PrimitiveValue::new_string("qux");
4446
4447 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
4448 let file_to_string: Value = Map::new(ty, [(key1, value1), (key2, value2)])
4449 .expect("should create map value")
4450 .into();
4451
4452 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
4454 let string_to_file = file_to_string
4455 .coerce(None, &ty)
4456 .expect("value should coerce");
4457 assert_eq!(
4458 string_to_file.to_string(),
4459 r#"{"foo": "bar", "baz": "qux"}"#
4460 );
4461
4462 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
4464 assert_eq!(
4465 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4466 "failed to coerce map key for element at index 0: cannot coerce type `String` to type \
4467 `Int`"
4468 );
4469
4470 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
4472 assert_eq!(
4473 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4474 "failed to coerce map value for element at index 0: cannot coerce type `File` to type \
4475 `Int`"
4476 );
4477
4478 let ty = StructType::new(
4480 "Foo",
4481 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
4482 )
4483 .into();
4484 let struct_value = string_to_file
4485 .coerce(None, &ty)
4486 .expect("value should coerce");
4487 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4488
4489 let ty = StructType::new(
4491 "Foo",
4492 [
4493 ("foo", PrimitiveType::String),
4494 ("baz", PrimitiveType::String),
4495 ],
4496 )
4497 .into();
4498 let struct_value = file_to_string
4499 .coerce(None, &ty)
4500 .expect("value should coerce");
4501 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4502
4503 let ty = StructType::new(
4505 "Foo",
4506 [
4507 ("foo", PrimitiveType::File),
4508 ("baz", PrimitiveType::File),
4509 ("qux", PrimitiveType::File),
4510 ],
4511 )
4512 .into();
4513 assert_eq!(
4514 format!("{e:#}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4515 "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
4516 );
4517
4518 let object_value = string_to_file
4520 .coerce(None, &Type::Object)
4521 .expect("value should coerce");
4522 assert_eq!(
4523 object_value.to_string(),
4524 r#"object {foo: "bar", baz: "qux"}"#
4525 );
4526
4527 let object_value = file_to_string
4529 .coerce(None, &Type::Object)
4530 .expect("value should coerce");
4531 assert_eq!(
4532 object_value.to_string(),
4533 r#"object {foo: "bar", baz: "qux"}"#
4534 );
4535 }
4536
4537 #[test]
4538 fn map_display() {
4539 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4540 let value: Value = Map::new(ty, [(1, true), (2, false)])
4541 .expect("should create map value")
4542 .into();
4543 assert_eq!(value.to_string(), "{1: true, 2: false}");
4544 }
4545
4546 #[test]
4547 fn pair_coercion() {
4548 let left = PrimitiveValue::new_file("foo");
4549 let right = PrimitiveValue::new_string("bar");
4550
4551 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4552 let value: Value = Pair::new(ty, left, right)
4553 .expect("should create pair value")
4554 .into();
4555
4556 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
4558 let value = value.coerce(None, &ty).expect("value should coerce");
4559 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
4560
4561 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
4563 assert_eq!(
4564 format!("{e:#}", e = value.coerce(None, &ty).unwrap_err()),
4565 "failed to coerce pair's left value: cannot coerce type `String` to type `Int`"
4566 );
4567 }
4568
4569 #[test]
4570 fn pair_display() {
4571 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4572 let value: Value = Pair::new(ty, 12345, false)
4573 .expect("should create pair value")
4574 .into();
4575 assert_eq!(value.to_string(), "(12345, false)");
4576 }
4577
4578 #[test]
4579 fn struct_coercion() {
4580 let ty = StructType::new(
4581 "Foo",
4582 [
4583 ("foo", PrimitiveType::Float),
4584 ("bar", PrimitiveType::Float),
4585 ("baz", PrimitiveType::Float),
4586 ],
4587 );
4588 let value: Value = Struct::new(ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
4589 .expect("should create map value")
4590 .into();
4591
4592 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
4594 let map_value = value.coerce(None, &ty).expect("value should coerce");
4595 assert_eq!(
4596 map_value.to_string(),
4597 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4598 );
4599
4600 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
4602 let map_value = value.coerce(None, &ty).expect("value should coerce");
4603 assert_eq!(
4604 map_value.to_string(),
4605 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4606 );
4607
4608 let ty = StructType::new(
4610 "Bar",
4611 [
4612 ("foo", PrimitiveType::Float),
4613 ("bar", PrimitiveType::Float),
4614 ("baz", PrimitiveType::Float),
4615 ],
4616 )
4617 .into();
4618 let struct_value = value.coerce(None, &ty).expect("value should coerce");
4619 assert_eq!(
4620 struct_value.to_string(),
4621 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4622 );
4623
4624 let object_value = value
4626 .coerce(None, &Type::Object)
4627 .expect("value should coerce");
4628 assert_eq!(
4629 object_value.to_string(),
4630 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4631 );
4632 }
4633
4634 #[test]
4635 fn struct_display() {
4636 let ty = StructType::new(
4637 "Foo",
4638 [
4639 ("foo", PrimitiveType::Float),
4640 ("bar", PrimitiveType::String),
4641 ("baz", PrimitiveType::Integer),
4642 ],
4643 );
4644 let value: Value = Struct::new(
4645 ty,
4646 [
4647 ("foo", Value::from(1.101)),
4648 ("bar", PrimitiveValue::new_string("foo").into()),
4649 ("baz", 1234.into()),
4650 ],
4651 )
4652 .expect("should create map value")
4653 .into();
4654 assert_eq!(
4655 value.to_string(),
4656 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
4657 );
4658 }
4659
4660 #[test]
4661 fn pair_serialization() {
4662 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4663 let pair: Value = Pair::new(
4664 pair_ty,
4665 PrimitiveValue::new_file("foo"),
4666 PrimitiveValue::new_string("bar"),
4667 )
4668 .expect("should create pair value")
4669 .into();
4670 let value_serializer = ValueSerializer::new(None, &pair, true);
4672 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4673 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
4674
4675 let value_serializer = ValueSerializer::new(None, &pair, false);
4677 assert!(serde_json::to_string(&value_serializer).is_err());
4678
4679 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
4680 let array: Value = Array::new(array_ty, [pair])
4681 .expect("should create array value")
4682 .into();
4683
4684 let value_serializer = ValueSerializer::new(None, &array, true);
4686 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4687 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
4688 }
4689
4690 #[test]
4691 fn type_name_ref_equality() {
4692 use wdl_analysis::types::EnumType;
4693
4694 let enum_type = Type::Compound(
4695 CompoundType::Custom(CustomType::Enum(
4696 EnumType::new(
4697 "MyEnum",
4698 Span::new(0, 0),
4699 Type::Primitive(PrimitiveType::Integer, false),
4700 Vec::<(String, Type)>::new(),
4701 &[],
4702 )
4703 .expect("should create enum type"),
4704 )),
4705 false,
4706 );
4707
4708 let value1 = Value::TypeNameRef(enum_type.clone());
4709 let value2 = Value::TypeNameRef(enum_type.clone());
4710
4711 assert_eq!(value1.ty(), value2.ty());
4712 }
4713
4714 #[test]
4715 fn type_name_ref_ty() {
4716 let struct_type = Type::Compound(
4717 CompoundType::Custom(CustomType::Struct(StructType::new(
4718 "MyStruct",
4719 empty::<(&str, Type)>(),
4720 ))),
4721 false,
4722 );
4723
4724 let value = Value::TypeNameRef(struct_type.clone());
4725 assert_eq!(value.ty(), struct_type);
4726 }
4727
4728 #[test]
4729 fn type_name_ref_display() {
4730 use wdl_analysis::types::EnumType;
4731
4732 let enum_type = Type::Compound(
4733 CompoundType::Custom(CustomType::Enum(
4734 EnumType::new(
4735 "Color",
4736 Span::new(0, 0),
4737 Type::Primitive(PrimitiveType::Integer, false),
4738 Vec::<(String, Type)>::new(),
4739 &[],
4740 )
4741 .expect("should create enum type"),
4742 )),
4743 false,
4744 );
4745
4746 let value = Value::TypeNameRef(enum_type);
4747 assert_eq!(value.to_string(), "Color");
4748 }
4749}