1use std::borrow::Cow;
4use std::cmp::Ordering;
5use std::fmt;
6use std::hash::Hash;
7use std::hash::Hasher;
8use std::path::Path;
9use std::sync::Arc;
10
11use anyhow::Context;
12use anyhow::Result;
13use anyhow::anyhow;
14use anyhow::bail;
15use futures::FutureExt;
16use futures::StreamExt as _;
17use futures::TryStreamExt as _;
18use futures::future::BoxFuture;
19use indexmap::IndexMap;
20use itertools::Either;
21use ordered_float::OrderedFloat;
22use serde::ser::SerializeMap;
23use serde::ser::SerializeSeq;
24use wdl_analysis::stdlib::STDLIB as ANALYSIS_STDLIB;
25use wdl_analysis::types::ArrayType;
26use wdl_analysis::types::CallType;
27use wdl_analysis::types::Coercible as _;
28use wdl_analysis::types::CompoundType;
29use wdl_analysis::types::HiddenType;
30use wdl_analysis::types::MapType;
31use wdl_analysis::types::Optional;
32use wdl_analysis::types::PrimitiveType;
33use wdl_analysis::types::Type;
34use wdl_analysis::types::v1::task_member_type_post_evaluation;
35use wdl_ast::AstToken;
36use wdl_ast::SupportedVersion;
37use wdl_ast::TreeNode;
38use wdl_ast::v1;
39use wdl_ast::v1::TASK_FIELD_ATTEMPT;
40use wdl_ast::v1::TASK_FIELD_CONTAINER;
41use wdl_ast::v1::TASK_FIELD_CPU;
42use wdl_ast::v1::TASK_FIELD_DISKS;
43use wdl_ast::v1::TASK_FIELD_END_TIME;
44use wdl_ast::v1::TASK_FIELD_EXT;
45use wdl_ast::v1::TASK_FIELD_FPGA;
46use wdl_ast::v1::TASK_FIELD_GPU;
47use wdl_ast::v1::TASK_FIELD_ID;
48use wdl_ast::v1::TASK_FIELD_MAX_RETRIES;
49use wdl_ast::v1::TASK_FIELD_MEMORY;
50use wdl_ast::v1::TASK_FIELD_META;
51use wdl_ast::v1::TASK_FIELD_NAME;
52use wdl_ast::v1::TASK_FIELD_PARAMETER_META;
53use wdl_ast::v1::TASK_FIELD_PREVIOUS;
54use wdl_ast::v1::TASK_FIELD_RETURN_CODE;
55use wdl_ast::version::V1;
56
57use crate::EvaluationContext;
58use crate::GuestPath;
59use crate::HostPath;
60use crate::Outputs;
61use crate::backend::TaskExecutionConstraints;
62use crate::http::Transferer;
63use crate::path;
64
65pub trait Coercible: Sized {
67 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self>;
75}
76
77#[derive(Debug, Clone)]
81pub enum Value {
82 None(Type),
86 Primitive(PrimitiveValue),
88 Compound(CompoundValue),
90 Hidden(HiddenValue),
95 Call(CallValue),
97}
98
99impl Value {
100 pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
106 match value {
107 v1::MetadataValue::Boolean(v) => v.value().into(),
108 v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
109 v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
110 v1::MetadataValue::String(v) => PrimitiveValue::new_string(
111 v.text()
112 .expect("metadata strings shouldn't have placeholders")
113 .text(),
114 )
115 .into(),
116 v1::MetadataValue::Null(_) => Self::new_none(Type::None),
117 v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
118 v1::MetadataValue::Array(a) => Array::new_unchecked(
119 ANALYSIS_STDLIB.array_object_type().clone(),
120 a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
121 )
122 .into(),
123 }
124 }
125
126 pub fn new_none(ty: Type) -> Self {
132 assert!(ty.is_optional(), "the provided `None` type is not optional");
133 Self::None(ty)
134 }
135
136 pub fn ty(&self) -> Type {
138 match self {
139 Self::None(ty) => ty.clone(),
140 Self::Primitive(v) => v.ty(),
141 Self::Compound(v) => v.ty(),
142 Self::Hidden(v) => v.ty(),
143 Self::Call(v) => Type::Call(v.ty.clone()),
144 }
145 }
146
147 pub fn is_none(&self) -> bool {
149 matches!(self, Self::None(_))
150 }
151
152 pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
156 match self {
157 Self::Primitive(v) => Some(v),
158 _ => None,
159 }
160 }
161
162 pub fn as_compound(&self) -> Option<&CompoundValue> {
166 match self {
167 Self::Compound(v) => Some(v),
168 _ => None,
169 }
170 }
171
172 pub fn as_boolean(&self) -> Option<bool> {
176 match self {
177 Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
178 _ => None,
179 }
180 }
181
182 pub fn unwrap_boolean(self) -> bool {
188 match self {
189 Self::Primitive(PrimitiveValue::Boolean(v)) => v,
190 _ => panic!("value is not a boolean"),
191 }
192 }
193
194 pub fn as_integer(&self) -> Option<i64> {
198 match self {
199 Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
200 _ => None,
201 }
202 }
203
204 pub fn unwrap_integer(self) -> i64 {
210 match self {
211 Self::Primitive(PrimitiveValue::Integer(v)) => v,
212 _ => panic!("value is not an integer"),
213 }
214 }
215
216 pub fn as_float(&self) -> Option<f64> {
220 match self {
221 Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
222 _ => None,
223 }
224 }
225
226 pub fn unwrap_float(self) -> f64 {
232 match self {
233 Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
234 _ => panic!("value is not a float"),
235 }
236 }
237
238 pub fn as_string(&self) -> Option<&Arc<String>> {
242 match self {
243 Self::Primitive(PrimitiveValue::String(s)) => Some(s),
244 _ => None,
245 }
246 }
247
248 pub fn unwrap_string(self) -> Arc<String> {
254 match self {
255 Self::Primitive(PrimitiveValue::String(s)) => s,
256 _ => panic!("value is not a string"),
257 }
258 }
259
260 pub fn as_file(&self) -> Option<&HostPath> {
264 match self {
265 Self::Primitive(PrimitiveValue::File(p)) => Some(p),
266 _ => None,
267 }
268 }
269
270 pub fn unwrap_file(self) -> HostPath {
276 match self {
277 Self::Primitive(PrimitiveValue::File(p)) => p,
278 _ => panic!("value is not a file"),
279 }
280 }
281
282 pub fn as_directory(&self) -> Option<&HostPath> {
286 match self {
287 Self::Primitive(PrimitiveValue::Directory(p)) => Some(p),
288 _ => None,
289 }
290 }
291
292 pub fn unwrap_directory(self) -> HostPath {
298 match self {
299 Self::Primitive(PrimitiveValue::Directory(p)) => p,
300 _ => panic!("value is not a directory"),
301 }
302 }
303
304 pub fn as_pair(&self) -> Option<&Pair> {
308 match self {
309 Self::Compound(CompoundValue::Pair(v)) => Some(v),
310 _ => None,
311 }
312 }
313
314 pub fn unwrap_pair(self) -> Pair {
320 match self {
321 Self::Compound(CompoundValue::Pair(v)) => v,
322 _ => panic!("value is not a pair"),
323 }
324 }
325
326 pub fn as_array(&self) -> Option<&Array> {
330 match self {
331 Self::Compound(CompoundValue::Array(v)) => Some(v),
332 _ => None,
333 }
334 }
335
336 pub fn unwrap_array(self) -> Array {
342 match self {
343 Self::Compound(CompoundValue::Array(v)) => v,
344 _ => panic!("value is not an array"),
345 }
346 }
347
348 pub fn as_map(&self) -> Option<&Map> {
352 match self {
353 Self::Compound(CompoundValue::Map(v)) => Some(v),
354 _ => None,
355 }
356 }
357
358 pub fn unwrap_map(self) -> Map {
364 match self {
365 Self::Compound(CompoundValue::Map(v)) => v,
366 _ => panic!("value is not a map"),
367 }
368 }
369
370 pub fn as_object(&self) -> Option<&Object> {
374 match self {
375 Self::Compound(CompoundValue::Object(v)) => Some(v),
376 _ => None,
377 }
378 }
379
380 pub fn unwrap_object(self) -> Object {
386 match self {
387 Self::Compound(CompoundValue::Object(v)) => v,
388 _ => panic!("value is not an object"),
389 }
390 }
391
392 pub fn as_struct(&self) -> Option<&Struct> {
396 match self {
397 Self::Compound(CompoundValue::Struct(v)) => Some(v),
398 _ => None,
399 }
400 }
401
402 pub fn unwrap_struct(self) -> Struct {
408 match self {
409 Self::Compound(CompoundValue::Struct(v)) => v,
410 _ => panic!("value is not a struct"),
411 }
412 }
413
414 pub fn as_task_pre_evaluation(&self) -> Option<&TaskPreEvaluationValue> {
418 match self {
419 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => Some(v),
420 _ => None,
421 }
422 }
423
424 pub fn unwrap_task_pre_evaluation(self) -> TaskPreEvaluationValue {
430 match self {
431 Self::Hidden(HiddenValue::TaskPreEvaluation(v)) => v,
432 _ => panic!("value is not a pre-evaluation task"),
433 }
434 }
435
436 pub fn as_task_post_evaluation(&self) -> Option<&TaskPostEvaluationValue> {
440 match self {
441 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
442 _ => None,
443 }
444 }
445
446 pub(crate) fn as_task_post_evaluation_mut(&mut self) -> Option<&mut TaskPostEvaluationValue> {
450 match self {
451 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => Some(v),
452 _ => None,
453 }
454 }
455
456 pub fn unwrap_task_post_evaluation(self) -> TaskPostEvaluationValue {
462 match self {
463 Self::Hidden(HiddenValue::TaskPostEvaluation(v)) => v,
464 _ => panic!("value is not a post-evaluation task"),
465 }
466 }
467
468 pub fn as_hints(&self) -> Option<&HintsValue> {
472 match self {
473 Self::Hidden(HiddenValue::Hints(v)) => Some(v),
474 _ => None,
475 }
476 }
477
478 pub fn unwrap_hints(self) -> HintsValue {
484 match self {
485 Self::Hidden(HiddenValue::Hints(v)) => v,
486 _ => panic!("value is not a hints value"),
487 }
488 }
489
490 pub fn as_call(&self) -> Option<&CallValue> {
494 match self {
495 Self::Call(v) => Some(v),
496 _ => None,
497 }
498 }
499
500 pub fn unwrap_call(self) -> CallValue {
506 match self {
507 Self::Call(v) => v,
508 _ => panic!("value is not a call value"),
509 }
510 }
511
512 pub(crate) fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
517 where
518 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
519 {
520 match self {
521 Self::Primitive(PrimitiveValue::File(path)) => cb(true, path),
522 Self::Primitive(PrimitiveValue::Directory(path)) => cb(false, path),
523 Self::Compound(v) => v.visit_paths(cb),
524 _ => Ok(()),
525 }
526 }
527
528 pub(crate) async fn resolve_paths<F>(
547 &self,
548 optional: bool,
549 base_dir: Option<&Path>,
550 transferer: Option<&dyn Transferer>,
551 translate: &F,
552 ) -> Result<Self>
553 where
554 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
555 {
556 match self {
557 Self::Primitive(v @ PrimitiveValue::File(path))
558 | Self::Primitive(v @ PrimitiveValue::Directory(path)) => {
559 let is_file = v.as_file().is_some();
562 let path = translate(path)?;
563
564 if path::is_file_url(path.as_str()) {
565 let exists = path::parse_supported_url(path.as_str())
568 .and_then(|url| url.to_file_path().ok())
569 .map(|p| p.exists())
570 .unwrap_or(false);
571 if exists {
572 let v = PrimitiveValue::new_file_or_directory(is_file, path);
573 return Ok(Self::Primitive(v));
574 }
575
576 if optional && !exists {
577 return Ok(Value::new_none(self.ty().optional()));
578 }
579
580 bail!("path `{path}` does not exist");
581 } else if path::is_supported_url(path.as_str()) {
582 match transferer {
583 Some(transferer) => {
584 let exists = transferer
585 .exists(
586 &path
587 .as_str()
588 .parse()
589 .with_context(|| format!("invalid URL `{path}`"))?,
590 )
591 .await?;
592 if exists {
593 let v = PrimitiveValue::new_file_or_directory(is_file, path);
594 return Ok(Self::Primitive(v));
595 }
596
597 if optional && !exists {
598 return Ok(Value::new_none(self.ty().optional()));
599 }
600
601 bail!("URL `{path}` does not exist");
602 }
603 None => {
604 let v = PrimitiveValue::new_file_or_directory(is_file, path);
606 return Ok(Self::Primitive(v));
607 }
608 }
609 }
610
611 let exists_path: Cow<'_, Path> = base_dir
613 .map(|d| d.join(path.as_str()).into())
614 .unwrap_or_else(|| Path::new(path.as_str()).into());
615 if is_file && !exists_path.is_file() {
616 if optional {
617 return Ok(Value::new_none(self.ty().optional()));
618 } else {
619 bail!("file `{}` does not exist", exists_path.display());
620 }
621 } else if !is_file && !exists_path.is_dir() {
622 if optional {
623 return Ok(Value::new_none(self.ty().optional()));
624 } else {
625 bail!("directory `{}` does not exist", exists_path.display())
626 }
627 }
628
629 let v = PrimitiveValue::new_file_or_directory(is_file, path);
630 Ok(Self::Primitive(v))
631 }
632 Self::Compound(v) => Ok(Self::Compound(
633 v.resolve_paths(base_dir, transferer, translate)
634 .boxed()
635 .await?,
636 )),
637 v => Ok(v.clone()),
638 }
639 }
640
641 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
646 match (left, right) {
647 (Value::None(_), Value::None(_)) => Some(true),
648 (Value::None(_), _) | (_, Value::None(_)) => Some(false),
649 (Value::Primitive(left), Value::Primitive(right)) => {
650 Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
651 }
652 (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
653 _ => None,
654 }
655 }
656}
657
658impl fmt::Display for Value {
659 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
660 match self {
661 Self::None(_) => write!(f, "None"),
662 Self::Primitive(v) => v.fmt(f),
663 Self::Compound(v) => v.fmt(f),
664 Self::Hidden(v) => v.fmt(f),
665 Self::Call(c) => c.fmt(f),
666 }
667 }
668}
669
670impl Coercible for Value {
671 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
672 if target.is_union() || target.is_none() || self.ty().eq(target) {
673 return Ok(self.clone());
674 }
675
676 match self {
677 Self::None(_) => {
678 if target.is_optional() {
679 Ok(Self::new_none(target.clone()))
680 } else {
681 bail!("cannot coerce `None` to non-optional type `{target}`");
682 }
683 }
684 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
685 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
686 Self::Hidden(v) => v.coerce(context, target).map(Self::Hidden),
687 Self::Call(_) => {
688 bail!("call values cannot be coerced to any other type");
689 }
690 }
691 }
692}
693
694impl From<bool> for Value {
695 fn from(value: bool) -> Self {
696 Self::Primitive(value.into())
697 }
698}
699
700impl From<i64> for Value {
701 fn from(value: i64) -> Self {
702 Self::Primitive(value.into())
703 }
704}
705
706impl TryFrom<u64> for Value {
707 type Error = std::num::TryFromIntError;
708
709 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
710 let value: i64 = value.try_into()?;
711 Ok(value.into())
712 }
713}
714
715impl From<f64> for Value {
716 fn from(value: f64) -> Self {
717 Self::Primitive(value.into())
718 }
719}
720
721impl From<String> for Value {
722 fn from(value: String) -> Self {
723 Self::Primitive(value.into())
724 }
725}
726
727impl From<PrimitiveValue> for Value {
728 fn from(value: PrimitiveValue) -> Self {
729 Self::Primitive(value)
730 }
731}
732
733impl From<Option<PrimitiveValue>> for Value {
734 fn from(value: Option<PrimitiveValue>) -> Self {
735 match value {
736 Some(v) => v.into(),
737 None => Self::new_none(Type::None),
738 }
739 }
740}
741
742impl From<CompoundValue> for Value {
743 fn from(value: CompoundValue) -> Self {
744 Self::Compound(value)
745 }
746}
747
748impl From<HiddenValue> for Value {
749 fn from(value: HiddenValue) -> Self {
750 Self::Hidden(value)
751 }
752}
753
754impl From<Pair> for Value {
755 fn from(value: Pair) -> Self {
756 Self::Compound(value.into())
757 }
758}
759
760impl From<Array> for Value {
761 fn from(value: Array) -> Self {
762 Self::Compound(value.into())
763 }
764}
765
766impl From<Map> for Value {
767 fn from(value: Map) -> Self {
768 Self::Compound(value.into())
769 }
770}
771
772impl From<Object> for Value {
773 fn from(value: Object) -> Self {
774 Self::Compound(value.into())
775 }
776}
777
778impl From<Struct> for Value {
779 fn from(value: Struct) -> Self {
780 Self::Compound(value.into())
781 }
782}
783
784impl From<CallValue> for Value {
785 fn from(value: CallValue) -> Self {
786 Self::Call(value)
787 }
788}
789
790impl<'de> serde::Deserialize<'de> for Value {
791 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
792 where
793 D: serde::Deserializer<'de>,
794 {
795 use serde::Deserialize as _;
796
797 struct Visitor;
799
800 impl<'de> serde::de::Visitor<'de> for Visitor {
801 type Value = Value;
802
803 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
804 where
805 E: serde::de::Error,
806 {
807 Ok(Value::new_none(Type::None))
808 }
809
810 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
811 where
812 E: serde::de::Error,
813 {
814 Ok(Value::new_none(Type::None))
815 }
816
817 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
818 where
819 D: serde::Deserializer<'de>,
820 {
821 Value::deserialize(deserializer)
822 }
823
824 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
825 where
826 E: serde::de::Error,
827 {
828 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
829 }
830
831 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
832 where
833 E: serde::de::Error,
834 {
835 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
836 }
837
838 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
839 where
840 E: serde::de::Error,
841 {
842 Ok(Value::Primitive(PrimitiveValue::Integer(
843 v.try_into().map_err(|_| {
844 E::custom("integer not in range for a 64-bit signed integer")
845 })?,
846 )))
847 }
848
849 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
850 where
851 E: serde::de::Error,
852 {
853 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
854 }
855
856 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
857 where
858 E: serde::de::Error,
859 {
860 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
861 }
862
863 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
864 where
865 E: serde::de::Error,
866 {
867 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
868 }
869
870 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
871 where
872 A: serde::de::SeqAccess<'de>,
873 {
874 use serde::de::Error as _;
875
876 let mut elements = vec![];
877 while let Some(element) = seq.next_element::<Value>()? {
878 elements.push(element);
879 }
880
881 let mut candidate_ty = None;
883 for element in elements.iter() {
884 let new_candidate_ty = element.ty();
885 let old_candidate_ty =
886 candidate_ty.get_or_insert_with(|| new_candidate_ty.clone());
887 let Some(new_common_ty) = old_candidate_ty.common_type(&new_candidate_ty)
888 else {
889 return Err(A::Error::custom(format!(
890 "a common element type does not exist between `{old_candidate_ty}` \
891 and `{new_candidate_ty}`"
892 )));
893 };
894 candidate_ty = Some(new_common_ty);
895 }
896 let array_ty: Type = ArrayType::new(candidate_ty.unwrap_or(Type::Union)).into();
898 Ok(Array::new(None, array_ty.clone(), elements)
899 .map_err(|e| {
900 A::Error::custom(format!("cannot coerce value to `{array_ty}`: {e:#}"))
901 })?
902 .into())
903 }
904
905 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
906 where
907 A: serde::de::MapAccess<'de>,
908 {
909 let mut members = IndexMap::new();
910 while let Some(key) = map.next_key::<String>()? {
911 members.insert(key, map.next_value()?);
912 }
913
914 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
915 }
916
917 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
918 write!(f, "a WDL value")
919 }
920 }
921
922 deserializer.deserialize_any(Visitor)
923 }
924}
925
926#[derive(Debug, Clone)]
930pub enum PrimitiveValue {
931 Boolean(bool),
933 Integer(i64),
935 Float(OrderedFloat<f64>),
937 String(Arc<String>),
939 File(HostPath),
941 Directory(HostPath),
943}
944
945impl PrimitiveValue {
946 pub fn new_string(s: impl Into<String>) -> Self {
948 Self::String(Arc::new(s.into()))
949 }
950
951 pub fn new_file(path: impl Into<HostPath>) -> Self {
953 Self::File(path.into())
954 }
955
956 pub fn new_directory(path: impl Into<HostPath>) -> Self {
958 Self::Directory(path.into())
959 }
960
961 fn new_file_or_directory(is_file: bool, path: impl Into<HostPath>) -> Self {
968 if is_file {
969 Self::File(path.into())
970 } else {
971 Self::Directory(path.into())
972 }
973 }
974
975 pub fn ty(&self) -> Type {
977 match self {
978 Self::Boolean(_) => PrimitiveType::Boolean.into(),
979 Self::Integer(_) => PrimitiveType::Integer.into(),
980 Self::Float(_) => PrimitiveType::Float.into(),
981 Self::String(_) => PrimitiveType::String.into(),
982 Self::File(_) => PrimitiveType::File.into(),
983 Self::Directory(_) => PrimitiveType::Directory.into(),
984 }
985 }
986
987 pub fn as_boolean(&self) -> Option<bool> {
991 match self {
992 Self::Boolean(v) => Some(*v),
993 _ => None,
994 }
995 }
996
997 pub fn unwrap_boolean(self) -> bool {
1003 match self {
1004 Self::Boolean(v) => v,
1005 _ => panic!("value is not a boolean"),
1006 }
1007 }
1008
1009 pub fn as_integer(&self) -> Option<i64> {
1013 match self {
1014 Self::Integer(v) => Some(*v),
1015 _ => None,
1016 }
1017 }
1018
1019 pub fn unwrap_integer(self) -> i64 {
1025 match self {
1026 Self::Integer(v) => v,
1027 _ => panic!("value is not an integer"),
1028 }
1029 }
1030
1031 pub fn as_float(&self) -> Option<f64> {
1035 match self {
1036 Self::Float(v) => Some((*v).into()),
1037 _ => None,
1038 }
1039 }
1040
1041 pub fn unwrap_float(self) -> f64 {
1047 match self {
1048 Self::Float(v) => v.into(),
1049 _ => panic!("value is not a float"),
1050 }
1051 }
1052
1053 pub fn as_string(&self) -> Option<&Arc<String>> {
1057 match self {
1058 Self::String(s) => Some(s),
1059 _ => None,
1060 }
1061 }
1062
1063 pub fn unwrap_string(self) -> Arc<String> {
1069 match self {
1070 Self::String(s) => s,
1071 _ => panic!("value is not a string"),
1072 }
1073 }
1074
1075 pub fn as_file(&self) -> Option<&HostPath> {
1079 match self {
1080 Self::File(p) => Some(p),
1081 _ => None,
1082 }
1083 }
1084
1085 pub fn unwrap_file(self) -> HostPath {
1091 match self {
1092 Self::File(p) => p,
1093 _ => panic!("value is not a file"),
1094 }
1095 }
1096
1097 pub fn as_directory(&self) -> Option<&HostPath> {
1101 match self {
1102 Self::Directory(p) => Some(p),
1103 _ => None,
1104 }
1105 }
1106
1107 pub fn unwrap_directory(self) -> HostPath {
1113 match self {
1114 Self::Directory(p) => p,
1115 _ => panic!("value is not a directory"),
1116 }
1117 }
1118
1119 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1126 match (left, right) {
1127 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1128 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1129 (Self::Integer(left), Self::Float(right)) => {
1130 Some(OrderedFloat(*left as f64).cmp(right))
1131 }
1132 (Self::Float(left), Self::Integer(right)) => {
1133 Some(left.cmp(&OrderedFloat(*right as f64)))
1134 }
1135 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1136 (Self::String(left), Self::String(right))
1137 | (Self::String(left), Self::File(HostPath(right)))
1138 | (Self::String(left), Self::Directory(HostPath(right)))
1139 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1140 | (Self::File(HostPath(left)), Self::String(right))
1141 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1142 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1143 _ => None,
1144 }
1145 }
1146
1147 pub fn raw<'a>(
1155 &'a self,
1156 context: Option<&'a dyn EvaluationContext>,
1157 ) -> impl fmt::Display + use<'a> {
1158 struct Display<'a> {
1160 value: &'a PrimitiveValue,
1162 context: Option<&'a dyn EvaluationContext>,
1164 }
1165
1166 impl fmt::Display for Display<'_> {
1167 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1168 match self.value {
1169 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1170 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1171 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1172 PrimitiveValue::String(v) => write!(f, "{v}"),
1173 PrimitiveValue::File(v) => {
1174 write!(
1175 f,
1176 "{v}",
1177 v = self
1178 .context
1179 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1180 .unwrap_or(Cow::Borrowed(&v.0))
1181 )
1182 }
1183 PrimitiveValue::Directory(v) => {
1184 write!(
1185 f,
1186 "{v}",
1187 v = self
1188 .context
1189 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1190 .unwrap_or(Cow::Borrowed(&v.0))
1191 )
1192 }
1193 }
1194 }
1195 }
1196
1197 Display {
1198 value: self,
1199 context,
1200 }
1201 }
1202}
1203
1204impl fmt::Display for PrimitiveValue {
1205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1206 match self {
1207 Self::Boolean(v) => write!(f, "{v}"),
1208 Self::Integer(v) => write!(f, "{v}"),
1209 Self::Float(v) => write!(f, "{v:.6?}"),
1210 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1211 write!(f, "\"{s}\"")
1213 }
1214 }
1215 }
1216}
1217
1218impl PartialEq for PrimitiveValue {
1219 fn eq(&self, other: &Self) -> bool {
1220 Self::compare(self, other) == Some(Ordering::Equal)
1221 }
1222}
1223
1224impl Eq for PrimitiveValue {}
1225
1226impl Hash for PrimitiveValue {
1227 fn hash<H: Hasher>(&self, state: &mut H) {
1228 match self {
1229 Self::Boolean(v) => {
1230 0.hash(state);
1231 v.hash(state);
1232 }
1233 Self::Integer(v) => {
1234 1.hash(state);
1235 v.hash(state);
1236 }
1237 Self::Float(v) => {
1238 1.hash(state);
1241 v.hash(state);
1242 }
1243 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1244 2.hash(state);
1247 v.hash(state);
1248 }
1249 }
1250 }
1251}
1252
1253impl From<bool> for PrimitiveValue {
1254 fn from(value: bool) -> Self {
1255 Self::Boolean(value)
1256 }
1257}
1258
1259impl From<i64> for PrimitiveValue {
1260 fn from(value: i64) -> Self {
1261 Self::Integer(value)
1262 }
1263}
1264
1265impl From<f64> for PrimitiveValue {
1266 fn from(value: f64) -> Self {
1267 Self::Float(value.into())
1268 }
1269}
1270
1271impl From<String> for PrimitiveValue {
1272 fn from(value: String) -> Self {
1273 Self::String(value.into())
1274 }
1275}
1276
1277impl Coercible for PrimitiveValue {
1278 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1279 if target.is_union() || target.is_none() || self.ty().eq(target) {
1280 return Ok(self.clone());
1281 }
1282
1283 match self {
1284 Self::Boolean(v) => {
1285 target
1286 .as_primitive()
1287 .and_then(|ty| match ty {
1288 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1290 _ => None,
1291 })
1292 .with_context(|| format!("cannot coerce type `Boolean` to type `{target}`"))
1293 }
1294 Self::Integer(v) => {
1295 target
1296 .as_primitive()
1297 .and_then(|ty| match ty {
1298 PrimitiveType::Integer => Some(Self::Integer(*v)),
1300 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1302 _ => None,
1303 })
1304 .with_context(|| format!("cannot coerce type `Int` to type `{target}`"))
1305 }
1306 Self::Float(v) => {
1307 target
1308 .as_primitive()
1309 .and_then(|ty| match ty {
1310 PrimitiveType::Float => Some(Self::Float(*v)),
1312 _ => None,
1313 })
1314 .with_context(|| format!("cannot coerce type `Float` to type `{target}`"))
1315 }
1316 Self::String(s) => {
1317 target
1318 .as_primitive()
1319 .and_then(|ty| match ty {
1320 PrimitiveType::String => Some(Self::String(s.clone())),
1322 PrimitiveType::File => Some(Self::File(
1324 context
1325 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1326 .unwrap_or_else(|| s.clone().into()),
1327 )),
1328 PrimitiveType::Directory => Some(Self::Directory(
1330 context
1331 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1332 .unwrap_or_else(|| s.clone().into()),
1333 )),
1334 _ => None,
1335 })
1336 .with_context(|| format!("cannot coerce type `String` to type `{target}`"))
1337 }
1338 Self::File(p) => {
1339 target
1340 .as_primitive()
1341 .and_then(|ty| match ty {
1342 PrimitiveType::File => Some(Self::File(p.clone())),
1344 PrimitiveType::String => Some(Self::String(
1346 context
1347 .and_then(|c| c.guest_path(p).map(Into::into))
1348 .unwrap_or_else(|| p.clone().into()),
1349 )),
1350 _ => None,
1351 })
1352 .with_context(|| format!("cannot coerce type `File` to type `{target}`"))
1353 }
1354 Self::Directory(p) => {
1355 target
1356 .as_primitive()
1357 .and_then(|ty| match ty {
1358 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1360 PrimitiveType::String => Some(Self::String(
1362 context
1363 .and_then(|c| c.guest_path(p).map(Into::into))
1364 .unwrap_or_else(|| p.clone().into()),
1365 )),
1366 _ => None,
1367 })
1368 .with_context(|| format!("cannot coerce type `Directory` to type `{target}`"))
1369 }
1370 }
1371 }
1372}
1373
1374impl serde::Serialize for PrimitiveValue {
1375 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1376 where
1377 S: serde::Serializer,
1378 {
1379 match self {
1380 Self::Boolean(v) => v.serialize(serializer),
1381 Self::Integer(v) => v.serialize(serializer),
1382 Self::Float(v) => v.serialize(serializer),
1383 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1384 s.serialize(serializer)
1385 }
1386 }
1387 }
1388}
1389
1390#[derive(Debug, Clone)]
1394pub struct Pair {
1395 ty: Type,
1397 values: Arc<(Value, Value)>,
1399}
1400
1401impl Pair {
1402 pub fn new(
1411 context: Option<&dyn EvaluationContext>,
1412 ty: impl Into<Type>,
1413 left: impl Into<Value>,
1414 right: impl Into<Value>,
1415 ) -> Result<Self> {
1416 let ty = ty.into();
1417 if let Type::Compound(CompoundType::Pair(ty), _) = ty {
1418 let left = left
1419 .into()
1420 .coerce(context, ty.left_type())
1421 .context("failed to coerce pair's left value")?;
1422 let right = right
1423 .into()
1424 .coerce(context, ty.right_type())
1425 .context("failed to coerce pair's right value")?;
1426 return Ok(Self::new_unchecked(
1427 Type::Compound(CompoundType::Pair(ty), false),
1428 left,
1429 right,
1430 ));
1431 }
1432
1433 panic!("type `{ty}` is not a pair type");
1434 }
1435
1436 pub(crate) fn new_unchecked(ty: Type, left: Value, right: Value) -> Self {
1439 assert!(ty.as_pair().is_some());
1440 Self {
1441 ty: ty.require(),
1442 values: Arc::new((left, right)),
1443 }
1444 }
1445
1446 pub fn ty(&self) -> Type {
1448 self.ty.clone()
1449 }
1450
1451 pub fn left(&self) -> &Value {
1453 &self.values.0
1454 }
1455
1456 pub fn right(&self) -> &Value {
1458 &self.values.1
1459 }
1460}
1461
1462impl fmt::Display for Pair {
1463 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1464 write!(
1465 f,
1466 "({left}, {right})",
1467 left = self.values.0,
1468 right = self.values.1
1469 )
1470 }
1471}
1472
1473#[derive(Debug, Clone)]
1477pub struct Array {
1478 ty: Type,
1480 elements: Option<Arc<Vec<Value>>>,
1484}
1485
1486impl Array {
1487 pub fn new<V>(
1496 context: Option<&dyn EvaluationContext>,
1497 ty: impl Into<Type>,
1498 elements: impl IntoIterator<Item = V>,
1499 ) -> Result<Self>
1500 where
1501 V: Into<Value>,
1502 {
1503 let ty = ty.into();
1504 if let Type::Compound(CompoundType::Array(ty), _) = ty {
1505 let element_type = ty.element_type();
1506 let elements = elements
1507 .into_iter()
1508 .enumerate()
1509 .map(|(i, v)| {
1510 let v = v.into();
1511 v.coerce(context, element_type)
1512 .with_context(|| format!("failed to coerce array element at index {i}"))
1513 })
1514 .collect::<Result<Vec<_>>>()?;
1515
1516 return Ok(Self::new_unchecked(
1517 Type::Compound(CompoundType::Array(ty.unqualified()), false),
1518 elements,
1519 ));
1520 }
1521
1522 panic!("type `{ty}` is not an array type");
1523 }
1524
1525 pub(crate) fn new_unchecked(ty: Type, elements: Vec<Value>) -> Self {
1528 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty {
1529 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1530 } else {
1531 panic!("type is not an array type");
1532 };
1533
1534 Self {
1535 ty,
1536 elements: if elements.is_empty() {
1537 None
1538 } else {
1539 Some(Arc::new(elements))
1540 },
1541 }
1542 }
1543
1544 pub fn ty(&self) -> Type {
1546 self.ty.clone()
1547 }
1548
1549 pub fn as_slice(&self) -> &[Value] {
1551 self.elements.as_ref().map(|v| v.as_slice()).unwrap_or(&[])
1552 }
1553
1554 pub fn len(&self) -> usize {
1556 self.elements.as_ref().map(|v| v.len()).unwrap_or(0)
1557 }
1558
1559 pub fn is_empty(&self) -> bool {
1561 self.len() == 0
1562 }
1563}
1564
1565impl fmt::Display for Array {
1566 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1567 write!(f, "[")?;
1568
1569 if let Some(elements) = &self.elements {
1570 for (i, element) in elements.iter().enumerate() {
1571 if i > 0 {
1572 write!(f, ", ")?;
1573 }
1574
1575 write!(f, "{element}")?;
1576 }
1577 }
1578
1579 write!(f, "]")
1580 }
1581}
1582
1583#[derive(Debug, Clone)]
1587pub struct Map {
1588 ty: Type,
1590 elements: Option<Arc<IndexMap<Option<PrimitiveValue>, Value>>>,
1594}
1595
1596impl Map {
1597 pub fn new<K, V>(
1606 context: Option<&dyn EvaluationContext>,
1607 ty: impl Into<Type>,
1608 elements: impl IntoIterator<Item = (K, V)>,
1609 ) -> Result<Self>
1610 where
1611 K: Into<Value>,
1612 V: Into<Value>,
1613 {
1614 let ty = ty.into();
1615 if let Type::Compound(CompoundType::Map(ty), _) = ty {
1616 let key_type = ty.key_type();
1617 let value_type = ty.value_type();
1618
1619 let elements = elements
1620 .into_iter()
1621 .enumerate()
1622 .map(|(i, (k, v))| {
1623 let k = k.into();
1624 let v = v.into();
1625 Ok((
1626 if k.is_none() {
1627 None
1628 } else {
1629 match k.coerce(context, key_type).with_context(|| {
1630 format!("failed to coerce map key for element at index {i}")
1631 })? {
1632 Value::None(_) => None,
1633 Value::Primitive(v) => Some(v),
1634 _ => {
1635 bail!("not all key values are primitive")
1636 }
1637 }
1638 },
1639 v.coerce(context, value_type).with_context(|| {
1640 format!("failed to coerce map value for element at index {i}")
1641 })?,
1642 ))
1643 })
1644 .collect::<Result<_>>()?;
1645
1646 return Ok(Self::new_unchecked(
1647 Type::Compound(CompoundType::Map(ty), false),
1648 elements,
1649 ));
1650 }
1651
1652 panic!("type `{ty}` is not a map type");
1653 }
1654
1655 pub(crate) fn new_unchecked(
1658 ty: Type,
1659 elements: IndexMap<Option<PrimitiveValue>, Value>,
1660 ) -> Self {
1661 assert!(ty.as_map().is_some());
1662 Self {
1663 ty: ty.require(),
1664 elements: if elements.is_empty() {
1665 None
1666 } else {
1667 Some(Arc::new(elements))
1668 },
1669 }
1670 }
1671
1672 pub fn ty(&self) -> Type {
1674 self.ty.clone()
1675 }
1676
1677 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&Option<PrimitiveValue>, &Value)> {
1679 self.elements
1680 .as_ref()
1681 .map(|m| Either::Left(m.iter()))
1682 .unwrap_or(Either::Right(std::iter::empty()))
1683 }
1684
1685 pub fn keys(&self) -> impl ExactSizeIterator<Item = &Option<PrimitiveValue>> {
1687 self.elements
1688 .as_ref()
1689 .map(|m| Either::Left(m.keys()))
1690 .unwrap_or(Either::Right(std::iter::empty()))
1691 }
1692
1693 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1695 self.elements
1696 .as_ref()
1697 .map(|m| Either::Left(m.values()))
1698 .unwrap_or(Either::Right(std::iter::empty()))
1699 }
1700
1701 pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1703 self.elements
1704 .as_ref()
1705 .map(|m| m.contains_key(key))
1706 .unwrap_or(false)
1707 }
1708
1709 pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1711 self.elements.as_ref().and_then(|m| m.get(key))
1712 }
1713
1714 pub fn len(&self) -> usize {
1716 self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1717 }
1718
1719 pub fn is_empty(&self) -> bool {
1721 self.len() == 0
1722 }
1723}
1724
1725impl fmt::Display for Map {
1726 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1727 write!(f, "{{")?;
1728
1729 for (i, (k, v)) in self.iter().enumerate() {
1730 if i > 0 {
1731 write!(f, ", ")?;
1732 }
1733
1734 match k {
1735 Some(k) => write!(f, "{k}: {v}")?,
1736 None => write!(f, "None: {v}")?,
1737 }
1738 }
1739
1740 write!(f, "}}")
1741 }
1742}
1743
1744#[derive(Debug, Clone)]
1748pub struct Object {
1749 pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1753}
1754
1755impl Object {
1756 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1760 Self {
1761 members: if members.is_empty() {
1762 None
1763 } else {
1764 Some(Arc::new(members))
1765 },
1766 }
1767 }
1768
1769 pub fn empty() -> Self {
1771 Self::new(IndexMap::default())
1772 }
1773
1774 pub fn from_v1_metadata<N: TreeNode>(
1776 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1777 ) -> Self {
1778 Object::new(
1779 items
1780 .map(|i| {
1781 (
1782 i.name().text().to_string(),
1783 Value::from_v1_metadata(&i.value()),
1784 )
1785 })
1786 .collect::<IndexMap<_, _>>(),
1787 )
1788 }
1789
1790 pub fn ty(&self) -> Type {
1792 Type::Object
1793 }
1794
1795 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
1797 self.members
1798 .as_ref()
1799 .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1800 .unwrap_or(Either::Right(std::iter::empty()))
1801 }
1802
1803 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
1805 self.members
1806 .as_ref()
1807 .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1808 .unwrap_or(Either::Right(std::iter::empty()))
1809 }
1810
1811 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1813 self.members
1814 .as_ref()
1815 .map(|m| Either::Left(m.values()))
1816 .unwrap_or(Either::Right(std::iter::empty()))
1817 }
1818
1819 pub fn contains_key(&self, key: &str) -> bool {
1821 self.members
1822 .as_ref()
1823 .map(|m| m.contains_key(key))
1824 .unwrap_or(false)
1825 }
1826
1827 pub fn get(&self, key: &str) -> Option<&Value> {
1829 self.members.as_ref().and_then(|m| m.get(key))
1830 }
1831
1832 pub fn len(&self) -> usize {
1834 self.members.as_ref().map(|m| m.len()).unwrap_or(0)
1835 }
1836
1837 pub fn is_empty(&self) -> bool {
1839 self.len() == 0
1840 }
1841}
1842
1843impl fmt::Display for Object {
1844 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1845 write!(f, "object {{")?;
1846
1847 for (i, (k, v)) in self.iter().enumerate() {
1848 if i > 0 {
1849 write!(f, ", ")?;
1850 }
1851
1852 write!(f, "{k}: {v}")?;
1853 }
1854
1855 write!(f, "}}")
1856 }
1857}
1858
1859#[derive(Debug, Clone)]
1863pub struct Struct {
1864 ty: Type,
1866 name: Arc<String>,
1868 pub(crate) members: Arc<IndexMap<String, Value>>,
1870}
1871
1872impl Struct {
1873 pub fn new<S, V>(
1882 context: Option<&dyn EvaluationContext>,
1883 ty: impl Into<Type>,
1884 members: impl IntoIterator<Item = (S, V)>,
1885 ) -> Result<Self>
1886 where
1887 S: Into<String>,
1888 V: Into<Value>,
1889 {
1890 let ty = ty.into();
1891 if let Type::Compound(CompoundType::Struct(ty), optional) = ty {
1892 let mut members = members
1893 .into_iter()
1894 .map(|(n, v)| {
1895 let n = n.into();
1896 let v = v.into();
1897 let v = v
1898 .coerce(
1899 context,
1900 ty.members().get(&n).ok_or_else(|| {
1901 anyhow!("struct does not contain a member named `{n}`")
1902 })?,
1903 )
1904 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
1905 Ok((n, v))
1906 })
1907 .collect::<Result<IndexMap<_, _>>>()?;
1908
1909 for (name, ty) in ty.members().iter() {
1910 if ty.is_optional() {
1912 if !members.contains_key(name) {
1913 members.insert(name.clone(), Value::new_none(ty.clone()));
1914 }
1915 } else {
1916 if !members.contains_key(name) {
1918 bail!("missing a value for struct member `{name}`");
1919 }
1920 }
1921 }
1922
1923 let name = ty.name().to_string();
1924 return Ok(Self {
1925 ty: Type::Compound(CompoundType::Struct(ty), optional),
1926 name: Arc::new(name),
1927 members: Arc::new(members),
1928 });
1929 }
1930
1931 panic!("type `{ty}` is not a struct type");
1932 }
1933
1934 pub(crate) fn new_unchecked(
1937 ty: Type,
1938 name: Arc<String>,
1939 members: Arc<IndexMap<String, Value>>,
1940 ) -> Self {
1941 assert!(ty.as_struct().is_some());
1942 Self {
1943 ty: ty.require(),
1944 name,
1945 members,
1946 }
1947 }
1948
1949 pub fn ty(&self) -> Type {
1951 self.ty.clone()
1952 }
1953
1954 pub fn name(&self) -> &Arc<String> {
1956 &self.name
1957 }
1958
1959 pub fn iter(&self) -> impl ExactSizeIterator<Item = (&str, &Value)> {
1961 self.members.iter().map(|(k, v)| (k.as_str(), v))
1962 }
1963
1964 pub fn keys(&self) -> impl ExactSizeIterator<Item = &str> {
1966 self.members.keys().map(|k| k.as_str())
1967 }
1968
1969 pub fn values(&self) -> impl ExactSizeIterator<Item = &Value> {
1971 self.members.values()
1972 }
1973
1974 pub fn contains_key(&self, key: &str) -> bool {
1976 self.members.contains_key(key)
1977 }
1978
1979 pub fn get(&self, key: &str) -> Option<&Value> {
1981 self.members.get(key)
1982 }
1983}
1984
1985impl fmt::Display for Struct {
1986 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1987 write!(f, "{name} {{", name = self.name)?;
1988
1989 for (i, (k, v)) in self.members.iter().enumerate() {
1990 if i > 0 {
1991 write!(f, ", ")?;
1992 }
1993
1994 write!(f, "{k}: {v}")?;
1995 }
1996
1997 write!(f, "}}")
1998 }
1999}
2000
2001#[derive(Debug, Clone)]
2005pub enum CompoundValue {
2006 Pair(Pair),
2008 Array(Array),
2010 Map(Map),
2012 Object(Object),
2014 Struct(Struct),
2016}
2017
2018impl CompoundValue {
2019 pub fn ty(&self) -> Type {
2021 match self {
2022 CompoundValue::Pair(v) => v.ty(),
2023 CompoundValue::Array(v) => v.ty(),
2024 CompoundValue::Map(v) => v.ty(),
2025 CompoundValue::Object(v) => v.ty(),
2026 CompoundValue::Struct(v) => v.ty(),
2027 }
2028 }
2029
2030 pub fn as_pair(&self) -> Option<&Pair> {
2034 match self {
2035 Self::Pair(v) => Some(v),
2036 _ => None,
2037 }
2038 }
2039
2040 pub fn unwrap_pair(self) -> Pair {
2046 match self {
2047 Self::Pair(v) => v,
2048 _ => panic!("value is not a pair"),
2049 }
2050 }
2051
2052 pub fn as_array(&self) -> Option<&Array> {
2056 match self {
2057 Self::Array(v) => Some(v),
2058 _ => None,
2059 }
2060 }
2061
2062 pub fn unwrap_array(self) -> Array {
2068 match self {
2069 Self::Array(v) => v,
2070 _ => panic!("value is not an array"),
2071 }
2072 }
2073
2074 pub fn as_map(&self) -> Option<&Map> {
2078 match self {
2079 Self::Map(v) => Some(v),
2080 _ => None,
2081 }
2082 }
2083
2084 pub fn unwrap_map(self) -> Map {
2090 match self {
2091 Self::Map(v) => v,
2092 _ => panic!("value is not a map"),
2093 }
2094 }
2095
2096 pub fn as_object(&self) -> Option<&Object> {
2100 match self {
2101 Self::Object(v) => Some(v),
2102 _ => None,
2103 }
2104 }
2105
2106 pub fn unwrap_object(self) -> Object {
2112 match self {
2113 Self::Object(v) => v,
2114 _ => panic!("value is not an object"),
2115 }
2116 }
2117
2118 pub fn as_struct(&self) -> Option<&Struct> {
2122 match self {
2123 Self::Struct(v) => Some(v),
2124 _ => None,
2125 }
2126 }
2127
2128 pub fn unwrap_struct(self) -> Struct {
2134 match self {
2135 Self::Struct(v) => v,
2136 _ => panic!("value is not a struct"),
2137 }
2138 }
2139
2140 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2146 if left.ty() != right.ty() {
2149 return None;
2150 }
2151
2152 match (left, right) {
2153 (Self::Pair(left), Self::Pair(right)) => Some(
2154 Value::equals(left.left(), right.left())?
2155 && Value::equals(left.right(), right.right())?,
2156 ),
2157 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2158 left.len() == right.len()
2159 && left
2160 .as_slice()
2161 .iter()
2162 .zip(right.as_slice())
2163 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2164 ),
2165 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2166 left.len() == right.len()
2167 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2169 match (lk, rk) {
2170 (None, None) => {},
2171 (Some(lk), Some(rk)) if lk == rk => {},
2172 _ => return false
2173 }
2174
2175 Value::equals(lv, rv).unwrap_or(false)
2176 }),
2177 ),
2178 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2179 left.len() == right.len()
2180 && left.iter().all(|(k, left)| match right.get(k) {
2181 Some(right) => Value::equals(left, right).unwrap_or(false),
2182 None => false,
2183 }),
2184 ),
2185 (
2186 CompoundValue::Struct(Struct { members: left, .. }),
2187 CompoundValue::Struct(Struct { members: right, .. }),
2188 ) => Some(
2189 left.len() == right.len()
2190 && left.iter().all(|(k, left)| match right.get(k) {
2191 Some(right) => Value::equals(left, right).unwrap_or(false),
2192 None => false,
2193 }),
2194 ),
2195 _ => None,
2196 }
2197 }
2198
2199 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2204 where
2205 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2206 {
2207 match self {
2208 Self::Pair(pair) => {
2209 pair.left().visit_paths(cb)?;
2210 pair.right().visit_paths(cb)?;
2211 }
2212 Self::Array(array) => {
2213 for v in array.as_slice() {
2214 v.visit_paths(cb)?;
2215 }
2216 }
2217 Self::Map(map) => {
2218 for (k, v) in map.iter() {
2219 match k {
2220 Some(PrimitiveValue::File(path)) => cb(true, path)?,
2221 Some(PrimitiveValue::Directory(path)) => cb(false, path)?,
2222 _ => {}
2223 }
2224
2225 v.visit_paths(cb)?;
2226 }
2227 }
2228 Self::Object(object) => {
2229 for v in object.values() {
2230 v.visit_paths(cb)?;
2231 }
2232 }
2233 Self::Struct(s) => {
2234 for v in s.values() {
2235 v.visit_paths(cb)?;
2236 }
2237 }
2238 }
2239
2240 Ok(())
2241 }
2242
2243 fn resolve_paths<'a, F>(
2246 &'a self,
2247 base_dir: Option<&'a Path>,
2248 transferer: Option<&'a dyn Transferer>,
2249 translate: &'a F,
2250 ) -> BoxFuture<'a, Result<Self>>
2251 where
2252 F: Fn(&HostPath) -> Result<HostPath> + Send + Sync,
2253 {
2254 async move {
2255 match self {
2256 Self::Pair(pair) => {
2257 let ty = pair.ty.as_pair().expect("should be a pair type");
2258 let (left_optional, right_optional) =
2259 (ty.left_type().is_optional(), ty.right_type().is_optional());
2260 let (fst, snd) = pair.values.as_ref();
2261 let fst = fst
2262 .resolve_paths(left_optional, base_dir, transferer, translate)
2263 .await?;
2264 let snd = snd
2265 .resolve_paths(right_optional, base_dir, transferer, translate)
2266 .await?;
2267 Ok(Self::Pair(Pair::new_unchecked(ty.clone().into(), fst, snd)))
2268 }
2269 Self::Array(array) => {
2270 let ty = array.ty.as_array().expect("should be an array type");
2271 let optional = ty.element_type().is_optional();
2272 if let Some(elements) = &array.elements {
2273 let resolved_elements = futures::stream::iter(elements.iter())
2274 .then(|v| v.resolve_paths(optional, base_dir, transferer, translate))
2275 .try_collect()
2276 .await?;
2277 Ok(Self::Array(Array::new_unchecked(
2278 ty.clone().into(),
2279 resolved_elements,
2280 )))
2281 } else {
2282 Ok(self.clone())
2283 }
2284 }
2285 Self::Map(map) => {
2286 let ty = map.ty.as_map().expect("should be a map type").clone();
2287 let (key_optional, value_optional) =
2288 (ty.key_type().is_optional(), ty.value_type().is_optional());
2289 if let Some(elements) = &map.elements {
2290 let resolved_elements = futures::stream::iter(elements.iter())
2291 .then(async |(k, v)| {
2292 let resolved_key = if let Some(k) = k {
2293 Value::from(k.clone())
2294 .resolve_paths(
2295 key_optional,
2296 base_dir,
2297 transferer,
2298 translate,
2299 )
2300 .await?
2301 .as_primitive()
2302 .cloned()
2303 } else {
2304 None
2305 };
2306 let resolved_value = v
2307 .resolve_paths(value_optional, base_dir, transferer, translate)
2308 .await?;
2309 Ok::<_, anyhow::Error>((resolved_key, resolved_value))
2310 })
2311 .try_collect()
2312 .await?;
2313 Ok(Self::Map(Map::new_unchecked(ty.into(), resolved_elements)))
2314 } else {
2315 Ok(Self::Map(Map::new_unchecked(ty.into(), IndexMap::new())))
2316 }
2317 }
2318 Self::Object(object) => {
2319 if let Some(members) = &object.members {
2320 let resolved_members = futures::stream::iter(members.iter())
2321 .then(async |(n, v)| {
2322 let resolved = v
2323 .resolve_paths(false, base_dir, transferer, translate)
2324 .await?;
2325 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2326 })
2327 .try_collect()
2328 .await?;
2329 Ok(Self::Object(Object::new(resolved_members)))
2330 } else {
2331 Ok(self.clone())
2332 }
2333 }
2334 Self::Struct(s) => {
2335 let ty = s.ty.as_struct().expect("should be a struct type");
2336 let name = s.name();
2337 let resolved_members = futures::stream::iter(s.iter())
2338 .then(async |(n, v)| {
2339 let resolved = v
2340 .resolve_paths(
2341 ty.members()[n].is_optional(),
2342 base_dir,
2343 transferer,
2344 translate,
2345 )
2346 .await?;
2347 Ok::<_, anyhow::Error>((n.to_string(), resolved))
2348 })
2349 .try_collect()
2350 .await?;
2351 Ok(Self::Struct(Struct::new_unchecked(
2352 ty.clone().into(),
2353 name.clone(),
2354 Arc::new(resolved_members),
2355 )))
2356 }
2357 }
2358 }
2359 .boxed()
2360 }
2361}
2362
2363impl fmt::Display for CompoundValue {
2364 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2365 match self {
2366 Self::Pair(v) => v.fmt(f),
2367 Self::Array(v) => v.fmt(f),
2368 Self::Map(v) => v.fmt(f),
2369 Self::Object(v) => v.fmt(f),
2370 Self::Struct(v) => v.fmt(f),
2371 }
2372 }
2373}
2374
2375impl Coercible for CompoundValue {
2376 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2377 if target.is_union() || target.is_none() || self.ty().eq(target) {
2378 return Ok(self.clone());
2379 }
2380
2381 if let Type::Compound(target_ty, _) = target {
2382 match (self, target_ty) {
2383 (Self::Array(v), CompoundType::Array(target_ty)) => {
2385 if v.is_empty() && target_ty.is_non_empty() {
2388 bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2389 }
2390
2391 return Ok(Self::Array(Array::new(
2392 context,
2393 target.clone(),
2394 v.as_slice().iter().cloned(),
2395 )?));
2396 }
2397 (Self::Map(v), CompoundType::Map(map_ty)) => {
2399 return Ok(Self::Map(Map::new(
2400 context,
2401 target.clone(),
2402 v.iter().map(|(k, v)| {
2403 (
2404 k.clone()
2405 .map(Into::into)
2406 .unwrap_or(Value::new_none(map_ty.key_type().optional())),
2407 v.clone(),
2408 )
2409 }),
2410 )?));
2411 }
2412 (Self::Pair(v), CompoundType::Pair(_)) => {
2414 return Ok(Self::Pair(Pair::new(
2415 context,
2416 target.clone(),
2417 v.values.0.clone(),
2418 v.values.1.clone(),
2419 )?));
2420 }
2421 (Self::Map(v), CompoundType::Struct(target_ty)) => {
2423 let len = v.len();
2424 let expected_len = target_ty.members().len();
2425
2426 if len != expected_len {
2427 bail!(
2428 "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2429 as the struct has {expected_len} member{s2}",
2430 s1 = if len == 1 { "" } else { "s" },
2431 s2 = if expected_len == 1 { "" } else { "s" }
2432 );
2433 }
2434
2435 return Ok(Self::Struct(Struct {
2436 ty: target.clone(),
2437 name: target_ty.name().clone(),
2438 members: Arc::new(
2439 v.iter()
2440 .map(|(k, v)| {
2441 let k = k
2442 .as_ref()
2443 .and_then(|k| {
2444 k.coerce(context, &PrimitiveType::String.into()).ok()
2445 })
2446 .with_context(|| {
2447 format!(
2448 "cannot coerce a map of type `{map_type}` to \
2449 struct type `{target}` as the key type cannot \
2450 coerce to `String`",
2451 map_type = v.ty()
2452 )
2453 })?
2454 .unwrap_string();
2455 let ty =
2456 target_ty.members().get(k.as_ref()).with_context(|| {
2457 format!(
2458 "cannot coerce a map with key `{k}` to struct \
2459 type `{target}` as the struct does not contain a \
2460 member with that name"
2461 )
2462 })?;
2463 let v = v.coerce(context, ty).with_context(|| {
2464 format!("failed to coerce value of map key `{k}")
2465 })?;
2466 Ok((k.to_string(), v))
2467 })
2468 .collect::<Result<_>>()?,
2469 ),
2470 }));
2471 }
2472 (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2474 let key_ty = map_ty.key_type();
2475 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2476 bail!(
2477 "cannot coerce a struct to type `{target}` as key type `{key_ty}` \
2478 cannot be coerced from `String`"
2479 );
2480 }
2481
2482 let value_ty = map_ty.value_type();
2483 return Ok(Self::Map(Map::new_unchecked(
2484 target.clone(),
2485 members
2486 .iter()
2487 .map(|(n, v)| {
2488 let v = v
2489 .coerce(context, value_ty)
2490 .with_context(|| format!("failed to coerce member `{n}`"))?;
2491 Ok((
2492 PrimitiveValue::new_string(n)
2493 .coerce(context, key_ty)
2494 .expect("should coerce")
2495 .into(),
2496 v,
2497 ))
2498 })
2499 .collect::<Result<_>>()?,
2500 )));
2501 }
2502 (Self::Object(object), CompoundType::Map(map_ty)) => {
2504 let key_ty = map_ty.key_type();
2505 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2506 bail!(
2507 "cannot coerce an object to type `{target}` as key type `{key_ty}` \
2508 cannot be coerced from `String`"
2509 );
2510 }
2511
2512 let value_ty = map_ty.value_type();
2513 return Ok(Self::Map(Map::new_unchecked(
2514 target.clone(),
2515 object
2516 .iter()
2517 .map(|(n, v)| {
2518 let v = v
2519 .coerce(context, value_ty)
2520 .with_context(|| format!("failed to coerce member `{n}`"))?;
2521 Ok((
2522 PrimitiveValue::new_string(n)
2523 .coerce(context, key_ty)
2524 .expect("should coerce")
2525 .into(),
2526 v,
2527 ))
2528 })
2529 .collect::<Result<_>>()?,
2530 )));
2531 }
2532 (Self::Object(v), CompoundType::Struct(_)) => {
2534 return Ok(Self::Struct(Struct::new(
2535 context,
2536 target.clone(),
2537 v.iter().map(|(k, v)| (k, v.clone())),
2538 )?));
2539 }
2540 (Self::Struct(v), CompoundType::Struct(struct_ty)) => {
2542 let len = v.members.len();
2543 let expected_len = struct_ty.members().len();
2544
2545 if len != expected_len {
2546 bail!(
2547 "cannot coerce a struct of {len} members{s1} to struct type \
2548 `{target}` as the target struct has {expected_len} member{s2}",
2549 s1 = if len == 1 { "" } else { "s" },
2550 s2 = if expected_len == 1 { "" } else { "s" }
2551 );
2552 }
2553
2554 return Ok(Self::Struct(Struct {
2555 ty: target.clone(),
2556 name: struct_ty.name().clone(),
2557 members: Arc::new(
2558 v.members
2559 .iter()
2560 .map(|(k, v)| {
2561 let ty = struct_ty.members().get(k).ok_or_else(|| {
2562 anyhow!(
2563 "cannot coerce a struct with member `{k}` to struct \
2564 type `{target}` as the target struct does not \
2565 contain a member with that name",
2566 )
2567 })?;
2568 let v = v.coerce(context, ty).with_context(|| {
2569 format!("failed to coerce member `{k}`")
2570 })?;
2571 Ok((k.clone(), v))
2572 })
2573 .collect::<Result<_>>()?,
2574 ),
2575 }));
2576 }
2577 _ => {}
2578 }
2579 }
2580
2581 if let Type::Object = target {
2582 match self {
2583 Self::Map(v) => {
2585 return Ok(Self::Object(Object::new(
2586 v.iter()
2587 .map(|(k, v)| {
2588 let k = k
2589 .as_ref()
2590 .and_then(|k| {
2591 k.coerce(context, &PrimitiveType::String.into()).ok()
2592 })
2593 .with_context(|| {
2594 format!(
2595 "cannot coerce a map of type `{map_type}` to `Object` \
2596 as the key type cannot coerce to `String`",
2597 map_type = v.ty()
2598 )
2599 })?
2600 .unwrap_string();
2601 Ok((k.to_string(), v.clone()))
2602 })
2603 .collect::<Result<IndexMap<_, _>>>()?,
2604 )));
2605 }
2606 Self::Struct(v) => {
2608 return Ok(Self::Object(Object {
2609 members: Some(v.members.clone()),
2610 }));
2611 }
2612 _ => {}
2613 };
2614 }
2615
2616 bail!(
2617 "cannot coerce a value of type `{ty}` to type `{target}`",
2618 ty = self.ty()
2619 );
2620 }
2621}
2622
2623impl From<Pair> for CompoundValue {
2624 fn from(value: Pair) -> Self {
2625 Self::Pair(value)
2626 }
2627}
2628
2629impl From<Array> for CompoundValue {
2630 fn from(value: Array) -> Self {
2631 Self::Array(value)
2632 }
2633}
2634
2635impl From<Map> for CompoundValue {
2636 fn from(value: Map) -> Self {
2637 Self::Map(value)
2638 }
2639}
2640
2641impl From<Object> for CompoundValue {
2642 fn from(value: Object) -> Self {
2643 Self::Object(value)
2644 }
2645}
2646
2647impl From<Struct> for CompoundValue {
2648 fn from(value: Struct) -> Self {
2649 Self::Struct(value)
2650 }
2651}
2652
2653#[derive(Debug, Clone)]
2657pub enum HiddenValue {
2658 Hints(HintsValue),
2662 Input(InputValue),
2666 Output(OutputValue),
2670 TaskPreEvaluation(TaskPreEvaluationValue),
2675 TaskPostEvaluation(TaskPostEvaluationValue),
2680 PreviousTaskData(PreviousTaskDataValue),
2685}
2686
2687impl HiddenValue {
2688 pub fn ty(&self) -> Type {
2690 match self {
2691 Self::Hints(_) => Type::Hidden(HiddenType::Hints),
2692 Self::Input(_) => Type::Hidden(HiddenType::Input),
2693 Self::Output(_) => Type::Hidden(HiddenType::Output),
2694 Self::TaskPreEvaluation(_) => Type::Hidden(HiddenType::TaskPreEvaluation),
2695 Self::TaskPostEvaluation(_) => Type::Hidden(HiddenType::TaskPostEvaluation),
2696 Self::PreviousTaskData(_) => Type::Hidden(HiddenType::PreviousTaskData),
2697 }
2698 }
2699}
2700
2701impl fmt::Display for HiddenValue {
2702 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2703 match self {
2704 Self::Hints(v) => v.fmt(f),
2705 Self::Input(v) => v.fmt(f),
2706 Self::Output(v) => v.fmt(f),
2707 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => write!(f, "task"),
2708 Self::PreviousTaskData(_) => write!(f, "task.previous"),
2709 }
2710 }
2711}
2712
2713impl Coercible for HiddenValue {
2714 fn coerce(&self, _: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2715 match self {
2716 Self::Hints(_) => {
2717 if matches!(target, Type::Hidden(HiddenType::Hints)) {
2718 return Ok(self.clone());
2719 }
2720
2721 bail!("hints values cannot be coerced to any other type");
2722 }
2723 Self::Input(_) => {
2724 if matches!(target, Type::Hidden(HiddenType::Input)) {
2725 return Ok(self.clone());
2726 }
2727
2728 bail!("input values cannot be coerced to any other type");
2729 }
2730 Self::Output(_) => {
2731 if matches!(target, Type::Hidden(HiddenType::Output)) {
2732 return Ok(self.clone());
2733 }
2734
2735 bail!("output values cannot be coerced to any other type");
2736 }
2737 Self::TaskPreEvaluation(_) | Self::TaskPostEvaluation(_) => {
2738 if matches!(
2739 target,
2740 Type::Hidden(HiddenType::TaskPreEvaluation)
2741 | Type::Hidden(HiddenType::TaskPostEvaluation)
2742 ) {
2743 return Ok(self.clone());
2744 }
2745
2746 bail!("task variables cannot be coerced to any other type");
2747 }
2748 Self::PreviousTaskData(_) => {
2749 if matches!(target, Type::Hidden(HiddenType::PreviousTaskData)) {
2750 return Ok(self.clone());
2751 }
2752
2753 bail!("previous task data values cannot be coerced to any other type");
2754 }
2755 }
2756 }
2757}
2758
2759#[derive(Debug, Clone)]
2763pub struct TaskPostEvaluationData {
2764 container: Option<Arc<String>>,
2766 cpu: f64,
2768 memory: i64,
2770 gpu: Array,
2775 fpga: Array,
2780 disks: Map,
2787 max_retries: i64,
2789}
2790
2791#[derive(Debug, Clone)]
2795pub struct PreviousTaskDataValue(Option<Arc<TaskPostEvaluationData>>);
2796
2797impl PreviousTaskDataValue {
2798 pub fn new(data: Arc<TaskPostEvaluationData>) -> Self {
2800 Self(Some(data))
2801 }
2802
2803 pub fn empty() -> Self {
2805 Self(None)
2806 }
2807
2808 pub fn field(&self, name: &str) -> Option<Value> {
2814 match name {
2815 TASK_FIELD_MEMORY => Some(
2816 self.0
2817 .as_ref()
2818 .map(|data| Value::from(data.memory))
2819 .unwrap_or_else(|| {
2820 Value::new_none(Type::from(PrimitiveType::Integer).optional())
2821 }),
2822 ),
2823 TASK_FIELD_CPU => Some(
2824 self.0
2825 .as_ref()
2826 .map(|data| Value::from(data.cpu))
2827 .unwrap_or_else(|| {
2828 Value::new_none(Type::from(PrimitiveType::Float).optional())
2829 }),
2830 ),
2831 TASK_FIELD_CONTAINER => Some(
2832 self.0
2833 .as_ref()
2834 .and_then(|data| {
2835 data.container
2836 .as_ref()
2837 .map(|c| PrimitiveValue::String(c.clone()).into())
2838 })
2839 .unwrap_or_else(|| {
2840 Value::new_none(Type::from(PrimitiveType::String).optional())
2841 }),
2842 ),
2843 TASK_FIELD_GPU => Some(
2844 self.0
2845 .as_ref()
2846 .map(|data| Value::from(data.gpu.clone()))
2847 .unwrap_or_else(|| {
2848 Value::new_none(Type::Compound(
2849 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
2850 true,
2851 ))
2852 }),
2853 ),
2854 TASK_FIELD_FPGA => Some(
2855 self.0
2856 .as_ref()
2857 .map(|data| Value::from(data.fpga.clone()))
2858 .unwrap_or_else(|| {
2859 Value::new_none(Type::Compound(
2860 CompoundType::Array(ArrayType::new(PrimitiveType::String)),
2861 true,
2862 ))
2863 }),
2864 ),
2865 TASK_FIELD_DISKS => Some(
2866 self.0
2867 .as_ref()
2868 .map(|data| Value::from(data.disks.clone()))
2869 .unwrap_or_else(|| {
2870 Value::new_none(Type::Compound(
2871 CompoundType::Map(Arc::new(MapType::new(
2872 PrimitiveType::String,
2873 PrimitiveType::Integer,
2874 ))),
2875 true,
2876 ))
2877 }),
2878 ),
2879 TASK_FIELD_MAX_RETRIES => Some(
2880 self.0
2881 .as_ref()
2882 .map(|data| Value::from(data.max_retries))
2883 .unwrap_or_else(|| {
2884 Value::new_none(Type::from(PrimitiveType::Integer).optional())
2885 }),
2886 ),
2887 _ => None,
2888 }
2889 }
2890}
2891
2892#[derive(Debug, Clone)]
2899pub struct TaskPreEvaluationValue {
2900 name: Arc<String>,
2902 id: Arc<String>,
2904 attempt: i64,
2909 meta: Object,
2911 parameter_meta: Object,
2913 ext: Object,
2915 previous: PreviousTaskDataValue,
2921}
2922
2923impl TaskPreEvaluationValue {
2924 pub(crate) fn new(
2927 name: impl Into<String>,
2928 id: impl Into<String>,
2929 attempt: i64,
2930 meta: Object,
2931 parameter_meta: Object,
2932 ext: Object,
2933 ) -> Self {
2934 Self {
2935 name: Arc::new(name.into()),
2936 id: Arc::new(id.into()),
2937 meta,
2938 parameter_meta,
2939 ext,
2940 attempt,
2941 previous: PreviousTaskDataValue::empty(),
2942 }
2943 }
2944
2945 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
2947 self.previous = PreviousTaskDataValue::new(data);
2948 }
2949
2950 pub fn name(&self) -> &Arc<String> {
2952 &self.name
2953 }
2954
2955 pub fn id(&self) -> &Arc<String> {
2957 &self.id
2958 }
2959
2960 pub fn attempt(&self) -> i64 {
2962 self.attempt
2963 }
2964
2965 pub fn field(&self, name: &str) -> Option<Value> {
2969 match name {
2970 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
2971 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
2972 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
2973 TASK_FIELD_META => Some(self.meta.clone().into()),
2974 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
2975 TASK_FIELD_EXT => Some(self.ext.clone().into()),
2976 TASK_FIELD_PREVIOUS => {
2977 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
2978 }
2979 _ => None,
2980 }
2981 }
2982}
2983
2984#[derive(Debug, Clone)]
2990pub struct TaskPostEvaluationValue {
2991 data: Arc<TaskPostEvaluationData>,
2993 name: Arc<String>,
2995 id: Arc<String>,
2997 attempt: i64,
3002 meta: Object,
3004 parameter_meta: Object,
3006 ext: Object,
3008 return_code: Option<i64>,
3012 end_time: Option<i64>,
3016 previous: PreviousTaskDataValue,
3022}
3023
3024impl TaskPostEvaluationValue {
3025 #[allow(clippy::too_many_arguments)]
3028 pub(crate) fn new(
3029 name: impl Into<String>,
3030 id: impl Into<String>,
3031 constraints: TaskExecutionConstraints,
3032 max_retries: i64,
3033 attempt: i64,
3034 meta: Object,
3035 parameter_meta: Object,
3036 ext: Object,
3037 ) -> Self {
3038 Self {
3039 name: Arc::new(name.into()),
3040 id: Arc::new(id.into()),
3041 data: Arc::new(TaskPostEvaluationData {
3042 container: constraints.container.map(Into::into),
3043 cpu: constraints.cpu,
3044 memory: constraints.memory,
3045 gpu: Array::new_unchecked(
3046 ANALYSIS_STDLIB.array_string_type().clone(),
3047 constraints
3048 .gpu
3049 .into_iter()
3050 .map(|v| PrimitiveValue::new_string(v).into())
3051 .collect(),
3052 ),
3053 fpga: Array::new_unchecked(
3054 ANALYSIS_STDLIB.array_string_type().clone(),
3055 constraints
3056 .fpga
3057 .into_iter()
3058 .map(|v| PrimitiveValue::new_string(v).into())
3059 .collect(),
3060 ),
3061 disks: Map::new_unchecked(
3062 ANALYSIS_STDLIB.map_string_int_type().clone(),
3063 constraints
3064 .disks
3065 .into_iter()
3066 .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), v.into()))
3067 .collect(),
3068 ),
3069 max_retries,
3070 }),
3071 attempt,
3072 meta,
3073 parameter_meta,
3074 ext,
3075 return_code: None,
3076 end_time: None,
3077 previous: PreviousTaskDataValue::empty(),
3078 }
3079 }
3080
3081 pub fn name(&self) -> &Arc<String> {
3083 &self.name
3084 }
3085
3086 pub fn id(&self) -> &Arc<String> {
3088 &self.id
3089 }
3090
3091 pub fn container(&self) -> Option<&Arc<String>> {
3093 self.data.container.as_ref()
3094 }
3095
3096 pub fn cpu(&self) -> f64 {
3098 self.data.cpu
3099 }
3100
3101 pub fn memory(&self) -> i64 {
3103 self.data.memory
3104 }
3105
3106 pub fn gpu(&self) -> &Array {
3111 &self.data.gpu
3112 }
3113
3114 pub fn fpga(&self) -> &Array {
3119 &self.data.fpga
3120 }
3121
3122 pub fn disks(&self) -> &Map {
3129 &self.data.disks
3130 }
3131
3132 pub fn attempt(&self) -> i64 {
3137 self.attempt
3138 }
3139
3140 pub fn end_time(&self) -> Option<i64> {
3144 self.end_time
3145 }
3146
3147 pub fn return_code(&self) -> Option<i64> {
3151 self.return_code
3152 }
3153
3154 pub fn meta(&self) -> &Object {
3156 &self.meta
3157 }
3158
3159 pub fn parameter_meta(&self) -> &Object {
3161 &self.parameter_meta
3162 }
3163
3164 pub fn ext(&self) -> &Object {
3166 &self.ext
3167 }
3168
3169 pub(crate) fn set_return_code(&mut self, code: i32) {
3171 self.return_code = Some(code as i64);
3172 }
3173
3174 pub(crate) fn set_attempt(&mut self, attempt: i64) {
3176 self.attempt = attempt;
3177 }
3178
3179 pub(crate) fn set_previous(&mut self, data: Arc<TaskPostEvaluationData>) {
3181 self.previous = PreviousTaskDataValue::new(data);
3182 }
3183
3184 pub(crate) fn data(&self) -> &Arc<TaskPostEvaluationData> {
3186 &self.data
3187 }
3188
3189 pub fn field(&self, version: SupportedVersion, name: &str) -> Option<Value> {
3193 match name {
3194 TASK_FIELD_NAME => Some(PrimitiveValue::String(self.name.clone()).into()),
3195 TASK_FIELD_ID => Some(PrimitiveValue::String(self.id.clone()).into()),
3196 TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
3197 TASK_FIELD_CONTAINER => Some(
3198 self.data
3199 .container
3200 .clone()
3201 .map(|c| PrimitiveValue::String(c).into())
3202 .unwrap_or_else(|| {
3203 Value::new_none(
3204 task_member_type_post_evaluation(version, TASK_FIELD_CONTAINER)
3205 .expect("failed to get task field type"),
3206 )
3207 }),
3208 ),
3209 TASK_FIELD_CPU => Some(self.data.cpu.into()),
3210 TASK_FIELD_MEMORY => Some(self.data.memory.into()),
3211 TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
3212 TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
3213 TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
3214 TASK_FIELD_END_TIME => Some(self.end_time.map(Into::into).unwrap_or_else(|| {
3215 Value::new_none(
3216 task_member_type_post_evaluation(version, TASK_FIELD_END_TIME)
3217 .expect("failed to get task field type"),
3218 )
3219 })),
3220 TASK_FIELD_RETURN_CODE => Some(self.return_code.map(Into::into).unwrap_or_else(|| {
3221 Value::new_none(
3222 task_member_type_post_evaluation(version, TASK_FIELD_RETURN_CODE)
3223 .expect("failed to get task field type"),
3224 )
3225 })),
3226 TASK_FIELD_META => Some(self.meta.clone().into()),
3227 TASK_FIELD_PARAMETER_META => Some(self.parameter_meta.clone().into()),
3228 TASK_FIELD_EXT => Some(self.ext.clone().into()),
3229 TASK_FIELD_MAX_RETRIES if version >= SupportedVersion::V1(V1::Three) => {
3230 Some(self.data.max_retries.into())
3231 }
3232 TASK_FIELD_PREVIOUS if version >= SupportedVersion::V1(V1::Three) => {
3233 Some(HiddenValue::PreviousTaskData(self.previous.clone()).into())
3234 }
3235 _ => None,
3236 }
3237 }
3238}
3239
3240#[derive(Debug, Clone)]
3244pub struct HintsValue(Object);
3245
3246impl HintsValue {
3247 pub fn as_object(&self) -> &Object {
3249 &self.0
3250 }
3251}
3252
3253impl fmt::Display for HintsValue {
3254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3255 write!(f, "hints {{")?;
3256
3257 for (i, (k, v)) in self.0.iter().enumerate() {
3258 if i > 0 {
3259 write!(f, ", ")?;
3260 }
3261
3262 write!(f, "{k}: {v}")?;
3263 }
3264
3265 write!(f, "}}")
3266 }
3267}
3268
3269impl From<Object> for HintsValue {
3270 fn from(value: Object) -> Self {
3271 Self(value)
3272 }
3273}
3274
3275#[derive(Debug, Clone)]
3279pub struct InputValue(Object);
3280
3281impl InputValue {
3282 pub fn as_object(&self) -> &Object {
3284 &self.0
3285 }
3286}
3287
3288impl fmt::Display for InputValue {
3289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3290 write!(f, "input {{")?;
3291
3292 for (i, (k, v)) in self.0.iter().enumerate() {
3293 if i > 0 {
3294 write!(f, ", ")?;
3295 }
3296
3297 write!(f, "{k}: {v}")?;
3298 }
3299
3300 write!(f, "}}")
3301 }
3302}
3303
3304impl From<Object> for InputValue {
3305 fn from(value: Object) -> Self {
3306 Self(value)
3307 }
3308}
3309
3310#[derive(Debug, Clone)]
3314pub struct OutputValue(Object);
3315
3316impl OutputValue {
3317 pub fn as_object(&self) -> &Object {
3319 &self.0
3320 }
3321}
3322
3323impl fmt::Display for OutputValue {
3324 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3325 write!(f, "output {{")?;
3326
3327 for (i, (k, v)) in self.0.iter().enumerate() {
3328 if i > 0 {
3329 write!(f, ", ")?;
3330 }
3331
3332 write!(f, "{k}: {v}")?;
3333 }
3334
3335 write!(f, "}}")
3336 }
3337}
3338
3339impl From<Object> for OutputValue {
3340 fn from(value: Object) -> Self {
3341 Self(value)
3342 }
3343}
3344
3345#[derive(Debug, Clone)]
3349pub struct CallValue {
3350 ty: CallType,
3352 outputs: Arc<Outputs>,
3354}
3355
3356impl CallValue {
3357 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3360 Self { ty, outputs }
3361 }
3362
3363 pub fn ty(&self) -> &CallType {
3365 &self.ty
3366 }
3367
3368 pub fn outputs(&self) -> &Outputs {
3370 self.outputs.as_ref()
3371 }
3372}
3373
3374impl fmt::Display for CallValue {
3375 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3376 write!(f, "call output {{")?;
3377
3378 for (i, (k, v)) in self.outputs.iter().enumerate() {
3379 if i > 0 {
3380 write!(f, ", ")?;
3381 }
3382
3383 write!(f, "{k}: {v}")?;
3384 }
3385
3386 write!(f, "}}")
3387 }
3388}
3389
3390pub struct ValueSerializer<'a> {
3392 value: &'a Value,
3394 allow_pairs: bool,
3397}
3398
3399impl<'a> ValueSerializer<'a> {
3400 pub fn new(value: &'a Value, allow_pairs: bool) -> Self {
3402 Self { value, allow_pairs }
3403 }
3404}
3405
3406impl serde::Serialize for ValueSerializer<'_> {
3407 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3408 where
3409 S: serde::Serializer,
3410 {
3411 use serde::ser::Error;
3412
3413 match &self.value {
3414 Value::None(_) => serializer.serialize_none(),
3415 Value::Primitive(v) => v.serialize(serializer),
3416 Value::Compound(v) => {
3417 CompoundValueSerializer::new(v, self.allow_pairs).serialize(serializer)
3418 }
3419 Value::Hidden(_) | Value::Call(_) => {
3420 Err(S::Error::custom("value cannot be serialized"))
3421 }
3422 }
3423 }
3424}
3425
3426pub struct CompoundValueSerializer<'a> {
3428 value: &'a CompoundValue,
3430 allow_pairs: bool,
3433}
3434
3435impl<'a> CompoundValueSerializer<'a> {
3436 pub fn new(value: &'a CompoundValue, allow_pairs: bool) -> Self {
3438 Self { value, allow_pairs }
3439 }
3440}
3441
3442impl serde::Serialize for CompoundValueSerializer<'_> {
3443 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3444 where
3445 S: serde::Serializer,
3446 {
3447 use serde::ser::Error;
3448
3449 match &self.value {
3450 CompoundValue::Pair(pair) if self.allow_pairs => {
3451 let mut state = serializer.serialize_map(Some(2))?;
3452 let left = ValueSerializer::new(pair.left(), self.allow_pairs);
3453 let right = ValueSerializer::new(pair.right(), self.allow_pairs);
3454 state.serialize_entry("left", &left)?;
3455 state.serialize_entry("right", &right)?;
3456 state.end()
3457 }
3458 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3459 CompoundValue::Array(v) => {
3460 let mut s = serializer.serialize_seq(Some(v.len()))?;
3461 for v in v.as_slice() {
3462 s.serialize_element(&ValueSerializer::new(v, self.allow_pairs))?;
3463 }
3464
3465 s.end()
3466 }
3467 CompoundValue::Map(v) => {
3468 let ty = v.ty();
3469 let map_type = ty.as_map().expect("type should be a map");
3470 if !map_type
3471 .key_type()
3472 .is_coercible_to(&PrimitiveType::String.into())
3473 {
3474 return Err(S::Error::custom(format!(
3475 "cannot serialize a map of type `{ty}` as the key type cannot be coerced \
3476 to `String`",
3477 )));
3478 }
3479
3480 let mut s = serializer.serialize_map(Some(v.len()))?;
3481 for (k, v) in v.iter() {
3482 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3483 }
3484
3485 s.end()
3486 }
3487 CompoundValue::Object(object) => {
3488 let mut s = serializer.serialize_map(Some(object.len()))?;
3489 for (k, v) in object.iter() {
3490 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3491 }
3492
3493 s.end()
3494 }
3495 CompoundValue::Struct(Struct { members, .. }) => {
3496 let mut s = serializer.serialize_map(Some(members.len()))?;
3497 for (k, v) in members.iter() {
3498 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3499 }
3500
3501 s.end()
3502 }
3503 }
3504 }
3505}
3506
3507#[cfg(test)]
3508mod test {
3509 use approx::assert_relative_eq;
3510 use pretty_assertions::assert_eq;
3511 use wdl_analysis::types::ArrayType;
3512 use wdl_analysis::types::MapType;
3513 use wdl_analysis::types::PairType;
3514 use wdl_analysis::types::StructType;
3515 use wdl_ast::Diagnostic;
3516 use wdl_ast::Span;
3517 use wdl_ast::SupportedVersion;
3518
3519 use super::*;
3520 use crate::http::Transferer;
3521 use crate::path::EvaluationPath;
3522
3523 #[test]
3524 fn boolean_coercion() {
3525 assert_eq!(
3527 Value::from(false)
3528 .coerce(None, &PrimitiveType::Boolean.into())
3529 .expect("should coerce")
3530 .unwrap_boolean(),
3531 Value::from(false).unwrap_boolean()
3532 );
3533 assert_eq!(
3535 format!(
3536 "{e:?}",
3537 e = Value::from(true)
3538 .coerce(None, &PrimitiveType::String.into())
3539 .unwrap_err()
3540 ),
3541 "cannot coerce type `Boolean` to type `String`"
3542 );
3543 }
3544
3545 #[test]
3546 fn boolean_display() {
3547 assert_eq!(Value::from(false).to_string(), "false");
3548 assert_eq!(Value::from(true).to_string(), "true");
3549 }
3550
3551 #[test]
3552 fn integer_coercion() {
3553 assert_eq!(
3555 Value::from(12345)
3556 .coerce(None, &PrimitiveType::Integer.into())
3557 .expect("should coerce")
3558 .unwrap_integer(),
3559 Value::from(12345).unwrap_integer()
3560 );
3561 assert_relative_eq!(
3563 Value::from(12345)
3564 .coerce(None, &PrimitiveType::Float.into())
3565 .expect("should coerce")
3566 .unwrap_float(),
3567 Value::from(12345.0).unwrap_float()
3568 );
3569 assert_eq!(
3571 format!(
3572 "{e:?}",
3573 e = Value::from(12345)
3574 .coerce(None, &PrimitiveType::Boolean.into())
3575 .unwrap_err()
3576 ),
3577 "cannot coerce type `Int` to type `Boolean`"
3578 );
3579 }
3580
3581 #[test]
3582 fn integer_display() {
3583 assert_eq!(Value::from(12345).to_string(), "12345");
3584 assert_eq!(Value::from(-12345).to_string(), "-12345");
3585 }
3586
3587 #[test]
3588 fn float_coercion() {
3589 assert_relative_eq!(
3591 Value::from(12345.0)
3592 .coerce(None, &PrimitiveType::Float.into())
3593 .expect("should coerce")
3594 .unwrap_float(),
3595 Value::from(12345.0).unwrap_float()
3596 );
3597 assert_eq!(
3599 format!(
3600 "{e:?}",
3601 e = Value::from(12345.0)
3602 .coerce(None, &PrimitiveType::Integer.into())
3603 .unwrap_err()
3604 ),
3605 "cannot coerce type `Float` to type `Int`"
3606 );
3607 }
3608
3609 #[test]
3610 fn float_display() {
3611 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
3612 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
3613 }
3614
3615 #[test]
3616 fn string_coercion() {
3617 let value = PrimitiveValue::new_string("foo");
3618 assert_eq!(
3620 value
3621 .coerce(None, &PrimitiveType::String.into())
3622 .expect("should coerce"),
3623 value
3624 );
3625 assert_eq!(
3627 value
3628 .coerce(None, &PrimitiveType::File.into())
3629 .expect("should coerce"),
3630 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
3631 );
3632 assert_eq!(
3634 value
3635 .coerce(None, &PrimitiveType::Directory.into())
3636 .expect("should coerce"),
3637 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
3638 );
3639 assert_eq!(
3641 format!(
3642 "{e:?}",
3643 e = value
3644 .coerce(None, &PrimitiveType::Boolean.into())
3645 .unwrap_err()
3646 ),
3647 "cannot coerce type `String` to type `Boolean`"
3648 );
3649
3650 struct Context;
3651
3652 impl EvaluationContext for Context {
3653 fn version(&self) -> SupportedVersion {
3654 unimplemented!()
3655 }
3656
3657 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3658 unimplemented!()
3659 }
3660
3661 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3662 unimplemented!()
3663 }
3664
3665 fn base_dir(&self) -> &EvaluationPath {
3666 unimplemented!()
3667 }
3668
3669 fn temp_dir(&self) -> &Path {
3670 unimplemented!()
3671 }
3672
3673 fn transferer(&self) -> &dyn Transferer {
3674 unimplemented!()
3675 }
3676
3677 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
3678 if path.as_str() == "/mnt/task/input/0/path" {
3679 Some(HostPath::new("/some/host/path"))
3680 } else {
3681 None
3682 }
3683 }
3684 }
3685
3686 assert_eq!(
3688 PrimitiveValue::new_string("/mnt/task/input/0/path")
3689 .coerce(Some(&Context), &PrimitiveType::File.into())
3690 .expect("should coerce")
3691 .unwrap_file()
3692 .as_str(),
3693 "/some/host/path"
3694 );
3695
3696 assert_eq!(
3698 value
3699 .coerce(Some(&Context), &PrimitiveType::File.into())
3700 .expect("should coerce")
3701 .unwrap_file()
3702 .as_str(),
3703 "foo"
3704 );
3705
3706 assert_eq!(
3708 PrimitiveValue::new_string("/mnt/task/input/0/path")
3709 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3710 .expect("should coerce")
3711 .unwrap_directory()
3712 .as_str(),
3713 "/some/host/path"
3714 );
3715
3716 assert_eq!(
3718 value
3719 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3720 .expect("should coerce")
3721 .unwrap_directory()
3722 .as_str(),
3723 "foo"
3724 );
3725 }
3726
3727 #[test]
3728 fn string_display() {
3729 let value = PrimitiveValue::new_string("hello world!");
3730 assert_eq!(value.to_string(), "\"hello world!\"");
3731 }
3732
3733 #[test]
3734 fn file_coercion() {
3735 let value = PrimitiveValue::new_file("foo");
3736
3737 assert_eq!(
3739 value
3740 .coerce(None, &PrimitiveType::File.into())
3741 .expect("should coerce"),
3742 value
3743 );
3744 assert_eq!(
3746 value
3747 .coerce(None, &PrimitiveType::String.into())
3748 .expect("should coerce"),
3749 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
3750 );
3751 assert_eq!(
3753 format!(
3754 "{e:?}",
3755 e = value
3756 .coerce(None, &PrimitiveType::Directory.into())
3757 .unwrap_err()
3758 ),
3759 "cannot coerce type `File` to type `Directory`"
3760 );
3761
3762 struct Context;
3763
3764 impl EvaluationContext for Context {
3765 fn version(&self) -> SupportedVersion {
3766 unimplemented!()
3767 }
3768
3769 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3770 unimplemented!()
3771 }
3772
3773 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3774 unimplemented!()
3775 }
3776
3777 fn base_dir(&self) -> &EvaluationPath {
3778 unimplemented!()
3779 }
3780
3781 fn temp_dir(&self) -> &Path {
3782 unimplemented!()
3783 }
3784
3785 fn transferer(&self) -> &dyn Transferer {
3786 unimplemented!()
3787 }
3788
3789 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3790 if path.as_str() == "/some/host/path" {
3791 Some(GuestPath::new("/mnt/task/input/0/path"))
3792 } else {
3793 None
3794 }
3795 }
3796 }
3797
3798 assert_eq!(
3800 PrimitiveValue::new_file("/some/host/path")
3801 .coerce(Some(&Context), &PrimitiveType::String.into())
3802 .expect("should coerce")
3803 .unwrap_string()
3804 .as_str(),
3805 "/mnt/task/input/0/path"
3806 );
3807
3808 assert_eq!(
3810 value
3811 .coerce(Some(&Context), &PrimitiveType::String.into())
3812 .expect("should coerce")
3813 .unwrap_string()
3814 .as_str(),
3815 "foo"
3816 );
3817 }
3818
3819 #[test]
3820 fn file_display() {
3821 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
3822 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
3823 }
3824
3825 #[test]
3826 fn directory_coercion() {
3827 let value = PrimitiveValue::new_directory("foo");
3828
3829 assert_eq!(
3831 value
3832 .coerce(None, &PrimitiveType::Directory.into())
3833 .expect("should coerce"),
3834 value
3835 );
3836 assert_eq!(
3838 value
3839 .coerce(None, &PrimitiveType::String.into())
3840 .expect("should coerce"),
3841 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
3842 );
3843 assert_eq!(
3845 format!(
3846 "{e:?}",
3847 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
3848 ),
3849 "cannot coerce type `Directory` to type `File`"
3850 );
3851
3852 struct Context;
3853
3854 impl EvaluationContext for Context {
3855 fn version(&self) -> SupportedVersion {
3856 unimplemented!()
3857 }
3858
3859 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3860 unimplemented!()
3861 }
3862
3863 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3864 unimplemented!()
3865 }
3866
3867 fn base_dir(&self) -> &EvaluationPath {
3868 unimplemented!()
3869 }
3870
3871 fn temp_dir(&self) -> &Path {
3872 unimplemented!()
3873 }
3874
3875 fn transferer(&self) -> &dyn Transferer {
3876 unimplemented!()
3877 }
3878
3879 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3880 if path.as_str() == "/some/host/path" {
3881 Some(GuestPath::new("/mnt/task/input/0/path"))
3882 } else {
3883 None
3884 }
3885 }
3886 }
3887
3888 assert_eq!(
3890 PrimitiveValue::new_directory("/some/host/path")
3891 .coerce(Some(&Context), &PrimitiveType::String.into())
3892 .expect("should coerce")
3893 .unwrap_string()
3894 .as_str(),
3895 "/mnt/task/input/0/path"
3896 );
3897
3898 assert_eq!(
3900 value
3901 .coerce(Some(&Context), &PrimitiveType::String.into())
3902 .expect("should coerce")
3903 .unwrap_string()
3904 .as_str(),
3905 "foo"
3906 );
3907 }
3908
3909 #[test]
3910 fn directory_display() {
3911 let value = PrimitiveValue::new_directory("/foo/bar/baz");
3912 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
3913 }
3914
3915 #[test]
3916 fn none_coercion() {
3917 assert!(
3919 Value::new_none(Type::None)
3920 .coerce(None, &Type::from(PrimitiveType::String).optional())
3921 .expect("should coerce")
3922 .is_none(),
3923 );
3924
3925 assert_eq!(
3927 format!(
3928 "{e:?}",
3929 e = Value::new_none(Type::None)
3930 .coerce(None, &PrimitiveType::String.into())
3931 .unwrap_err()
3932 ),
3933 "cannot coerce `None` to non-optional type `String`"
3934 );
3935 }
3936
3937 #[test]
3938 fn none_display() {
3939 assert_eq!(Value::new_none(Type::None).to_string(), "None");
3940 }
3941
3942 #[test]
3943 fn array_coercion() {
3944 let src_ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3945 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
3946
3947 let src: CompoundValue = Array::new(None, src_ty, [1, 2, 3])
3949 .expect("should create array value")
3950 .into();
3951 let target = src.coerce(None, &target_ty).expect("should coerce");
3952 assert_eq!(
3953 target.unwrap_array().to_string(),
3954 "[1.000000, 2.000000, 3.000000]"
3955 );
3956
3957 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
3959 assert_eq!(
3960 format!("{e:?}", e = src.coerce(None, &target_ty).unwrap_err()),
3961 r#"failed to coerce array element at index 0
3962
3963Caused by:
3964 cannot coerce type `Int` to type `String`"#
3965 );
3966 }
3967
3968 #[test]
3969 fn non_empty_array_coercion() {
3970 let ty: Type = ArrayType::new(PrimitiveType::String).into();
3971 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
3972
3973 let string = PrimitiveValue::new_string("foo");
3975 let value: Value = Array::new(None, ty.clone(), [string])
3976 .expect("should create array")
3977 .into();
3978 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
3979
3980 let value: Value = Array::new::<Value>(None, ty, [])
3982 .expect("should create array")
3983 .into();
3984 assert_eq!(
3985 format!("{e:?}", e = value.coerce(None, &target_ty).unwrap_err()),
3986 "cannot coerce empty array value to non-empty array type `Array[String]+`"
3987 );
3988 }
3989
3990 #[test]
3991 fn array_display() {
3992 let ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3993 let value: Value = Array::new(None, ty, [1, 2, 3])
3994 .expect("should create array")
3995 .into();
3996
3997 assert_eq!(value.to_string(), "[1, 2, 3]");
3998 }
3999
4000 #[test]
4001 fn map_coerce() {
4002 let key1 = PrimitiveValue::new_file("foo");
4003 let value1 = PrimitiveValue::new_string("bar");
4004 let key2 = PrimitiveValue::new_file("baz");
4005 let value2 = PrimitiveValue::new_string("qux");
4006
4007 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
4008 let file_to_string: Value = Map::new(None, ty, [(key1, value1), (key2, value2)])
4009 .expect("should create map value")
4010 .into();
4011
4012 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
4014 let string_to_file = file_to_string
4015 .coerce(None, &ty)
4016 .expect("value should coerce");
4017 assert_eq!(
4018 string_to_file.to_string(),
4019 r#"{"foo": "bar", "baz": "qux"}"#
4020 );
4021
4022 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
4024 assert_eq!(
4025 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4026 r#"failed to coerce map key for element at index 0
4027
4028Caused by:
4029 cannot coerce type `String` to type `Int`"#
4030 );
4031
4032 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
4034 assert_eq!(
4035 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4036 r#"failed to coerce map value for element at index 0
4037
4038Caused by:
4039 cannot coerce type `File` to type `Int`"#
4040 );
4041
4042 let ty = StructType::new(
4044 "Foo",
4045 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
4046 )
4047 .into();
4048 let struct_value = string_to_file
4049 .coerce(None, &ty)
4050 .expect("value should coerce");
4051 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4052
4053 let ty = StructType::new(
4055 "Foo",
4056 [
4057 ("foo", PrimitiveType::String),
4058 ("baz", PrimitiveType::String),
4059 ],
4060 )
4061 .into();
4062 let struct_value = file_to_string
4063 .coerce(None, &ty)
4064 .expect("value should coerce");
4065 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
4066
4067 let ty = StructType::new(
4069 "Foo",
4070 [
4071 ("foo", PrimitiveType::File),
4072 ("baz", PrimitiveType::File),
4073 ("qux", PrimitiveType::File),
4074 ],
4075 )
4076 .into();
4077 assert_eq!(
4078 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
4079 "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
4080 );
4081
4082 let object_value = string_to_file
4084 .coerce(None, &Type::Object)
4085 .expect("value should coerce");
4086 assert_eq!(
4087 object_value.to_string(),
4088 r#"object {foo: "bar", baz: "qux"}"#
4089 );
4090
4091 let object_value = file_to_string
4093 .coerce(None, &Type::Object)
4094 .expect("value should coerce");
4095 assert_eq!(
4096 object_value.to_string(),
4097 r#"object {foo: "bar", baz: "qux"}"#
4098 );
4099 }
4100
4101 #[test]
4102 fn map_display() {
4103 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4104 let value: Value = Map::new(None, ty, [(1, true), (2, false)])
4105 .expect("should create map value")
4106 .into();
4107 assert_eq!(value.to_string(), "{1: true, 2: false}");
4108 }
4109
4110 #[test]
4111 fn pair_coercion() {
4112 let left = PrimitiveValue::new_file("foo");
4113 let right = PrimitiveValue::new_string("bar");
4114
4115 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4116 let value: Value = Pair::new(None, ty, left, right)
4117 .expect("should create pair value")
4118 .into();
4119
4120 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
4122 let value = value.coerce(None, &ty).expect("value should coerce");
4123 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
4124
4125 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
4127 assert_eq!(
4128 format!("{e:?}", e = value.coerce(None, &ty).unwrap_err()),
4129 r#"failed to coerce pair's left value
4130
4131Caused by:
4132 cannot coerce type `String` to type `Int`"#
4133 );
4134 }
4135
4136 #[test]
4137 fn pair_display() {
4138 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
4139 let value: Value = Pair::new(None, ty, 12345, false)
4140 .expect("should create pair value")
4141 .into();
4142 assert_eq!(value.to_string(), "(12345, false)");
4143 }
4144
4145 #[test]
4146 fn struct_coercion() {
4147 let ty = StructType::new(
4148 "Foo",
4149 [
4150 ("foo", PrimitiveType::Float),
4151 ("bar", PrimitiveType::Float),
4152 ("baz", PrimitiveType::Float),
4153 ],
4154 );
4155 let value: Value = Struct::new(None, ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
4156 .expect("should create map value")
4157 .into();
4158
4159 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
4161 let map_value = value.coerce(None, &ty).expect("value should coerce");
4162 assert_eq!(
4163 map_value.to_string(),
4164 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4165 );
4166
4167 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
4169 let map_value = value.coerce(None, &ty).expect("value should coerce");
4170 assert_eq!(
4171 map_value.to_string(),
4172 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
4173 );
4174
4175 let ty = StructType::new(
4177 "Bar",
4178 [
4179 ("foo", PrimitiveType::Float),
4180 ("bar", PrimitiveType::Float),
4181 ("baz", PrimitiveType::Float),
4182 ],
4183 )
4184 .into();
4185 let struct_value = value.coerce(None, &ty).expect("value should coerce");
4186 assert_eq!(
4187 struct_value.to_string(),
4188 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4189 );
4190
4191 let object_value = value
4193 .coerce(None, &Type::Object)
4194 .expect("value should coerce");
4195 assert_eq!(
4196 object_value.to_string(),
4197 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
4198 );
4199 }
4200
4201 #[test]
4202 fn struct_display() {
4203 let ty = StructType::new(
4204 "Foo",
4205 [
4206 ("foo", PrimitiveType::Float),
4207 ("bar", PrimitiveType::String),
4208 ("baz", PrimitiveType::Integer),
4209 ],
4210 );
4211 let value: Value = Struct::new(
4212 None,
4213 ty,
4214 [
4215 ("foo", Value::from(1.101)),
4216 ("bar", PrimitiveValue::new_string("foo").into()),
4217 ("baz", 1234.into()),
4218 ],
4219 )
4220 .expect("should create map value")
4221 .into();
4222 assert_eq!(
4223 value.to_string(),
4224 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
4225 );
4226 }
4227
4228 #[test]
4229 fn pair_serialization() {
4230 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
4231 let pair: Value = Pair::new(
4232 None,
4233 pair_ty,
4234 PrimitiveValue::new_file("foo"),
4235 PrimitiveValue::new_string("bar"),
4236 )
4237 .expect("should create pair value")
4238 .into();
4239 let value_serializer = ValueSerializer::new(&pair, true);
4241 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4242 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
4243
4244 let value_serializer = ValueSerializer::new(&pair, false);
4246 assert!(serde_json::to_string(&value_serializer).is_err());
4247
4248 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
4249 let array: Value = Array::new(None, array_ty, [pair])
4250 .expect("should create array value")
4251 .into();
4252
4253 let value_serializer = ValueSerializer::new(&array, true);
4255 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
4256 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
4257 }
4258}