1use std::collections::HashSet;
4use std::fmt;
5use std::sync::Arc;
6
7use indexmap::IndexMap;
8use wdl_ast::Diagnostic;
9use wdl_ast::Span;
10
11use crate::diagnostics::enum_variant_does_not_coerce_to_type;
12use crate::diagnostics::no_common_inferred_type_for_enum;
13use crate::document::Input;
14use crate::document::Output;
15
16pub mod v1;
17
18pub fn display_types(slice: &[Type]) -> impl fmt::Display + use<'_> {
20 struct Display<'a>(&'a [Type]);
22
23 impl fmt::Display for Display<'_> {
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 for (i, ty) in self.0.iter().enumerate() {
26 if i > 0 {
27 if self.0.len() == 2 {
28 write!(f, " ")?;
29 } else {
30 write!(f, ", ")?;
31 }
32
33 if i == self.0.len() - 1 {
34 write!(f, "or ")?;
35 }
36 }
37
38 write!(f, "type `{ty}`")?;
39 }
40
41 Ok(())
42 }
43 }
44
45 Display(slice)
46}
47
48pub trait TypeNameResolver {
50 fn resolve(&mut self, name: &str, span: Span) -> Result<Type, Diagnostic>;
52}
53
54pub trait Optional {
56 fn is_optional(&self) -> bool;
58
59 fn optional(&self) -> Self;
61
62 fn require(&self) -> Self;
64}
65
66pub trait Coercible {
68 fn is_coercible_to(&self, target: &Self) -> bool;
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
74pub enum PrimitiveType {
75 Boolean,
77 Integer,
79 Float,
81 String,
83 File,
85 Directory,
87}
88
89impl Coercible for PrimitiveType {
90 fn is_coercible_to(&self, target: &Self) -> bool {
91 if self == target {
92 return true;
93 }
94
95 match (self, target) {
96 (Self::String, Self::File) |
98 (Self::String, Self::Directory) |
100 (Self::Integer, Self::Float) |
102 (Self::File, Self::String) |
104 (Self::Directory, Self::String)
106 => true,
107
108 _ => false
110 }
111 }
112}
113
114impl fmt::Display for PrimitiveType {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 Self::Boolean => write!(f, "Boolean")?,
118 Self::Integer => write!(f, "Int")?,
119 Self::Float => write!(f, "Float")?,
120 Self::String => write!(f, "String")?,
121 Self::File => write!(f, "File")?,
122 Self::Directory => write!(f, "Directory")?,
123 }
124
125 Ok(())
126 }
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq)]
134pub enum HiddenType {
135 Hints,
137 Input,
139 Output,
141 TaskPreEvaluation,
144 TaskPostEvaluation,
147 PreviousTaskData,
152}
153
154impl fmt::Display for HiddenType {
155 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
156 match self {
157 Self::Hints => write!(f, "hints"),
158 Self::Input => write!(f, "input"),
159 Self::Output => write!(f, "output"),
160 Self::TaskPreEvaluation | Self::TaskPostEvaluation => write!(f, "task"),
161 Self::PreviousTaskData => write!(f, "task.previous"),
162 }
163 }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
168pub enum Type {
169 Primitive(PrimitiveType, bool),
173 Compound(CompoundType, bool),
177 Object,
179 OptionalObject,
181 Union,
187 None,
189 Hidden(HiddenType),
194 Call(CallType),
196 TypeNameRef(CustomType),
198}
199
200impl Type {
201 pub fn as_primitive(&self) -> Option<PrimitiveType> {
205 match self {
206 Self::Primitive(ty, _) => Some(*ty),
207 _ => None,
208 }
209 }
210
211 pub fn as_compound(&self) -> Option<&CompoundType> {
215 match self {
216 Self::Compound(ty, _) => Some(ty),
217 _ => None,
218 }
219 }
220
221 pub fn as_array(&self) -> Option<&ArrayType> {
225 match self {
226 Self::Compound(CompoundType::Array(ty), _) => Some(ty),
227 _ => None,
228 }
229 }
230
231 pub fn as_pair(&self) -> Option<&PairType> {
235 match self {
236 Self::Compound(CompoundType::Pair(ty), _) => Some(ty),
237 _ => None,
238 }
239 }
240
241 pub fn as_map(&self) -> Option<&MapType> {
245 match self {
246 Self::Compound(CompoundType::Map(ty), _) => Some(ty),
247 _ => None,
248 }
249 }
250
251 pub fn as_struct(&self) -> Option<&StructType> {
255 match self {
256 Self::Compound(CompoundType::Custom(CustomType::Struct(ty)), _) => Some(ty),
257 _ => None,
258 }
259 }
260
261 pub fn as_enum(&self) -> Option<&EnumType> {
265 match self {
266 Self::Compound(CompoundType::Custom(CustomType::Enum(ty)), _) => Some(ty),
267 _ => None,
268 }
269 }
270
271 pub fn as_custom(&self) -> Option<&CustomType> {
275 match self {
276 Self::Compound(CompoundType::Custom(ty), _) => Some(ty),
277 _ => None,
278 }
279 }
280
281 pub fn as_type_name_ref(&self) -> Option<&CustomType> {
285 match self {
286 Self::TypeNameRef(custom_ty) => Some(custom_ty),
287 _ => None,
288 }
289 }
290
291 pub fn as_call(&self) -> Option<&CallType> {
295 match self {
296 Self::Call(ty) => Some(ty),
297 _ => None,
298 }
299 }
300
301 pub fn is_union(&self) -> bool {
303 matches!(self, Type::Union)
304 }
305
306 pub fn is_none(&self) -> bool {
308 matches!(self, Type::None)
309 }
310
311 pub fn promote_scatter(&self) -> Self {
316 if let Self::Call(ty) = self {
318 return Self::Call(ty.promote_scatter());
319 }
320
321 Type::Compound(ArrayType::new(self.clone()).into(), false)
322 }
323
324 pub fn common_type(&self, other: &Type) -> Option<Type> {
328 if other.is_union() {
330 return Some(self.clone());
331 }
332
333 if self.is_union() {
335 return Some(other.clone());
336 }
337
338 if other.is_none() {
341 return Some(self.optional());
342 }
343
344 if self.is_none() {
346 return Some(other.optional());
347 }
348
349 if other.is_coercible_to(self) {
351 return Some(self.clone());
352 }
353
354 if self.is_coercible_to(other) {
356 return Some(other.clone());
357 }
358
359 if let (Some(this), Some(other)) = (self.as_compound(), other.as_compound())
361 && let Some(ty) = this.common_type(other)
362 {
363 return Some(Self::Compound(ty, self.is_optional()));
364 }
365
366 if let (Some(this), Some(other)) = (self.as_call(), self.as_call())
368 && this == other
369 {
370 return Some(Self::Call(this.clone()));
371 }
372
373 None
374 }
375
376 pub fn type_name_ref(&self) -> Option<Type> {
380 match self {
381 Type::Compound(CompoundType::Custom(ty), _) => Some(Type::TypeNameRef(ty.clone())),
382 _ => None,
383 }
384 }
385}
386
387impl fmt::Display for Type {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 match self {
390 Self::Primitive(ty, optional) => {
391 ty.fmt(f)?;
392 if *optional { write!(f, "?") } else { Ok(()) }
393 }
394 Self::Compound(ty, optional) => {
395 ty.fmt(f)?;
396 if *optional { write!(f, "?") } else { Ok(()) }
397 }
398 Self::Object => {
399 write!(f, "Object")
400 }
401 Self::OptionalObject => {
402 write!(f, "Object?")
403 }
404 Self::Union => write!(f, "Union"),
405 Self::None => write!(f, "None"),
406 Self::Hidden(ty) => ty.fmt(f),
407 Self::Call(ty) => ty.fmt(f),
408 Self::TypeNameRef(ty) => ty.fmt(f),
409 }
410 }
411}
412
413impl Optional for Type {
414 fn is_optional(&self) -> bool {
415 match self {
416 Self::Primitive(_, optional) => *optional,
417 Self::Compound(_, optional) => *optional,
418 Self::OptionalObject | Self::None => true,
419 Self::Object | Self::Union | Self::Hidden(_) | Self::Call(_) | Self::TypeNameRef(_) => {
420 false
421 }
422 }
423 }
424
425 fn optional(&self) -> Self {
426 match self {
427 Self::Primitive(ty, _) => Self::Primitive(*ty, true),
428 Self::Compound(ty, _) => Self::Compound(ty.clone(), true),
429 Self::Object => Self::OptionalObject,
430 Self::Union => Self::None,
431 Self::Call(ty) => Self::Call(ty.optional()),
432 ty => ty.clone(),
433 }
434 }
435
436 fn require(&self) -> Self {
437 match self {
438 Self::Primitive(ty, _) => Self::Primitive(*ty, false),
439 Self::Compound(ty, _) => Self::Compound(ty.clone(), false),
440 Self::OptionalObject => Self::Object,
441 Self::None => Self::Union,
442 ty => ty.clone(),
443 }
444 }
445}
446
447impl Coercible for Type {
448 fn is_coercible_to(&self, target: &Self) -> bool {
449 if self.eq(target) {
450 return true;
451 }
452
453 match (self, target) {
454 (Self::Primitive(src, src_opt), Self::Primitive(target, target_opt)) => {
455 if *src_opt && !*target_opt {
457 return false;
458 }
459
460 src.is_coercible_to(target)
461 }
462 (Self::Compound(src, src_opt), Self::Compound(target, target_opt)) => {
463 if *src_opt && !*target_opt {
465 return false;
466 }
467
468 src.is_coercible_to(target)
469 }
470
471 (Self::Object, Self::Object)
473 | (Self::Object, Self::OptionalObject)
474 | (Self::OptionalObject, Self::OptionalObject) => true,
475
476 (Self::Compound(src, false), Self::Object)
480 | (Self::Compound(src, false), Self::OptionalObject)
481 | (Self::Compound(src, _), Self::OptionalObject) => match src {
482 CompoundType::Map(src) => src
483 .key_type()
484 .is_coercible_to(&PrimitiveType::String.into()),
485 CompoundType::Custom(CustomType::Struct(_)) => true,
486 _ => false,
487 },
488
489 (Self::Object, Self::Compound(target, _))
495 | (Self::OptionalObject, Self::Compound(target, true)) => {
496 match target {
497 CompoundType::Map(target) => {
498 Type::from(PrimitiveType::String).is_coercible_to(target.key_type())
499 }
500 CompoundType::Custom(CustomType::Struct(_)) => {
501 true
503 }
504 _ => false,
505 }
506 }
507
508 (Self::Union, _) | (_, Self::Union) => true,
510
511 (Self::None, ty) if ty.is_optional() => true,
513
514 (
517 Self::Primitive(PrimitiveType::String, _),
518 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
519 )
520 | (
521 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
522 Self::Primitive(PrimitiveType::String, _),
523 ) => true,
524
525 _ => false,
527 }
528 }
529}
530
531impl From<PrimitiveType> for Type {
532 fn from(value: PrimitiveType) -> Self {
533 Self::Primitive(value, false)
534 }
535}
536
537impl From<CompoundType> for Type {
538 fn from(value: CompoundType) -> Self {
539 Self::Compound(value, false)
540 }
541}
542
543impl From<ArrayType> for Type {
544 fn from(value: ArrayType) -> Self {
545 Self::Compound(value.into(), false)
546 }
547}
548
549impl From<PairType> for Type {
550 fn from(value: PairType) -> Self {
551 Self::Compound(value.into(), false)
552 }
553}
554
555impl From<MapType> for Type {
556 fn from(value: MapType) -> Self {
557 Self::Compound(value.into(), false)
558 }
559}
560
561impl From<StructType> for Type {
562 fn from(value: StructType) -> Self {
563 Self::Compound(value.into(), false)
564 }
565}
566
567impl From<EnumType> for Type {
568 fn from(value: EnumType) -> Self {
569 Self::Compound(value.into(), false)
570 }
571}
572
573impl From<CallType> for Type {
574 fn from(value: CallType) -> Self {
575 Self::Call(value)
576 }
577}
578
579#[derive(Debug, Clone, PartialEq, Eq)]
581pub enum CustomType {
582 Struct(StructType),
584 Enum(EnumType),
586}
587
588impl CustomType {
589 pub fn name(&self) -> &Arc<String> {
591 match self {
592 Self::Struct(ty) => ty.name(),
593 Self::Enum(ty) => ty.name(),
594 }
595 }
596
597 pub fn as_struct(&self) -> Option<&StructType> {
601 match self {
602 Self::Struct(ty) => Some(ty),
603 _ => None,
604 }
605 }
606
607 pub fn as_enum(&self) -> Option<&EnumType> {
611 match self {
612 Self::Enum(ty) => Some(ty),
613 _ => None,
614 }
615 }
616}
617
618impl std::fmt::Display for CustomType {
619 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620 match self {
621 CustomType::Struct(ty) => ty.fmt(f),
622 CustomType::Enum(ty) => ty.fmt(f),
623 }
624 }
625}
626
627impl From<StructType> for CustomType {
628 fn from(value: StructType) -> Self {
629 Self::Struct(value)
630 }
631}
632
633impl From<EnumType> for CustomType {
634 fn from(value: EnumType) -> Self {
635 Self::Enum(value)
636 }
637}
638
639#[derive(Debug, Clone, PartialEq, Eq)]
641pub enum CompoundType {
642 Array(ArrayType),
644 Pair(PairType),
646 Map(MapType),
648 Custom(CustomType),
650}
651
652impl CompoundType {
653 pub fn as_array(&self) -> Option<&ArrayType> {
657 match self {
658 Self::Array(ty) => Some(ty),
659 _ => None,
660 }
661 }
662
663 pub fn as_pair(&self) -> Option<&PairType> {
667 match self {
668 Self::Pair(ty) => Some(ty),
669 _ => None,
670 }
671 }
672
673 pub fn as_map(&self) -> Option<&MapType> {
677 match self {
678 Self::Map(ty) => Some(ty),
679 _ => None,
680 }
681 }
682
683 pub fn as_struct(&self) -> Option<&StructType> {
687 match self {
688 Self::Custom(ty) => ty.as_struct(),
689 _ => None,
690 }
691 }
692
693 pub fn as_enum(&self) -> Option<&EnumType> {
697 match self {
698 Self::Custom(ty) => ty.as_enum(),
699 _ => None,
700 }
701 }
702
703 pub fn as_custom(&self) -> Option<&CustomType> {
707 match self {
708 Self::Custom(ty) => Some(ty),
709 _ => None,
710 }
711 }
712
713 fn common_type(&self, other: &Self) -> Option<CompoundType> {
718 match (self, other) {
721 (Self::Array(this), Self::Array(other)) => {
722 let element_type = this.element_type.common_type(&other.element_type)?;
723 Some(ArrayType::new(element_type).into())
724 }
725 (Self::Pair(this), Self::Pair(other)) => {
726 let left_type = this.left_type().common_type(other.left_type())?;
727 let right_type = this.right_type().common_type(other.right_type())?;
728 Some(PairType::new(left_type, right_type).into())
729 }
730 (Self::Map(this), Self::Map(other)) => {
731 let key_type = this.key_type().common_type(other.key_type())?;
732 let value_type = this.value_type().common_type(other.value_type())?;
733 Some(MapType::new(key_type, value_type).into())
734 }
735 _ => None,
736 }
737 }
738}
739
740impl fmt::Display for CompoundType {
741 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
742 match self {
743 Self::Array(ty) => ty.fmt(f),
744 Self::Pair(ty) => ty.fmt(f),
745 Self::Map(ty) => ty.fmt(f),
746 Self::Custom(ty) => match ty {
747 CustomType::Struct(ty) => ty.fmt(f),
748 CustomType::Enum(ty) => ty.fmt(f),
749 },
750 }
751 }
752}
753
754impl Coercible for CompoundType {
755 fn is_coercible_to(&self, target: &Self) -> bool {
756 match (self, target) {
757 (Self::Array(src), Self::Array(target)) => src.is_coercible_to(target),
760
761 (Self::Pair(src), Self::Pair(target)) => src.is_coercible_to(target),
764
765 (Self::Map(src), Self::Map(target)) => src.is_coercible_to(target),
768
769 (Self::Custom(CustomType::Struct(src)), Self::Custom(CustomType::Struct(target))) => {
772 src.is_coercible_to(target)
773 }
774
775 (Self::Map(src), Self::Custom(CustomType::Struct(target))) => {
778 if !src
779 .key_type()
780 .is_coercible_to(&PrimitiveType::String.into())
781 {
782 return false;
783 }
784
785 if !target
787 .members
788 .values()
789 .all(|ty| src.value_type().is_coercible_to(ty))
790 {
791 return false;
792 }
793
794 true
796 }
797
798 (Self::Custom(CustomType::Struct(src)), Self::Map(target)) => {
801 if !Type::from(PrimitiveType::String).is_coercible_to(target.key_type()) {
802 return false;
803 }
804
805 if !src
807 .members
808 .values()
809 .all(|ty| ty.is_coercible_to(target.value_type()))
810 {
811 return false;
812 }
813
814 true
815 }
816
817 _ => false,
818 }
819 }
820}
821
822impl From<ArrayType> for CompoundType {
823 fn from(value: ArrayType) -> Self {
824 Self::Array(value)
825 }
826}
827
828impl From<PairType> for CompoundType {
829 fn from(value: PairType) -> Self {
830 Self::Pair(value)
831 }
832}
833
834impl From<MapType> for CompoundType {
835 fn from(value: MapType) -> Self {
836 Self::Map(value)
837 }
838}
839
840impl From<StructType> for CompoundType {
841 fn from(value: StructType) -> Self {
842 Self::Custom(CustomType::Struct(value))
843 }
844}
845
846impl From<EnumType> for CompoundType {
847 fn from(value: EnumType) -> Self {
848 Self::Custom(CustomType::Enum(value))
849 }
850}
851
852#[derive(Debug, Clone, PartialEq, Eq)]
854pub struct ArrayType {
855 element_type: Arc<Type>,
857 non_empty: bool,
859}
860
861impl ArrayType {
862 pub fn new(element_type: impl Into<Type>) -> Self {
864 Self {
865 element_type: Arc::new(element_type.into()),
866 non_empty: false,
867 }
868 }
869
870 pub fn non_empty(element_type: impl Into<Type>) -> Self {
872 Self {
873 element_type: Arc::new(element_type.into()),
874 non_empty: true,
875 }
876 }
877
878 pub fn element_type(&self) -> &Type {
880 &self.element_type
881 }
882
883 pub fn is_non_empty(&self) -> bool {
885 self.non_empty
886 }
887
888 pub fn unqualified(self) -> ArrayType {
890 Self {
891 element_type: self.element_type,
892 non_empty: false,
893 }
894 }
895}
896
897impl fmt::Display for ArrayType {
898 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
899 write!(f, "Array[{ty}]", ty = self.element_type)?;
900
901 if self.non_empty {
902 write!(f, "+")?;
903 }
904
905 Ok(())
906 }
907}
908
909impl Coercible for ArrayType {
910 fn is_coercible_to(&self, target: &Self) -> bool {
911 self.element_type.is_coercible_to(&target.element_type)
913 }
914}
915
916#[derive(Debug, Clone, PartialEq, Eq)]
918pub struct PairType(Arc<(Type, Type)>);
919
920impl PairType {
921 pub fn new(left_type: impl Into<Type>, right_type: impl Into<Type>) -> Self {
923 Self(Arc::new((left_type.into(), right_type.into())))
924 }
925
926 pub fn left_type(&self) -> &Type {
928 &self.0.0
929 }
930
931 pub fn right_type(&self) -> &Type {
933 &self.0.1
934 }
935}
936
937impl fmt::Display for PairType {
938 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
939 write!(
940 f,
941 "Pair[{left}, {right}]",
942 left = self.left_type(),
943 right = self.right_type()
944 )?;
945
946 Ok(())
947 }
948}
949
950impl Coercible for PairType {
951 fn is_coercible_to(&self, target: &Self) -> bool {
952 self.left_type().is_coercible_to(target.left_type())
953 && self.right_type().is_coercible_to(target.right_type())
954 }
955}
956
957#[derive(Debug, Clone, PartialEq, Eq)]
959pub struct MapType(Arc<(Type, Type)>);
960
961impl MapType {
962 pub fn new(key_type: impl Into<Type>, value_type: impl Into<Type>) -> Self {
968 let key_type = key_type.into();
969 assert!(
970 key_type.is_union() || key_type.as_primitive().is_some(),
971 "map key type `{key_type}` is not primitive"
972 );
973 Self(Arc::new((key_type, value_type.into())))
974 }
975
976 pub fn key_type(&self) -> &Type {
978 &self.0.0
979 }
980
981 pub fn value_type(&self) -> &Type {
983 &self.0.1
984 }
985}
986
987impl fmt::Display for MapType {
988 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
989 write!(
990 f,
991 "Map[{key}, {value}]",
992 key = self.key_type(),
993 value = self.value_type()
994 )?;
995
996 Ok(())
997 }
998}
999
1000impl Coercible for MapType {
1001 fn is_coercible_to(&self, target: &Self) -> bool {
1002 self.key_type().is_coercible_to(target.key_type())
1003 && self.value_type().is_coercible_to(target.value_type())
1004 }
1005}
1006
1007#[derive(Debug, Clone, PartialEq, Eq)]
1009pub struct StructType {
1010 name: Arc<String>,
1012 members: Arc<IndexMap<String, Type>>,
1014}
1015
1016impl StructType {
1017 pub fn new<N, T>(name: impl Into<String>, members: impl IntoIterator<Item = (N, T)>) -> Self
1019 where
1020 N: Into<String>,
1021 T: Into<Type>,
1022 {
1023 Self {
1024 name: Arc::new(name.into()),
1025 members: Arc::new(
1026 members
1027 .into_iter()
1028 .map(|(n, ty)| (n.into(), ty.into()))
1029 .collect(),
1030 ),
1031 }
1032 }
1033
1034 pub fn name(&self) -> &Arc<String> {
1036 &self.name
1037 }
1038
1039 pub fn members(&self) -> &IndexMap<String, Type> {
1041 &self.members
1042 }
1043}
1044
1045impl fmt::Display for StructType {
1046 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1047 write!(f, "{name}", name = self.name)
1048 }
1049}
1050
1051impl Coercible for StructType {
1052 fn is_coercible_to(&self, target: &Self) -> bool {
1053 if self.members.len() != target.members.len() {
1054 return false;
1055 }
1056
1057 self.members.iter().all(|(k, v)| {
1058 target
1059 .members
1060 .get(k)
1061 .map(|target| v.is_coercible_to(target))
1062 .unwrap_or(false)
1063 })
1064 }
1065}
1066
1067#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
1069pub struct EnumVariantCacheKey {
1070 enum_index: usize,
1072 variant_index: usize,
1074}
1075
1076impl EnumVariantCacheKey {
1077 pub(crate) fn new(enum_index: usize, variant_index: usize) -> Self {
1079 Self {
1080 enum_index,
1081 variant_index,
1082 }
1083 }
1084}
1085
1086#[derive(Debug, Clone, PartialEq, Eq)]
1088pub struct EnumType {
1089 name: Arc<String>,
1091 inner_value_type: Arc<Type>,
1093 variants: Arc<[String]>,
1095}
1096
1097impl EnumType {
1098 pub fn new(
1104 enum_name: impl Into<String>,
1105 enum_span: Span,
1106 explicit_inner_type: Type,
1107 variants: Vec<(String, Type)>,
1108 variant_spans: &[Span],
1109 ) -> Result<Self, Diagnostic> {
1110 assert_eq!(variants.len(), variant_spans.len());
1111 let enum_name = enum_name.into();
1112 let mut results = Vec::with_capacity(variants.len());
1113
1114 for (variant_idx, (variant_name, variant_type)) in variants.iter().enumerate() {
1116 if !variant_type.is_coercible_to(&explicit_inner_type) {
1117 return Err(enum_variant_does_not_coerce_to_type(
1118 &enum_name,
1119 enum_span,
1120 variant_name,
1121 variant_spans[variant_idx],
1122 &explicit_inner_type,
1123 variant_type,
1124 ));
1125 }
1126
1127 results.push(variant_name.to_owned());
1128 }
1129
1130 Ok(Self {
1131 name: Arc::new(enum_name),
1132 inner_value_type: explicit_inner_type.into(),
1133 variants: results.into(),
1134 })
1135 }
1136
1137 pub fn infer(
1145 enum_name: impl Into<String>,
1146 variants: Vec<(String, Type)>,
1147 variant_spans: &[Span],
1148 ) -> Result<Self, Diagnostic> {
1149 assert_eq!(variants.len(), variant_spans.len());
1150 let enum_name = enum_name.into();
1151
1152 let mut common_ty: Option<Type> = None;
1153 let mut names = Vec::with_capacity(variants.len());
1154 for (i, (name, variant_ty)) in variants.into_iter().enumerate() {
1155 match common_ty {
1156 Some(current_common_ty) => match current_common_ty.common_type(&variant_ty) {
1157 Some(new_common_ty) => {
1158 common_ty = Some(new_common_ty);
1159 }
1160 None => {
1161 return Err(no_common_inferred_type_for_enum(
1162 &enum_name,
1163 ¤t_common_ty,
1164 variant_spans[i - 1],
1165 &variant_ty,
1166 variant_spans[i],
1167 ));
1168 }
1169 },
1170 None => common_ty = Some(variant_ty),
1171 }
1172
1173 names.push(name);
1174 }
1175
1176 Ok(Self {
1177 name: Arc::new(enum_name),
1178 inner_value_type: common_ty.unwrap_or(Type::Union).into(),
1179 variants: names.into(),
1180 })
1181 }
1182
1183 pub fn name(&self) -> &Arc<String> {
1185 &self.name
1186 }
1187
1188 pub fn inner_value_type(&self) -> &Type {
1190 &self.inner_value_type
1191 }
1192
1193 pub fn variants(&self) -> &[String] {
1195 &self.variants
1196 }
1197}
1198
1199impl fmt::Display for EnumType {
1200 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1201 write!(f, "{name}", name = self.name)
1202 }
1203}
1204
1205impl Coercible for EnumType {
1206 fn is_coercible_to(&self, _: &Self) -> bool {
1207 false
1208 }
1209}
1210
1211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1213pub enum CallKind {
1214 Task,
1216 Workflow,
1218}
1219
1220impl fmt::Display for CallKind {
1221 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1222 match self {
1223 Self::Task => write!(f, "task"),
1224 Self::Workflow => write!(f, "workflow"),
1225 }
1226 }
1227}
1228
1229#[derive(Debug, Clone, Eq)]
1231pub struct CallType {
1232 kind: CallKind,
1234 namespace: Option<Arc<String>>,
1236 name: Arc<String>,
1238 specified: Arc<HashSet<String>>,
1240 inputs: Arc<IndexMap<String, Input>>,
1242 outputs: Arc<IndexMap<String, Output>>,
1244}
1245
1246impl CallType {
1247 pub(crate) fn new(
1249 kind: CallKind,
1250 name: impl Into<String>,
1251 specified: Arc<HashSet<String>>,
1252 inputs: Arc<IndexMap<String, Input>>,
1253 outputs: Arc<IndexMap<String, Output>>,
1254 ) -> Self {
1255 Self {
1256 kind,
1257 namespace: None,
1258 name: Arc::new(name.into()),
1259 specified,
1260 inputs,
1261 outputs,
1262 }
1263 }
1264
1265 pub(crate) fn namespaced(
1268 kind: CallKind,
1269 namespace: impl Into<String>,
1270 name: impl Into<String>,
1271 specified: Arc<HashSet<String>>,
1272 inputs: Arc<IndexMap<String, Input>>,
1273 outputs: Arc<IndexMap<String, Output>>,
1274 ) -> Self {
1275 Self {
1276 kind,
1277 namespace: Some(Arc::new(namespace.into())),
1278 name: Arc::new(name.into()),
1279 specified,
1280 inputs,
1281 outputs,
1282 }
1283 }
1284
1285 pub fn kind(&self) -> CallKind {
1287 self.kind
1288 }
1289
1290 pub fn namespace(&self) -> Option<&str> {
1294 self.namespace.as_ref().map(|ns| ns.as_str())
1295 }
1296
1297 pub fn name(&self) -> &str {
1299 &self.name
1300 }
1301
1302 pub fn specified(&self) -> &HashSet<String> {
1304 &self.specified
1305 }
1306
1307 pub fn inputs(&self) -> &IndexMap<String, Input> {
1309 &self.inputs
1310 }
1311
1312 pub fn outputs(&self) -> &IndexMap<String, Output> {
1314 &self.outputs
1315 }
1316
1317 pub fn optional(&self) -> Self {
1319 let mut ty = self.clone();
1320 for output in Arc::make_mut(&mut ty.outputs).values_mut() {
1321 *output = Output::new(output.ty().optional(), output.name_span());
1322 }
1323
1324 ty
1325 }
1326
1327 pub fn promote_scatter(&self) -> Self {
1329 let mut ty = self.clone();
1330 for output in Arc::make_mut(&mut ty.outputs).values_mut() {
1331 *output = Output::new(output.ty().promote_scatter(), output.name_span());
1332 }
1333
1334 ty
1335 }
1336}
1337
1338impl Coercible for CallType {
1339 fn is_coercible_to(&self, _: &Self) -> bool {
1340 false
1342 }
1343}
1344
1345impl fmt::Display for CallType {
1346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1347 if let Some(ns) = &self.namespace {
1348 write!(
1349 f,
1350 "call to {kind} `{ns}.{name}`",
1351 kind = self.kind,
1352 name = self.name,
1353 )
1354 } else {
1355 write!(
1356 f,
1357 "call to {kind} `{name}`",
1358 kind = self.kind,
1359 name = self.name,
1360 )
1361 }
1362 }
1363}
1364
1365impl PartialEq for CallType {
1366 fn eq(&self, other: &Self) -> bool {
1367 std::ptr::eq(self, other)
1369 }
1370}
1371
1372#[cfg(test)]
1373mod test {
1374 use pretty_assertions::assert_eq;
1375
1376 use super::*;
1377
1378 #[test]
1379 fn primitive_type_display() {
1380 assert_eq!(PrimitiveType::Boolean.to_string(), "Boolean");
1381 assert_eq!(PrimitiveType::Integer.to_string(), "Int");
1382 assert_eq!(PrimitiveType::Float.to_string(), "Float");
1383 assert_eq!(PrimitiveType::String.to_string(), "String");
1384 assert_eq!(PrimitiveType::File.to_string(), "File");
1385 assert_eq!(PrimitiveType::Directory.to_string(), "Directory");
1386 assert_eq!(
1387 Type::from(PrimitiveType::Boolean).optional().to_string(),
1388 "Boolean?"
1389 );
1390 assert_eq!(
1391 Type::from(PrimitiveType::Integer).optional().to_string(),
1392 "Int?"
1393 );
1394 assert_eq!(
1395 Type::from(PrimitiveType::Float).optional().to_string(),
1396 "Float?"
1397 );
1398 assert_eq!(
1399 Type::from(PrimitiveType::String).optional().to_string(),
1400 "String?"
1401 );
1402 assert_eq!(
1403 Type::from(PrimitiveType::File).optional().to_string(),
1404 "File?"
1405 );
1406 assert_eq!(
1407 Type::from(PrimitiveType::Directory).optional().to_string(),
1408 "Directory?"
1409 );
1410 }
1411
1412 #[test]
1413 fn array_type_display() {
1414 assert_eq!(
1415 ArrayType::new(PrimitiveType::String).to_string(),
1416 "Array[String]"
1417 );
1418 assert_eq!(
1419 ArrayType::non_empty(PrimitiveType::String).to_string(),
1420 "Array[String]+"
1421 );
1422
1423 let ty: Type = ArrayType::new(ArrayType::new(PrimitiveType::String)).into();
1424 assert_eq!(ty.to_string(), "Array[Array[String]]");
1425
1426 let ty = Type::from(ArrayType::non_empty(
1427 Type::from(ArrayType::non_empty(
1428 Type::from(PrimitiveType::String).optional(),
1429 ))
1430 .optional(),
1431 ))
1432 .optional();
1433 assert_eq!(ty.to_string(), "Array[Array[String?]+?]+?");
1434 }
1435
1436 #[test]
1437 fn pair_type_display() {
1438 assert_eq!(
1439 PairType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1440 "Pair[String, Boolean]"
1441 );
1442
1443 let ty: Type = PairType::new(
1444 ArrayType::new(PrimitiveType::String),
1445 ArrayType::new(PrimitiveType::String),
1446 )
1447 .into();
1448 assert_eq!(ty.to_string(), "Pair[Array[String], Array[String]]");
1449
1450 let ty = Type::from(PairType::new(
1451 Type::from(ArrayType::non_empty(
1452 Type::from(PrimitiveType::File).optional(),
1453 ))
1454 .optional(),
1455 Type::from(ArrayType::non_empty(
1456 Type::from(PrimitiveType::File).optional(),
1457 ))
1458 .optional(),
1459 ))
1460 .optional();
1461 assert_eq!(ty.to_string(), "Pair[Array[File?]+?, Array[File?]+?]?");
1462 }
1463
1464 #[test]
1465 fn map_type_display() {
1466 assert_eq!(
1467 MapType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1468 "Map[String, Boolean]"
1469 );
1470
1471 let ty: Type = MapType::new(
1472 PrimitiveType::Boolean,
1473 ArrayType::new(PrimitiveType::String),
1474 )
1475 .into();
1476 assert_eq!(ty.to_string(), "Map[Boolean, Array[String]]");
1477
1478 let ty: Type = Type::from(MapType::new(
1479 PrimitiveType::String,
1480 Type::from(ArrayType::non_empty(
1481 Type::from(PrimitiveType::File).optional(),
1482 ))
1483 .optional(),
1484 ))
1485 .optional();
1486 assert_eq!(ty.to_string(), "Map[String, Array[File?]+?]?");
1487 }
1488
1489 #[test]
1490 fn struct_type_display() {
1491 assert_eq!(
1492 StructType::new("Foobar", std::iter::empty::<(String, Type)>()).to_string(),
1493 "Foobar"
1494 );
1495 }
1496
1497 #[test]
1498 fn object_type_display() {
1499 assert_eq!(Type::Object.to_string(), "Object");
1500 assert_eq!(Type::OptionalObject.to_string(), "Object?");
1501 }
1502
1503 #[test]
1504 fn union_type_display() {
1505 assert_eq!(Type::Union.to_string(), "Union");
1506 }
1507
1508 #[test]
1509 fn none_type_display() {
1510 assert_eq!(Type::None.to_string(), "None");
1511 }
1512
1513 #[test]
1514 fn primitive_type_coercion() {
1515 for ty in [
1518 Type::from(PrimitiveType::Boolean),
1519 PrimitiveType::Directory.into(),
1520 PrimitiveType::File.into(),
1521 PrimitiveType::Float.into(),
1522 PrimitiveType::Integer.into(),
1523 PrimitiveType::String.into(),
1524 ] {
1525 assert!(ty.is_coercible_to(&ty));
1526 assert!(ty.optional().is_coercible_to(&ty.optional()));
1527 assert!(ty.is_coercible_to(&ty.optional()));
1528 assert!(!ty.optional().is_coercible_to(&ty));
1529 }
1530
1531 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::File));
1533 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::Directory));
1534 assert!(PrimitiveType::Integer.is_coercible_to(&PrimitiveType::Float));
1535 assert!(PrimitiveType::File.is_coercible_to(&PrimitiveType::String));
1536 assert!(PrimitiveType::Directory.is_coercible_to(&PrimitiveType::String));
1537 assert!(!PrimitiveType::Float.is_coercible_to(&PrimitiveType::Integer));
1538 }
1539
1540 #[test]
1541 fn object_type_coercion() {
1542 assert!(Type::Object.is_coercible_to(&Type::Object));
1543 assert!(Type::Object.is_coercible_to(&Type::OptionalObject));
1544 assert!(Type::OptionalObject.is_coercible_to(&Type::OptionalObject));
1545 assert!(!Type::OptionalObject.is_coercible_to(&Type::Object));
1546
1547 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1549 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1550
1551 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1553 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1554
1555 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
1557 assert!(!Type::Object.is_coercible_to(&ty));
1558
1559 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1561 assert!(Type::Object.is_coercible_to(&ty));
1562
1563 let ty = Type::from(MapType::new(PrimitiveType::File, PrimitiveType::String)).optional();
1565 assert!(Type::Object.is_coercible_to(&ty));
1566
1567 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1569 assert!(Type::OptionalObject.is_coercible_to(&ty));
1570
1571 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1573 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1574
1575 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1577 assert!(Type::Object.is_coercible_to(&ty));
1578
1579 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1581 assert!(Type::Object.is_coercible_to(&ty));
1582
1583 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1585 assert!(Type::OptionalObject.is_coercible_to(&ty));
1586
1587 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1589 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1590 }
1591
1592 #[test]
1593 fn array_type_coercion() {
1594 assert!(
1596 ArrayType::new(PrimitiveType::String)
1597 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1598 );
1599 assert!(
1600 ArrayType::new(PrimitiveType::File)
1601 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1602 );
1603 assert!(
1604 ArrayType::new(PrimitiveType::String)
1605 .is_coercible_to(&ArrayType::new(PrimitiveType::File))
1606 );
1607
1608 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1610 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1611 assert!(type1.is_coercible_to(&type2));
1612 assert!(!type2.is_coercible_to(&type1));
1613
1614 let type1: Type = ArrayType::new(type1).into();
1616 let type2 = ArrayType::new(type2).into();
1617 assert!(type1.is_coercible_to(&type2));
1618 assert!(!type2.is_coercible_to(&type1));
1619
1620 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1622 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1623 assert!(type1.is_coercible_to(&type2));
1624 assert!(!type2.is_coercible_to(&type1));
1625
1626 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1628 let type2 = ArrayType::new(Type::from(PrimitiveType::String).optional()).into();
1629 assert!(type1.is_coercible_to(&type2));
1630 assert!(!type2.is_coercible_to(&type1));
1631
1632 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1634 let type2 = ArrayType::new(PrimitiveType::String).into();
1635 assert!(type1.is_coercible_to(&type2));
1636 assert!(type2.is_coercible_to(&type1));
1637
1638 let type1 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1640 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1641 assert!(type1.is_coercible_to(&type2));
1642 assert!(type2.is_coercible_to(&type1));
1643
1644 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1646 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1647 assert!(type1.is_coercible_to(&type2));
1648 assert!(!type2.is_coercible_to(&type1));
1649 }
1650
1651 #[test]
1652 fn pair_type_coercion() {
1653 assert!(
1655 PairType::new(PrimitiveType::String, PrimitiveType::String)
1656 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1657 );
1658 assert!(
1659 PairType::new(PrimitiveType::String, PrimitiveType::String).is_coercible_to(
1660 &PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1661 )
1662 );
1663 assert!(
1664 PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1665 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1666 );
1667
1668 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1670 let type2 = PairType::new(
1671 Type::from(PrimitiveType::File).optional(),
1672 Type::from(PrimitiveType::Directory).optional(),
1673 )
1674 .into();
1675 assert!(type1.is_coercible_to(&type2));
1676 assert!(!type2.is_coercible_to(&type1));
1677
1678 let type1: Type = PairType::new(type1.clone(), type1).into();
1680 let type2 = PairType::new(type2.clone(), type2).into();
1681 assert!(type1.is_coercible_to(&type2));
1682 assert!(!type2.is_coercible_to(&type1));
1683
1684 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1686 let type2 = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1687 assert!(type1.is_coercible_to(&type2));
1688 assert!(type2.is_coercible_to(&type1));
1689
1690 let type1 =
1692 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1693 let type2 =
1694 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1695 assert!(type1.is_coercible_to(&type2));
1696 assert!(type2.is_coercible_to(&type1));
1697
1698 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1700 let type2 =
1701 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1702 assert!(type1.is_coercible_to(&type2));
1703 assert!(!type2.is_coercible_to(&type1));
1704 }
1705
1706 #[test]
1707 fn map_type_coercion() {
1708 assert!(
1710 MapType::new(PrimitiveType::String, PrimitiveType::String)
1711 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1712 );
1713 assert!(
1714 MapType::new(PrimitiveType::String, PrimitiveType::String)
1715 .is_coercible_to(&MapType::new(PrimitiveType::File, PrimitiveType::Directory))
1716 );
1717 assert!(
1718 MapType::new(PrimitiveType::File, PrimitiveType::Directory)
1719 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1720 );
1721
1722 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1724 let type2 = MapType::new(
1725 Type::from(PrimitiveType::File).optional(),
1726 Type::from(PrimitiveType::Directory).optional(),
1727 )
1728 .into();
1729 assert!(type1.is_coercible_to(&type2));
1730 assert!(!type2.is_coercible_to(&type1));
1731
1732 let type1: Type = MapType::new(PrimitiveType::String, type1).into();
1734 let type2 = MapType::new(PrimitiveType::Directory, type2).into();
1735 assert!(type1.is_coercible_to(&type2));
1736 assert!(!type2.is_coercible_to(&type1));
1737
1738 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1740 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1741 assert!(type1.is_coercible_to(&type2));
1742 assert!(type2.is_coercible_to(&type1));
1743
1744 let type1: Type =
1746 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1747 let type2: Type =
1748 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1749 assert!(type1.is_coercible_to(&type2));
1750 assert!(type2.is_coercible_to(&type1));
1751
1752 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1754 let type2 =
1755 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1756 assert!(type1.is_coercible_to(&type2));
1757 assert!(!type2.is_coercible_to(&type1));
1758
1759 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1761 let type2 = StructType::new(
1762 "Foo",
1763 [
1764 ("foo", PrimitiveType::Integer),
1765 ("bar", PrimitiveType::Integer),
1766 ("baz", PrimitiveType::Integer),
1767 ],
1768 )
1769 .into();
1770 assert!(type1.is_coercible_to(&type2));
1771
1772 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1774 let type2 = StructType::new(
1775 "Foo",
1776 [
1777 ("foo", PrimitiveType::Integer),
1778 ("bar", PrimitiveType::Integer),
1779 ("baz", PrimitiveType::Integer),
1780 ],
1781 )
1782 .into();
1783 assert!(type1.is_coercible_to(&type2));
1784
1785 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1787 let type2 = StructType::new(
1788 "Foo",
1789 [
1790 ("foo", PrimitiveType::Integer),
1791 ("bar", PrimitiveType::String),
1792 ("baz", PrimitiveType::Integer),
1793 ],
1794 )
1795 .into();
1796 assert!(!type1.is_coercible_to(&type2));
1797
1798 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
1800 let type2 = StructType::new(
1801 "Foo",
1802 [
1803 ("foo", PrimitiveType::Integer),
1804 ("bar", PrimitiveType::Integer),
1805 ("baz", PrimitiveType::Integer),
1806 ],
1807 )
1808 .into();
1809 assert!(!type1.is_coercible_to(&type2));
1810
1811 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1813 assert!(type1.is_coercible_to(&Type::Object));
1814
1815 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1817 assert!(type1.is_coercible_to(&Type::OptionalObject));
1818
1819 let type1: Type =
1821 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
1822 assert!(type1.is_coercible_to(&Type::OptionalObject));
1823
1824 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1826 assert!(type1.is_coercible_to(&Type::Object));
1827
1828 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1830 assert!(type1.is_coercible_to(&Type::OptionalObject));
1831
1832 let type1: Type =
1834 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
1835 assert!(type1.is_coercible_to(&Type::OptionalObject));
1836
1837 let type1: Type =
1839 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
1840 assert!(!type1.is_coercible_to(&Type::Object));
1841
1842 let type1: Type =
1844 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
1845 assert!(!type1.is_coercible_to(&Type::Object));
1846
1847 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
1849 assert!(!type1.is_coercible_to(&Type::Object));
1850 }
1851
1852 #[test]
1853 fn struct_type_coercion() {
1854 let type1: Type = StructType::new(
1856 "Foo",
1857 [
1858 ("foo", PrimitiveType::String),
1859 ("bar", PrimitiveType::String),
1860 ("baz", PrimitiveType::Integer),
1861 ],
1862 )
1863 .into();
1864 let type2 = StructType::new(
1865 "Foo",
1866 [
1867 ("foo", PrimitiveType::String),
1868 ("bar", PrimitiveType::String),
1869 ("baz", PrimitiveType::Integer),
1870 ],
1871 )
1872 .into();
1873 assert!(type1.is_coercible_to(&type2));
1874 assert!(type2.is_coercible_to(&type1));
1875
1876 let type1: Type = StructType::new(
1878 "Foo",
1879 [
1880 ("foo", PrimitiveType::String),
1881 ("bar", PrimitiveType::String),
1882 ("baz", PrimitiveType::Integer),
1883 ],
1884 )
1885 .into();
1886 let type2 = Type::from(StructType::new(
1887 "Foo",
1888 [
1889 ("foo", PrimitiveType::String),
1890 ("bar", PrimitiveType::String),
1891 ("baz", PrimitiveType::Integer),
1892 ],
1893 ))
1894 .optional();
1895 assert!(type1.is_coercible_to(&type2));
1896 assert!(!type2.is_coercible_to(&type1));
1897
1898 let type1: Type = Type::from(StructType::new(
1900 "Foo",
1901 [
1902 ("foo", PrimitiveType::String),
1903 ("bar", PrimitiveType::String),
1904 ("baz", PrimitiveType::Integer),
1905 ],
1906 ))
1907 .optional();
1908 let type2 = Type::from(StructType::new(
1909 "Foo",
1910 [
1911 ("foo", PrimitiveType::String),
1912 ("bar", PrimitiveType::String),
1913 ("baz", PrimitiveType::Integer),
1914 ],
1915 ))
1916 .optional();
1917 assert!(type1.is_coercible_to(&type2));
1918 assert!(type2.is_coercible_to(&type1));
1919
1920 let type1: Type = StructType::new(
1922 "Foo",
1923 [
1924 ("foo", PrimitiveType::String),
1925 ("bar", PrimitiveType::String),
1926 ("baz", PrimitiveType::Integer),
1927 ],
1928 )
1929 .into();
1930 let type2 = StructType::new(
1931 "Bar",
1932 [
1933 ("foo", PrimitiveType::File),
1934 ("bar", PrimitiveType::Directory),
1935 ("baz", PrimitiveType::Float),
1936 ],
1937 )
1938 .into();
1939 assert!(type1.is_coercible_to(&type2));
1940 assert!(!type2.is_coercible_to(&type1));
1941
1942 let type1: Type = StructType::new(
1944 "Foo",
1945 [
1946 ("foo", PrimitiveType::String),
1947 ("bar", PrimitiveType::String),
1948 ("baz", PrimitiveType::Integer),
1949 ],
1950 )
1951 .into();
1952 let type2 = StructType::new("Bar", [("baz", PrimitiveType::Float)]).into();
1953 assert!(!type1.is_coercible_to(&type2));
1954 assert!(!type2.is_coercible_to(&type1));
1955
1956 let type1: Type = StructType::new(
1958 "Foo",
1959 [
1960 ("foo", PrimitiveType::String),
1961 ("bar", PrimitiveType::String),
1962 ("baz", PrimitiveType::String),
1963 ],
1964 )
1965 .into();
1966 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1967 assert!(type1.is_coercible_to(&type2));
1968
1969 let type1: Type = StructType::new(
1971 "Foo",
1972 [
1973 ("foo", PrimitiveType::String),
1974 ("bar", PrimitiveType::String),
1975 ("baz", PrimitiveType::String),
1976 ],
1977 )
1978 .into();
1979 let type2 = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1980 assert!(type1.is_coercible_to(&type2));
1981
1982 let type1: Type = StructType::new(
1984 "Foo",
1985 [
1986 ("foo", PrimitiveType::String),
1987 ("bar", PrimitiveType::Integer),
1988 ("baz", PrimitiveType::String),
1989 ],
1990 )
1991 .into();
1992 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1993 assert!(!type1.is_coercible_to(&type2));
1994
1995 let type1: Type = StructType::new(
1997 "Foo",
1998 [
1999 ("foo", PrimitiveType::String),
2000 ("bar", PrimitiveType::String),
2001 ("baz", PrimitiveType::String),
2002 ],
2003 )
2004 .into();
2005 let type2 = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2006 assert!(!type1.is_coercible_to(&type2));
2007
2008 assert!(type1.is_coercible_to(&Type::Object));
2010
2011 assert!(type1.is_coercible_to(&Type::OptionalObject));
2013
2014 let type1: Type =
2016 Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
2017 assert!(type1.is_coercible_to(&Type::OptionalObject));
2018
2019 assert!(!type1.is_coercible_to(&Type::Object));
2021 }
2022
2023 #[test]
2024 fn union_type_coercion() {
2025 for ty in [
2027 Type::from(PrimitiveType::Boolean),
2028 PrimitiveType::Directory.into(),
2029 PrimitiveType::File.into(),
2030 PrimitiveType::Float.into(),
2031 PrimitiveType::Integer.into(),
2032 PrimitiveType::String.into(),
2033 ] {
2034 assert!(Type::Union.is_coercible_to(&ty));
2035 assert!(Type::Union.is_coercible_to(&ty.optional()));
2036 assert!(ty.is_coercible_to(&Type::Union));
2037 }
2038
2039 for optional in [true, false] {
2040 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2042 let ty = if optional { ty.optional() } else { ty };
2043
2044 let coercible = Type::Union.is_coercible_to(&ty);
2045 assert!(coercible);
2046
2047 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2049 let ty = if optional { ty.optional() } else { ty };
2050 let coercible = Type::Union.is_coercible_to(&ty);
2051 assert!(coercible);
2052
2053 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2055 let ty = if optional { ty.optional() } else { ty };
2056 let coercible = Type::Union.is_coercible_to(&ty);
2057 assert!(coercible);
2058
2059 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2061 let ty = if optional { ty.optional() } else { ty };
2062 let coercible = Type::Union.is_coercible_to(&ty);
2063 assert!(coercible);
2064 }
2065 }
2066
2067 #[test]
2068 fn none_type_coercion() {
2069 for ty in [
2071 Type::from(PrimitiveType::Boolean),
2072 PrimitiveType::Directory.into(),
2073 PrimitiveType::File.into(),
2074 PrimitiveType::Float.into(),
2075 PrimitiveType::Integer.into(),
2076 PrimitiveType::String.into(),
2077 ] {
2078 assert!(!Type::None.is_coercible_to(&ty));
2079 assert!(Type::None.is_coercible_to(&ty.optional()));
2080 assert!(!ty.is_coercible_to(&Type::None));
2081 }
2082
2083 for optional in [true, false] {
2084 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2086 let ty = if optional { ty.optional() } else { ty };
2087 let coercible = Type::None.is_coercible_to(&ty);
2088 if optional {
2089 assert!(coercible);
2090 } else {
2091 assert!(!coercible);
2092 }
2093
2094 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2096 let ty = if optional { ty.optional() } else { ty };
2097 let coercible = Type::None.is_coercible_to(&ty);
2098 if optional {
2099 assert!(coercible);
2100 } else {
2101 assert!(!coercible);
2102 }
2103
2104 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2106 let ty = if optional { ty.optional() } else { ty };
2107 let coercible = Type::None.is_coercible_to(&ty);
2108 if optional {
2109 assert!(coercible);
2110 } else {
2111 assert!(!coercible);
2112 }
2113
2114 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2116 let ty = if optional { ty.optional() } else { ty };
2117 let coercible = Type::None.is_coercible_to(&ty);
2118 if optional {
2119 assert!(coercible);
2120 } else {
2121 assert!(!coercible);
2122 }
2123 }
2124 }
2125
2126 #[test]
2127 fn primitive_equality() {
2128 for ty in [
2129 Type::from(PrimitiveType::Boolean),
2130 PrimitiveType::Directory.into(),
2131 PrimitiveType::File.into(),
2132 PrimitiveType::Float.into(),
2133 PrimitiveType::Integer.into(),
2134 PrimitiveType::String.into(),
2135 ] {
2136 assert!(ty.eq(&ty));
2137 assert!(!ty.optional().eq(&ty));
2138 assert!(!ty.eq(&ty.optional()));
2139 assert!(ty.optional().eq(&ty.optional()));
2140 assert!(!ty.eq(&Type::Object));
2141 assert!(!ty.eq(&Type::OptionalObject));
2142 assert!(!ty.eq(&Type::Union));
2143 assert!(!ty.eq(&Type::None));
2144 }
2145 }
2146
2147 #[test]
2148 fn array_equality() {
2149 let a: Type = ArrayType::new(PrimitiveType::String).into();
2151 let b: Type = ArrayType::new(PrimitiveType::String).into();
2152 assert!(a.eq(&b));
2153 assert!(!a.optional().eq(&b));
2154 assert!(!a.eq(&b.optional()));
2155 assert!(a.optional().eq(&b.optional()));
2156
2157 let a: Type = ArrayType::new(a).into();
2159 let b: Type = ArrayType::new(b).into();
2160 assert!(a.eq(&b));
2161
2162 let a: Type = ArrayType::non_empty(a).into();
2164 let b: Type = ArrayType::non_empty(b).into();
2165 assert!(a.eq(&b));
2166
2167 let a: Type = ArrayType::new(PrimitiveType::String).into();
2169 let b: Type = ArrayType::non_empty(PrimitiveType::String).into();
2170 assert!(!a.eq(&b));
2171
2172 let a: Type = ArrayType::new(PrimitiveType::String).into();
2174 let b: Type = ArrayType::new(PrimitiveType::Integer).into();
2175 assert!(!a.eq(&b));
2176
2177 assert!(!a.eq(&Type::Object));
2178 assert!(!a.eq(&Type::OptionalObject));
2179 assert!(!a.eq(&Type::Union));
2180 assert!(!a.eq(&Type::None));
2181 }
2182
2183 #[test]
2184 fn pair_equality() {
2185 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2187 let b: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2188 assert!(a.eq(&b));
2189 assert!(!a.optional().eq(&b));
2190 assert!(!a.eq(&b.optional()));
2191 assert!(a.optional().eq(&b.optional()));
2192
2193 let a: Type = PairType::new(a.clone(), a).into();
2196 let b: Type = PairType::new(b.clone(), b).into();
2197 assert!(a.eq(&b));
2198
2199 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2201 let b: Type =
2202 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2203 assert!(!a.eq(&b));
2204
2205 assert!(!a.eq(&Type::Object));
2206 assert!(!a.eq(&Type::OptionalObject));
2207 assert!(!a.eq(&Type::Union));
2208 assert!(!a.eq(&Type::None));
2209 }
2210
2211 #[test]
2212 fn map_equality() {
2213 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2215 let b = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2216 assert!(a.eq(&b));
2217 assert!(!a.optional().eq(&b));
2218 assert!(!a.eq(&b.optional()));
2219 assert!(a.optional().eq(&b.optional()));
2220
2221 let a: Type = MapType::new(PrimitiveType::File, a).into();
2223 let b = MapType::new(PrimitiveType::File, b).into();
2224 assert!(a.eq(&b));
2225
2226 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2228 let b = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2229 assert!(!a.eq(&b));
2230
2231 assert!(!a.eq(&Type::Object));
2232 assert!(!a.eq(&Type::OptionalObject));
2233 assert!(!a.eq(&Type::Union));
2234 assert!(!a.eq(&Type::None));
2235 }
2236
2237 #[test]
2238 fn struct_equality() {
2239 let a: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2240 assert!(a.eq(&a));
2241 assert!(!a.optional().eq(&a));
2242 assert!(!a.eq(&a.optional()));
2243 assert!(a.optional().eq(&a.optional()));
2244
2245 let b: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2246 assert!(a.eq(&b));
2247 let b: Type = StructType::new("Bar", [("foo", PrimitiveType::String)]).into();
2248 assert!(!a.eq(&b));
2249 }
2250
2251 #[test]
2252 fn object_equality() {
2253 assert!(Type::Object.eq(&Type::Object));
2254 assert!(!Type::OptionalObject.eq(&Type::Object));
2255 assert!(!Type::Object.eq(&Type::OptionalObject));
2256 assert!(Type::OptionalObject.eq(&Type::OptionalObject));
2257 }
2258
2259 #[test]
2260 fn union_equality() {
2261 assert!(Type::Union.eq(&Type::Union));
2262 assert!(!Type::None.eq(&Type::Union));
2263 assert!(!Type::Union.eq(&Type::None));
2264 assert!(Type::None.eq(&Type::None));
2265 }
2266
2267 #[test]
2268 fn enum_type_new_with_explicit_type() {
2269 let status = EnumType::new(
2271 "Status",
2272 Span::new(0, 0),
2273 PrimitiveType::String.into(),
2274 vec![
2275 ("Pending".into(), PrimitiveType::String.into()),
2276 ("Running".into(), PrimitiveType::String.into()),
2277 ("Complete".into(), PrimitiveType::String.into()),
2278 ],
2279 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)][..],
2280 )
2281 .unwrap();
2282
2283 assert_eq!(status.name().as_ref(), "Status");
2284 assert_eq!(
2285 status.inner_value_type(),
2286 &Type::from(PrimitiveType::String)
2287 );
2288 assert_eq!(status.variants().len(), 3);
2289 }
2290
2291 #[test]
2292 fn enum_type_new_fails_when_not_coercible() {
2293 let result = EnumType::new(
2295 "Bad",
2296 Span::new(0, 0),
2297 PrimitiveType::Integer.into(),
2298 vec![
2299 ("First".into(), PrimitiveType::String.into()),
2300 ("Second".into(), PrimitiveType::Integer.into()),
2301 ],
2302 &[Span::new(0, 0), Span::new(0, 0)][..],
2303 );
2304
2305 assert!(
2306 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot coerce variant `First` in enum `Bad` from type `String` to type `Int`")
2307 );
2308 }
2309
2310 #[test]
2311 fn enum_type_infer_finds_common_type() {
2312 let priority = EnumType::infer(
2314 "Priority",
2315 vec![
2316 ("Low".into(), PrimitiveType::Integer.into()),
2317 ("Medium".into(), PrimitiveType::Integer.into()),
2318 ("High".into(), PrimitiveType::Integer.into()),
2319 ],
2320 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)],
2321 )
2322 .unwrap();
2323
2324 assert_eq!(priority.name().as_str(), "Priority");
2325 assert_eq!(
2326 priority.inner_value_type(),
2327 &Type::from(PrimitiveType::Integer)
2328 );
2329 assert_eq!(priority.variants().len(), 3);
2330 }
2331
2332 #[test]
2333 fn enum_type_infer_coerces_int_to_float() {
2334 let mixed = EnumType::infer(
2336 "Mixed",
2337 vec![
2338 ("IntValue".into(), PrimitiveType::Integer.into()),
2339 ("FloatValue".into(), PrimitiveType::Float.into()),
2340 ],
2341 &[Span::new(0, 0), Span::new(0, 0)],
2342 )
2343 .unwrap();
2344
2345 assert_eq!(mixed.name().as_str(), "Mixed");
2346 assert_eq!(mixed.inner_value_type(), &Type::from(PrimitiveType::Float));
2347 assert_eq!(mixed.variants().len(), 2);
2348 }
2349
2350 #[test]
2351 fn enum_type_infer_fails_without_common_type() {
2352 let result = EnumType::infer(
2354 "Bad",
2355 vec![
2356 ("StringVal".into(), PrimitiveType::String.into()),
2357 ("IntVal".into(), PrimitiveType::Integer.into()),
2358 ],
2359 &[Span::new(0, 0), Span::new(0, 0)][..],
2360 );
2361
2362 assert!(
2363 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot infer a common type for enum `Bad`")
2364 );
2365 }
2366
2367 #[test]
2368 fn enum_type_empty_has_union_type() {
2369 let result = EnumType::infer("Empty", Vec::<(String, Type)>::new(), &[]);
2371
2372 let empty = result.unwrap();
2373 assert_eq!(empty.name().as_str(), "Empty");
2374 assert_eq!(empty.inner_value_type(), &Type::Union);
2375 assert_eq!(empty.variants().len(), 0);
2376 }
2377
2378 #[test]
2379 fn enum_type_display() {
2380 let enum_type = EnumType::new(
2381 "Color",
2382 Span::new(0, 0),
2383 PrimitiveType::String.into(),
2384 vec![("Red".into(), PrimitiveType::String.into())],
2385 &[Span::new(0, 0)][..],
2386 )
2387 .unwrap();
2388 assert_eq!(enum_type.to_string(), "Color");
2389 }
2390
2391 #[test]
2392 fn enum_type_not_coercible_to_other_enums() {
2393 let color = EnumType::new(
2394 "Color",
2395 Span::new(0, 0),
2396 PrimitiveType::String.into(),
2397 vec![("Red".into(), PrimitiveType::String.into())],
2398 &[Span::new(0, 0)][..],
2399 )
2400 .unwrap();
2401 let status = EnumType::new(
2402 "Status",
2403 Span::new(0, 0),
2404 PrimitiveType::String.into(),
2405 vec![("Active".into(), PrimitiveType::String.into())],
2406 &[Span::new(0, 0)][..],
2407 )
2408 .unwrap();
2409 assert!(!color.is_coercible_to(&status));
2410 }
2411}