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::future::BoxFuture;
17use indexmap::IndexMap;
18use itertools::Either;
19use ordered_float::OrderedFloat;
20use serde::ser::SerializeMap;
21use serde::ser::SerializeSeq;
22use wdl_analysis::stdlib::STDLIB as ANALYSIS_STDLIB;
23use wdl_analysis::types::ArrayType;
24use wdl_analysis::types::CallType;
25use wdl_analysis::types::Coercible as _;
26use wdl_analysis::types::CompoundType;
27use wdl_analysis::types::Optional;
28use wdl_analysis::types::PrimitiveType;
29use wdl_analysis::types::Type;
30use wdl_analysis::types::v1::task_member_type;
31use wdl_ast::AstToken;
32use wdl_ast::TreeNode;
33use wdl_ast::v1;
34use wdl_ast::v1::TASK_FIELD_ATTEMPT;
35use wdl_ast::v1::TASK_FIELD_CONTAINER;
36use wdl_ast::v1::TASK_FIELD_CPU;
37use wdl_ast::v1::TASK_FIELD_DISKS;
38use wdl_ast::v1::TASK_FIELD_END_TIME;
39use wdl_ast::v1::TASK_FIELD_EXT;
40use wdl_ast::v1::TASK_FIELD_FPGA;
41use wdl_ast::v1::TASK_FIELD_GPU;
42use wdl_ast::v1::TASK_FIELD_ID;
43use wdl_ast::v1::TASK_FIELD_MEMORY;
44use wdl_ast::v1::TASK_FIELD_META;
45use wdl_ast::v1::TASK_FIELD_NAME;
46use wdl_ast::v1::TASK_FIELD_PARAMETER_META;
47use wdl_ast::v1::TASK_FIELD_RETURN_CODE;
48
49use crate::EvaluationContext;
50use crate::GuestPath;
51use crate::HostPath;
52use crate::Outputs;
53use crate::TaskExecutionConstraints;
54use crate::http::Transferer;
55use crate::path;
56
57pub trait Coercible: Sized {
59 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self>;
67}
68
69#[derive(Debug, Clone)]
73pub enum Value {
74 None(Type),
78 Primitive(PrimitiveValue),
80 Compound(CompoundValue),
82 Task(TaskValue),
87 Hints(HintsValue),
91 Input(InputValue),
95 Output(OutputValue),
99 Call(CallValue),
101}
102
103impl Value {
104 pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
110 match value {
111 v1::MetadataValue::Boolean(v) => v.value().into(),
112 v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
113 v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
114 v1::MetadataValue::String(v) => PrimitiveValue::new_string(
115 v.text()
116 .expect("metadata strings shouldn't have placeholders")
117 .text(),
118 )
119 .into(),
120 v1::MetadataValue::Null(_) => Self::new_none(Type::None),
121 v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
122 v1::MetadataValue::Array(a) => Array::new_unchecked(
123 ANALYSIS_STDLIB.array_object_type().clone(),
124 a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
125 )
126 .into(),
127 }
128 }
129
130 pub fn new_none(ty: Type) -> Self {
136 assert!(ty.is_optional(), "the provided `None` type is not optional");
137 Self::None(ty)
138 }
139
140 pub fn ty(&self) -> Type {
142 match self {
143 Self::None(ty) => ty.clone(),
144 Self::Primitive(v) => v.ty(),
145 Self::Compound(v) => v.ty(),
146 Self::Task(_) => Type::Task,
147 Self::Hints(_) => Type::Hints,
148 Self::Input(_) => Type::Input,
149 Self::Output(_) => Type::Output,
150 Self::Call(v) => Type::Call(v.ty.clone()),
151 }
152 }
153
154 pub fn is_none(&self) -> bool {
156 matches!(self, Self::None(_))
157 }
158
159 pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
163 match self {
164 Self::Primitive(v) => Some(v),
165 _ => None,
166 }
167 }
168
169 pub fn as_compound(&self) -> Option<&CompoundValue> {
173 match self {
174 Self::Compound(v) => Some(v),
175 _ => None,
176 }
177 }
178
179 pub fn as_boolean(&self) -> Option<bool> {
183 match self {
184 Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
185 _ => None,
186 }
187 }
188
189 pub fn unwrap_boolean(self) -> bool {
195 match self {
196 Self::Primitive(PrimitiveValue::Boolean(v)) => v,
197 _ => panic!("value is not a boolean"),
198 }
199 }
200
201 pub fn as_integer(&self) -> Option<i64> {
205 match self {
206 Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
207 _ => None,
208 }
209 }
210
211 pub fn unwrap_integer(self) -> i64 {
217 match self {
218 Self::Primitive(PrimitiveValue::Integer(v)) => v,
219 _ => panic!("value is not an integer"),
220 }
221 }
222
223 pub fn as_float(&self) -> Option<f64> {
227 match self {
228 Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
229 _ => None,
230 }
231 }
232
233 pub fn unwrap_float(self) -> f64 {
239 match self {
240 Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
241 _ => panic!("value is not a float"),
242 }
243 }
244
245 pub fn as_string(&self) -> Option<&Arc<String>> {
249 match self {
250 Self::Primitive(PrimitiveValue::String(s)) => Some(s),
251 _ => None,
252 }
253 }
254
255 pub fn unwrap_string(self) -> Arc<String> {
261 match self {
262 Self::Primitive(PrimitiveValue::String(s)) => s,
263 _ => panic!("value is not a string"),
264 }
265 }
266
267 pub fn as_file(&self) -> Option<&HostPath> {
271 match self {
272 Self::Primitive(PrimitiveValue::File(p)) => Some(p),
273 _ => None,
274 }
275 }
276
277 pub fn unwrap_file(self) -> HostPath {
283 match self {
284 Self::Primitive(PrimitiveValue::File(p)) => p,
285 _ => panic!("value is not a file"),
286 }
287 }
288
289 pub fn as_directory(&self) -> Option<&HostPath> {
293 match self {
294 Self::Primitive(PrimitiveValue::Directory(p)) => Some(p),
295 _ => None,
296 }
297 }
298
299 pub fn unwrap_directory(self) -> HostPath {
305 match self {
306 Self::Primitive(PrimitiveValue::Directory(p)) => p,
307 _ => panic!("value is not a directory"),
308 }
309 }
310
311 pub fn as_pair(&self) -> Option<&Pair> {
315 match self {
316 Self::Compound(CompoundValue::Pair(v)) => Some(v),
317 _ => None,
318 }
319 }
320
321 pub fn unwrap_pair(self) -> Pair {
327 match self {
328 Self::Compound(CompoundValue::Pair(v)) => v,
329 _ => panic!("value is not a pair"),
330 }
331 }
332
333 pub fn as_array(&self) -> Option<&Array> {
337 match self {
338 Self::Compound(CompoundValue::Array(v)) => Some(v),
339 _ => None,
340 }
341 }
342
343 pub fn unwrap_array(self) -> Array {
349 match self {
350 Self::Compound(CompoundValue::Array(v)) => v,
351 _ => panic!("value is not an array"),
352 }
353 }
354
355 pub fn as_map(&self) -> Option<&Map> {
359 match self {
360 Self::Compound(CompoundValue::Map(v)) => Some(v),
361 _ => None,
362 }
363 }
364
365 pub fn unwrap_map(self) -> Map {
371 match self {
372 Self::Compound(CompoundValue::Map(v)) => v,
373 _ => panic!("value is not a map"),
374 }
375 }
376
377 pub fn as_object(&self) -> Option<&Object> {
381 match self {
382 Self::Compound(CompoundValue::Object(v)) => Some(v),
383 _ => None,
384 }
385 }
386
387 pub fn unwrap_object(self) -> Object {
393 match self {
394 Self::Compound(CompoundValue::Object(v)) => v,
395 _ => panic!("value is not an object"),
396 }
397 }
398
399 pub fn as_struct(&self) -> Option<&Struct> {
403 match self {
404 Self::Compound(CompoundValue::Struct(v)) => Some(v),
405 _ => None,
406 }
407 }
408
409 pub fn unwrap_struct(self) -> Struct {
415 match self {
416 Self::Compound(CompoundValue::Struct(v)) => v,
417 _ => panic!("value is not a struct"),
418 }
419 }
420
421 pub fn as_task(&self) -> Option<&TaskValue> {
425 match self {
426 Self::Task(v) => Some(v),
427 _ => None,
428 }
429 }
430
431 pub(crate) fn as_task_mut(&mut self) -> Option<&mut TaskValue> {
435 match self {
436 Self::Task(v) => Some(v),
437 _ => None,
438 }
439 }
440
441 pub fn unwrap_task(self) -> TaskValue {
447 match self {
448 Self::Task(v) => v,
449 _ => panic!("value is not a task"),
450 }
451 }
452
453 pub fn as_hints(&self) -> Option<&HintsValue> {
457 match self {
458 Self::Hints(v) => Some(v),
459 _ => None,
460 }
461 }
462
463 pub fn unwrap_hints(self) -> HintsValue {
469 match self {
470 Self::Hints(v) => v,
471 _ => panic!("value is not a hints value"),
472 }
473 }
474
475 pub fn as_call(&self) -> Option<&CallValue> {
479 match self {
480 Self::Call(v) => Some(v),
481 _ => None,
482 }
483 }
484
485 pub fn unwrap_call(self) -> CallValue {
491 match self {
492 Self::Call(v) => v,
493 _ => panic!("value is not a call value"),
494 }
495 }
496
497 pub(crate) fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
502 where
503 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
504 {
505 match self {
506 Self::Primitive(PrimitiveValue::File(path)) => cb(true, path),
507 Self::Primitive(PrimitiveValue::Directory(path)) => cb(false, path),
508 Self::Compound(v) => v.visit_paths(cb),
509 _ => Ok(()),
510 }
511 }
512
513 pub(crate) async fn ensure_paths_exist<F>(
530 &mut self,
531 optional: bool,
532 base_dir: Option<&Path>,
533 transferer: Option<&dyn Transferer>,
534 translate: &F,
535 ) -> Result<()>
536 where
537 F: Fn(&mut HostPath) -> Result<()> + Send + Sync,
538 {
539 match self {
540 Self::Primitive(v @ PrimitiveValue::File(_))
541 | Self::Primitive(v @ PrimitiveValue::Directory(_)) => {
542 let (path, is_file) = match v {
543 PrimitiveValue::File(path) => (path, true),
544 PrimitiveValue::Directory(path) => (path, false),
545 _ => unreachable!("not a `File` or `Directory` value"),
546 };
547
548 translate(path)?;
549
550 if path::is_file_url(path.as_str()) {
552 let exists = path::parse_url(path.as_str())
553 .and_then(|url| url.to_file_path().ok())
554 .map(|p| p.exists())
555 .unwrap_or(false);
556 if exists {
557 return Ok(());
558 }
559
560 if optional && !exists {
561 *self = Value::new_none(self.ty().optional());
562 return Ok(());
563 }
564
565 bail!("path `{path}` does not exist");
566 } else if path::is_url(path.as_str()) {
567 match transferer {
568 Some(transferer) => {
569 let exists = transferer
570 .exists(
571 &path
572 .as_str()
573 .parse()
574 .with_context(|| format!("invalid URL `{path}`"))?,
575 )
576 .await?;
577 if exists {
578 return Ok(());
579 }
580
581 if optional && !exists {
582 *self = Value::new_none(self.ty().optional());
583 return Ok(());
584 }
585
586 bail!("URL `{path}` does not exist");
587 }
588 None => {
589 return Ok(());
591 }
592 }
593 }
594
595 let path: Cow<'_, Path> = base_dir
597 .map(|d| d.join(path.as_str()).into())
598 .unwrap_or_else(|| Path::new(path.as_str()).into());
599 if is_file && !path.is_file() {
600 if optional {
601 *self = Value::new_none(self.ty().optional());
602 return Ok(());
603 }
604
605 bail!("file `{path}` does not exist", path = path.display());
606 } else if !is_file && !path.is_dir() {
607 if optional {
608 *self = Value::new_none(self.ty().optional());
609 return Ok(());
610 }
611
612 bail!("directory `{path}` does not exist", path = path.display())
613 }
614
615 Ok(())
616 }
617 Self::Compound(v) => v.ensure_paths_exist(base_dir, transferer, translate).await,
618 _ => Ok(()),
619 }
620 }
621
622 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
627 match (left, right) {
628 (Value::None(_), Value::None(_)) => Some(true),
629 (Value::None(_), _) | (_, Value::None(_)) => Some(false),
630 (Value::Primitive(left), Value::Primitive(right)) => {
631 Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
632 }
633 (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
634 _ => None,
635 }
636 }
637}
638
639impl fmt::Display for Value {
640 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
641 match self {
642 Self::None(_) => write!(f, "None"),
643 Self::Primitive(v) => v.fmt(f),
644 Self::Compound(v) => v.fmt(f),
645 Self::Task(_) => write!(f, "task"),
646 Self::Hints(v) => v.fmt(f),
647 Self::Input(v) => v.fmt(f),
648 Self::Output(v) => v.fmt(f),
649 Self::Call(c) => c.fmt(f),
650 }
651 }
652}
653
654impl Coercible for Value {
655 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
656 if target.is_union() || target.is_none() || self.ty().eq(target) {
657 return Ok(self.clone());
658 }
659
660 match self {
661 Self::None(_) => {
662 if target.is_optional() {
663 Ok(Self::new_none(target.clone()))
664 } else {
665 bail!("cannot coerce `None` to non-optional type `{target}`");
666 }
667 }
668 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
669 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
670 Self::Task(_) => {
671 if matches!(target, Type::Task) {
672 return Ok(self.clone());
673 }
674
675 bail!("task variables cannot be coerced to any other type");
676 }
677 Self::Hints(_) => {
678 if matches!(target, Type::Hints) {
679 return Ok(self.clone());
680 }
681
682 bail!("hints values cannot be coerced to any other type");
683 }
684 Self::Input(_) => {
685 if matches!(target, Type::Input) {
686 return Ok(self.clone());
687 }
688
689 bail!("input values cannot be coerced to any other type");
690 }
691 Self::Output(_) => {
692 if matches!(target, Type::Output) {
693 return Ok(self.clone());
694 }
695
696 bail!("output values cannot be coerced to any other type");
697 }
698 Self::Call(_) => {
699 bail!("call values cannot be coerced to any other type");
700 }
701 }
702 }
703}
704
705impl From<bool> for Value {
706 fn from(value: bool) -> Self {
707 Self::Primitive(value.into())
708 }
709}
710
711impl From<i64> for Value {
712 fn from(value: i64) -> Self {
713 Self::Primitive(value.into())
714 }
715}
716
717impl TryFrom<u64> for Value {
718 type Error = std::num::TryFromIntError;
719
720 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
721 let value: i64 = value.try_into()?;
722 Ok(value.into())
723 }
724}
725
726impl From<f64> for Value {
727 fn from(value: f64) -> Self {
728 Self::Primitive(value.into())
729 }
730}
731
732impl From<String> for Value {
733 fn from(value: String) -> Self {
734 Self::Primitive(value.into())
735 }
736}
737
738impl From<PrimitiveValue> for Value {
739 fn from(value: PrimitiveValue) -> Self {
740 Self::Primitive(value)
741 }
742}
743
744impl From<Option<PrimitiveValue>> for Value {
745 fn from(value: Option<PrimitiveValue>) -> Self {
746 match value {
747 Some(v) => v.into(),
748 None => Self::new_none(Type::None),
749 }
750 }
751}
752
753impl From<CompoundValue> for Value {
754 fn from(value: CompoundValue) -> Self {
755 Self::Compound(value)
756 }
757}
758
759impl From<Pair> for Value {
760 fn from(value: Pair) -> Self {
761 Self::Compound(value.into())
762 }
763}
764
765impl From<Array> for Value {
766 fn from(value: Array) -> Self {
767 Self::Compound(value.into())
768 }
769}
770
771impl From<Map> for Value {
772 fn from(value: Map) -> Self {
773 Self::Compound(value.into())
774 }
775}
776
777impl From<Object> for Value {
778 fn from(value: Object) -> Self {
779 Self::Compound(value.into())
780 }
781}
782
783impl From<Struct> for Value {
784 fn from(value: Struct) -> Self {
785 Self::Compound(value.into())
786 }
787}
788
789impl From<TaskValue> for Value {
790 fn from(value: TaskValue) -> Self {
791 Self::Task(value)
792 }
793}
794
795impl From<HintsValue> for Value {
796 fn from(value: HintsValue) -> Self {
797 Self::Hints(value)
798 }
799}
800
801impl From<CallValue> for Value {
802 fn from(value: CallValue) -> Self {
803 Self::Call(value)
804 }
805}
806
807impl<'de> serde::Deserialize<'de> for Value {
808 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
809 where
810 D: serde::Deserializer<'de>,
811 {
812 use serde::Deserialize as _;
813
814 struct Deserialize;
816
817 impl<'de> serde::de::DeserializeSeed<'de> for Deserialize {
818 type Value = Value;
819
820 fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
821 where
822 D: serde::Deserializer<'de>,
823 {
824 deserializer.deserialize_any(Visitor)
825 }
826 }
827
828 struct Visitor;
830
831 impl<'de> serde::de::Visitor<'de> for Visitor {
832 type Value = Value;
833
834 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
835 where
836 E: serde::de::Error,
837 {
838 Ok(Value::new_none(Type::None))
839 }
840
841 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
842 where
843 E: serde::de::Error,
844 {
845 Ok(Value::new_none(Type::None))
846 }
847
848 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
849 where
850 D: serde::Deserializer<'de>,
851 {
852 Value::deserialize(deserializer)
853 }
854
855 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
856 where
857 E: serde::de::Error,
858 {
859 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
860 }
861
862 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
863 where
864 E: serde::de::Error,
865 {
866 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
867 }
868
869 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
870 where
871 E: serde::de::Error,
872 {
873 Ok(Value::Primitive(PrimitiveValue::Integer(
874 v.try_into().map_err(|_| {
875 E::custom("integer not in range for a 64-bit signed integer")
876 })?,
877 )))
878 }
879
880 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
881 where
882 E: serde::de::Error,
883 {
884 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
885 }
886
887 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
888 where
889 E: serde::de::Error,
890 {
891 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
892 }
893
894 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
895 where
896 E: serde::de::Error,
897 {
898 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
899 }
900
901 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
902 where
903 A: serde::de::SeqAccess<'de>,
904 {
905 use serde::de::Error;
906
907 let mut elements = Vec::new();
908 while let Some(v) = seq.next_element_seed(Deserialize)? {
909 elements.push(v);
910 }
911
912 let element_ty = elements
913 .iter()
914 .try_fold(None, |mut ty, element| {
915 let element_ty = element.ty();
916 let ty = ty.get_or_insert(element_ty.clone());
917 ty.common_type(&element_ty).map(Some).ok_or_else(|| {
918 A::Error::custom(format!(
919 "a common element type does not exist between `{ty}` and \
920 `{element_ty}`"
921 ))
922 })
923 })?
924 .unwrap_or(Type::Union);
925
926 let ty: Type = ArrayType::new(element_ty).into();
927 Ok(Array::new(None, ty.clone(), elements)
928 .map_err(|e| A::Error::custom(format!("cannot coerce value to `{ty}`: {e:#}")))?
929 .into())
930 }
931
932 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
933 where
934 A: serde::de::MapAccess<'de>,
935 {
936 let mut members = IndexMap::new();
937 while let Some(key) = map.next_key::<String>()? {
938 members.insert(key, map.next_value_seed(Deserialize)?);
939 }
940
941 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
942 }
943
944 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
945 write!(f, "a WDL value")
946 }
947 }
948
949 deserializer.deserialize_any(Visitor)
950 }
951}
952
953#[derive(Debug, Clone)]
957pub enum PrimitiveValue {
958 Boolean(bool),
960 Integer(i64),
962 Float(OrderedFloat<f64>),
964 String(Arc<String>),
966 File(HostPath),
968 Directory(HostPath),
970}
971
972impl PrimitiveValue {
973 pub fn new_string(s: impl Into<String>) -> Self {
975 Self::String(Arc::new(s.into()))
976 }
977
978 pub fn new_file(path: impl Into<String>) -> Self {
980 Self::File(Arc::new(path.into()).into())
981 }
982
983 pub fn new_directory(path: impl Into<String>) -> Self {
985 Self::Directory(Arc::new(path.into()).into())
986 }
987
988 pub fn ty(&self) -> Type {
990 match self {
991 Self::Boolean(_) => PrimitiveType::Boolean.into(),
992 Self::Integer(_) => PrimitiveType::Integer.into(),
993 Self::Float(_) => PrimitiveType::Float.into(),
994 Self::String(_) => PrimitiveType::String.into(),
995 Self::File(_) => PrimitiveType::File.into(),
996 Self::Directory(_) => PrimitiveType::Directory.into(),
997 }
998 }
999
1000 pub fn as_boolean(&self) -> Option<bool> {
1004 match self {
1005 Self::Boolean(v) => Some(*v),
1006 _ => None,
1007 }
1008 }
1009
1010 pub fn unwrap_boolean(self) -> bool {
1016 match self {
1017 Self::Boolean(v) => v,
1018 _ => panic!("value is not a boolean"),
1019 }
1020 }
1021
1022 pub fn as_integer(&self) -> Option<i64> {
1026 match self {
1027 Self::Integer(v) => Some(*v),
1028 _ => None,
1029 }
1030 }
1031
1032 pub fn unwrap_integer(self) -> i64 {
1038 match self {
1039 Self::Integer(v) => v,
1040 _ => panic!("value is not an integer"),
1041 }
1042 }
1043
1044 pub fn as_float(&self) -> Option<f64> {
1048 match self {
1049 Self::Float(v) => Some((*v).into()),
1050 _ => None,
1051 }
1052 }
1053
1054 pub fn unwrap_float(self) -> f64 {
1060 match self {
1061 Self::Float(v) => v.into(),
1062 _ => panic!("value is not a float"),
1063 }
1064 }
1065
1066 pub fn as_string(&self) -> Option<&Arc<String>> {
1070 match self {
1071 Self::String(s) => Some(s),
1072 _ => None,
1073 }
1074 }
1075
1076 pub fn unwrap_string(self) -> Arc<String> {
1082 match self {
1083 Self::String(s) => s,
1084 _ => panic!("value is not a string"),
1085 }
1086 }
1087
1088 pub fn as_file(&self) -> Option<&HostPath> {
1092 match self {
1093 Self::File(p) => Some(p),
1094 _ => None,
1095 }
1096 }
1097
1098 pub fn unwrap_file(self) -> HostPath {
1104 match self {
1105 Self::File(p) => p,
1106 _ => panic!("value is not a file"),
1107 }
1108 }
1109
1110 pub fn as_directory(&self) -> Option<&HostPath> {
1114 match self {
1115 Self::Directory(p) => Some(p),
1116 _ => None,
1117 }
1118 }
1119
1120 pub fn unwrap_directory(self) -> HostPath {
1126 match self {
1127 Self::Directory(p) => p,
1128 _ => panic!("value is not a directory"),
1129 }
1130 }
1131
1132 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1139 match (left, right) {
1140 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1141 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1142 (Self::Integer(left), Self::Float(right)) => {
1143 Some(OrderedFloat(*left as f64).cmp(right))
1144 }
1145 (Self::Float(left), Self::Integer(right)) => {
1146 Some(left.cmp(&OrderedFloat(*right as f64)))
1147 }
1148 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1149 (Self::String(left), Self::String(right))
1150 | (Self::String(left), Self::File(HostPath(right)))
1151 | (Self::String(left), Self::Directory(HostPath(right)))
1152 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1153 | (Self::File(HostPath(left)), Self::String(right))
1154 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1155 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1156 _ => None,
1157 }
1158 }
1159
1160 pub fn raw<'a>(
1168 &'a self,
1169 context: Option<&'a dyn EvaluationContext>,
1170 ) -> impl fmt::Display + use<'a> {
1171 struct Display<'a> {
1173 value: &'a PrimitiveValue,
1175 context: Option<&'a dyn EvaluationContext>,
1177 }
1178
1179 impl fmt::Display for Display<'_> {
1180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1181 match self.value {
1182 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1183 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1184 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1185 PrimitiveValue::String(v) => write!(f, "{v}"),
1186 PrimitiveValue::File(v) => {
1187 write!(
1188 f,
1189 "{v}",
1190 v = self
1191 .context
1192 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1193 .unwrap_or(Cow::Borrowed(&v.0))
1194 )
1195 }
1196 PrimitiveValue::Directory(v) => {
1197 write!(
1198 f,
1199 "{v}",
1200 v = self
1201 .context
1202 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1203 .unwrap_or(Cow::Borrowed(&v.0))
1204 )
1205 }
1206 }
1207 }
1208 }
1209
1210 Display {
1211 value: self,
1212 context,
1213 }
1214 }
1215}
1216
1217impl fmt::Display for PrimitiveValue {
1218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1219 match self {
1220 Self::Boolean(v) => write!(f, "{v}"),
1221 Self::Integer(v) => write!(f, "{v}"),
1222 Self::Float(v) => write!(f, "{v:.6?}"),
1223 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1224 write!(f, "\"{s}\"")
1226 }
1227 }
1228 }
1229}
1230
1231impl PartialEq for PrimitiveValue {
1232 fn eq(&self, other: &Self) -> bool {
1233 Self::compare(self, other) == Some(Ordering::Equal)
1234 }
1235}
1236
1237impl Eq for PrimitiveValue {}
1238
1239impl Hash for PrimitiveValue {
1240 fn hash<H: Hasher>(&self, state: &mut H) {
1241 match self {
1242 Self::Boolean(v) => {
1243 0.hash(state);
1244 v.hash(state);
1245 }
1246 Self::Integer(v) => {
1247 1.hash(state);
1248 v.hash(state);
1249 }
1250 Self::Float(v) => {
1251 1.hash(state);
1254 v.hash(state);
1255 }
1256 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1257 2.hash(state);
1260 v.hash(state);
1261 }
1262 }
1263 }
1264}
1265
1266impl From<bool> for PrimitiveValue {
1267 fn from(value: bool) -> Self {
1268 Self::Boolean(value)
1269 }
1270}
1271
1272impl From<i64> for PrimitiveValue {
1273 fn from(value: i64) -> Self {
1274 Self::Integer(value)
1275 }
1276}
1277
1278impl From<f64> for PrimitiveValue {
1279 fn from(value: f64) -> Self {
1280 Self::Float(value.into())
1281 }
1282}
1283
1284impl From<String> for PrimitiveValue {
1285 fn from(value: String) -> Self {
1286 Self::String(value.into())
1287 }
1288}
1289
1290impl Coercible for PrimitiveValue {
1291 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1292 if target.is_union() || target.is_none() || self.ty().eq(target) {
1293 return Ok(self.clone());
1294 }
1295
1296 match self {
1297 Self::Boolean(v) => {
1298 target
1299 .as_primitive()
1300 .and_then(|ty| match ty {
1301 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1303 _ => None,
1304 })
1305 .with_context(|| format!("cannot coerce type `Boolean` to type `{target}`"))
1306 }
1307 Self::Integer(v) => {
1308 target
1309 .as_primitive()
1310 .and_then(|ty| match ty {
1311 PrimitiveType::Integer => Some(Self::Integer(*v)),
1313 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1315 _ => None,
1316 })
1317 .with_context(|| format!("cannot coerce type `Int` to type `{target}`"))
1318 }
1319 Self::Float(v) => {
1320 target
1321 .as_primitive()
1322 .and_then(|ty| match ty {
1323 PrimitiveType::Float => Some(Self::Float(*v)),
1325 _ => None,
1326 })
1327 .with_context(|| format!("cannot coerce type `Float` to type `{target}`"))
1328 }
1329 Self::String(s) => {
1330 target
1331 .as_primitive()
1332 .and_then(|ty| match ty {
1333 PrimitiveType::String => Some(Self::String(s.clone())),
1335 PrimitiveType::File => Some(Self::File(
1337 context
1338 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1339 .unwrap_or_else(|| s.clone().into()),
1340 )),
1341 PrimitiveType::Directory => Some(Self::Directory(
1343 context
1344 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1345 .unwrap_or_else(|| s.clone().into()),
1346 )),
1347 _ => None,
1348 })
1349 .with_context(|| format!("cannot coerce type `String` to type `{target}`"))
1350 }
1351 Self::File(p) => {
1352 target
1353 .as_primitive()
1354 .and_then(|ty| match ty {
1355 PrimitiveType::File => Some(Self::File(p.clone())),
1357 PrimitiveType::String => Some(Self::String(
1359 context
1360 .and_then(|c| c.guest_path(p).map(Into::into))
1361 .unwrap_or_else(|| p.clone().into()),
1362 )),
1363 _ => None,
1364 })
1365 .with_context(|| format!("cannot coerce type `File` to type `{target}`"))
1366 }
1367 Self::Directory(p) => {
1368 target
1369 .as_primitive()
1370 .and_then(|ty| match ty {
1371 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1373 PrimitiveType::String => Some(Self::String(
1375 context
1376 .and_then(|c| c.guest_path(p).map(Into::into))
1377 .unwrap_or_else(|| p.clone().into()),
1378 )),
1379 _ => None,
1380 })
1381 .with_context(|| format!("cannot coerce type `Directory` to type `{target}`"))
1382 }
1383 }
1384 }
1385}
1386
1387impl serde::Serialize for PrimitiveValue {
1388 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1389 where
1390 S: serde::Serializer,
1391 {
1392 match self {
1393 Self::Boolean(v) => v.serialize(serializer),
1394 Self::Integer(v) => v.serialize(serializer),
1395 Self::Float(v) => v.serialize(serializer),
1396 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1397 s.serialize(serializer)
1398 }
1399 }
1400 }
1401}
1402
1403#[derive(Debug, Clone)]
1407pub struct Pair {
1408 ty: Type,
1410 values: Arc<(Value, Value)>,
1412}
1413
1414impl Pair {
1415 pub fn new(
1424 context: Option<&dyn EvaluationContext>,
1425 ty: impl Into<Type>,
1426 left: impl Into<Value>,
1427 right: impl Into<Value>,
1428 ) -> Result<Self> {
1429 let ty = ty.into();
1430 if let Type::Compound(CompoundType::Pair(ty), _) = ty {
1431 let left = left
1432 .into()
1433 .coerce(context, ty.left_type())
1434 .context("failed to coerce pair's left value")?;
1435 let right = right
1436 .into()
1437 .coerce(context, ty.right_type())
1438 .context("failed to coerce pair's right value")?;
1439 return Ok(Self::new_unchecked(
1440 Type::Compound(CompoundType::Pair(ty), false),
1441 left,
1442 right,
1443 ));
1444 }
1445
1446 panic!("type `{ty}` is not a pair type");
1447 }
1448
1449 pub(crate) fn new_unchecked(ty: Type, left: Value, right: Value) -> Self {
1452 assert!(ty.as_pair().is_some());
1453 Self {
1454 ty: ty.require(),
1455 values: Arc::new((left, right)),
1456 }
1457 }
1458
1459 pub fn ty(&self) -> Type {
1461 self.ty.clone()
1462 }
1463
1464 pub fn left(&self) -> &Value {
1466 &self.values.0
1467 }
1468
1469 pub fn right(&self) -> &Value {
1471 &self.values.1
1472 }
1473}
1474
1475impl fmt::Display for Pair {
1476 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1477 write!(
1478 f,
1479 "({left}, {right})",
1480 left = self.values.0,
1481 right = self.values.1
1482 )
1483 }
1484}
1485
1486#[derive(Debug, Clone)]
1490pub struct Array {
1491 ty: Type,
1493 elements: Option<Arc<Vec<Value>>>,
1497}
1498
1499impl Array {
1500 pub fn new<V>(
1509 context: Option<&dyn EvaluationContext>,
1510 ty: impl Into<Type>,
1511 elements: impl IntoIterator<Item = V>,
1512 ) -> Result<Self>
1513 where
1514 V: Into<Value>,
1515 {
1516 let ty = ty.into();
1517 if let Type::Compound(CompoundType::Array(ty), _) = ty {
1518 let element_type = ty.element_type();
1519 let elements = elements
1520 .into_iter()
1521 .enumerate()
1522 .map(|(i, v)| {
1523 let v = v.into();
1524 v.coerce(context, element_type)
1525 .with_context(|| format!("failed to coerce array element at index {i}"))
1526 })
1527 .collect::<Result<Vec<_>>>()?;
1528
1529 return Ok(Self::new_unchecked(
1530 Type::Compound(CompoundType::Array(ty.unqualified()), false),
1531 elements,
1532 ));
1533 }
1534
1535 panic!("type `{ty}` is not an array type");
1536 }
1537
1538 pub(crate) fn new_unchecked(ty: Type, elements: Vec<Value>) -> Self {
1541 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty {
1542 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1543 } else {
1544 panic!("type is not an array type");
1545 };
1546
1547 Self {
1548 ty,
1549 elements: if elements.is_empty() {
1550 None
1551 } else {
1552 Some(Arc::new(elements))
1553 },
1554 }
1555 }
1556
1557 pub fn ty(&self) -> Type {
1559 self.ty.clone()
1560 }
1561
1562 pub fn as_slice(&self) -> &[Value] {
1564 self.elements.as_ref().map(|v| v.as_slice()).unwrap_or(&[])
1565 }
1566
1567 pub fn len(&self) -> usize {
1569 self.elements.as_ref().map(|v| v.len()).unwrap_or(0)
1570 }
1571
1572 pub fn is_empty(&self) -> bool {
1574 self.len() == 0
1575 }
1576}
1577
1578impl fmt::Display for Array {
1579 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1580 write!(f, "[")?;
1581
1582 if let Some(elements) = &self.elements {
1583 for (i, element) in elements.iter().enumerate() {
1584 if i > 0 {
1585 write!(f, ", ")?;
1586 }
1587
1588 write!(f, "{element}")?;
1589 }
1590 }
1591
1592 write!(f, "]")
1593 }
1594}
1595
1596#[derive(Debug, Clone)]
1600pub struct Map {
1601 ty: Type,
1603 elements: Option<Arc<IndexMap<Option<PrimitiveValue>, Value>>>,
1607}
1608
1609impl Map {
1610 pub fn new<K, V>(
1619 context: Option<&dyn EvaluationContext>,
1620 ty: impl Into<Type>,
1621 elements: impl IntoIterator<Item = (K, V)>,
1622 ) -> Result<Self>
1623 where
1624 K: Into<Value>,
1625 V: Into<Value>,
1626 {
1627 let ty = ty.into();
1628 if let Type::Compound(CompoundType::Map(ty), _) = ty {
1629 let key_type = ty.key_type();
1630 let value_type = ty.value_type();
1631
1632 let elements = elements
1633 .into_iter()
1634 .enumerate()
1635 .map(|(i, (k, v))| {
1636 let k = k.into();
1637 let v = v.into();
1638 Ok((
1639 if k.is_none() {
1640 None
1641 } else {
1642 match k.coerce(context, key_type).with_context(|| {
1643 format!("failed to coerce map key for element at index {i}")
1644 })? {
1645 Value::None(_) => None,
1646 Value::Primitive(v) => Some(v),
1647 _ => {
1648 bail!("not all key values are primitive")
1649 }
1650 }
1651 },
1652 v.coerce(context, value_type).with_context(|| {
1653 format!("failed to coerce map value for element at index {i}")
1654 })?,
1655 ))
1656 })
1657 .collect::<Result<_>>()?;
1658
1659 return Ok(Self::new_unchecked(
1660 Type::Compound(CompoundType::Map(ty), false),
1661 elements,
1662 ));
1663 }
1664
1665 panic!("type `{ty}` is not a map type");
1666 }
1667
1668 pub(crate) fn new_unchecked(
1671 ty: Type,
1672 elements: IndexMap<Option<PrimitiveValue>, Value>,
1673 ) -> Self {
1674 assert!(ty.as_map().is_some());
1675 Self {
1676 ty: ty.require(),
1677 elements: if elements.is_empty() {
1678 None
1679 } else {
1680 Some(Arc::new(elements))
1681 },
1682 }
1683 }
1684
1685 pub fn ty(&self) -> Type {
1687 self.ty.clone()
1688 }
1689
1690 pub fn iter(&self) -> impl Iterator<Item = (&Option<PrimitiveValue>, &Value)> {
1692 self.elements
1693 .as_ref()
1694 .map(|m| Either::Left(m.iter()))
1695 .unwrap_or(Either::Right(std::iter::empty()))
1696 }
1697
1698 pub fn keys(&self) -> impl Iterator<Item = &Option<PrimitiveValue>> {
1700 self.elements
1701 .as_ref()
1702 .map(|m| Either::Left(m.keys()))
1703 .unwrap_or(Either::Right(std::iter::empty()))
1704 }
1705
1706 pub fn values(&self) -> impl Iterator<Item = &Value> {
1708 self.elements
1709 .as_ref()
1710 .map(|m| Either::Left(m.values()))
1711 .unwrap_or(Either::Right(std::iter::empty()))
1712 }
1713
1714 pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1716 self.elements
1717 .as_ref()
1718 .map(|m| m.contains_key(key))
1719 .unwrap_or(false)
1720 }
1721
1722 pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1724 self.elements.as_ref().and_then(|m| m.get(key))
1725 }
1726
1727 pub fn len(&self) -> usize {
1729 self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1730 }
1731
1732 pub fn is_empty(&self) -> bool {
1734 self.len() == 0
1735 }
1736}
1737
1738impl fmt::Display for Map {
1739 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1740 write!(f, "{{")?;
1741
1742 for (i, (k, v)) in self.iter().enumerate() {
1743 if i > 0 {
1744 write!(f, ", ")?;
1745 }
1746
1747 match k {
1748 Some(k) => write!(f, "{k}: {v}")?,
1749 None => write!(f, "None: {v}")?,
1750 }
1751 }
1752
1753 write!(f, "}}")
1754 }
1755}
1756
1757#[derive(Debug, Clone)]
1761pub struct Object {
1762 pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1766}
1767
1768impl Object {
1769 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1773 Self {
1774 members: if members.is_empty() {
1775 None
1776 } else {
1777 Some(Arc::new(members))
1778 },
1779 }
1780 }
1781
1782 pub fn empty() -> Self {
1784 Self::new(IndexMap::default())
1785 }
1786
1787 pub fn from_v1_metadata<N: TreeNode>(
1789 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1790 ) -> Self {
1791 Object::new(
1792 items
1793 .map(|i| {
1794 (
1795 i.name().text().to_string(),
1796 Value::from_v1_metadata(&i.value()),
1797 )
1798 })
1799 .collect::<IndexMap<_, _>>(),
1800 )
1801 }
1802
1803 pub fn ty(&self) -> Type {
1805 Type::Object
1806 }
1807
1808 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1810 self.members
1811 .as_ref()
1812 .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1813 .unwrap_or(Either::Right(std::iter::empty()))
1814 }
1815
1816 pub fn keys(&self) -> impl Iterator<Item = &str> {
1818 self.members
1819 .as_ref()
1820 .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1821 .unwrap_or(Either::Right(std::iter::empty()))
1822 }
1823
1824 pub fn values(&self) -> impl Iterator<Item = &Value> {
1826 self.members
1827 .as_ref()
1828 .map(|m| Either::Left(m.values()))
1829 .unwrap_or(Either::Right(std::iter::empty()))
1830 }
1831
1832 pub fn contains_key(&self, key: &str) -> bool {
1834 self.members
1835 .as_ref()
1836 .map(|m| m.contains_key(key))
1837 .unwrap_or(false)
1838 }
1839
1840 pub fn get(&self, key: &str) -> Option<&Value> {
1842 self.members.as_ref().and_then(|m| m.get(key))
1843 }
1844
1845 pub fn len(&self) -> usize {
1847 self.members.as_ref().map(|m| m.len()).unwrap_or(0)
1848 }
1849
1850 pub fn is_empty(&self) -> bool {
1852 self.len() == 0
1853 }
1854}
1855
1856impl fmt::Display for Object {
1857 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1858 write!(f, "object {{")?;
1859
1860 for (i, (k, v)) in self.iter().enumerate() {
1861 if i > 0 {
1862 write!(f, ", ")?;
1863 }
1864
1865 write!(f, "{k}: {v}")?;
1866 }
1867
1868 write!(f, "}}")
1869 }
1870}
1871
1872#[derive(Debug, Clone)]
1876pub struct Struct {
1877 ty: Type,
1879 name: Arc<String>,
1881 pub(crate) members: Arc<IndexMap<String, Value>>,
1883}
1884
1885impl Struct {
1886 pub fn new<S, V>(
1895 context: Option<&dyn EvaluationContext>,
1896 ty: impl Into<Type>,
1897 members: impl IntoIterator<Item = (S, V)>,
1898 ) -> Result<Self>
1899 where
1900 S: Into<String>,
1901 V: Into<Value>,
1902 {
1903 let ty = ty.into();
1904 if let Type::Compound(CompoundType::Struct(ty), optional) = ty {
1905 let mut members = members
1906 .into_iter()
1907 .map(|(n, v)| {
1908 let n = n.into();
1909 let v = v.into();
1910 let v = v
1911 .coerce(
1912 context,
1913 ty.members().get(&n).ok_or_else(|| {
1914 anyhow!("struct does not contain a member named `{n}`")
1915 })?,
1916 )
1917 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
1918 Ok((n, v))
1919 })
1920 .collect::<Result<IndexMap<_, _>>>()?;
1921
1922 for (name, ty) in ty.members().iter() {
1923 if ty.is_optional() {
1925 if !members.contains_key(name) {
1926 members.insert(name.clone(), Value::new_none(ty.clone()));
1927 }
1928 } else {
1929 if !members.contains_key(name) {
1931 bail!("missing a value for struct member `{name}`");
1932 }
1933 }
1934 }
1935
1936 let name = ty.name().to_string();
1937 return Ok(Self {
1938 ty: Type::Compound(CompoundType::Struct(ty), optional),
1939 name: Arc::new(name),
1940 members: Arc::new(members),
1941 });
1942 }
1943
1944 panic!("type `{ty}` is not a struct type");
1945 }
1946
1947 pub(crate) fn new_unchecked(
1950 ty: Type,
1951 name: Arc<String>,
1952 members: Arc<IndexMap<String, Value>>,
1953 ) -> Self {
1954 assert!(ty.as_struct().is_some());
1955 Self {
1956 ty: ty.require(),
1957 name,
1958 members,
1959 }
1960 }
1961
1962 pub fn ty(&self) -> Type {
1964 self.ty.clone()
1965 }
1966
1967 pub fn name(&self) -> &Arc<String> {
1969 &self.name
1970 }
1971
1972 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1974 self.members.iter().map(|(k, v)| (k.as_str(), v))
1975 }
1976
1977 pub fn keys(&self) -> impl Iterator<Item = &str> {
1979 self.members.keys().map(|k| k.as_str())
1980 }
1981
1982 pub fn values(&self) -> impl Iterator<Item = &Value> {
1984 self.members.values()
1985 }
1986
1987 pub fn contains_key(&self, key: &str) -> bool {
1989 self.members.contains_key(key)
1990 }
1991
1992 pub fn get(&self, key: &str) -> Option<&Value> {
1994 self.members.get(key)
1995 }
1996}
1997
1998impl fmt::Display for Struct {
1999 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2000 write!(f, "{name} {{", name = self.name)?;
2001
2002 for (i, (k, v)) in self.members.iter().enumerate() {
2003 if i > 0 {
2004 write!(f, ", ")?;
2005 }
2006
2007 write!(f, "{k}: {v}")?;
2008 }
2009
2010 write!(f, "}}")
2011 }
2012}
2013
2014#[derive(Debug, Clone)]
2018pub enum CompoundValue {
2019 Pair(Pair),
2021 Array(Array),
2023 Map(Map),
2025 Object(Object),
2027 Struct(Struct),
2029}
2030
2031impl CompoundValue {
2032 pub fn ty(&self) -> Type {
2034 match self {
2035 CompoundValue::Pair(v) => v.ty(),
2036 CompoundValue::Array(v) => v.ty(),
2037 CompoundValue::Map(v) => v.ty(),
2038 CompoundValue::Object(v) => v.ty(),
2039 CompoundValue::Struct(v) => v.ty(),
2040 }
2041 }
2042
2043 pub fn as_pair(&self) -> Option<&Pair> {
2047 match self {
2048 Self::Pair(v) => Some(v),
2049 _ => None,
2050 }
2051 }
2052
2053 pub fn unwrap_pair(self) -> Pair {
2059 match self {
2060 Self::Pair(v) => v,
2061 _ => panic!("value is not a pair"),
2062 }
2063 }
2064
2065 pub fn as_array(&self) -> Option<&Array> {
2069 match self {
2070 Self::Array(v) => Some(v),
2071 _ => None,
2072 }
2073 }
2074
2075 pub fn unwrap_array(self) -> Array {
2081 match self {
2082 Self::Array(v) => v,
2083 _ => panic!("value is not an array"),
2084 }
2085 }
2086
2087 pub fn as_map(&self) -> Option<&Map> {
2091 match self {
2092 Self::Map(v) => Some(v),
2093 _ => None,
2094 }
2095 }
2096
2097 pub fn unwrap_map(self) -> Map {
2103 match self {
2104 Self::Map(v) => v,
2105 _ => panic!("value is not a map"),
2106 }
2107 }
2108
2109 pub fn as_object(&self) -> Option<&Object> {
2113 match self {
2114 Self::Object(v) => Some(v),
2115 _ => None,
2116 }
2117 }
2118
2119 pub fn unwrap_object(self) -> Object {
2125 match self {
2126 Self::Object(v) => v,
2127 _ => panic!("value is not an object"),
2128 }
2129 }
2130
2131 pub fn as_struct(&self) -> Option<&Struct> {
2135 match self {
2136 Self::Struct(v) => Some(v),
2137 _ => None,
2138 }
2139 }
2140
2141 pub fn unwrap_struct(self) -> Struct {
2147 match self {
2148 Self::Struct(v) => v,
2149 _ => panic!("value is not a struct"),
2150 }
2151 }
2152
2153 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2159 if left.ty() != right.ty() {
2162 return None;
2163 }
2164
2165 match (left, right) {
2166 (Self::Pair(left), Self::Pair(right)) => Some(
2167 Value::equals(left.left(), right.left())?
2168 && Value::equals(left.right(), right.right())?,
2169 ),
2170 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2171 left.len() == right.len()
2172 && left
2173 .as_slice()
2174 .iter()
2175 .zip(right.as_slice())
2176 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2177 ),
2178 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2179 left.len() == right.len()
2180 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2182 match (lk, rk) {
2183 (None, None) => {},
2184 (Some(lk), Some(rk)) if lk == rk => {},
2185 _ => return false
2186 }
2187
2188 Value::equals(lv, rv).unwrap_or(false)
2189 }),
2190 ),
2191 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2192 left.len() == right.len()
2193 && left.iter().all(|(k, left)| match right.get(k) {
2194 Some(right) => Value::equals(left, right).unwrap_or(false),
2195 None => false,
2196 }),
2197 ),
2198 (
2199 CompoundValue::Struct(Struct { members: left, .. }),
2200 CompoundValue::Struct(Struct { members: right, .. }),
2201 ) => Some(
2202 left.len() == right.len()
2203 && left.iter().all(|(k, left)| match right.get(k) {
2204 Some(right) => Value::equals(left, right).unwrap_or(false),
2205 None => false,
2206 }),
2207 ),
2208 _ => None,
2209 }
2210 }
2211
2212 fn visit_paths<F>(&self, cb: &mut F) -> Result<()>
2217 where
2218 F: FnMut(bool, &HostPath) -> Result<()> + Send + Sync,
2219 {
2220 match self {
2221 Self::Pair(pair) => {
2222 pair.left().visit_paths(cb)?;
2223 pair.right().visit_paths(cb)?;
2224 }
2225 Self::Array(array) => {
2226 for v in array.as_slice() {
2227 v.visit_paths(cb)?;
2228 }
2229 }
2230 Self::Map(map) => {
2231 for (k, v) in map.iter() {
2232 match k {
2233 Some(PrimitiveValue::File(path)) => cb(true, path)?,
2234 Some(PrimitiveValue::Directory(path)) => cb(false, path)?,
2235 _ => {}
2236 }
2237
2238 v.visit_paths(cb)?;
2239 }
2240 }
2241 Self::Object(object) => {
2242 for v in object.values() {
2243 v.visit_paths(cb)?;
2244 }
2245 }
2246 Self::Struct(s) => {
2247 for v in s.values() {
2248 v.visit_paths(cb)?;
2249 }
2250 }
2251 }
2252
2253 Ok(())
2254 }
2255
2256 fn ensure_paths_exist<'a, F>(
2263 &'a mut self,
2264 base_dir: Option<&'a Path>,
2265 transferer: Option<&'a dyn Transferer>,
2266 translate: &'a F,
2267 ) -> BoxFuture<'a, Result<()>>
2268 where
2269 F: Fn(&mut HostPath) -> Result<()> + Send + Sync,
2270 {
2271 async move {
2272 match self {
2273 Self::Pair(pair) => {
2274 let ty = pair.ty.as_pair().expect("should be a pair type");
2275 let (left_optional, right_optional) =
2276 (ty.left_type().is_optional(), ty.right_type().is_optional());
2277 let values = Arc::make_mut(&mut pair.values);
2278 values
2279 .0
2280 .ensure_paths_exist(left_optional, base_dir, transferer, translate)
2281 .await?;
2282 values
2283 .1
2284 .ensure_paths_exist(right_optional, base_dir, transferer, translate)
2285 .await?;
2286 }
2287 Self::Array(array) => {
2288 let ty = array.ty.as_array().expect("should be an array type");
2289 let optional = ty.element_type().is_optional();
2290 if let Some(elements) = &mut array.elements {
2291 for v in Arc::make_mut(elements) {
2292 v.ensure_paths_exist(optional, base_dir, transferer, translate)
2293 .await?;
2294 }
2295 }
2296 }
2297 Self::Map(map) => {
2298 let ty = map.ty.as_map().expect("should be a map type");
2299 let (key_optional, value_optional) =
2300 (ty.key_type().is_optional(), ty.value_type().is_optional());
2301 if let Some(elements) = &mut map.elements {
2302 if elements
2303 .iter()
2304 .find_map(|(k, _)| {
2305 k.as_ref().map(|v| {
2306 matches!(
2307 v,
2308 PrimitiveValue::File(_) | PrimitiveValue::Directory(_)
2309 )
2310 })
2311 })
2312 .unwrap_or(false)
2313 {
2314 let elements = Arc::make_mut(elements);
2317 let mut new = Vec::with_capacity(elements.len());
2318 for (mut k, mut v) in elements.drain(..) {
2319 if let Some(v) = k {
2320 let mut v: Value = v.into();
2321 v.ensure_paths_exist(
2322 key_optional,
2323 base_dir,
2324 transferer,
2325 translate,
2326 )
2327 .await?;
2328 k = match v {
2329 Value::None(_) => None,
2330 Value::Primitive(v) => Some(v),
2331 _ => unreachable!("unexpected value"),
2332 };
2333 }
2334
2335 v.ensure_paths_exist(
2336 value_optional,
2337 base_dir,
2338 transferer,
2339 translate,
2340 )
2341 .await?;
2342 new.push((k, v));
2343 }
2344
2345 elements.extend(new);
2346 } else {
2347 for v in Arc::make_mut(elements).values_mut() {
2349 v.ensure_paths_exist(
2350 value_optional,
2351 base_dir,
2352 transferer,
2353 translate,
2354 )
2355 .await?;
2356 }
2357 }
2358 }
2359 }
2360 Self::Object(object) => {
2361 if let Some(members) = &mut object.members {
2362 for v in Arc::make_mut(members).values_mut() {
2363 v.ensure_paths_exist(false, base_dir, transferer, translate)
2364 .await?;
2365 }
2366 }
2367 }
2368 Self::Struct(s) => {
2369 let ty = s.ty.as_struct().expect("should be a struct type");
2370 for (n, v) in Arc::make_mut(&mut s.members).iter_mut() {
2371 v.ensure_paths_exist(
2372 ty.members()[n].is_optional(),
2373 base_dir,
2374 transferer,
2375 translate,
2376 )
2377 .await?;
2378 }
2379 }
2380 }
2381
2382 Ok(())
2383 }
2384 .boxed()
2385 }
2386}
2387
2388impl fmt::Display for CompoundValue {
2389 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2390 match self {
2391 Self::Pair(v) => v.fmt(f),
2392 Self::Array(v) => v.fmt(f),
2393 Self::Map(v) => v.fmt(f),
2394 Self::Object(v) => v.fmt(f),
2395 Self::Struct(v) => v.fmt(f),
2396 }
2397 }
2398}
2399
2400impl Coercible for CompoundValue {
2401 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2402 if target.is_union() || target.is_none() || self.ty().eq(target) {
2403 return Ok(self.clone());
2404 }
2405
2406 if let Type::Compound(target_ty, _) = target {
2407 match (self, target_ty) {
2408 (Self::Array(v), CompoundType::Array(target_ty)) => {
2410 if v.is_empty() && target_ty.is_non_empty() {
2413 bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2414 }
2415
2416 return Ok(Self::Array(Array::new(
2417 context,
2418 target.clone(),
2419 v.as_slice().iter().cloned(),
2420 )?));
2421 }
2422 (Self::Map(v), CompoundType::Map(map_ty)) => {
2424 return Ok(Self::Map(Map::new(
2425 context,
2426 target.clone(),
2427 v.iter().map(|(k, v)| {
2428 (
2429 k.clone()
2430 .map(Into::into)
2431 .unwrap_or(Value::new_none(map_ty.key_type().optional())),
2432 v.clone(),
2433 )
2434 }),
2435 )?));
2436 }
2437 (Self::Pair(v), CompoundType::Pair(_)) => {
2439 return Ok(Self::Pair(Pair::new(
2440 context,
2441 target.clone(),
2442 v.values.0.clone(),
2443 v.values.1.clone(),
2444 )?));
2445 }
2446 (Self::Map(v), CompoundType::Struct(target_ty)) => {
2448 let len = v.len();
2449 let expected_len = target_ty.members().len();
2450
2451 if len != expected_len {
2452 bail!(
2453 "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2454 as the struct has {expected_len} member{s2}",
2455 s1 = if len == 1 { "" } else { "s" },
2456 s2 = if expected_len == 1 { "" } else { "s" }
2457 );
2458 }
2459
2460 return Ok(Self::Struct(Struct {
2461 ty: target.clone(),
2462 name: target_ty.name().clone(),
2463 members: Arc::new(
2464 v.iter()
2465 .map(|(k, v)| {
2466 let k = k
2467 .as_ref()
2468 .and_then(|k| {
2469 k.coerce(context, &PrimitiveType::String.into()).ok()
2470 })
2471 .with_context(|| {
2472 format!(
2473 "cannot coerce a map of type `{map_type}` to \
2474 struct type `{target}` as the key type cannot \
2475 coerce to `String`",
2476 map_type = v.ty()
2477 )
2478 })?
2479 .unwrap_string();
2480 let ty =
2481 target_ty.members().get(k.as_ref()).with_context(|| {
2482 format!(
2483 "cannot coerce a map with key `{k}` to struct \
2484 type `{target}` as the struct does not contain a \
2485 member with that name"
2486 )
2487 })?;
2488 let v = v.coerce(context, ty).with_context(|| {
2489 format!("failed to coerce value of map key `{k}")
2490 })?;
2491 Ok((k.to_string(), v))
2492 })
2493 .collect::<Result<_>>()?,
2494 ),
2495 }));
2496 }
2497 (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2499 let key_ty = map_ty.key_type();
2500 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2501 bail!(
2502 "cannot coerce a struct to type `{target}` as key type `{key_ty}` \
2503 cannot be coerced from `String`"
2504 );
2505 }
2506
2507 let value_ty = map_ty.value_type();
2508 return Ok(Self::Map(Map::new_unchecked(
2509 target.clone(),
2510 members
2511 .iter()
2512 .map(|(n, v)| {
2513 let v = v
2514 .coerce(context, value_ty)
2515 .with_context(|| format!("failed to coerce member `{n}`"))?;
2516 Ok((
2517 PrimitiveValue::new_string(n)
2518 .coerce(context, key_ty)
2519 .expect("should coerce")
2520 .into(),
2521 v,
2522 ))
2523 })
2524 .collect::<Result<_>>()?,
2525 )));
2526 }
2527 (Self::Object(object), CompoundType::Map(map_ty)) => {
2529 let key_ty = map_ty.key_type();
2530 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2531 bail!(
2532 "cannot coerce an object to type `{target}` as key type `{key_ty}` \
2533 cannot be coerced from `String`"
2534 );
2535 }
2536
2537 let value_ty = map_ty.value_type();
2538 return Ok(Self::Map(Map::new_unchecked(
2539 target.clone(),
2540 object
2541 .iter()
2542 .map(|(n, v)| {
2543 let v = v
2544 .coerce(context, value_ty)
2545 .with_context(|| format!("failed to coerce member `{n}`"))?;
2546 Ok((
2547 PrimitiveValue::new_string(n)
2548 .coerce(context, key_ty)
2549 .expect("should coerce")
2550 .into(),
2551 v,
2552 ))
2553 })
2554 .collect::<Result<_>>()?,
2555 )));
2556 }
2557 (Self::Object(v), CompoundType::Struct(_)) => {
2559 return Ok(Self::Struct(Struct::new(
2560 context,
2561 target.clone(),
2562 v.iter().map(|(k, v)| (k, v.clone())),
2563 )?));
2564 }
2565 (Self::Struct(v), CompoundType::Struct(struct_ty)) => {
2567 let len = v.members.len();
2568 let expected_len = struct_ty.members().len();
2569
2570 if len != expected_len {
2571 bail!(
2572 "cannot coerce a struct of {len} members{s1} to struct type \
2573 `{target}` as the target struct has {expected_len} member{s2}",
2574 s1 = if len == 1 { "" } else { "s" },
2575 s2 = if expected_len == 1 { "" } else { "s" }
2576 );
2577 }
2578
2579 return Ok(Self::Struct(Struct {
2580 ty: target.clone(),
2581 name: struct_ty.name().clone(),
2582 members: Arc::new(
2583 v.members
2584 .iter()
2585 .map(|(k, v)| {
2586 let ty = struct_ty.members().get(k).ok_or_else(|| {
2587 anyhow!(
2588 "cannot coerce a struct with member `{k}` to struct \
2589 type `{target}` as the target struct does not \
2590 contain a member with that name",
2591 )
2592 })?;
2593 let v = v.coerce(context, ty).with_context(|| {
2594 format!("failed to coerce member `{k}`")
2595 })?;
2596 Ok((k.clone(), v))
2597 })
2598 .collect::<Result<_>>()?,
2599 ),
2600 }));
2601 }
2602 _ => {}
2603 }
2604 }
2605
2606 if let Type::Object = target {
2607 match self {
2608 Self::Map(v) => {
2610 return Ok(Self::Object(Object::new(
2611 v.iter()
2612 .map(|(k, v)| {
2613 let k = k
2614 .as_ref()
2615 .and_then(|k| {
2616 k.coerce(context, &PrimitiveType::String.into()).ok()
2617 })
2618 .with_context(|| {
2619 format!(
2620 "cannot coerce a map of type `{map_type}` to `Object` \
2621 as the key type cannot coerce to `String`",
2622 map_type = v.ty()
2623 )
2624 })?
2625 .unwrap_string();
2626 Ok((k.to_string(), v.clone()))
2627 })
2628 .collect::<Result<IndexMap<_, _>>>()?,
2629 )));
2630 }
2631 Self::Struct(v) => {
2633 return Ok(Self::Object(Object {
2634 members: Some(v.members.clone()),
2635 }));
2636 }
2637 _ => {}
2638 };
2639 }
2640
2641 bail!(
2642 "cannot coerce a value of type `{ty}` to type `{target}`",
2643 ty = self.ty()
2644 );
2645 }
2646}
2647
2648impl From<Pair> for CompoundValue {
2649 fn from(value: Pair) -> Self {
2650 Self::Pair(value)
2651 }
2652}
2653
2654impl From<Array> for CompoundValue {
2655 fn from(value: Array) -> Self {
2656 Self::Array(value)
2657 }
2658}
2659
2660impl From<Map> for CompoundValue {
2661 fn from(value: Map) -> Self {
2662 Self::Map(value)
2663 }
2664}
2665
2666impl From<Object> for CompoundValue {
2667 fn from(value: Object) -> Self {
2668 Self::Object(value)
2669 }
2670}
2671
2672impl From<Struct> for CompoundValue {
2673 fn from(value: Struct) -> Self {
2674 Self::Struct(value)
2675 }
2676}
2677
2678#[derive(Debug)]
2680struct TaskData {
2681 name: Arc<String>,
2683 id: Arc<String>,
2685 container: Option<Arc<String>>,
2687 cpu: f64,
2689 memory: i64,
2691 gpu: Array,
2696 fpga: Array,
2701 disks: Map,
2708 end_time: Option<i64>,
2712 meta: Object,
2714 parameter_meta: Object,
2716 ext: Object,
2718}
2719
2720#[derive(Debug, Clone)]
2724pub struct TaskValue {
2725 data: Arc<TaskData>,
2727 attempt: i64,
2732 return_code: Option<i64>,
2736}
2737
2738impl TaskValue {
2739 pub(crate) fn new_v1<N: TreeNode>(
2741 name: impl Into<String>,
2742 id: impl Into<String>,
2743 definition: &v1::TaskDefinition<N>,
2744 constraints: TaskExecutionConstraints,
2745 attempt: i64,
2746 ) -> Self {
2747 Self {
2748 data: Arc::new(TaskData {
2749 name: Arc::new(name.into()),
2750 id: Arc::new(id.into()),
2751 container: constraints.container.map(Into::into),
2752 cpu: constraints.cpu,
2753 memory: constraints.memory,
2754 gpu: Array::new_unchecked(
2755 ANALYSIS_STDLIB.array_string_type().clone(),
2756 constraints
2757 .gpu
2758 .into_iter()
2759 .map(|v| PrimitiveValue::new_string(v).into())
2760 .collect(),
2761 ),
2762 fpga: Array::new_unchecked(
2763 ANALYSIS_STDLIB.array_string_type().clone(),
2764 constraints
2765 .fpga
2766 .into_iter()
2767 .map(|v| PrimitiveValue::new_string(v).into())
2768 .collect(),
2769 ),
2770 disks: Map::new_unchecked(
2771 ANALYSIS_STDLIB.map_string_int_type().clone(),
2772 constraints
2773 .disks
2774 .into_iter()
2775 .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), v.into()))
2776 .collect(),
2777 ),
2778 end_time: None,
2779 meta: definition
2780 .metadata()
2781 .map(|s| Object::from_v1_metadata(s.items()))
2782 .unwrap_or_else(Object::empty),
2783 parameter_meta: definition
2784 .parameter_metadata()
2785 .map(|s| Object::from_v1_metadata(s.items()))
2786 .unwrap_or_else(Object::empty),
2787 ext: Object::empty(),
2788 }),
2789 attempt,
2790 return_code: None,
2791 }
2792 }
2793
2794 pub fn name(&self) -> &Arc<String> {
2796 &self.data.name
2797 }
2798
2799 pub fn id(&self) -> &Arc<String> {
2801 &self.data.id
2802 }
2803
2804 pub fn container(&self) -> Option<&Arc<String>> {
2806 self.data.container.as_ref()
2807 }
2808
2809 pub fn cpu(&self) -> f64 {
2811 self.data.cpu
2812 }
2813
2814 pub fn memory(&self) -> i64 {
2816 self.data.memory
2817 }
2818
2819 pub fn gpu(&self) -> &Array {
2824 &self.data.gpu
2825 }
2826
2827 pub fn fpga(&self) -> &Array {
2832 &self.data.fpga
2833 }
2834
2835 pub fn disks(&self) -> &Map {
2842 &self.data.disks
2843 }
2844
2845 pub fn attempt(&self) -> i64 {
2850 self.attempt
2851 }
2852
2853 pub fn end_time(&self) -> Option<i64> {
2857 self.data.end_time
2858 }
2859
2860 pub fn return_code(&self) -> Option<i64> {
2864 self.return_code
2865 }
2866
2867 pub fn meta(&self) -> &Object {
2869 &self.data.meta
2870 }
2871
2872 pub fn parameter_meta(&self) -> &Object {
2874 &self.data.parameter_meta
2875 }
2876
2877 pub fn ext(&self) -> &Object {
2879 &self.data.ext
2880 }
2881
2882 pub(crate) fn set_return_code(&mut self, code: i32) {
2884 self.return_code = Some(code as i64);
2885 }
2886
2887 pub(crate) fn set_attempt(&mut self, attempt: i64) {
2889 self.attempt = attempt;
2890 }
2891
2892 pub fn field(&self, name: &str) -> Option<Value> {
2896 match name {
2897 n if n == TASK_FIELD_NAME => {
2898 Some(PrimitiveValue::String(self.data.name.clone()).into())
2899 }
2900 n if n == TASK_FIELD_ID => Some(PrimitiveValue::String(self.data.id.clone()).into()),
2901 n if n == TASK_FIELD_CONTAINER => Some(
2902 self.data
2903 .container
2904 .clone()
2905 .map(|c| PrimitiveValue::String(c).into())
2906 .unwrap_or_else(|| {
2907 Value::new_none(
2908 task_member_type(TASK_FIELD_CONTAINER)
2909 .expect("failed to get task field type"),
2910 )
2911 }),
2912 ),
2913 n if n == TASK_FIELD_CPU => Some(self.data.cpu.into()),
2914 n if n == TASK_FIELD_MEMORY => Some(self.data.memory.into()),
2915 n if n == TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
2916 n if n == TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
2917 n if n == TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
2918 n if n == TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
2919 n if n == TASK_FIELD_END_TIME => {
2920 Some(self.data.end_time.map(Into::into).unwrap_or_else(|| {
2921 Value::new_none(
2922 task_member_type(TASK_FIELD_END_TIME)
2923 .expect("failed to get task field type"),
2924 )
2925 }))
2926 }
2927 n if n == TASK_FIELD_RETURN_CODE => {
2928 Some(self.return_code.map(Into::into).unwrap_or_else(|| {
2929 Value::new_none(
2930 task_member_type(TASK_FIELD_RETURN_CODE)
2931 .expect("failed to get task field type"),
2932 )
2933 }))
2934 }
2935 n if n == TASK_FIELD_META => Some(self.data.meta.clone().into()),
2936 n if n == TASK_FIELD_PARAMETER_META => Some(self.data.parameter_meta.clone().into()),
2937 n if n == TASK_FIELD_EXT => Some(self.data.ext.clone().into()),
2938 _ => None,
2939 }
2940 }
2941}
2942
2943#[derive(Debug, Clone)]
2947pub struct HintsValue(Object);
2948
2949impl HintsValue {
2950 pub fn as_object(&self) -> &Object {
2952 &self.0
2953 }
2954}
2955
2956impl fmt::Display for HintsValue {
2957 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2958 write!(f, "hints {{")?;
2959
2960 for (i, (k, v)) in self.0.iter().enumerate() {
2961 if i > 0 {
2962 write!(f, ", ")?;
2963 }
2964
2965 write!(f, "{k}: {v}")?;
2966 }
2967
2968 write!(f, "}}")
2969 }
2970}
2971
2972impl From<Object> for HintsValue {
2973 fn from(value: Object) -> Self {
2974 Self(value)
2975 }
2976}
2977
2978#[derive(Debug, Clone)]
2982pub struct InputValue(Object);
2983
2984impl InputValue {
2985 pub fn as_object(&self) -> &Object {
2987 &self.0
2988 }
2989}
2990
2991impl fmt::Display for InputValue {
2992 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2993 write!(f, "input {{")?;
2994
2995 for (i, (k, v)) in self.0.iter().enumerate() {
2996 if i > 0 {
2997 write!(f, ", ")?;
2998 }
2999
3000 write!(f, "{k}: {v}")?;
3001 }
3002
3003 write!(f, "}}")
3004 }
3005}
3006
3007impl From<Object> for InputValue {
3008 fn from(value: Object) -> Self {
3009 Self(value)
3010 }
3011}
3012
3013#[derive(Debug, Clone)]
3017pub struct OutputValue(Object);
3018
3019impl OutputValue {
3020 pub fn as_object(&self) -> &Object {
3022 &self.0
3023 }
3024}
3025
3026impl fmt::Display for OutputValue {
3027 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3028 write!(f, "output {{")?;
3029
3030 for (i, (k, v)) in self.0.iter().enumerate() {
3031 if i > 0 {
3032 write!(f, ", ")?;
3033 }
3034
3035 write!(f, "{k}: {v}")?;
3036 }
3037
3038 write!(f, "}}")
3039 }
3040}
3041
3042impl From<Object> for OutputValue {
3043 fn from(value: Object) -> Self {
3044 Self(value)
3045 }
3046}
3047
3048#[derive(Debug, Clone)]
3052pub struct CallValue {
3053 ty: CallType,
3055 outputs: Arc<Outputs>,
3057}
3058
3059impl CallValue {
3060 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
3063 Self { ty, outputs }
3064 }
3065
3066 pub fn ty(&self) -> &CallType {
3068 &self.ty
3069 }
3070
3071 pub fn outputs(&self) -> &Outputs {
3073 self.outputs.as_ref()
3074 }
3075}
3076
3077impl fmt::Display for CallValue {
3078 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3079 write!(f, "call output {{")?;
3080
3081 for (i, (k, v)) in self.outputs.iter().enumerate() {
3082 if i > 0 {
3083 write!(f, ", ")?;
3084 }
3085
3086 write!(f, "{k}: {v}")?;
3087 }
3088
3089 write!(f, "}}")
3090 }
3091}
3092
3093pub struct ValueSerializer<'a> {
3095 value: &'a Value,
3097 allow_pairs: bool,
3100}
3101
3102impl<'a> ValueSerializer<'a> {
3103 pub fn new(value: &'a Value, allow_pairs: bool) -> Self {
3105 Self { value, allow_pairs }
3106 }
3107}
3108
3109impl serde::Serialize for ValueSerializer<'_> {
3110 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3111 where
3112 S: serde::Serializer,
3113 {
3114 use serde::ser::Error;
3115
3116 match &self.value {
3117 Value::None(_) => serializer.serialize_none(),
3118 Value::Primitive(v) => v.serialize(serializer),
3119 Value::Compound(v) => {
3120 CompoundValueSerializer::new(v, self.allow_pairs).serialize(serializer)
3121 }
3122 Value::Task(_)
3123 | Value::Hints(_)
3124 | Value::Input(_)
3125 | Value::Output(_)
3126 | Value::Call(_) => Err(S::Error::custom("value cannot be serialized")),
3127 }
3128 }
3129}
3130
3131pub struct CompoundValueSerializer<'a> {
3133 value: &'a CompoundValue,
3135 allow_pairs: bool,
3138}
3139
3140impl<'a> CompoundValueSerializer<'a> {
3141 pub fn new(value: &'a CompoundValue, allow_pairs: bool) -> Self {
3143 Self { value, allow_pairs }
3144 }
3145}
3146
3147impl serde::Serialize for CompoundValueSerializer<'_> {
3148 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3149 where
3150 S: serde::Serializer,
3151 {
3152 use serde::ser::Error;
3153
3154 match &self.value {
3155 CompoundValue::Pair(pair) if self.allow_pairs => {
3156 let mut state = serializer.serialize_map(Some(2))?;
3157 let left = ValueSerializer::new(pair.left(), self.allow_pairs);
3158 let right = ValueSerializer::new(pair.right(), self.allow_pairs);
3159 state.serialize_entry("left", &left)?;
3160 state.serialize_entry("right", &right)?;
3161 state.end()
3162 }
3163 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3164 CompoundValue::Array(v) => {
3165 let mut s = serializer.serialize_seq(Some(v.len()))?;
3166 for v in v.as_slice() {
3167 s.serialize_element(&ValueSerializer::new(v, self.allow_pairs))?;
3168 }
3169
3170 s.end()
3171 }
3172 CompoundValue::Map(v) => {
3173 let ty = v.ty();
3174 let map_type = ty.as_map().expect("type should be a map");
3175 if !map_type
3176 .key_type()
3177 .is_coercible_to(&PrimitiveType::String.into())
3178 {
3179 return Err(S::Error::custom(format!(
3180 "cannot serialize a map of type `{ty}` as the key type cannot be coerced \
3181 to `String`",
3182 )));
3183 }
3184
3185 let mut s = serializer.serialize_map(Some(v.len()))?;
3186 for (k, v) in v.iter() {
3187 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3188 }
3189
3190 s.end()
3191 }
3192 CompoundValue::Object(object) => {
3193 let mut s = serializer.serialize_map(Some(object.len()))?;
3194 for (k, v) in object.iter() {
3195 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3196 }
3197
3198 s.end()
3199 }
3200 CompoundValue::Struct(Struct { members, .. }) => {
3201 let mut s = serializer.serialize_map(Some(members.len()))?;
3202 for (k, v) in members.iter() {
3203 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3204 }
3205
3206 s.end()
3207 }
3208 }
3209 }
3210}
3211
3212#[cfg(test)]
3213mod test {
3214 use approx::assert_relative_eq;
3215 use pretty_assertions::assert_eq;
3216 use wdl_analysis::types::ArrayType;
3217 use wdl_analysis::types::MapType;
3218 use wdl_analysis::types::PairType;
3219 use wdl_analysis::types::StructType;
3220 use wdl_ast::Diagnostic;
3221 use wdl_ast::Span;
3222 use wdl_ast::SupportedVersion;
3223
3224 use super::*;
3225 use crate::http::Transferer;
3226 use crate::path::EvaluationPath;
3227
3228 #[test]
3229 fn boolean_coercion() {
3230 assert_eq!(
3232 Value::from(false)
3233 .coerce(None, &PrimitiveType::Boolean.into())
3234 .expect("should coerce")
3235 .unwrap_boolean(),
3236 Value::from(false).unwrap_boolean()
3237 );
3238 assert_eq!(
3240 format!(
3241 "{e:?}",
3242 e = Value::from(true)
3243 .coerce(None, &PrimitiveType::String.into())
3244 .unwrap_err()
3245 ),
3246 "cannot coerce type `Boolean` to type `String`"
3247 );
3248 }
3249
3250 #[test]
3251 fn boolean_display() {
3252 assert_eq!(Value::from(false).to_string(), "false");
3253 assert_eq!(Value::from(true).to_string(), "true");
3254 }
3255
3256 #[test]
3257 fn integer_coercion() {
3258 assert_eq!(
3260 Value::from(12345)
3261 .coerce(None, &PrimitiveType::Integer.into())
3262 .expect("should coerce")
3263 .unwrap_integer(),
3264 Value::from(12345).unwrap_integer()
3265 );
3266 assert_relative_eq!(
3268 Value::from(12345)
3269 .coerce(None, &PrimitiveType::Float.into())
3270 .expect("should coerce")
3271 .unwrap_float(),
3272 Value::from(12345.0).unwrap_float()
3273 );
3274 assert_eq!(
3276 format!(
3277 "{e:?}",
3278 e = Value::from(12345)
3279 .coerce(None, &PrimitiveType::Boolean.into())
3280 .unwrap_err()
3281 ),
3282 "cannot coerce type `Int` to type `Boolean`"
3283 );
3284 }
3285
3286 #[test]
3287 fn integer_display() {
3288 assert_eq!(Value::from(12345).to_string(), "12345");
3289 assert_eq!(Value::from(-12345).to_string(), "-12345");
3290 }
3291
3292 #[test]
3293 fn float_coercion() {
3294 assert_relative_eq!(
3296 Value::from(12345.0)
3297 .coerce(None, &PrimitiveType::Float.into())
3298 .expect("should coerce")
3299 .unwrap_float(),
3300 Value::from(12345.0).unwrap_float()
3301 );
3302 assert_eq!(
3304 format!(
3305 "{e:?}",
3306 e = Value::from(12345.0)
3307 .coerce(None, &PrimitiveType::Integer.into())
3308 .unwrap_err()
3309 ),
3310 "cannot coerce type `Float` to type `Int`"
3311 );
3312 }
3313
3314 #[test]
3315 fn float_display() {
3316 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
3317 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
3318 }
3319
3320 #[test]
3321 fn string_coercion() {
3322 let value = PrimitiveValue::new_string("foo");
3323 assert_eq!(
3325 value
3326 .coerce(None, &PrimitiveType::String.into())
3327 .expect("should coerce"),
3328 value
3329 );
3330 assert_eq!(
3332 value
3333 .coerce(None, &PrimitiveType::File.into())
3334 .expect("should coerce"),
3335 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
3336 );
3337 assert_eq!(
3339 value
3340 .coerce(None, &PrimitiveType::Directory.into())
3341 .expect("should coerce"),
3342 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
3343 );
3344 assert_eq!(
3346 format!(
3347 "{e:?}",
3348 e = value
3349 .coerce(None, &PrimitiveType::Boolean.into())
3350 .unwrap_err()
3351 ),
3352 "cannot coerce type `String` to type `Boolean`"
3353 );
3354
3355 struct Context;
3356
3357 impl EvaluationContext for Context {
3358 fn version(&self) -> SupportedVersion {
3359 unimplemented!()
3360 }
3361
3362 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3363 unimplemented!()
3364 }
3365
3366 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3367 unimplemented!()
3368 }
3369
3370 fn base_dir(&self) -> &EvaluationPath {
3371 unimplemented!()
3372 }
3373
3374 fn temp_dir(&self) -> &Path {
3375 unimplemented!()
3376 }
3377
3378 fn transferer(&self) -> &dyn Transferer {
3379 unimplemented!()
3380 }
3381
3382 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
3383 if path.as_str() == "/mnt/task/input/0/path" {
3384 Some(HostPath::new("/some/host/path"))
3385 } else {
3386 None
3387 }
3388 }
3389 }
3390
3391 assert_eq!(
3393 PrimitiveValue::new_string("/mnt/task/input/0/path")
3394 .coerce(Some(&Context), &PrimitiveType::File.into())
3395 .expect("should coerce")
3396 .unwrap_file()
3397 .as_str(),
3398 "/some/host/path"
3399 );
3400
3401 assert_eq!(
3403 value
3404 .coerce(Some(&Context), &PrimitiveType::File.into())
3405 .expect("should coerce")
3406 .unwrap_file()
3407 .as_str(),
3408 "foo"
3409 );
3410
3411 assert_eq!(
3413 PrimitiveValue::new_string("/mnt/task/input/0/path")
3414 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3415 .expect("should coerce")
3416 .unwrap_directory()
3417 .as_str(),
3418 "/some/host/path"
3419 );
3420
3421 assert_eq!(
3423 value
3424 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3425 .expect("should coerce")
3426 .unwrap_directory()
3427 .as_str(),
3428 "foo"
3429 );
3430 }
3431
3432 #[test]
3433 fn string_display() {
3434 let value = PrimitiveValue::new_string("hello world!");
3435 assert_eq!(value.to_string(), "\"hello world!\"");
3436 }
3437
3438 #[test]
3439 fn file_coercion() {
3440 let value = PrimitiveValue::new_file("foo");
3441
3442 assert_eq!(
3444 value
3445 .coerce(None, &PrimitiveType::File.into())
3446 .expect("should coerce"),
3447 value
3448 );
3449 assert_eq!(
3451 value
3452 .coerce(None, &PrimitiveType::String.into())
3453 .expect("should coerce"),
3454 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
3455 );
3456 assert_eq!(
3458 format!(
3459 "{e:?}",
3460 e = value
3461 .coerce(None, &PrimitiveType::Directory.into())
3462 .unwrap_err()
3463 ),
3464 "cannot coerce type `File` to type `Directory`"
3465 );
3466
3467 struct Context;
3468
3469 impl EvaluationContext for Context {
3470 fn version(&self) -> SupportedVersion {
3471 unimplemented!()
3472 }
3473
3474 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3475 unimplemented!()
3476 }
3477
3478 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3479 unimplemented!()
3480 }
3481
3482 fn base_dir(&self) -> &EvaluationPath {
3483 unimplemented!()
3484 }
3485
3486 fn temp_dir(&self) -> &Path {
3487 unimplemented!()
3488 }
3489
3490 fn transferer(&self) -> &dyn Transferer {
3491 unimplemented!()
3492 }
3493
3494 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3495 if path.as_str() == "/some/host/path" {
3496 Some(GuestPath::new("/mnt/task/input/0/path"))
3497 } else {
3498 None
3499 }
3500 }
3501 }
3502
3503 assert_eq!(
3505 PrimitiveValue::new_file("/some/host/path")
3506 .coerce(Some(&Context), &PrimitiveType::String.into())
3507 .expect("should coerce")
3508 .unwrap_string()
3509 .as_str(),
3510 "/mnt/task/input/0/path"
3511 );
3512
3513 assert_eq!(
3515 value
3516 .coerce(Some(&Context), &PrimitiveType::String.into())
3517 .expect("should coerce")
3518 .unwrap_string()
3519 .as_str(),
3520 "foo"
3521 );
3522 }
3523
3524 #[test]
3525 fn file_display() {
3526 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
3527 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
3528 }
3529
3530 #[test]
3531 fn directory_coercion() {
3532 let value = PrimitiveValue::new_directory("foo");
3533
3534 assert_eq!(
3536 value
3537 .coerce(None, &PrimitiveType::Directory.into())
3538 .expect("should coerce"),
3539 value
3540 );
3541 assert_eq!(
3543 value
3544 .coerce(None, &PrimitiveType::String.into())
3545 .expect("should coerce"),
3546 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
3547 );
3548 assert_eq!(
3550 format!(
3551 "{e:?}",
3552 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
3553 ),
3554 "cannot coerce type `Directory` to type `File`"
3555 );
3556
3557 struct Context;
3558
3559 impl EvaluationContext for Context {
3560 fn version(&self) -> SupportedVersion {
3561 unimplemented!()
3562 }
3563
3564 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3565 unimplemented!()
3566 }
3567
3568 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3569 unimplemented!()
3570 }
3571
3572 fn base_dir(&self) -> &EvaluationPath {
3573 unimplemented!()
3574 }
3575
3576 fn temp_dir(&self) -> &Path {
3577 unimplemented!()
3578 }
3579
3580 fn transferer(&self) -> &dyn Transferer {
3581 unimplemented!()
3582 }
3583
3584 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3585 if path.as_str() == "/some/host/path" {
3586 Some(GuestPath::new("/mnt/task/input/0/path"))
3587 } else {
3588 None
3589 }
3590 }
3591 }
3592
3593 assert_eq!(
3595 PrimitiveValue::new_directory("/some/host/path")
3596 .coerce(Some(&Context), &PrimitiveType::String.into())
3597 .expect("should coerce")
3598 .unwrap_string()
3599 .as_str(),
3600 "/mnt/task/input/0/path"
3601 );
3602
3603 assert_eq!(
3605 value
3606 .coerce(Some(&Context), &PrimitiveType::String.into())
3607 .expect("should coerce")
3608 .unwrap_string()
3609 .as_str(),
3610 "foo"
3611 );
3612 }
3613
3614 #[test]
3615 fn directory_display() {
3616 let value = PrimitiveValue::new_directory("/foo/bar/baz");
3617 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
3618 }
3619
3620 #[test]
3621 fn none_coercion() {
3622 assert!(
3624 Value::new_none(Type::None)
3625 .coerce(None, &Type::from(PrimitiveType::String).optional())
3626 .expect("should coerce")
3627 .is_none(),
3628 );
3629
3630 assert_eq!(
3632 format!(
3633 "{e:?}",
3634 e = Value::new_none(Type::None)
3635 .coerce(None, &PrimitiveType::String.into())
3636 .unwrap_err()
3637 ),
3638 "cannot coerce `None` to non-optional type `String`"
3639 );
3640 }
3641
3642 #[test]
3643 fn none_display() {
3644 assert_eq!(Value::new_none(Type::None).to_string(), "None");
3645 }
3646
3647 #[test]
3648 fn array_coercion() {
3649 let src_ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3650 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
3651
3652 let src: CompoundValue = Array::new(None, src_ty, [1, 2, 3])
3654 .expect("should create array value")
3655 .into();
3656 let target = src.coerce(None, &target_ty).expect("should coerce");
3657 assert_eq!(
3658 target.unwrap_array().to_string(),
3659 "[1.000000, 2.000000, 3.000000]"
3660 );
3661
3662 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
3664 assert_eq!(
3665 format!("{e:?}", e = src.coerce(None, &target_ty).unwrap_err()),
3666 r#"failed to coerce array element at index 0
3667
3668Caused by:
3669 cannot coerce type `Int` to type `String`"#
3670 );
3671 }
3672
3673 #[test]
3674 fn non_empty_array_coercion() {
3675 let ty: Type = ArrayType::new(PrimitiveType::String).into();
3676 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
3677
3678 let string = PrimitiveValue::new_string("foo");
3680 let value: Value = Array::new(None, ty.clone(), [string])
3681 .expect("should create array")
3682 .into();
3683 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
3684
3685 let value: Value = Array::new::<Value>(None, ty, [])
3687 .expect("should create array")
3688 .into();
3689 assert_eq!(
3690 format!("{e:?}", e = value.coerce(None, &target_ty).unwrap_err()),
3691 "cannot coerce empty array value to non-empty array type `Array[String]+`"
3692 );
3693 }
3694
3695 #[test]
3696 fn array_display() {
3697 let ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3698 let value: Value = Array::new(None, ty, [1, 2, 3])
3699 .expect("should create array")
3700 .into();
3701
3702 assert_eq!(value.to_string(), "[1, 2, 3]");
3703 }
3704
3705 #[test]
3706 fn map_coerce() {
3707 let key1 = PrimitiveValue::new_file("foo");
3708 let value1 = PrimitiveValue::new_string("bar");
3709 let key2 = PrimitiveValue::new_file("baz");
3710 let value2 = PrimitiveValue::new_string("qux");
3711
3712 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
3713 let file_to_string: Value = Map::new(None, ty, [(key1, value1), (key2, value2)])
3714 .expect("should create map value")
3715 .into();
3716
3717 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
3719 let string_to_file = file_to_string
3720 .coerce(None, &ty)
3721 .expect("value should coerce");
3722 assert_eq!(
3723 string_to_file.to_string(),
3724 r#"{"foo": "bar", "baz": "qux"}"#
3725 );
3726
3727 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
3729 assert_eq!(
3730 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3731 r#"failed to coerce map key for element at index 0
3732
3733Caused by:
3734 cannot coerce type `String` to type `Int`"#
3735 );
3736
3737 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
3739 assert_eq!(
3740 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3741 r#"failed to coerce map value for element at index 0
3742
3743Caused by:
3744 cannot coerce type `File` to type `Int`"#
3745 );
3746
3747 let ty = StructType::new(
3749 "Foo",
3750 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
3751 )
3752 .into();
3753 let struct_value = string_to_file
3754 .coerce(None, &ty)
3755 .expect("value should coerce");
3756 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
3757
3758 let ty = StructType::new(
3760 "Foo",
3761 [
3762 ("foo", PrimitiveType::String),
3763 ("baz", PrimitiveType::String),
3764 ],
3765 )
3766 .into();
3767 let struct_value = file_to_string
3768 .coerce(None, &ty)
3769 .expect("value should coerce");
3770 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
3771
3772 let ty = StructType::new(
3774 "Foo",
3775 [
3776 ("foo", PrimitiveType::File),
3777 ("baz", PrimitiveType::File),
3778 ("qux", PrimitiveType::File),
3779 ],
3780 )
3781 .into();
3782 assert_eq!(
3783 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3784 "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
3785 );
3786
3787 let object_value = string_to_file
3789 .coerce(None, &Type::Object)
3790 .expect("value should coerce");
3791 assert_eq!(
3792 object_value.to_string(),
3793 r#"object {foo: "bar", baz: "qux"}"#
3794 );
3795
3796 let object_value = file_to_string
3798 .coerce(None, &Type::Object)
3799 .expect("value should coerce");
3800 assert_eq!(
3801 object_value.to_string(),
3802 r#"object {foo: "bar", baz: "qux"}"#
3803 );
3804 }
3805
3806 #[test]
3807 fn map_display() {
3808 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3809 let value: Value = Map::new(None, ty, [(1, true), (2, false)])
3810 .expect("should create map value")
3811 .into();
3812 assert_eq!(value.to_string(), "{1: true, 2: false}");
3813 }
3814
3815 #[test]
3816 fn pair_coercion() {
3817 let left = PrimitiveValue::new_file("foo");
3818 let right = PrimitiveValue::new_string("bar");
3819
3820 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3821 let value: Value = Pair::new(None, ty, left, right)
3822 .expect("should create pair value")
3823 .into();
3824
3825 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
3827 let value = value.coerce(None, &ty).expect("value should coerce");
3828 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
3829
3830 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
3832 assert_eq!(
3833 format!("{e:?}", e = value.coerce(None, &ty).unwrap_err()),
3834 r#"failed to coerce pair's left value
3835
3836Caused by:
3837 cannot coerce type `String` to type `Int`"#
3838 );
3839 }
3840
3841 #[test]
3842 fn pair_display() {
3843 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3844 let value: Value = Pair::new(None, ty, 12345, false)
3845 .expect("should create pair value")
3846 .into();
3847 assert_eq!(value.to_string(), "(12345, false)");
3848 }
3849
3850 #[test]
3851 fn struct_coercion() {
3852 let ty = StructType::new(
3853 "Foo",
3854 [
3855 ("foo", PrimitiveType::Float),
3856 ("bar", PrimitiveType::Float),
3857 ("baz", PrimitiveType::Float),
3858 ],
3859 );
3860 let value: Value = Struct::new(None, ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
3861 .expect("should create map value")
3862 .into();
3863
3864 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
3866 let map_value = value.coerce(None, &ty).expect("value should coerce");
3867 assert_eq!(
3868 map_value.to_string(),
3869 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
3870 );
3871
3872 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
3874 let map_value = value.coerce(None, &ty).expect("value should coerce");
3875 assert_eq!(
3876 map_value.to_string(),
3877 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
3878 );
3879
3880 let ty = StructType::new(
3882 "Bar",
3883 [
3884 ("foo", PrimitiveType::Float),
3885 ("bar", PrimitiveType::Float),
3886 ("baz", PrimitiveType::Float),
3887 ],
3888 )
3889 .into();
3890 let struct_value = value.coerce(None, &ty).expect("value should coerce");
3891 assert_eq!(
3892 struct_value.to_string(),
3893 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3894 );
3895
3896 let object_value = value
3898 .coerce(None, &Type::Object)
3899 .expect("value should coerce");
3900 assert_eq!(
3901 object_value.to_string(),
3902 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3903 );
3904 }
3905
3906 #[test]
3907 fn struct_display() {
3908 let ty = StructType::new(
3909 "Foo",
3910 [
3911 ("foo", PrimitiveType::Float),
3912 ("bar", PrimitiveType::String),
3913 ("baz", PrimitiveType::Integer),
3914 ],
3915 );
3916 let value: Value = Struct::new(
3917 None,
3918 ty,
3919 [
3920 ("foo", Value::from(1.101)),
3921 ("bar", PrimitiveValue::new_string("foo").into()),
3922 ("baz", 1234.into()),
3923 ],
3924 )
3925 .expect("should create map value")
3926 .into();
3927 assert_eq!(
3928 value.to_string(),
3929 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
3930 );
3931 }
3932
3933 #[test]
3934 fn pair_serialization() {
3935 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3936 let pair: Value = Pair::new(
3937 None,
3938 pair_ty,
3939 PrimitiveValue::new_file("foo"),
3940 PrimitiveValue::new_string("bar"),
3941 )
3942 .expect("should create pair value")
3943 .into();
3944 let value_serializer = ValueSerializer::new(&pair, true);
3946 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3947 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
3948
3949 let value_serializer = ValueSerializer::new(&pair, false);
3951 assert!(serde_json::to_string(&value_serializer).is_err());
3952
3953 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
3954 let array: Value = Array::new(None, array_ty, [pair])
3955 .expect("should create array value")
3956 .into();
3957
3958 let value_serializer = ValueSerializer::new(&array, true);
3960 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3961 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
3962 }
3963}