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 indexmap::IndexMap;
16use itertools::Either;
17use ordered_float::OrderedFloat;
18use path_clean::PathClean;
19use serde::ser::SerializeMap;
20use serde::ser::SerializeSeq;
21use wdl_analysis::stdlib::STDLIB as ANALYSIS_STDLIB;
22use wdl_analysis::types::ArrayType;
23use wdl_analysis::types::CallType;
24use wdl_analysis::types::Coercible as _;
25use wdl_analysis::types::CompoundType;
26use wdl_analysis::types::Optional;
27use wdl_analysis::types::PrimitiveType;
28use wdl_analysis::types::Type;
29use wdl_analysis::types::v1::task_member_type;
30use wdl_ast::AstToken;
31use wdl_ast::TreeNode;
32use wdl_ast::v1;
33use wdl_ast::v1::TASK_FIELD_ATTEMPT;
34use wdl_ast::v1::TASK_FIELD_CONTAINER;
35use wdl_ast::v1::TASK_FIELD_CPU;
36use wdl_ast::v1::TASK_FIELD_DISKS;
37use wdl_ast::v1::TASK_FIELD_END_TIME;
38use wdl_ast::v1::TASK_FIELD_EXT;
39use wdl_ast::v1::TASK_FIELD_FPGA;
40use wdl_ast::v1::TASK_FIELD_GPU;
41use wdl_ast::v1::TASK_FIELD_ID;
42use wdl_ast::v1::TASK_FIELD_MEMORY;
43use wdl_ast::v1::TASK_FIELD_META;
44use wdl_ast::v1::TASK_FIELD_NAME;
45use wdl_ast::v1::TASK_FIELD_PARAMETER_META;
46use wdl_ast::v1::TASK_FIELD_RETURN_CODE;
47
48use crate::EvaluationContext;
49use crate::GuestPath;
50use crate::HostPath;
51use crate::Outputs;
52use crate::TaskExecutionConstraints;
53use crate::path;
54
55pub trait Coercible: Sized {
57 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self>;
65}
66
67#[derive(Debug, Clone)]
71pub enum Value {
72 None(Type),
76 Primitive(PrimitiveValue),
78 Compound(CompoundValue),
80 Task(TaskValue),
85 Hints(HintsValue),
89 Input(InputValue),
93 Output(OutputValue),
97 Call(CallValue),
99}
100
101impl Value {
102 pub fn from_v1_metadata<N: TreeNode>(value: &v1::MetadataValue<N>) -> Self {
108 match value {
109 v1::MetadataValue::Boolean(v) => v.value().into(),
110 v1::MetadataValue::Integer(v) => v.value().expect("number should be in range").into(),
111 v1::MetadataValue::Float(v) => v.value().expect("number should be in range").into(),
112 v1::MetadataValue::String(v) => PrimitiveValue::new_string(
113 v.text()
114 .expect("metadata strings shouldn't have placeholders")
115 .text(),
116 )
117 .into(),
118 v1::MetadataValue::Null(_) => Self::new_none(Type::None),
119 v1::MetadataValue::Object(o) => Object::from_v1_metadata(o.items()).into(),
120 v1::MetadataValue::Array(a) => Array::new_unchecked(
121 ANALYSIS_STDLIB.array_object_type().clone(),
122 a.elements().map(|v| Value::from_v1_metadata(&v)).collect(),
123 )
124 .into(),
125 }
126 }
127
128 pub fn new_none(ty: Type) -> Self {
134 assert!(ty.is_optional(), "the provided `None` type is not optional");
135 Self::None(ty)
136 }
137
138 pub fn ty(&self) -> Type {
140 match self {
141 Self::None(ty) => ty.clone(),
142 Self::Primitive(v) => v.ty(),
143 Self::Compound(v) => v.ty(),
144 Self::Task(_) => Type::Task,
145 Self::Hints(_) => Type::Hints,
146 Self::Input(_) => Type::Input,
147 Self::Output(_) => Type::Output,
148 Self::Call(v) => Type::Call(v.ty.clone()),
149 }
150 }
151
152 pub fn is_none(&self) -> bool {
154 matches!(self, Self::None(_))
155 }
156
157 pub fn as_primitive(&self) -> Option<&PrimitiveValue> {
161 match self {
162 Self::Primitive(v) => Some(v),
163 _ => None,
164 }
165 }
166
167 pub fn as_compound(&self) -> Option<&CompoundValue> {
171 match self {
172 Self::Compound(v) => Some(v),
173 _ => None,
174 }
175 }
176
177 pub fn as_boolean(&self) -> Option<bool> {
181 match self {
182 Self::Primitive(PrimitiveValue::Boolean(v)) => Some(*v),
183 _ => None,
184 }
185 }
186
187 pub fn unwrap_boolean(self) -> bool {
193 match self {
194 Self::Primitive(PrimitiveValue::Boolean(v)) => v,
195 _ => panic!("value is not a boolean"),
196 }
197 }
198
199 pub fn as_integer(&self) -> Option<i64> {
203 match self {
204 Self::Primitive(PrimitiveValue::Integer(v)) => Some(*v),
205 _ => None,
206 }
207 }
208
209 pub fn unwrap_integer(self) -> i64 {
215 match self {
216 Self::Primitive(PrimitiveValue::Integer(v)) => v,
217 _ => panic!("value is not an integer"),
218 }
219 }
220
221 pub fn as_float(&self) -> Option<f64> {
225 match self {
226 Self::Primitive(PrimitiveValue::Float(v)) => Some((*v).into()),
227 _ => None,
228 }
229 }
230
231 pub fn unwrap_float(self) -> f64 {
237 match self {
238 Self::Primitive(PrimitiveValue::Float(v)) => v.into(),
239 _ => panic!("value is not a float"),
240 }
241 }
242
243 pub fn as_string(&self) -> Option<&Arc<String>> {
247 match self {
248 Self::Primitive(PrimitiveValue::String(s)) => Some(s),
249 _ => None,
250 }
251 }
252
253 pub fn unwrap_string(self) -> Arc<String> {
259 match self {
260 Self::Primitive(PrimitiveValue::String(s)) => s,
261 _ => panic!("value is not a string"),
262 }
263 }
264
265 pub fn as_file(&self) -> Option<&HostPath> {
269 match self {
270 Self::Primitive(PrimitiveValue::File(p)) => Some(p),
271 _ => None,
272 }
273 }
274
275 pub fn unwrap_file(self) -> HostPath {
281 match self {
282 Self::Primitive(PrimitiveValue::File(p)) => p,
283 _ => panic!("value is not a file"),
284 }
285 }
286
287 pub fn as_directory(&self) -> Option<&HostPath> {
291 match self {
292 Self::Primitive(PrimitiveValue::Directory(p)) => Some(p),
293 _ => None,
294 }
295 }
296
297 pub fn unwrap_directory(self) -> HostPath {
303 match self {
304 Self::Primitive(PrimitiveValue::Directory(p)) => p,
305 _ => panic!("value is not a directory"),
306 }
307 }
308
309 pub fn as_pair(&self) -> Option<&Pair> {
313 match self {
314 Self::Compound(CompoundValue::Pair(v)) => Some(v),
315 _ => None,
316 }
317 }
318
319 pub fn unwrap_pair(self) -> Pair {
325 match self {
326 Self::Compound(CompoundValue::Pair(v)) => v,
327 _ => panic!("value is not a pair"),
328 }
329 }
330
331 pub fn as_array(&self) -> Option<&Array> {
335 match self {
336 Self::Compound(CompoundValue::Array(v)) => Some(v),
337 _ => None,
338 }
339 }
340
341 pub fn unwrap_array(self) -> Array {
347 match self {
348 Self::Compound(CompoundValue::Array(v)) => v,
349 _ => panic!("value is not an array"),
350 }
351 }
352
353 pub fn as_map(&self) -> Option<&Map> {
357 match self {
358 Self::Compound(CompoundValue::Map(v)) => Some(v),
359 _ => None,
360 }
361 }
362
363 pub fn unwrap_map(self) -> Map {
369 match self {
370 Self::Compound(CompoundValue::Map(v)) => v,
371 _ => panic!("value is not a map"),
372 }
373 }
374
375 pub fn as_object(&self) -> Option<&Object> {
379 match self {
380 Self::Compound(CompoundValue::Object(v)) => Some(v),
381 _ => None,
382 }
383 }
384
385 pub fn unwrap_object(self) -> Object {
391 match self {
392 Self::Compound(CompoundValue::Object(v)) => v,
393 _ => panic!("value is not an object"),
394 }
395 }
396
397 pub fn as_struct(&self) -> Option<&Struct> {
401 match self {
402 Self::Compound(CompoundValue::Struct(v)) => Some(v),
403 _ => None,
404 }
405 }
406
407 pub fn unwrap_struct(self) -> Struct {
413 match self {
414 Self::Compound(CompoundValue::Struct(v)) => v,
415 _ => panic!("value is not a struct"),
416 }
417 }
418
419 pub fn as_task(&self) -> Option<&TaskValue> {
423 match self {
424 Self::Task(v) => Some(v),
425 _ => None,
426 }
427 }
428
429 pub(crate) fn as_task_mut(&mut self) -> Option<&mut TaskValue> {
433 match self {
434 Self::Task(v) => Some(v),
435 _ => None,
436 }
437 }
438
439 pub fn unwrap_task(self) -> TaskValue {
445 match self {
446 Self::Task(v) => v,
447 _ => panic!("value is not a task"),
448 }
449 }
450
451 pub fn as_hints(&self) -> Option<&HintsValue> {
455 match self {
456 Self::Hints(v) => Some(v),
457 _ => None,
458 }
459 }
460
461 pub fn unwrap_hints(self) -> HintsValue {
467 match self {
468 Self::Hints(v) => v,
469 _ => panic!("value is not a hints value"),
470 }
471 }
472
473 pub fn as_call(&self) -> Option<&CallValue> {
477 match self {
478 Self::Call(v) => Some(v),
479 _ => None,
480 }
481 }
482
483 pub fn unwrap_call(self) -> CallValue {
489 match self {
490 Self::Call(v) => v,
491 _ => panic!("value is not a call value"),
492 }
493 }
494
495 pub(crate) fn visit_paths_mut(
502 &mut self,
503 optional: bool,
504 cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
505 ) -> Result<()> {
506 match self {
507 Self::Primitive(v) => {
508 if !v.visit_paths_mut(optional, cb)? {
509 *self = Value::new_none(v.ty().optional());
510 }
511
512 Ok(())
513 }
514 Self::Compound(v) => v.visit_paths_mut(cb),
515 _ => Ok(()),
516 }
517 }
518
519 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
524 match (left, right) {
525 (Value::None(_), Value::None(_)) => Some(true),
526 (Value::None(_), _) | (_, Value::None(_)) => Some(false),
527 (Value::Primitive(left), Value::Primitive(right)) => {
528 Some(PrimitiveValue::compare(left, right)? == Ordering::Equal)
529 }
530 (Value::Compound(left), Value::Compound(right)) => CompoundValue::equals(left, right),
531 _ => None,
532 }
533 }
534}
535
536impl fmt::Display for Value {
537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538 match self {
539 Self::None(_) => write!(f, "None"),
540 Self::Primitive(v) => v.fmt(f),
541 Self::Compound(v) => v.fmt(f),
542 Self::Task(_) => write!(f, "task"),
543 Self::Hints(v) => v.fmt(f),
544 Self::Input(v) => v.fmt(f),
545 Self::Output(v) => v.fmt(f),
546 Self::Call(c) => c.fmt(f),
547 }
548 }
549}
550
551impl Coercible for Value {
552 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
553 if target.is_union() || target.is_none() || self.ty().eq(target) {
554 return Ok(self.clone());
555 }
556
557 match self {
558 Self::None(_) => {
559 if target.is_optional() {
560 Ok(Self::new_none(target.clone()))
561 } else {
562 bail!("cannot coerce `None` to non-optional type `{target}`");
563 }
564 }
565 Self::Primitive(v) => v.coerce(context, target).map(Self::Primitive),
566 Self::Compound(v) => v.coerce(context, target).map(Self::Compound),
567 Self::Task(_) => {
568 if matches!(target, Type::Task) {
569 return Ok(self.clone());
570 }
571
572 bail!("task variables cannot be coerced to any other type");
573 }
574 Self::Hints(_) => {
575 if matches!(target, Type::Hints) {
576 return Ok(self.clone());
577 }
578
579 bail!("hints values cannot be coerced to any other type");
580 }
581 Self::Input(_) => {
582 if matches!(target, Type::Input) {
583 return Ok(self.clone());
584 }
585
586 bail!("input values cannot be coerced to any other type");
587 }
588 Self::Output(_) => {
589 if matches!(target, Type::Output) {
590 return Ok(self.clone());
591 }
592
593 bail!("output values cannot be coerced to any other type");
594 }
595 Self::Call(_) => {
596 bail!("call values cannot be coerced to any other type");
597 }
598 }
599 }
600}
601
602impl From<bool> for Value {
603 fn from(value: bool) -> Self {
604 Self::Primitive(value.into())
605 }
606}
607
608impl From<i64> for Value {
609 fn from(value: i64) -> Self {
610 Self::Primitive(value.into())
611 }
612}
613
614impl TryFrom<u64> for Value {
615 type Error = std::num::TryFromIntError;
616
617 fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
618 let value: i64 = value.try_into()?;
619 Ok(value.into())
620 }
621}
622
623impl From<f64> for Value {
624 fn from(value: f64) -> Self {
625 Self::Primitive(value.into())
626 }
627}
628
629impl From<String> for Value {
630 fn from(value: String) -> Self {
631 Self::Primitive(value.into())
632 }
633}
634
635impl From<PrimitiveValue> for Value {
636 fn from(value: PrimitiveValue) -> Self {
637 Self::Primitive(value)
638 }
639}
640
641impl From<Option<PrimitiveValue>> for Value {
642 fn from(value: Option<PrimitiveValue>) -> Self {
643 match value {
644 Some(v) => v.into(),
645 None => Self::new_none(Type::None),
646 }
647 }
648}
649
650impl From<CompoundValue> for Value {
651 fn from(value: CompoundValue) -> Self {
652 Self::Compound(value)
653 }
654}
655
656impl From<Pair> for Value {
657 fn from(value: Pair) -> Self {
658 Self::Compound(value.into())
659 }
660}
661
662impl From<Array> for Value {
663 fn from(value: Array) -> Self {
664 Self::Compound(value.into())
665 }
666}
667
668impl From<Map> for Value {
669 fn from(value: Map) -> Self {
670 Self::Compound(value.into())
671 }
672}
673
674impl From<Object> for Value {
675 fn from(value: Object) -> Self {
676 Self::Compound(value.into())
677 }
678}
679
680impl From<Struct> for Value {
681 fn from(value: Struct) -> Self {
682 Self::Compound(value.into())
683 }
684}
685
686impl From<TaskValue> for Value {
687 fn from(value: TaskValue) -> Self {
688 Self::Task(value)
689 }
690}
691
692impl From<HintsValue> for Value {
693 fn from(value: HintsValue) -> Self {
694 Self::Hints(value)
695 }
696}
697
698impl From<CallValue> for Value {
699 fn from(value: CallValue) -> Self {
700 Self::Call(value)
701 }
702}
703
704impl<'de> serde::Deserialize<'de> for Value {
705 fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
706 where
707 D: serde::Deserializer<'de>,
708 {
709 use serde::Deserialize as _;
710
711 struct Deserialize;
713
714 impl<'de> serde::de::DeserializeSeed<'de> for Deserialize {
715 type Value = Value;
716
717 fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
718 where
719 D: serde::Deserializer<'de>,
720 {
721 deserializer.deserialize_any(Visitor)
722 }
723 }
724
725 struct Visitor;
727
728 impl<'de> serde::de::Visitor<'de> for Visitor {
729 type Value = Value;
730
731 fn visit_unit<E>(self) -> std::result::Result<Self::Value, E>
732 where
733 E: serde::de::Error,
734 {
735 Ok(Value::new_none(Type::None))
736 }
737
738 fn visit_none<E>(self) -> std::result::Result<Self::Value, E>
739 where
740 E: serde::de::Error,
741 {
742 Ok(Value::new_none(Type::None))
743 }
744
745 fn visit_some<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
746 where
747 D: serde::Deserializer<'de>,
748 {
749 Value::deserialize(deserializer)
750 }
751
752 fn visit_bool<E>(self, v: bool) -> std::result::Result<Self::Value, E>
753 where
754 E: serde::de::Error,
755 {
756 Ok(Value::Primitive(PrimitiveValue::Boolean(v)))
757 }
758
759 fn visit_i64<E>(self, v: i64) -> std::result::Result<Self::Value, E>
760 where
761 E: serde::de::Error,
762 {
763 Ok(Value::Primitive(PrimitiveValue::Integer(v)))
764 }
765
766 fn visit_u64<E>(self, v: u64) -> std::result::Result<Self::Value, E>
767 where
768 E: serde::de::Error,
769 {
770 Ok(Value::Primitive(PrimitiveValue::Integer(
771 v.try_into().map_err(|_| {
772 E::custom("integer not in range for a 64-bit signed integer")
773 })?,
774 )))
775 }
776
777 fn visit_f64<E>(self, v: f64) -> std::result::Result<Self::Value, E>
778 where
779 E: serde::de::Error,
780 {
781 Ok(Value::Primitive(PrimitiveValue::Float(v.into())))
782 }
783
784 fn visit_str<E>(self, v: &str) -> std::result::Result<Self::Value, E>
785 where
786 E: serde::de::Error,
787 {
788 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
789 }
790
791 fn visit_string<E>(self, v: String) -> std::result::Result<Self::Value, E>
792 where
793 E: serde::de::Error,
794 {
795 Ok(Value::Primitive(PrimitiveValue::new_string(v)))
796 }
797
798 fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
799 where
800 A: serde::de::SeqAccess<'de>,
801 {
802 use serde::de::Error;
803
804 let mut elements = Vec::new();
805 while let Some(v) = seq.next_element_seed(Deserialize)? {
806 elements.push(v);
807 }
808
809 let element_ty = elements
810 .iter()
811 .try_fold(None, |mut ty, element| {
812 let element_ty = element.ty();
813 let ty = ty.get_or_insert(element_ty.clone());
814 ty.common_type(&element_ty).map(Some).ok_or_else(|| {
815 A::Error::custom(format!(
816 "a common element type does not exist between `{ty}` and \
817 `{element_ty}`"
818 ))
819 })
820 })?
821 .unwrap_or(Type::Union);
822
823 let ty: Type = ArrayType::new(element_ty).into();
824 Ok(Array::new(None, ty.clone(), elements)
825 .map_err(|e| A::Error::custom(format!("cannot coerce value to `{ty}`: {e:#}")))?
826 .into())
827 }
828
829 fn visit_map<A>(self, mut map: A) -> std::result::Result<Self::Value, A::Error>
830 where
831 A: serde::de::MapAccess<'de>,
832 {
833 let mut members = IndexMap::new();
834 while let Some(key) = map.next_key::<String>()? {
835 members.insert(key, map.next_value_seed(Deserialize)?);
836 }
837
838 Ok(Value::Compound(CompoundValue::Object(Object::new(members))))
839 }
840
841 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
842 write!(f, "a WDL value")
843 }
844 }
845
846 deserializer.deserialize_any(Visitor)
847 }
848}
849
850#[derive(Debug, Clone)]
854pub enum PrimitiveValue {
855 Boolean(bool),
857 Integer(i64),
859 Float(OrderedFloat<f64>),
861 String(Arc<String>),
863 File(HostPath),
865 Directory(HostPath),
867}
868
869impl PrimitiveValue {
870 pub fn new_string(s: impl Into<String>) -> Self {
872 Self::String(Arc::new(s.into()))
873 }
874
875 pub fn new_file(path: impl Into<String>) -> Self {
877 Self::File(Arc::new(path.into()).into())
878 }
879
880 pub fn new_directory(path: impl Into<String>) -> Self {
882 Self::Directory(Arc::new(path.into()).into())
883 }
884
885 pub fn ty(&self) -> Type {
887 match self {
888 Self::Boolean(_) => PrimitiveType::Boolean.into(),
889 Self::Integer(_) => PrimitiveType::Integer.into(),
890 Self::Float(_) => PrimitiveType::Float.into(),
891 Self::String(_) => PrimitiveType::String.into(),
892 Self::File(_) => PrimitiveType::File.into(),
893 Self::Directory(_) => PrimitiveType::Directory.into(),
894 }
895 }
896
897 pub fn as_boolean(&self) -> Option<bool> {
901 match self {
902 Self::Boolean(v) => Some(*v),
903 _ => None,
904 }
905 }
906
907 pub fn unwrap_boolean(self) -> bool {
913 match self {
914 Self::Boolean(v) => v,
915 _ => panic!("value is not a boolean"),
916 }
917 }
918
919 pub fn as_integer(&self) -> Option<i64> {
923 match self {
924 Self::Integer(v) => Some(*v),
925 _ => None,
926 }
927 }
928
929 pub fn unwrap_integer(self) -> i64 {
935 match self {
936 Self::Integer(v) => v,
937 _ => panic!("value is not an integer"),
938 }
939 }
940
941 pub fn as_float(&self) -> Option<f64> {
945 match self {
946 Self::Float(v) => Some((*v).into()),
947 _ => None,
948 }
949 }
950
951 pub fn unwrap_float(self) -> f64 {
957 match self {
958 Self::Float(v) => v.into(),
959 _ => panic!("value is not a float"),
960 }
961 }
962
963 pub fn as_string(&self) -> Option<&Arc<String>> {
967 match self {
968 Self::String(s) => Some(s),
969 _ => None,
970 }
971 }
972
973 pub fn unwrap_string(self) -> Arc<String> {
979 match self {
980 Self::String(s) => s,
981 _ => panic!("value is not a string"),
982 }
983 }
984
985 pub fn as_file(&self) -> Option<&HostPath> {
989 match self {
990 Self::File(p) => Some(p),
991 _ => None,
992 }
993 }
994
995 pub fn unwrap_file(self) -> HostPath {
1001 match self {
1002 Self::File(p) => p,
1003 _ => panic!("value is not a file"),
1004 }
1005 }
1006
1007 pub fn as_directory(&self) -> Option<&HostPath> {
1011 match self {
1012 Self::Directory(p) => Some(p),
1013 _ => None,
1014 }
1015 }
1016
1017 pub fn unwrap_directory(self) -> HostPath {
1023 match self {
1024 Self::Directory(p) => p,
1025 _ => panic!("value is not a directory"),
1026 }
1027 }
1028
1029 pub fn compare(left: &Self, right: &Self) -> Option<Ordering> {
1036 match (left, right) {
1037 (Self::Boolean(left), Self::Boolean(right)) => Some(left.cmp(right)),
1038 (Self::Integer(left), Self::Integer(right)) => Some(left.cmp(right)),
1039 (Self::Integer(left), Self::Float(right)) => {
1040 Some(OrderedFloat(*left as f64).cmp(right))
1041 }
1042 (Self::Float(left), Self::Integer(right)) => {
1043 Some(left.cmp(&OrderedFloat(*right as f64)))
1044 }
1045 (Self::Float(left), Self::Float(right)) => Some(left.cmp(right)),
1046 (Self::String(left), Self::String(right))
1047 | (Self::String(left), Self::File(HostPath(right)))
1048 | (Self::String(left), Self::Directory(HostPath(right)))
1049 | (Self::File(HostPath(left)), Self::File(HostPath(right)))
1050 | (Self::File(HostPath(left)), Self::String(right))
1051 | (Self::Directory(HostPath(left)), Self::Directory(HostPath(right)))
1052 | (Self::Directory(HostPath(left)), Self::String(right)) => Some(left.cmp(right)),
1053 _ => None,
1054 }
1055 }
1056
1057 pub fn raw<'a>(
1065 &'a self,
1066 context: Option<&'a dyn EvaluationContext>,
1067 ) -> impl fmt::Display + use<'a> {
1068 struct Display<'a> {
1070 value: &'a PrimitiveValue,
1072 context: Option<&'a dyn EvaluationContext>,
1074 }
1075
1076 impl fmt::Display for Display<'_> {
1077 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1078 match self.value {
1079 PrimitiveValue::Boolean(v) => write!(f, "{v}"),
1080 PrimitiveValue::Integer(v) => write!(f, "{v}"),
1081 PrimitiveValue::Float(v) => write!(f, "{v:.6?}"),
1082 PrimitiveValue::String(v) => write!(f, "{v}"),
1083 PrimitiveValue::File(v) => {
1084 write!(
1085 f,
1086 "{v}",
1087 v = self
1088 .context
1089 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1090 .unwrap_or(Cow::Borrowed(&v.0))
1091 )
1092 }
1093 PrimitiveValue::Directory(v) => {
1094 write!(
1095 f,
1096 "{v}",
1097 v = self
1098 .context
1099 .and_then(|c| c.guest_path(v).map(|p| Cow::Owned(p.0)))
1100 .unwrap_or(Cow::Borrowed(&v.0))
1101 )
1102 }
1103 }
1104 }
1105 }
1106
1107 Display {
1108 value: self,
1109 context,
1110 }
1111 }
1112
1113 fn visit_paths_mut(
1120 &mut self,
1121 optional: bool,
1122 cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
1123 ) -> Result<bool> {
1124 match self {
1125 Self::File(_) | Self::Directory(_) => cb(optional, self),
1126 _ => Ok(true),
1127 }
1128 }
1129
1130 pub(crate) fn expand_path(&mut self, base_path: &Path) -> Result<()> {
1134 let path = match self {
1135 PrimitiveValue::File(path) => path,
1136 PrimitiveValue::Directory(path) => path,
1137 _ => unreachable!("only file and directory values can be expanded"),
1138 };
1139
1140 if let Cow::Owned(s) = shellexpand::full(path.as_str())
1142 .with_context(|| format!("failed to shell expand path `{path}`"))?
1143 {
1144 *Arc::make_mut(&mut path.0) = s;
1145 }
1146
1147 if path::is_url(path.as_str()) {
1149 return Ok(());
1150 }
1151
1152 if let Ok(s) = base_path
1154 .join(path.as_str())
1155 .clean()
1156 .into_os_string()
1157 .into_string()
1158 {
1159 *Arc::make_mut(&mut path.0) = s;
1160 }
1161
1162 Ok(())
1163 }
1164
1165 pub(crate) fn ensure_path_exists(
1180 &self,
1181 optional: bool,
1182 base_dir: Option<&Path>,
1183 ) -> Result<bool> {
1184 let (path, is_file) = match self {
1185 PrimitiveValue::File(path) => (path, true),
1186 PrimitiveValue::Directory(path) => (path, false),
1187 _ => unreachable!("only file and directory values should be passed to the callback"),
1188 };
1189
1190 if path::is_file_url(path.as_str()) {
1192 let exists = path::parse_url(path.as_str())
1193 .and_then(|url| url.to_file_path().ok())
1194 .map(|p| p.exists())
1195 .unwrap_or(false);
1196 if exists {
1197 return Ok(true);
1198 }
1199
1200 if optional && !exists {
1201 return Ok(false);
1202 }
1203
1204 bail!("path `{path}` does not exist");
1205 } else if path::is_url(path.as_str()) {
1206 return Ok(true);
1208 }
1209
1210 let path: Cow<'_, Path> = base_dir
1212 .map(|d| d.join(path.as_str()).into())
1213 .unwrap_or_else(|| Path::new(path.as_str()).into());
1214 if is_file && !path.is_file() {
1215 if optional {
1216 return Ok(false);
1217 }
1218
1219 bail!("file `{path}` does not exist", path = path.display());
1220 } else if !is_file && !path.is_dir() {
1221 if optional {
1222 return Ok(false);
1223 }
1224
1225 bail!("directory `{path}` does not exist", path = path.display())
1226 }
1227
1228 Ok(true)
1229 }
1230}
1231
1232impl fmt::Display for PrimitiveValue {
1233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1234 match self {
1235 Self::Boolean(v) => write!(f, "{v}"),
1236 Self::Integer(v) => write!(f, "{v}"),
1237 Self::Float(v) => write!(f, "{v:.6?}"),
1238 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1239 write!(f, "\"{s}\"")
1241 }
1242 }
1243 }
1244}
1245
1246impl PartialEq for PrimitiveValue {
1247 fn eq(&self, other: &Self) -> bool {
1248 Self::compare(self, other) == Some(Ordering::Equal)
1249 }
1250}
1251
1252impl Eq for PrimitiveValue {}
1253
1254impl Hash for PrimitiveValue {
1255 fn hash<H: Hasher>(&self, state: &mut H) {
1256 match self {
1257 Self::Boolean(v) => {
1258 0.hash(state);
1259 v.hash(state);
1260 }
1261 Self::Integer(v) => {
1262 1.hash(state);
1263 v.hash(state);
1264 }
1265 Self::Float(v) => {
1266 1.hash(state);
1269 v.hash(state);
1270 }
1271 Self::String(v) | Self::File(HostPath(v)) | Self::Directory(HostPath(v)) => {
1272 2.hash(state);
1275 v.hash(state);
1276 }
1277 }
1278 }
1279}
1280
1281impl From<bool> for PrimitiveValue {
1282 fn from(value: bool) -> Self {
1283 Self::Boolean(value)
1284 }
1285}
1286
1287impl From<i64> for PrimitiveValue {
1288 fn from(value: i64) -> Self {
1289 Self::Integer(value)
1290 }
1291}
1292
1293impl From<f64> for PrimitiveValue {
1294 fn from(value: f64) -> Self {
1295 Self::Float(value.into())
1296 }
1297}
1298
1299impl From<String> for PrimitiveValue {
1300 fn from(value: String) -> Self {
1301 Self::String(value.into())
1302 }
1303}
1304
1305impl Coercible for PrimitiveValue {
1306 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
1307 if target.is_union() || target.is_none() || self.ty().eq(target) {
1308 return Ok(self.clone());
1309 }
1310
1311 match self {
1312 Self::Boolean(v) => {
1313 target
1314 .as_primitive()
1315 .and_then(|ty| match ty {
1316 PrimitiveType::Boolean => Some(Self::Boolean(*v)),
1318 _ => None,
1319 })
1320 .with_context(|| format!("cannot coerce type `Boolean` to type `{target}`"))
1321 }
1322 Self::Integer(v) => {
1323 target
1324 .as_primitive()
1325 .and_then(|ty| match ty {
1326 PrimitiveType::Integer => Some(Self::Integer(*v)),
1328 PrimitiveType::Float => Some(Self::Float((*v as f64).into())),
1330 _ => None,
1331 })
1332 .with_context(|| format!("cannot coerce type `Int` to type `{target}`"))
1333 }
1334 Self::Float(v) => {
1335 target
1336 .as_primitive()
1337 .and_then(|ty| match ty {
1338 PrimitiveType::Float => Some(Self::Float(*v)),
1340 _ => None,
1341 })
1342 .with_context(|| format!("cannot coerce type `Float` to type `{target}`"))
1343 }
1344 Self::String(s) => {
1345 target
1346 .as_primitive()
1347 .and_then(|ty| match ty {
1348 PrimitiveType::String => Some(Self::String(s.clone())),
1350 PrimitiveType::File => Some(Self::File(
1352 context
1353 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1354 .unwrap_or_else(|| s.clone().into()),
1355 )),
1356 PrimitiveType::Directory => Some(Self::Directory(
1358 context
1359 .and_then(|c| c.host_path(&GuestPath(s.clone())))
1360 .unwrap_or_else(|| s.clone().into()),
1361 )),
1362 _ => None,
1363 })
1364 .with_context(|| format!("cannot coerce type `String` to type `{target}`"))
1365 }
1366 Self::File(p) => {
1367 target
1368 .as_primitive()
1369 .and_then(|ty| match ty {
1370 PrimitiveType::File => Some(Self::File(p.clone())),
1372 PrimitiveType::String => Some(Self::String(
1374 context
1375 .and_then(|c| c.guest_path(p).map(Into::into))
1376 .unwrap_or_else(|| p.clone().into()),
1377 )),
1378 _ => None,
1379 })
1380 .with_context(|| format!("cannot coerce type `File` to type `{target}`"))
1381 }
1382 Self::Directory(p) => {
1383 target
1384 .as_primitive()
1385 .and_then(|ty| match ty {
1386 PrimitiveType::Directory => Some(Self::Directory(p.clone())),
1388 PrimitiveType::String => Some(Self::String(
1390 context
1391 .and_then(|c| c.guest_path(p).map(Into::into))
1392 .unwrap_or_else(|| p.clone().into()),
1393 )),
1394 _ => None,
1395 })
1396 .with_context(|| format!("cannot coerce type `Directory` to type `{target}`"))
1397 }
1398 }
1399 }
1400}
1401
1402impl serde::Serialize for PrimitiveValue {
1403 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
1404 where
1405 S: serde::Serializer,
1406 {
1407 match self {
1408 Self::Boolean(v) => v.serialize(serializer),
1409 Self::Integer(v) => v.serialize(serializer),
1410 Self::Float(v) => v.serialize(serializer),
1411 Self::String(s) | Self::File(HostPath(s)) | Self::Directory(HostPath(s)) => {
1412 s.serialize(serializer)
1413 }
1414 }
1415 }
1416}
1417
1418#[derive(Debug, Clone)]
1422pub struct Pair {
1423 ty: Type,
1425 values: Arc<(Value, Value)>,
1427}
1428
1429impl Pair {
1430 pub fn new(
1439 context: Option<&dyn EvaluationContext>,
1440 ty: impl Into<Type>,
1441 left: impl Into<Value>,
1442 right: impl Into<Value>,
1443 ) -> Result<Self> {
1444 let ty = ty.into();
1445 if let Type::Compound(CompoundType::Pair(ty), _) = ty {
1446 let left = left
1447 .into()
1448 .coerce(context, ty.left_type())
1449 .context("failed to coerce pair's left value")?;
1450 let right = right
1451 .into()
1452 .coerce(context, ty.right_type())
1453 .context("failed to coerce pair's right value")?;
1454 return Ok(Self::new_unchecked(
1455 Type::Compound(CompoundType::Pair(ty), false),
1456 left,
1457 right,
1458 ));
1459 }
1460
1461 panic!("type `{ty}` is not a pair type");
1462 }
1463
1464 pub(crate) fn new_unchecked(ty: Type, left: Value, right: Value) -> Self {
1467 assert!(ty.as_pair().is_some());
1468 Self {
1469 ty: ty.require(),
1470 values: Arc::new((left, right)),
1471 }
1472 }
1473
1474 pub fn ty(&self) -> Type {
1476 self.ty.clone()
1477 }
1478
1479 pub fn left(&self) -> &Value {
1481 &self.values.0
1482 }
1483
1484 pub fn right(&self) -> &Value {
1486 &self.values.1
1487 }
1488}
1489
1490impl fmt::Display for Pair {
1491 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1492 write!(
1493 f,
1494 "({left}, {right})",
1495 left = self.values.0,
1496 right = self.values.1
1497 )
1498 }
1499}
1500
1501#[derive(Debug, Clone)]
1505pub struct Array {
1506 ty: Type,
1508 elements: Option<Arc<Vec<Value>>>,
1512}
1513
1514impl Array {
1515 pub fn new<V>(
1524 context: Option<&dyn EvaluationContext>,
1525 ty: impl Into<Type>,
1526 elements: impl IntoIterator<Item = V>,
1527 ) -> Result<Self>
1528 where
1529 V: Into<Value>,
1530 {
1531 let ty = ty.into();
1532 if let Type::Compound(CompoundType::Array(ty), _) = ty {
1533 let element_type = ty.element_type();
1534 let elements = elements
1535 .into_iter()
1536 .enumerate()
1537 .map(|(i, v)| {
1538 let v = v.into();
1539 v.coerce(context, element_type)
1540 .with_context(|| format!("failed to coerce array element at index {i}"))
1541 })
1542 .collect::<Result<Vec<_>>>()?;
1543
1544 return Ok(Self::new_unchecked(
1545 Type::Compound(CompoundType::Array(ty.unqualified()), false),
1546 elements,
1547 ));
1548 }
1549
1550 panic!("type `{ty}` is not an array type");
1551 }
1552
1553 pub(crate) fn new_unchecked(ty: Type, elements: Vec<Value>) -> Self {
1556 let ty = if let Type::Compound(CompoundType::Array(ty), _) = ty {
1557 Type::Compound(CompoundType::Array(ty.unqualified()), false)
1558 } else {
1559 panic!("type is not an array type");
1560 };
1561
1562 Self {
1563 ty,
1564 elements: if elements.is_empty() {
1565 None
1566 } else {
1567 Some(Arc::new(elements))
1568 },
1569 }
1570 }
1571
1572 pub fn ty(&self) -> Type {
1574 self.ty.clone()
1575 }
1576
1577 pub fn as_slice(&self) -> &[Value] {
1579 self.elements.as_ref().map(|v| v.as_slice()).unwrap_or(&[])
1580 }
1581
1582 pub fn len(&self) -> usize {
1584 self.elements.as_ref().map(|v| v.len()).unwrap_or(0)
1585 }
1586
1587 pub fn is_empty(&self) -> bool {
1589 self.len() == 0
1590 }
1591}
1592
1593impl fmt::Display for Array {
1594 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1595 write!(f, "[")?;
1596
1597 if let Some(elements) = &self.elements {
1598 for (i, element) in elements.iter().enumerate() {
1599 if i > 0 {
1600 write!(f, ", ")?;
1601 }
1602
1603 write!(f, "{element}")?;
1604 }
1605 }
1606
1607 write!(f, "]")
1608 }
1609}
1610
1611#[derive(Debug, Clone)]
1615pub struct Map {
1616 ty: Type,
1618 elements: Option<Arc<IndexMap<Option<PrimitiveValue>, Value>>>,
1622}
1623
1624impl Map {
1625 pub fn new<K, V>(
1634 context: Option<&dyn EvaluationContext>,
1635 ty: impl Into<Type>,
1636 elements: impl IntoIterator<Item = (K, V)>,
1637 ) -> Result<Self>
1638 where
1639 K: Into<Value>,
1640 V: Into<Value>,
1641 {
1642 let ty = ty.into();
1643 if let Type::Compound(CompoundType::Map(ty), _) = ty {
1644 let key_type = ty.key_type();
1645 let value_type = ty.value_type();
1646
1647 let elements = elements
1648 .into_iter()
1649 .enumerate()
1650 .map(|(i, (k, v))| {
1651 let k = k.into();
1652 let v = v.into();
1653 Ok((
1654 if k.is_none() {
1655 None
1656 } else {
1657 match k.coerce(context, key_type).with_context(|| {
1658 format!("failed to coerce map key for element at index {i}")
1659 })? {
1660 Value::None(_) => None,
1661 Value::Primitive(v) => Some(v),
1662 _ => {
1663 bail!("not all key values are primitive")
1664 }
1665 }
1666 },
1667 v.coerce(context, value_type).with_context(|| {
1668 format!("failed to coerce map value for element at index {i}")
1669 })?,
1670 ))
1671 })
1672 .collect::<Result<_>>()?;
1673
1674 return Ok(Self::new_unchecked(
1675 Type::Compound(CompoundType::Map(ty), false),
1676 elements,
1677 ));
1678 }
1679
1680 panic!("type `{ty}` is not a map type");
1681 }
1682
1683 pub(crate) fn new_unchecked(
1686 ty: Type,
1687 elements: IndexMap<Option<PrimitiveValue>, Value>,
1688 ) -> Self {
1689 assert!(ty.as_map().is_some());
1690 Self {
1691 ty: ty.require(),
1692 elements: if elements.is_empty() {
1693 None
1694 } else {
1695 Some(Arc::new(elements))
1696 },
1697 }
1698 }
1699
1700 pub fn ty(&self) -> Type {
1702 self.ty.clone()
1703 }
1704
1705 pub fn iter(&self) -> impl Iterator<Item = (&Option<PrimitiveValue>, &Value)> {
1707 self.elements
1708 .as_ref()
1709 .map(|m| Either::Left(m.iter()))
1710 .unwrap_or(Either::Right(std::iter::empty()))
1711 }
1712
1713 pub fn keys(&self) -> impl Iterator<Item = &Option<PrimitiveValue>> {
1715 self.elements
1716 .as_ref()
1717 .map(|m| Either::Left(m.keys()))
1718 .unwrap_or(Either::Right(std::iter::empty()))
1719 }
1720
1721 pub fn values(&self) -> impl Iterator<Item = &Value> {
1723 self.elements
1724 .as_ref()
1725 .map(|m| Either::Left(m.values()))
1726 .unwrap_or(Either::Right(std::iter::empty()))
1727 }
1728
1729 pub fn contains_key(&self, key: &Option<PrimitiveValue>) -> bool {
1731 self.elements
1732 .as_ref()
1733 .map(|m| m.contains_key(key))
1734 .unwrap_or(false)
1735 }
1736
1737 pub fn get(&self, key: &Option<PrimitiveValue>) -> Option<&Value> {
1739 self.elements.as_ref().and_then(|m| m.get(key))
1740 }
1741
1742 pub fn len(&self) -> usize {
1744 self.elements.as_ref().map(|m| m.len()).unwrap_or(0)
1745 }
1746
1747 pub fn is_empty(&self) -> bool {
1749 self.len() == 0
1750 }
1751}
1752
1753impl fmt::Display for Map {
1754 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1755 write!(f, "{{")?;
1756
1757 for (i, (k, v)) in self.iter().enumerate() {
1758 if i > 0 {
1759 write!(f, ", ")?;
1760 }
1761
1762 match k {
1763 Some(k) => write!(f, "{k}: {v}")?,
1764 None => write!(f, "None: {v}")?,
1765 }
1766 }
1767
1768 write!(f, "}}")
1769 }
1770}
1771
1772#[derive(Debug, Clone)]
1776pub struct Object {
1777 pub(crate) members: Option<Arc<IndexMap<String, Value>>>,
1781}
1782
1783impl Object {
1784 pub(crate) fn new(members: IndexMap<String, Value>) -> Self {
1788 Self {
1789 members: if members.is_empty() {
1790 None
1791 } else {
1792 Some(Arc::new(members))
1793 },
1794 }
1795 }
1796
1797 pub fn empty() -> Self {
1799 Self::new(IndexMap::default())
1800 }
1801
1802 pub fn from_v1_metadata<N: TreeNode>(
1804 items: impl Iterator<Item = v1::MetadataObjectItem<N>>,
1805 ) -> Self {
1806 Object::new(
1807 items
1808 .map(|i| {
1809 (
1810 i.name().text().to_string(),
1811 Value::from_v1_metadata(&i.value()),
1812 )
1813 })
1814 .collect::<IndexMap<_, _>>(),
1815 )
1816 }
1817
1818 pub fn ty(&self) -> Type {
1820 Type::Object
1821 }
1822
1823 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1825 self.members
1826 .as_ref()
1827 .map(|m| Either::Left(m.iter().map(|(k, v)| (k.as_str(), v))))
1828 .unwrap_or(Either::Right(std::iter::empty()))
1829 }
1830
1831 pub fn keys(&self) -> impl Iterator<Item = &str> {
1833 self.members
1834 .as_ref()
1835 .map(|m| Either::Left(m.keys().map(|k| k.as_str())))
1836 .unwrap_or(Either::Right(std::iter::empty()))
1837 }
1838
1839 pub fn values(&self) -> impl Iterator<Item = &Value> {
1841 self.members
1842 .as_ref()
1843 .map(|m| Either::Left(m.values()))
1844 .unwrap_or(Either::Right(std::iter::empty()))
1845 }
1846
1847 pub fn contains_key(&self, key: &str) -> bool {
1849 self.members
1850 .as_ref()
1851 .map(|m| m.contains_key(key))
1852 .unwrap_or(false)
1853 }
1854
1855 pub fn get(&self, key: &str) -> Option<&Value> {
1857 self.members.as_ref().and_then(|m| m.get(key))
1858 }
1859
1860 pub fn len(&self) -> usize {
1862 self.members.as_ref().map(|m| m.len()).unwrap_or(0)
1863 }
1864
1865 pub fn is_empty(&self) -> bool {
1867 self.len() == 0
1868 }
1869}
1870
1871impl fmt::Display for Object {
1872 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1873 write!(f, "object {{")?;
1874
1875 for (i, (k, v)) in self.iter().enumerate() {
1876 if i > 0 {
1877 write!(f, ", ")?;
1878 }
1879
1880 write!(f, "{k}: {v}")?;
1881 }
1882
1883 write!(f, "}}")
1884 }
1885}
1886
1887#[derive(Debug, Clone)]
1891pub struct Struct {
1892 ty: Type,
1894 name: Arc<String>,
1896 pub(crate) members: Arc<IndexMap<String, Value>>,
1898}
1899
1900impl Struct {
1901 pub fn new<S, V>(
1910 context: Option<&dyn EvaluationContext>,
1911 ty: impl Into<Type>,
1912 members: impl IntoIterator<Item = (S, V)>,
1913 ) -> Result<Self>
1914 where
1915 S: Into<String>,
1916 V: Into<Value>,
1917 {
1918 let ty = ty.into();
1919 if let Type::Compound(CompoundType::Struct(ty), optional) = ty {
1920 let mut members = members
1921 .into_iter()
1922 .map(|(n, v)| {
1923 let n = n.into();
1924 let v = v.into();
1925 let v = v
1926 .coerce(
1927 context,
1928 ty.members().get(&n).ok_or_else(|| {
1929 anyhow!("struct does not contain a member named `{n}`")
1930 })?,
1931 )
1932 .with_context(|| format!("failed to coerce struct member `{n}`"))?;
1933 Ok((n, v))
1934 })
1935 .collect::<Result<IndexMap<_, _>>>()?;
1936
1937 for (name, ty) in ty.members().iter() {
1938 if ty.is_optional() {
1940 if !members.contains_key(name) {
1941 members.insert(name.clone(), Value::new_none(ty.clone()));
1942 }
1943 } else {
1944 if !members.contains_key(name) {
1946 bail!("missing a value for struct member `{name}`");
1947 }
1948 }
1949 }
1950
1951 let name = ty.name().to_string();
1952 return Ok(Self {
1953 ty: Type::Compound(CompoundType::Struct(ty), optional),
1954 name: Arc::new(name),
1955 members: Arc::new(members),
1956 });
1957 }
1958
1959 panic!("type `{ty}` is not a struct type");
1960 }
1961
1962 pub(crate) fn new_unchecked(
1965 ty: Type,
1966 name: Arc<String>,
1967 members: Arc<IndexMap<String, Value>>,
1968 ) -> Self {
1969 assert!(ty.as_struct().is_some());
1970 Self {
1971 ty: ty.require(),
1972 name,
1973 members,
1974 }
1975 }
1976
1977 pub fn ty(&self) -> Type {
1979 self.ty.clone()
1980 }
1981
1982 pub fn name(&self) -> &Arc<String> {
1984 &self.name
1985 }
1986
1987 pub fn iter(&self) -> impl Iterator<Item = (&str, &Value)> {
1989 self.members.iter().map(|(k, v)| (k.as_str(), v))
1990 }
1991
1992 pub fn keys(&self) -> impl Iterator<Item = &str> {
1994 self.members.keys().map(|k| k.as_str())
1995 }
1996
1997 pub fn values(&self) -> impl Iterator<Item = &Value> {
1999 self.members.values()
2000 }
2001
2002 pub fn contains_key(&self, key: &str) -> bool {
2004 self.members.contains_key(key)
2005 }
2006
2007 pub fn get(&self, key: &str) -> Option<&Value> {
2009 self.members.get(key)
2010 }
2011}
2012
2013impl fmt::Display for Struct {
2014 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2015 write!(f, "{name} {{", name = self.name)?;
2016
2017 for (i, (k, v)) in self.members.iter().enumerate() {
2018 if i > 0 {
2019 write!(f, ", ")?;
2020 }
2021
2022 write!(f, "{k}: {v}")?;
2023 }
2024
2025 write!(f, "}}")
2026 }
2027}
2028
2029#[derive(Debug, Clone)]
2033pub enum CompoundValue {
2034 Pair(Pair),
2036 Array(Array),
2038 Map(Map),
2040 Object(Object),
2042 Struct(Struct),
2044}
2045
2046impl CompoundValue {
2047 pub fn ty(&self) -> Type {
2049 match self {
2050 CompoundValue::Pair(v) => v.ty(),
2051 CompoundValue::Array(v) => v.ty(),
2052 CompoundValue::Map(v) => v.ty(),
2053 CompoundValue::Object(v) => v.ty(),
2054 CompoundValue::Struct(v) => v.ty(),
2055 }
2056 }
2057
2058 pub fn as_pair(&self) -> Option<&Pair> {
2062 match self {
2063 Self::Pair(v) => Some(v),
2064 _ => None,
2065 }
2066 }
2067
2068 pub fn unwrap_pair(self) -> Pair {
2074 match self {
2075 Self::Pair(v) => v,
2076 _ => panic!("value is not a pair"),
2077 }
2078 }
2079
2080 pub fn as_array(&self) -> Option<&Array> {
2084 match self {
2085 Self::Array(v) => Some(v),
2086 _ => None,
2087 }
2088 }
2089
2090 pub fn unwrap_array(self) -> Array {
2096 match self {
2097 Self::Array(v) => v,
2098 _ => panic!("value is not an array"),
2099 }
2100 }
2101
2102 pub fn as_map(&self) -> Option<&Map> {
2106 match self {
2107 Self::Map(v) => Some(v),
2108 _ => None,
2109 }
2110 }
2111
2112 pub fn unwrap_map(self) -> Map {
2118 match self {
2119 Self::Map(v) => v,
2120 _ => panic!("value is not a map"),
2121 }
2122 }
2123
2124 pub fn as_object(&self) -> Option<&Object> {
2128 match self {
2129 Self::Object(v) => Some(v),
2130 _ => None,
2131 }
2132 }
2133
2134 pub fn unwrap_object(self) -> Object {
2140 match self {
2141 Self::Object(v) => v,
2142 _ => panic!("value is not an object"),
2143 }
2144 }
2145
2146 pub fn as_struct(&self) -> Option<&Struct> {
2150 match self {
2151 Self::Struct(v) => Some(v),
2152 _ => None,
2153 }
2154 }
2155
2156 pub fn unwrap_struct(self) -> Struct {
2162 match self {
2163 Self::Struct(v) => v,
2164 _ => panic!("value is not a struct"),
2165 }
2166 }
2167
2168 pub fn equals(left: &Self, right: &Self) -> Option<bool> {
2174 if left.ty() != right.ty() {
2177 return None;
2178 }
2179
2180 match (left, right) {
2181 (Self::Pair(left), Self::Pair(right)) => Some(
2182 Value::equals(left.left(), right.left())?
2183 && Value::equals(left.right(), right.right())?,
2184 ),
2185 (CompoundValue::Array(left), CompoundValue::Array(right)) => Some(
2186 left.len() == right.len()
2187 && left
2188 .as_slice()
2189 .iter()
2190 .zip(right.as_slice())
2191 .all(|(l, r)| Value::equals(l, r).unwrap_or(false)),
2192 ),
2193 (CompoundValue::Map(left), CompoundValue::Map(right)) => Some(
2194 left.len() == right.len()
2195 && left.iter().zip(right.iter()).all(|((lk, lv), (rk, rv))| {
2197 match (lk, rk) {
2198 (None, None) => {},
2199 (Some(lk), Some(rk)) if lk == rk => {},
2200 _ => return false
2201 }
2202
2203 Value::equals(lv, rv).unwrap_or(false)
2204 }),
2205 ),
2206 (CompoundValue::Object(left), CompoundValue::Object(right)) => Some(
2207 left.len() == right.len()
2208 && left.iter().all(|(k, left)| match right.get(k) {
2209 Some(right) => Value::equals(left, right).unwrap_or(false),
2210 None => false,
2211 }),
2212 ),
2213 (
2214 CompoundValue::Struct(Struct { members: left, .. }),
2215 CompoundValue::Struct(Struct { members: right, .. }),
2216 ) => Some(
2217 left.len() == right.len()
2218 && left.iter().all(|(k, left)| match right.get(k) {
2219 Some(right) => Value::equals(left, right).unwrap_or(false),
2220 None => false,
2221 }),
2222 ),
2223 _ => None,
2224 }
2225 }
2226
2227 fn visit_paths_mut(
2234 &mut self,
2235 cb: &mut impl FnMut(bool, &mut PrimitiveValue) -> Result<bool>,
2236 ) -> Result<()> {
2237 match self {
2238 Self::Pair(pair) => {
2239 let ty = pair.ty.as_pair().expect("should be a pair type");
2240 let (left_optional, right_optional) =
2241 (ty.left_type().is_optional(), ty.right_type().is_optional());
2242 let values = Arc::make_mut(&mut pair.values);
2243 values.0.visit_paths_mut(left_optional, cb)?;
2244 values.1.visit_paths_mut(right_optional, cb)?;
2245 }
2246 Self::Array(array) => {
2247 let ty = array.ty.as_array().expect("should be an array type");
2248 let optional = ty.element_type().is_optional();
2249 if let Some(elements) = &mut array.elements {
2250 for v in Arc::make_mut(elements) {
2251 v.visit_paths_mut(optional, cb)?;
2252 }
2253 }
2254 }
2255 Self::Map(map) => {
2256 let ty = map.ty.as_map().expect("should be a map type");
2257 let (key_optional, value_optional) =
2258 (ty.key_type().is_optional(), ty.value_type().is_optional());
2259 if let Some(elements) = &mut map.elements {
2260 if elements
2261 .iter()
2262 .find_map(|(k, _)| {
2263 k.as_ref().map(|v| {
2264 matches!(v, PrimitiveValue::File(_) | PrimitiveValue::Directory(_))
2265 })
2266 })
2267 .unwrap_or(false)
2268 {
2269 let elements = Arc::make_mut(elements);
2272 let new = elements
2273 .drain(..)
2274 .map(|(mut k, mut v)| {
2275 if let Some(v) = &mut k
2276 && !v.visit_paths_mut(key_optional, cb)?
2277 {
2278 k = None;
2279 }
2280
2281 v.visit_paths_mut(value_optional, cb)?;
2282 Ok((k, v))
2283 })
2284 .collect::<Result<Vec<_>>>()?;
2285 elements.extend(new);
2286 } else {
2287 for v in Arc::make_mut(elements).values_mut() {
2289 v.visit_paths_mut(value_optional, cb)?;
2290 }
2291 }
2292 }
2293 }
2294 Self::Object(object) => {
2295 if let Some(members) = &mut object.members {
2296 for v in Arc::make_mut(members).values_mut() {
2297 v.visit_paths_mut(false, cb)?;
2298 }
2299 }
2300 }
2301 Self::Struct(s) => {
2302 let ty = s.ty.as_struct().expect("should be a struct type");
2303 for (n, v) in Arc::make_mut(&mut s.members).iter_mut() {
2304 v.visit_paths_mut(ty.members()[n].is_optional(), cb)?;
2305 }
2306 }
2307 }
2308
2309 Ok(())
2310 }
2311}
2312
2313impl fmt::Display for CompoundValue {
2314 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2315 match self {
2316 Self::Pair(v) => v.fmt(f),
2317 Self::Array(v) => v.fmt(f),
2318 Self::Map(v) => v.fmt(f),
2319 Self::Object(v) => v.fmt(f),
2320 Self::Struct(v) => v.fmt(f),
2321 }
2322 }
2323}
2324
2325impl Coercible for CompoundValue {
2326 fn coerce(&self, context: Option<&dyn EvaluationContext>, target: &Type) -> Result<Self> {
2327 if target.is_union() || target.is_none() || self.ty().eq(target) {
2328 return Ok(self.clone());
2329 }
2330
2331 if let Type::Compound(target_ty, _) = target {
2332 match (self, target_ty) {
2333 (Self::Array(v), CompoundType::Array(target_ty)) => {
2335 if v.is_empty() && target_ty.is_non_empty() {
2338 bail!("cannot coerce empty array value to non-empty array type `{target}`",);
2339 }
2340
2341 return Ok(Self::Array(Array::new(
2342 context,
2343 target.clone(),
2344 v.as_slice().iter().cloned(),
2345 )?));
2346 }
2347 (Self::Map(v), CompoundType::Map(map_ty)) => {
2349 return Ok(Self::Map(Map::new(
2350 context,
2351 target.clone(),
2352 v.iter().map(|(k, v)| {
2353 (
2354 k.clone()
2355 .map(Into::into)
2356 .unwrap_or(Value::new_none(map_ty.key_type().optional())),
2357 v.clone(),
2358 )
2359 }),
2360 )?));
2361 }
2362 (Self::Pair(v), CompoundType::Pair(_)) => {
2364 return Ok(Self::Pair(Pair::new(
2365 context,
2366 target.clone(),
2367 v.values.0.clone(),
2368 v.values.1.clone(),
2369 )?));
2370 }
2371 (Self::Map(v), CompoundType::Struct(target_ty)) => {
2373 let len = v.len();
2374 let expected_len = target_ty.members().len();
2375
2376 if len != expected_len {
2377 bail!(
2378 "cannot coerce a map of {len} element{s1} to struct type `{target}` \
2379 as the struct has {expected_len} member{s2}",
2380 s1 = if len == 1 { "" } else { "s" },
2381 s2 = if expected_len == 1 { "" } else { "s" }
2382 );
2383 }
2384
2385 return Ok(Self::Struct(Struct {
2386 ty: target.clone(),
2387 name: target_ty.name().clone(),
2388 members: Arc::new(
2389 v.iter()
2390 .map(|(k, v)| {
2391 let k = k
2392 .as_ref()
2393 .and_then(|k| {
2394 k.coerce(context, &PrimitiveType::String.into()).ok()
2395 })
2396 .with_context(|| {
2397 format!(
2398 "cannot coerce a map of type `{map_type}` to \
2399 struct type `{target}` as the key type cannot \
2400 coerce to `String`",
2401 map_type = v.ty()
2402 )
2403 })?
2404 .unwrap_string();
2405 let ty =
2406 target_ty.members().get(k.as_ref()).with_context(|| {
2407 format!(
2408 "cannot coerce a map with key `{k}` to struct \
2409 type `{target}` as the struct does not contain a \
2410 member with that name"
2411 )
2412 })?;
2413 let v = v.coerce(context, ty).with_context(|| {
2414 format!("failed to coerce value of map key `{k}")
2415 })?;
2416 Ok((k.to_string(), v))
2417 })
2418 .collect::<Result<_>>()?,
2419 ),
2420 }));
2421 }
2422 (Self::Struct(Struct { members, .. }), CompoundType::Map(map_ty)) => {
2424 let key_ty = map_ty.key_type();
2425 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2426 bail!(
2427 "cannot coerce a struct to type `{target}` as key type `{key_ty}` \
2428 cannot be coerced from `String`"
2429 );
2430 }
2431
2432 let value_ty = map_ty.value_type();
2433 return Ok(Self::Map(Map::new_unchecked(
2434 target.clone(),
2435 members
2436 .iter()
2437 .map(|(n, v)| {
2438 let v = v
2439 .coerce(context, value_ty)
2440 .with_context(|| format!("failed to coerce member `{n}`"))?;
2441 Ok((
2442 PrimitiveValue::new_string(n)
2443 .coerce(context, key_ty)
2444 .expect("should coerce")
2445 .into(),
2446 v,
2447 ))
2448 })
2449 .collect::<Result<_>>()?,
2450 )));
2451 }
2452 (Self::Object(object), CompoundType::Map(map_ty)) => {
2454 let key_ty = map_ty.key_type();
2455 if !Type::from(PrimitiveType::String).is_coercible_to(key_ty) {
2456 bail!(
2457 "cannot coerce an object to type `{target}` as key type `{key_ty}` \
2458 cannot be coerced from `String`"
2459 );
2460 }
2461
2462 let value_ty = map_ty.value_type();
2463 return Ok(Self::Map(Map::new_unchecked(
2464 target.clone(),
2465 object
2466 .iter()
2467 .map(|(n, v)| {
2468 let v = v
2469 .coerce(context, value_ty)
2470 .with_context(|| format!("failed to coerce member `{n}`"))?;
2471 Ok((
2472 PrimitiveValue::new_string(n)
2473 .coerce(context, key_ty)
2474 .expect("should coerce")
2475 .into(),
2476 v,
2477 ))
2478 })
2479 .collect::<Result<_>>()?,
2480 )));
2481 }
2482 (Self::Object(v), CompoundType::Struct(_)) => {
2484 return Ok(Self::Struct(Struct::new(
2485 context,
2486 target.clone(),
2487 v.iter().map(|(k, v)| (k, v.clone())),
2488 )?));
2489 }
2490 (Self::Struct(v), CompoundType::Struct(struct_ty)) => {
2492 let len = v.members.len();
2493 let expected_len = struct_ty.members().len();
2494
2495 if len != expected_len {
2496 bail!(
2497 "cannot coerce a struct of {len} members{s1} to struct type \
2498 `{target}` as the target struct has {expected_len} member{s2}",
2499 s1 = if len == 1 { "" } else { "s" },
2500 s2 = if expected_len == 1 { "" } else { "s" }
2501 );
2502 }
2503
2504 return Ok(Self::Struct(Struct {
2505 ty: target.clone(),
2506 name: struct_ty.name().clone(),
2507 members: Arc::new(
2508 v.members
2509 .iter()
2510 .map(|(k, v)| {
2511 let ty = struct_ty.members().get(k).ok_or_else(|| {
2512 anyhow!(
2513 "cannot coerce a struct with member `{k}` to struct \
2514 type `{target}` as the target struct does not \
2515 contain a member with that name",
2516 )
2517 })?;
2518 let v = v.coerce(context, ty).with_context(|| {
2519 format!("failed to coerce member `{k}`")
2520 })?;
2521 Ok((k.clone(), v))
2522 })
2523 .collect::<Result<_>>()?,
2524 ),
2525 }));
2526 }
2527 _ => {}
2528 }
2529 }
2530
2531 if let Type::Object = target {
2532 match self {
2533 Self::Map(v) => {
2535 return Ok(Self::Object(Object::new(
2536 v.iter()
2537 .map(|(k, v)| {
2538 let k = k
2539 .as_ref()
2540 .and_then(|k| {
2541 k.coerce(context, &PrimitiveType::String.into()).ok()
2542 })
2543 .with_context(|| {
2544 format!(
2545 "cannot coerce a map of type `{map_type}` to `Object` \
2546 as the key type cannot coerce to `String`",
2547 map_type = v.ty()
2548 )
2549 })?
2550 .unwrap_string();
2551 Ok((k.to_string(), v.clone()))
2552 })
2553 .collect::<Result<IndexMap<_, _>>>()?,
2554 )));
2555 }
2556 Self::Struct(v) => {
2558 return Ok(Self::Object(Object {
2559 members: Some(v.members.clone()),
2560 }));
2561 }
2562 _ => {}
2563 };
2564 }
2565
2566 bail!(
2567 "cannot coerce a value of type `{ty}` to type `{target}`",
2568 ty = self.ty()
2569 );
2570 }
2571}
2572
2573impl From<Pair> for CompoundValue {
2574 fn from(value: Pair) -> Self {
2575 Self::Pair(value)
2576 }
2577}
2578
2579impl From<Array> for CompoundValue {
2580 fn from(value: Array) -> Self {
2581 Self::Array(value)
2582 }
2583}
2584
2585impl From<Map> for CompoundValue {
2586 fn from(value: Map) -> Self {
2587 Self::Map(value)
2588 }
2589}
2590
2591impl From<Object> for CompoundValue {
2592 fn from(value: Object) -> Self {
2593 Self::Object(value)
2594 }
2595}
2596
2597impl From<Struct> for CompoundValue {
2598 fn from(value: Struct) -> Self {
2599 Self::Struct(value)
2600 }
2601}
2602
2603#[derive(Debug)]
2605struct TaskData {
2606 name: Arc<String>,
2608 id: Arc<String>,
2610 container: Option<Arc<String>>,
2612 cpu: f64,
2614 memory: i64,
2616 gpu: Array,
2621 fpga: Array,
2626 disks: Map,
2633 end_time: Option<i64>,
2637 meta: Object,
2639 parameter_meta: Object,
2641 ext: Object,
2643}
2644
2645#[derive(Debug, Clone)]
2649pub struct TaskValue {
2650 data: Arc<TaskData>,
2652 attempt: i64,
2657 return_code: Option<i64>,
2661}
2662
2663impl TaskValue {
2664 pub(crate) fn new_v1<N: TreeNode>(
2666 name: impl Into<String>,
2667 id: impl Into<String>,
2668 definition: &v1::TaskDefinition<N>,
2669 constraints: TaskExecutionConstraints,
2670 attempt: i64,
2671 ) -> Self {
2672 Self {
2673 data: Arc::new(TaskData {
2674 name: Arc::new(name.into()),
2675 id: Arc::new(id.into()),
2676 container: constraints.container.map(Into::into),
2677 cpu: constraints.cpu,
2678 memory: constraints.memory,
2679 gpu: Array::new_unchecked(
2680 ANALYSIS_STDLIB.array_string_type().clone(),
2681 constraints
2682 .gpu
2683 .into_iter()
2684 .map(|v| PrimitiveValue::new_string(v).into())
2685 .collect(),
2686 ),
2687 fpga: Array::new_unchecked(
2688 ANALYSIS_STDLIB.array_string_type().clone(),
2689 constraints
2690 .fpga
2691 .into_iter()
2692 .map(|v| PrimitiveValue::new_string(v).into())
2693 .collect(),
2694 ),
2695 disks: Map::new_unchecked(
2696 ANALYSIS_STDLIB.map_string_int_type().clone(),
2697 constraints
2698 .disks
2699 .into_iter()
2700 .map(|(k, v)| (Some(PrimitiveValue::new_string(k)), v.into()))
2701 .collect(),
2702 ),
2703 end_time: None,
2704 meta: definition
2705 .metadata()
2706 .map(|s| Object::from_v1_metadata(s.items()))
2707 .unwrap_or_else(Object::empty),
2708 parameter_meta: definition
2709 .parameter_metadata()
2710 .map(|s| Object::from_v1_metadata(s.items()))
2711 .unwrap_or_else(Object::empty),
2712 ext: Object::empty(),
2713 }),
2714 attempt,
2715 return_code: None,
2716 }
2717 }
2718
2719 pub fn name(&self) -> &Arc<String> {
2721 &self.data.name
2722 }
2723
2724 pub fn id(&self) -> &Arc<String> {
2726 &self.data.id
2727 }
2728
2729 pub fn container(&self) -> Option<&Arc<String>> {
2731 self.data.container.as_ref()
2732 }
2733
2734 pub fn cpu(&self) -> f64 {
2736 self.data.cpu
2737 }
2738
2739 pub fn memory(&self) -> i64 {
2741 self.data.memory
2742 }
2743
2744 pub fn gpu(&self) -> &Array {
2749 &self.data.gpu
2750 }
2751
2752 pub fn fpga(&self) -> &Array {
2757 &self.data.fpga
2758 }
2759
2760 pub fn disks(&self) -> &Map {
2767 &self.data.disks
2768 }
2769
2770 pub fn attempt(&self) -> i64 {
2775 self.attempt
2776 }
2777
2778 pub fn end_time(&self) -> Option<i64> {
2782 self.data.end_time
2783 }
2784
2785 pub fn return_code(&self) -> Option<i64> {
2789 self.return_code
2790 }
2791
2792 pub fn meta(&self) -> &Object {
2794 &self.data.meta
2795 }
2796
2797 pub fn parameter_meta(&self) -> &Object {
2799 &self.data.parameter_meta
2800 }
2801
2802 pub fn ext(&self) -> &Object {
2804 &self.data.ext
2805 }
2806
2807 pub(crate) fn set_return_code(&mut self, code: i32) {
2809 self.return_code = Some(code as i64);
2810 }
2811
2812 pub(crate) fn set_attempt(&mut self, attempt: i64) {
2814 self.attempt = attempt;
2815 }
2816
2817 pub fn field(&self, name: &str) -> Option<Value> {
2821 match name {
2822 n if n == TASK_FIELD_NAME => {
2823 Some(PrimitiveValue::String(self.data.name.clone()).into())
2824 }
2825 n if n == TASK_FIELD_ID => Some(PrimitiveValue::String(self.data.id.clone()).into()),
2826 n if n == TASK_FIELD_CONTAINER => Some(
2827 self.data
2828 .container
2829 .clone()
2830 .map(|c| PrimitiveValue::String(c).into())
2831 .unwrap_or_else(|| {
2832 Value::new_none(
2833 task_member_type(TASK_FIELD_CONTAINER)
2834 .expect("failed to get task field type"),
2835 )
2836 }),
2837 ),
2838 n if n == TASK_FIELD_CPU => Some(self.data.cpu.into()),
2839 n if n == TASK_FIELD_MEMORY => Some(self.data.memory.into()),
2840 n if n == TASK_FIELD_GPU => Some(self.data.gpu.clone().into()),
2841 n if n == TASK_FIELD_FPGA => Some(self.data.fpga.clone().into()),
2842 n if n == TASK_FIELD_DISKS => Some(self.data.disks.clone().into()),
2843 n if n == TASK_FIELD_ATTEMPT => Some(self.attempt.into()),
2844 n if n == TASK_FIELD_END_TIME => {
2845 Some(self.data.end_time.map(Into::into).unwrap_or_else(|| {
2846 Value::new_none(
2847 task_member_type(TASK_FIELD_END_TIME)
2848 .expect("failed to get task field type"),
2849 )
2850 }))
2851 }
2852 n if n == TASK_FIELD_RETURN_CODE => {
2853 Some(self.return_code.map(Into::into).unwrap_or_else(|| {
2854 Value::new_none(
2855 task_member_type(TASK_FIELD_RETURN_CODE)
2856 .expect("failed to get task field type"),
2857 )
2858 }))
2859 }
2860 n if n == TASK_FIELD_META => Some(self.data.meta.clone().into()),
2861 n if n == TASK_FIELD_PARAMETER_META => Some(self.data.parameter_meta.clone().into()),
2862 n if n == TASK_FIELD_EXT => Some(self.data.ext.clone().into()),
2863 _ => None,
2864 }
2865 }
2866}
2867
2868#[derive(Debug, Clone)]
2872pub struct HintsValue(Object);
2873
2874impl HintsValue {
2875 pub fn as_object(&self) -> &Object {
2877 &self.0
2878 }
2879}
2880
2881impl fmt::Display for HintsValue {
2882 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2883 write!(f, "hints {{")?;
2884
2885 for (i, (k, v)) in self.0.iter().enumerate() {
2886 if i > 0 {
2887 write!(f, ", ")?;
2888 }
2889
2890 write!(f, "{k}: {v}")?;
2891 }
2892
2893 write!(f, "}}")
2894 }
2895}
2896
2897impl From<Object> for HintsValue {
2898 fn from(value: Object) -> Self {
2899 Self(value)
2900 }
2901}
2902
2903#[derive(Debug, Clone)]
2907pub struct InputValue(Object);
2908
2909impl InputValue {
2910 pub fn as_object(&self) -> &Object {
2912 &self.0
2913 }
2914}
2915
2916impl fmt::Display for InputValue {
2917 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2918 write!(f, "input {{")?;
2919
2920 for (i, (k, v)) in self.0.iter().enumerate() {
2921 if i > 0 {
2922 write!(f, ", ")?;
2923 }
2924
2925 write!(f, "{k}: {v}")?;
2926 }
2927
2928 write!(f, "}}")
2929 }
2930}
2931
2932impl From<Object> for InputValue {
2933 fn from(value: Object) -> Self {
2934 Self(value)
2935 }
2936}
2937
2938#[derive(Debug, Clone)]
2942pub struct OutputValue(Object);
2943
2944impl OutputValue {
2945 pub fn as_object(&self) -> &Object {
2947 &self.0
2948 }
2949}
2950
2951impl fmt::Display for OutputValue {
2952 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2953 write!(f, "output {{")?;
2954
2955 for (i, (k, v)) in self.0.iter().enumerate() {
2956 if i > 0 {
2957 write!(f, ", ")?;
2958 }
2959
2960 write!(f, "{k}: {v}")?;
2961 }
2962
2963 write!(f, "}}")
2964 }
2965}
2966
2967impl From<Object> for OutputValue {
2968 fn from(value: Object) -> Self {
2969 Self(value)
2970 }
2971}
2972
2973#[derive(Debug, Clone)]
2977pub struct CallValue {
2978 ty: CallType,
2980 outputs: Arc<Outputs>,
2982}
2983
2984impl CallValue {
2985 pub(crate) fn new_unchecked(ty: CallType, outputs: Arc<Outputs>) -> Self {
2988 Self { ty, outputs }
2989 }
2990
2991 pub fn ty(&self) -> &CallType {
2993 &self.ty
2994 }
2995
2996 pub fn outputs(&self) -> &Outputs {
2998 self.outputs.as_ref()
2999 }
3000}
3001
3002impl fmt::Display for CallValue {
3003 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
3004 write!(f, "call output {{")?;
3005
3006 for (i, (k, v)) in self.outputs.iter().enumerate() {
3007 if i > 0 {
3008 write!(f, ", ")?;
3009 }
3010
3011 write!(f, "{k}: {v}")?;
3012 }
3013
3014 write!(f, "}}")
3015 }
3016}
3017
3018pub struct ValueSerializer<'a> {
3020 value: &'a Value,
3022 allow_pairs: bool,
3025}
3026
3027impl<'a> ValueSerializer<'a> {
3028 pub fn new(value: &'a Value, allow_pairs: bool) -> Self {
3030 Self { value, allow_pairs }
3031 }
3032}
3033
3034impl serde::Serialize for ValueSerializer<'_> {
3035 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3036 where
3037 S: serde::Serializer,
3038 {
3039 use serde::ser::Error;
3040
3041 match &self.value {
3042 Value::None(_) => serializer.serialize_none(),
3043 Value::Primitive(v) => v.serialize(serializer),
3044 Value::Compound(v) => {
3045 CompoundValueSerializer::new(v, self.allow_pairs).serialize(serializer)
3046 }
3047 Value::Task(_)
3048 | Value::Hints(_)
3049 | Value::Input(_)
3050 | Value::Output(_)
3051 | Value::Call(_) => Err(S::Error::custom("value cannot be serialized")),
3052 }
3053 }
3054}
3055
3056pub struct CompoundValueSerializer<'a> {
3058 value: &'a CompoundValue,
3060 allow_pairs: bool,
3063}
3064
3065impl<'a> CompoundValueSerializer<'a> {
3066 pub fn new(value: &'a CompoundValue, allow_pairs: bool) -> Self {
3068 Self { value, allow_pairs }
3069 }
3070}
3071
3072impl serde::Serialize for CompoundValueSerializer<'_> {
3073 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
3074 where
3075 S: serde::Serializer,
3076 {
3077 use serde::ser::Error;
3078
3079 match &self.value {
3080 CompoundValue::Pair(pair) if self.allow_pairs => {
3081 let mut state = serializer.serialize_map(Some(2))?;
3082 let left = ValueSerializer::new(pair.left(), self.allow_pairs);
3083 let right = ValueSerializer::new(pair.right(), self.allow_pairs);
3084 state.serialize_entry("left", &left)?;
3085 state.serialize_entry("right", &right)?;
3086 state.end()
3087 }
3088 CompoundValue::Pair(_) => Err(S::Error::custom("a pair cannot be serialized")),
3089 CompoundValue::Array(v) => {
3090 let mut s = serializer.serialize_seq(Some(v.len()))?;
3091 for v in v.as_slice() {
3092 s.serialize_element(&ValueSerializer::new(v, self.allow_pairs))?;
3093 }
3094
3095 s.end()
3096 }
3097 CompoundValue::Map(v) => {
3098 let ty = v.ty();
3099 let map_type = ty.as_map().expect("type should be a map");
3100 if !map_type
3101 .key_type()
3102 .is_coercible_to(&PrimitiveType::String.into())
3103 {
3104 return Err(S::Error::custom(format!(
3105 "cannot serialize a map of type `{ty}` as the key type cannot be coerced \
3106 to `String`",
3107 )));
3108 }
3109
3110 let mut s = serializer.serialize_map(Some(v.len()))?;
3111 for (k, v) in v.iter() {
3112 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3113 }
3114
3115 s.end()
3116 }
3117 CompoundValue::Object(object) => {
3118 let mut s = serializer.serialize_map(Some(object.len()))?;
3119 for (k, v) in object.iter() {
3120 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3121 }
3122
3123 s.end()
3124 }
3125 CompoundValue::Struct(Struct { members, .. }) => {
3126 let mut s = serializer.serialize_map(Some(members.len()))?;
3127 for (k, v) in members.iter() {
3128 s.serialize_entry(k, &ValueSerializer::new(v, self.allow_pairs))?;
3129 }
3130
3131 s.end()
3132 }
3133 }
3134 }
3135}
3136
3137#[cfg(test)]
3138mod test {
3139 use approx::assert_relative_eq;
3140 use pretty_assertions::assert_eq;
3141 use wdl_analysis::types::ArrayType;
3142 use wdl_analysis::types::MapType;
3143 use wdl_analysis::types::PairType;
3144 use wdl_analysis::types::StructType;
3145 use wdl_ast::Diagnostic;
3146 use wdl_ast::Span;
3147 use wdl_ast::SupportedVersion;
3148
3149 use super::*;
3150 use crate::http::Transferer;
3151 use crate::path::EvaluationPath;
3152
3153 #[test]
3154 fn boolean_coercion() {
3155 assert_eq!(
3157 Value::from(false)
3158 .coerce(None, &PrimitiveType::Boolean.into())
3159 .expect("should coerce")
3160 .unwrap_boolean(),
3161 Value::from(false).unwrap_boolean()
3162 );
3163 assert_eq!(
3165 format!(
3166 "{e:?}",
3167 e = Value::from(true)
3168 .coerce(None, &PrimitiveType::String.into())
3169 .unwrap_err()
3170 ),
3171 "cannot coerce type `Boolean` to type `String`"
3172 );
3173 }
3174
3175 #[test]
3176 fn boolean_display() {
3177 assert_eq!(Value::from(false).to_string(), "false");
3178 assert_eq!(Value::from(true).to_string(), "true");
3179 }
3180
3181 #[test]
3182 fn integer_coercion() {
3183 assert_eq!(
3185 Value::from(12345)
3186 .coerce(None, &PrimitiveType::Integer.into())
3187 .expect("should coerce")
3188 .unwrap_integer(),
3189 Value::from(12345).unwrap_integer()
3190 );
3191 assert_relative_eq!(
3193 Value::from(12345)
3194 .coerce(None, &PrimitiveType::Float.into())
3195 .expect("should coerce")
3196 .unwrap_float(),
3197 Value::from(12345.0).unwrap_float()
3198 );
3199 assert_eq!(
3201 format!(
3202 "{e:?}",
3203 e = Value::from(12345)
3204 .coerce(None, &PrimitiveType::Boolean.into())
3205 .unwrap_err()
3206 ),
3207 "cannot coerce type `Int` to type `Boolean`"
3208 );
3209 }
3210
3211 #[test]
3212 fn integer_display() {
3213 assert_eq!(Value::from(12345).to_string(), "12345");
3214 assert_eq!(Value::from(-12345).to_string(), "-12345");
3215 }
3216
3217 #[test]
3218 fn float_coercion() {
3219 assert_relative_eq!(
3221 Value::from(12345.0)
3222 .coerce(None, &PrimitiveType::Float.into())
3223 .expect("should coerce")
3224 .unwrap_float(),
3225 Value::from(12345.0).unwrap_float()
3226 );
3227 assert_eq!(
3229 format!(
3230 "{e:?}",
3231 e = Value::from(12345.0)
3232 .coerce(None, &PrimitiveType::Integer.into())
3233 .unwrap_err()
3234 ),
3235 "cannot coerce type `Float` to type `Int`"
3236 );
3237 }
3238
3239 #[test]
3240 fn float_display() {
3241 assert_eq!(Value::from(12345.12345).to_string(), "12345.123450");
3242 assert_eq!(Value::from(-12345.12345).to_string(), "-12345.123450");
3243 }
3244
3245 #[test]
3246 fn string_coercion() {
3247 let value = PrimitiveValue::new_string("foo");
3248 assert_eq!(
3250 value
3251 .coerce(None, &PrimitiveType::String.into())
3252 .expect("should coerce"),
3253 value
3254 );
3255 assert_eq!(
3257 value
3258 .coerce(None, &PrimitiveType::File.into())
3259 .expect("should coerce"),
3260 PrimitiveValue::File(value.as_string().expect("should be string").clone().into())
3261 );
3262 assert_eq!(
3264 value
3265 .coerce(None, &PrimitiveType::Directory.into())
3266 .expect("should coerce"),
3267 PrimitiveValue::Directory(value.as_string().expect("should be string").clone().into())
3268 );
3269 assert_eq!(
3271 format!(
3272 "{e:?}",
3273 e = value
3274 .coerce(None, &PrimitiveType::Boolean.into())
3275 .unwrap_err()
3276 ),
3277 "cannot coerce type `String` to type `Boolean`"
3278 );
3279
3280 struct Context;
3281
3282 impl EvaluationContext for Context {
3283 fn version(&self) -> SupportedVersion {
3284 unimplemented!()
3285 }
3286
3287 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3288 unimplemented!()
3289 }
3290
3291 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3292 unimplemented!()
3293 }
3294
3295 fn base_dir(&self) -> &EvaluationPath {
3296 unimplemented!()
3297 }
3298
3299 fn temp_dir(&self) -> &Path {
3300 unimplemented!()
3301 }
3302
3303 fn transferer(&self) -> &dyn Transferer {
3304 unimplemented!()
3305 }
3306
3307 fn host_path(&self, path: &GuestPath) -> Option<HostPath> {
3308 if path.as_str() == "/mnt/task/input/0/path" {
3309 Some(HostPath::new("/some/host/path"))
3310 } else {
3311 None
3312 }
3313 }
3314 }
3315
3316 assert_eq!(
3318 PrimitiveValue::new_string("/mnt/task/input/0/path")
3319 .coerce(Some(&Context), &PrimitiveType::File.into())
3320 .expect("should coerce")
3321 .unwrap_file()
3322 .as_str(),
3323 "/some/host/path"
3324 );
3325
3326 assert_eq!(
3328 value
3329 .coerce(Some(&Context), &PrimitiveType::File.into())
3330 .expect("should coerce")
3331 .unwrap_file()
3332 .as_str(),
3333 "foo"
3334 );
3335
3336 assert_eq!(
3338 PrimitiveValue::new_string("/mnt/task/input/0/path")
3339 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3340 .expect("should coerce")
3341 .unwrap_directory()
3342 .as_str(),
3343 "/some/host/path"
3344 );
3345
3346 assert_eq!(
3348 value
3349 .coerce(Some(&Context), &PrimitiveType::Directory.into())
3350 .expect("should coerce")
3351 .unwrap_directory()
3352 .as_str(),
3353 "foo"
3354 );
3355 }
3356
3357 #[test]
3358 fn string_display() {
3359 let value = PrimitiveValue::new_string("hello world!");
3360 assert_eq!(value.to_string(), "\"hello world!\"");
3361 }
3362
3363 #[test]
3364 fn file_coercion() {
3365 let value = PrimitiveValue::new_file("foo");
3366
3367 assert_eq!(
3369 value
3370 .coerce(None, &PrimitiveType::File.into())
3371 .expect("should coerce"),
3372 value
3373 );
3374 assert_eq!(
3376 value
3377 .coerce(None, &PrimitiveType::String.into())
3378 .expect("should coerce"),
3379 PrimitiveValue::String(value.as_file().expect("should be file").0.clone())
3380 );
3381 assert_eq!(
3383 format!(
3384 "{e:?}",
3385 e = value
3386 .coerce(None, &PrimitiveType::Directory.into())
3387 .unwrap_err()
3388 ),
3389 "cannot coerce type `File` to type `Directory`"
3390 );
3391
3392 struct Context;
3393
3394 impl EvaluationContext for Context {
3395 fn version(&self) -> SupportedVersion {
3396 unimplemented!()
3397 }
3398
3399 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3400 unimplemented!()
3401 }
3402
3403 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3404 unimplemented!()
3405 }
3406
3407 fn base_dir(&self) -> &EvaluationPath {
3408 unimplemented!()
3409 }
3410
3411 fn temp_dir(&self) -> &Path {
3412 unimplemented!()
3413 }
3414
3415 fn transferer(&self) -> &dyn Transferer {
3416 unimplemented!()
3417 }
3418
3419 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3420 if path.as_str() == "/some/host/path" {
3421 Some(GuestPath::new("/mnt/task/input/0/path"))
3422 } else {
3423 None
3424 }
3425 }
3426 }
3427
3428 assert_eq!(
3430 PrimitiveValue::new_file("/some/host/path")
3431 .coerce(Some(&Context), &PrimitiveType::String.into())
3432 .expect("should coerce")
3433 .unwrap_string()
3434 .as_str(),
3435 "/mnt/task/input/0/path"
3436 );
3437
3438 assert_eq!(
3440 value
3441 .coerce(Some(&Context), &PrimitiveType::String.into())
3442 .expect("should coerce")
3443 .unwrap_string()
3444 .as_str(),
3445 "foo"
3446 );
3447 }
3448
3449 #[test]
3450 fn file_display() {
3451 let value = PrimitiveValue::new_file("/foo/bar/baz.txt");
3452 assert_eq!(value.to_string(), "\"/foo/bar/baz.txt\"");
3453 }
3454
3455 #[test]
3456 fn directory_coercion() {
3457 let value = PrimitiveValue::new_directory("foo");
3458
3459 assert_eq!(
3461 value
3462 .coerce(None, &PrimitiveType::Directory.into())
3463 .expect("should coerce"),
3464 value
3465 );
3466 assert_eq!(
3468 value
3469 .coerce(None, &PrimitiveType::String.into())
3470 .expect("should coerce"),
3471 PrimitiveValue::String(value.as_directory().expect("should be directory").0.clone())
3472 );
3473 assert_eq!(
3475 format!(
3476 "{e:?}",
3477 e = value.coerce(None, &PrimitiveType::File.into()).unwrap_err()
3478 ),
3479 "cannot coerce type `Directory` to type `File`"
3480 );
3481
3482 struct Context;
3483
3484 impl EvaluationContext for Context {
3485 fn version(&self) -> SupportedVersion {
3486 unimplemented!()
3487 }
3488
3489 fn resolve_name(&self, _: &str, _: Span) -> Result<Value, Diagnostic> {
3490 unimplemented!()
3491 }
3492
3493 fn resolve_type_name(&self, _: &str, _: Span) -> Result<Type, Diagnostic> {
3494 unimplemented!()
3495 }
3496
3497 fn base_dir(&self) -> &EvaluationPath {
3498 unimplemented!()
3499 }
3500
3501 fn temp_dir(&self) -> &Path {
3502 unimplemented!()
3503 }
3504
3505 fn transferer(&self) -> &dyn Transferer {
3506 unimplemented!()
3507 }
3508
3509 fn guest_path(&self, path: &HostPath) -> Option<GuestPath> {
3510 if path.as_str() == "/some/host/path" {
3511 Some(GuestPath::new("/mnt/task/input/0/path"))
3512 } else {
3513 None
3514 }
3515 }
3516 }
3517
3518 assert_eq!(
3520 PrimitiveValue::new_directory("/some/host/path")
3521 .coerce(Some(&Context), &PrimitiveType::String.into())
3522 .expect("should coerce")
3523 .unwrap_string()
3524 .as_str(),
3525 "/mnt/task/input/0/path"
3526 );
3527
3528 assert_eq!(
3530 value
3531 .coerce(Some(&Context), &PrimitiveType::String.into())
3532 .expect("should coerce")
3533 .unwrap_string()
3534 .as_str(),
3535 "foo"
3536 );
3537 }
3538
3539 #[test]
3540 fn directory_display() {
3541 let value = PrimitiveValue::new_directory("/foo/bar/baz");
3542 assert_eq!(value.to_string(), "\"/foo/bar/baz\"");
3543 }
3544
3545 #[test]
3546 fn none_coercion() {
3547 assert!(
3549 Value::new_none(Type::None)
3550 .coerce(None, &Type::from(PrimitiveType::String).optional())
3551 .expect("should coerce")
3552 .is_none(),
3553 );
3554
3555 assert_eq!(
3557 format!(
3558 "{e:?}",
3559 e = Value::new_none(Type::None)
3560 .coerce(None, &PrimitiveType::String.into())
3561 .unwrap_err()
3562 ),
3563 "cannot coerce `None` to non-optional type `String`"
3564 );
3565 }
3566
3567 #[test]
3568 fn none_display() {
3569 assert_eq!(Value::new_none(Type::None).to_string(), "None");
3570 }
3571
3572 #[test]
3573 fn array_coercion() {
3574 let src_ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3575 let target_ty: Type = ArrayType::new(PrimitiveType::Float).into();
3576
3577 let src: CompoundValue = Array::new(None, src_ty, [1, 2, 3])
3579 .expect("should create array value")
3580 .into();
3581 let target = src.coerce(None, &target_ty).expect("should coerce");
3582 assert_eq!(
3583 target.unwrap_array().to_string(),
3584 "[1.000000, 2.000000, 3.000000]"
3585 );
3586
3587 let target_ty: Type = ArrayType::new(PrimitiveType::String).into();
3589 assert_eq!(
3590 format!("{e:?}", e = src.coerce(None, &target_ty).unwrap_err()),
3591 r#"failed to coerce array element at index 0
3592
3593Caused by:
3594 cannot coerce type `Int` to type `String`"#
3595 );
3596 }
3597
3598 #[test]
3599 fn non_empty_array_coercion() {
3600 let ty: Type = ArrayType::new(PrimitiveType::String).into();
3601 let target_ty: Type = ArrayType::non_empty(PrimitiveType::String).into();
3602
3603 let string = PrimitiveValue::new_string("foo");
3605 let value: Value = Array::new(None, ty.clone(), [string])
3606 .expect("should create array")
3607 .into();
3608 assert!(value.coerce(None, &target_ty).is_ok(), "should coerce");
3609
3610 let value: Value = Array::new::<Value>(None, ty, [])
3612 .expect("should create array")
3613 .into();
3614 assert_eq!(
3615 format!("{e:?}", e = value.coerce(None, &target_ty).unwrap_err()),
3616 "cannot coerce empty array value to non-empty array type `Array[String]+`"
3617 );
3618 }
3619
3620 #[test]
3621 fn array_display() {
3622 let ty: Type = ArrayType::new(PrimitiveType::Integer).into();
3623 let value: Value = Array::new(None, ty, [1, 2, 3])
3624 .expect("should create array")
3625 .into();
3626
3627 assert_eq!(value.to_string(), "[1, 2, 3]");
3628 }
3629
3630 #[test]
3631 fn map_coerce() {
3632 let key1 = PrimitiveValue::new_file("foo");
3633 let value1 = PrimitiveValue::new_string("bar");
3634 let key2 = PrimitiveValue::new_file("baz");
3635 let value2 = PrimitiveValue::new_string("qux");
3636
3637 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String);
3638 let file_to_string: Value = Map::new(None, ty, [(key1, value1), (key2, value2)])
3639 .expect("should create map value")
3640 .into();
3641
3642 let ty = MapType::new(PrimitiveType::String, PrimitiveType::File).into();
3644 let string_to_file = file_to_string
3645 .coerce(None, &ty)
3646 .expect("value should coerce");
3647 assert_eq!(
3648 string_to_file.to_string(),
3649 r#"{"foo": "bar", "baz": "qux"}"#
3650 );
3651
3652 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::File).into();
3654 assert_eq!(
3655 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3656 r#"failed to coerce map key for element at index 0
3657
3658Caused by:
3659 cannot coerce type `String` to type `Int`"#
3660 );
3661
3662 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
3664 assert_eq!(
3665 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3666 r#"failed to coerce map value for element at index 0
3667
3668Caused by:
3669 cannot coerce type `File` to type `Int`"#
3670 );
3671
3672 let ty = StructType::new(
3674 "Foo",
3675 [("foo", PrimitiveType::File), ("baz", PrimitiveType::File)],
3676 )
3677 .into();
3678 let struct_value = string_to_file
3679 .coerce(None, &ty)
3680 .expect("value should coerce");
3681 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
3682
3683 let ty = StructType::new(
3685 "Foo",
3686 [
3687 ("foo", PrimitiveType::String),
3688 ("baz", PrimitiveType::String),
3689 ],
3690 )
3691 .into();
3692 let struct_value = file_to_string
3693 .coerce(None, &ty)
3694 .expect("value should coerce");
3695 assert_eq!(struct_value.to_string(), r#"Foo {foo: "bar", baz: "qux"}"#);
3696
3697 let ty = StructType::new(
3699 "Foo",
3700 [
3701 ("foo", PrimitiveType::File),
3702 ("baz", PrimitiveType::File),
3703 ("qux", PrimitiveType::File),
3704 ],
3705 )
3706 .into();
3707 assert_eq!(
3708 format!("{e:?}", e = string_to_file.coerce(None, &ty).unwrap_err()),
3709 "cannot coerce a map of 2 elements to struct type `Foo` as the struct has 3 members"
3710 );
3711
3712 let object_value = string_to_file
3714 .coerce(None, &Type::Object)
3715 .expect("value should coerce");
3716 assert_eq!(
3717 object_value.to_string(),
3718 r#"object {foo: "bar", baz: "qux"}"#
3719 );
3720
3721 let object_value = file_to_string
3723 .coerce(None, &Type::Object)
3724 .expect("value should coerce");
3725 assert_eq!(
3726 object_value.to_string(),
3727 r#"object {foo: "bar", baz: "qux"}"#
3728 );
3729 }
3730
3731 #[test]
3732 fn map_display() {
3733 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3734 let value: Value = Map::new(None, ty, [(1, true), (2, false)])
3735 .expect("should create map value")
3736 .into();
3737 assert_eq!(value.to_string(), "{1: true, 2: false}");
3738 }
3739
3740 #[test]
3741 fn pair_coercion() {
3742 let left = PrimitiveValue::new_file("foo");
3743 let right = PrimitiveValue::new_string("bar");
3744
3745 let ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3746 let value: Value = Pair::new(None, ty, left, right)
3747 .expect("should create pair value")
3748 .into();
3749
3750 let ty = PairType::new(PrimitiveType::String, PrimitiveType::File).into();
3752 let value = value.coerce(None, &ty).expect("value should coerce");
3753 assert_eq!(value.to_string(), r#"("foo", "bar")"#);
3754
3755 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
3757 assert_eq!(
3758 format!("{e:?}", e = value.coerce(None, &ty).unwrap_err()),
3759 r#"failed to coerce pair's left value
3760
3761Caused by:
3762 cannot coerce type `String` to type `Int`"#
3763 );
3764 }
3765
3766 #[test]
3767 fn pair_display() {
3768 let ty = PairType::new(PrimitiveType::Integer, PrimitiveType::Boolean);
3769 let value: Value = Pair::new(None, ty, 12345, false)
3770 .expect("should create pair value")
3771 .into();
3772 assert_eq!(value.to_string(), "(12345, false)");
3773 }
3774
3775 #[test]
3776 fn struct_coercion() {
3777 let ty = StructType::new(
3778 "Foo",
3779 [
3780 ("foo", PrimitiveType::Float),
3781 ("bar", PrimitiveType::Float),
3782 ("baz", PrimitiveType::Float),
3783 ],
3784 );
3785 let value: Value = Struct::new(None, ty, [("foo", 1.0), ("bar", 2.0), ("baz", 3.0)])
3786 .expect("should create map value")
3787 .into();
3788
3789 let ty = MapType::new(PrimitiveType::String, PrimitiveType::Float).into();
3791 let map_value = value.coerce(None, &ty).expect("value should coerce");
3792 assert_eq!(
3793 map_value.to_string(),
3794 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
3795 );
3796
3797 let ty = MapType::new(PrimitiveType::File, PrimitiveType::Float).into();
3799 let map_value = value.coerce(None, &ty).expect("value should coerce");
3800 assert_eq!(
3801 map_value.to_string(),
3802 r#"{"foo": 1.000000, "bar": 2.000000, "baz": 3.000000}"#
3803 );
3804
3805 let ty = StructType::new(
3807 "Bar",
3808 [
3809 ("foo", PrimitiveType::Float),
3810 ("bar", PrimitiveType::Float),
3811 ("baz", PrimitiveType::Float),
3812 ],
3813 )
3814 .into();
3815 let struct_value = value.coerce(None, &ty).expect("value should coerce");
3816 assert_eq!(
3817 struct_value.to_string(),
3818 r#"Bar {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3819 );
3820
3821 let object_value = value
3823 .coerce(None, &Type::Object)
3824 .expect("value should coerce");
3825 assert_eq!(
3826 object_value.to_string(),
3827 r#"object {foo: 1.000000, bar: 2.000000, baz: 3.000000}"#
3828 );
3829 }
3830
3831 #[test]
3832 fn struct_display() {
3833 let ty = StructType::new(
3834 "Foo",
3835 [
3836 ("foo", PrimitiveType::Float),
3837 ("bar", PrimitiveType::String),
3838 ("baz", PrimitiveType::Integer),
3839 ],
3840 );
3841 let value: Value = Struct::new(
3842 None,
3843 ty,
3844 [
3845 ("foo", Value::from(1.101)),
3846 ("bar", PrimitiveValue::new_string("foo").into()),
3847 ("baz", 1234.into()),
3848 ],
3849 )
3850 .expect("should create map value")
3851 .into();
3852 assert_eq!(
3853 value.to_string(),
3854 r#"Foo {foo: 1.101000, bar: "foo", baz: 1234}"#
3855 );
3856 }
3857
3858 #[test]
3859 fn pair_serialization() {
3860 let pair_ty = PairType::new(PrimitiveType::File, PrimitiveType::String);
3861 let pair: Value = Pair::new(
3862 None,
3863 pair_ty,
3864 PrimitiveValue::new_file("foo"),
3865 PrimitiveValue::new_string("bar"),
3866 )
3867 .expect("should create pair value")
3868 .into();
3869 let value_serializer = ValueSerializer::new(&pair, true);
3871 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3872 assert_eq!(serialized, r#"{"left":"foo","right":"bar"}"#);
3873
3874 let value_serializer = ValueSerializer::new(&pair, false);
3876 assert!(serde_json::to_string(&value_serializer).is_err());
3877
3878 let array_ty = ArrayType::new(PairType::new(PrimitiveType::File, PrimitiveType::String));
3879 let array: Value = Array::new(None, array_ty, [pair])
3880 .expect("should create array value")
3881 .into();
3882
3883 let value_serializer = ValueSerializer::new(&array, true);
3885 let serialized = serde_json::to_string(&value_serializer).expect("should serialize");
3886 assert_eq!(serialized, r#"[{"left":"foo","right":"bar"}]"#);
3887 }
3888}