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, "{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
200const _: () = {
204 assert!(std::mem::size_of::<Type>() <= 24);
205};
206
207impl Type {
208 pub fn as_primitive(&self) -> Option<PrimitiveType> {
212 match self {
213 Self::Primitive(ty, _) => Some(*ty),
214 _ => None,
215 }
216 }
217
218 pub fn as_compound(&self) -> Option<&CompoundType> {
222 match self {
223 Self::Compound(ty, _) => Some(ty),
224 _ => None,
225 }
226 }
227
228 pub fn as_array(&self) -> Option<&ArrayType> {
232 match self {
233 Self::Compound(ty, _) => ty.as_array(),
234 _ => None,
235 }
236 }
237
238 pub fn as_pair(&self) -> Option<&PairType> {
242 match self {
243 Self::Compound(ty, _) => ty.as_pair(),
244 _ => None,
245 }
246 }
247
248 pub fn as_map(&self) -> Option<&MapType> {
252 match self {
253 Self::Compound(ty, _) => ty.as_map(),
254 _ => None,
255 }
256 }
257
258 pub fn as_struct(&self) -> Option<&StructType> {
262 match self {
263 Self::Compound(ty, _) => ty.as_struct(),
264 _ => None,
265 }
266 }
267
268 pub fn as_enum(&self) -> Option<&EnumType> {
272 match self {
273 Self::Compound(ty, _) => ty.as_enum(),
274 _ => None,
275 }
276 }
277
278 pub fn as_custom(&self) -> Option<&CustomType> {
282 match self {
283 Self::Compound(ty, _) => ty.as_custom(),
284 _ => None,
285 }
286 }
287
288 pub fn as_type_name_ref(&self) -> Option<&CustomType> {
292 match self {
293 Self::TypeNameRef(custom_ty) => Some(custom_ty),
294 _ => None,
295 }
296 }
297
298 pub fn as_call(&self) -> Option<&CallType> {
302 match self {
303 Self::Call(ty) => Some(ty),
304 _ => None,
305 }
306 }
307
308 pub fn is_union(&self) -> bool {
310 matches!(self, Type::Union)
311 }
312
313 pub fn is_none(&self) -> bool {
315 matches!(self, Type::None)
316 }
317
318 pub fn promote_scatter(&self) -> Self {
323 if let Self::Call(ty) = self {
325 return Self::Call(ty.promote_scatter());
326 }
327
328 Type::Compound(ArrayType::new(self.clone()).into(), false)
329 }
330
331 pub fn common_type(&self, other: &Type) -> Option<Type> {
335 if other.is_union() {
337 return Some(self.clone());
338 }
339
340 if self.is_union() {
342 return Some(other.clone());
343 }
344
345 if other.is_none() {
348 return Some(self.optional());
349 }
350
351 if self.is_none() {
353 return Some(other.optional());
354 }
355
356 if other.is_coercible_to(self) {
358 return Some(self.clone());
359 }
360
361 if self.is_coercible_to(other) {
363 return Some(other.clone());
364 }
365
366 if let (Some(this), Some(other)) = (self.as_compound(), other.as_compound())
368 && let Some(ty) = this.common_type(other)
369 {
370 return Some(Self::Compound(ty, self.is_optional()));
371 }
372
373 if let (Some(this), Some(other)) = (self.as_call(), self.as_call())
375 && this == other
376 {
377 return Some(Self::Call(this.clone()));
378 }
379
380 None
381 }
382
383 pub fn type_name_ref(&self) -> Option<Type> {
387 match self {
388 Type::Compound(CompoundType::Custom(ty), _) => Some(Type::TypeNameRef(ty.clone())),
389 _ => None,
390 }
391 }
392}
393
394impl fmt::Display for Type {
395 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
396 match self {
397 Self::Primitive(ty, optional) => {
398 write!(
399 f,
400 "{prefix}{ty}{opt}{suffix}",
401 prefix = if f.alternate() { "type `" } else { "" },
402 opt = if *optional { "?" } else { "" },
403 suffix = if f.alternate() { "`" } else { "" },
404 )
405 }
406 Self::Compound(CompoundType::Custom(CustomType::Struct(ty)), optional) => {
407 write!(
408 f,
409 "{prefix}{ty}{opt}{suffix}",
410 prefix = if f.alternate() {
411 "an instance of struct `"
412 } else {
413 ""
414 },
415 opt = if *optional { "?" } else { "" },
416 suffix = if f.alternate() { "`" } else { "" },
417 )
418 }
419 Self::Compound(CompoundType::Custom(CustomType::Enum(ty)), optional) => {
420 write!(
421 f,
422 "{prefix}{ty}{opt}{suffix}",
423 prefix = if f.alternate() {
424 "an instance of enum `"
425 } else {
426 ""
427 },
428 opt = if *optional { "?" } else { "" },
429 suffix = if f.alternate() { "`" } else { "" },
430 )
431 }
432 Self::Compound(ty, optional) => {
433 write!(
434 f,
435 "{prefix}{ty}{opt}{suffix}",
436 prefix = if f.alternate() { "type `" } else { "" },
437 opt = if *optional { "?" } else { "" },
438 suffix = if f.alternate() { "`" } else { "" },
439 )
440 }
441 Self::Object => {
442 write!(
443 f,
444 "{prefix}Object{suffix}",
445 prefix = if f.alternate() { "type `" } else { "" },
446 suffix = if f.alternate() { "`" } else { "" },
447 )
448 }
449 Self::OptionalObject => {
450 write!(
451 f,
452 "{prefix}Object?{suffix}",
453 prefix = if f.alternate() { "type `" } else { "" },
454 suffix = if f.alternate() { "`" } else { "" },
455 )
456 }
457 Self::Union => {
458 write!(
459 f,
460 "{prefix}Union{suffix}",
461 prefix = if f.alternate() { "built-in type `" } else { "" },
462 suffix = if f.alternate() { "`" } else { "" },
463 )
464 }
465 Self::None => {
466 write!(
467 f,
468 "{prefix}None{suffix}",
469 prefix = if f.alternate() { "built-in type `" } else { "" },
470 suffix = if f.alternate() { "`" } else { "" },
471 )
472 }
473 Self::Hidden(ty) => {
474 write!(
475 f,
476 "{prefix}{ty}{suffix}",
477 prefix = if f.alternate() { "built-in type `" } else { "" },
478 suffix = if f.alternate() { "`" } else { "" },
479 )
480 }
481 Self::Call(ty) => ty.fmt(f),
482 Self::TypeNameRef(ty) => {
483 write!(
484 f,
485 "{prefix}{ty}{suffix}",
486 prefix = if f.alternate() { "type name `" } else { "" },
487 suffix = if f.alternate() { "`" } else { "" },
488 )
489 }
490 }
491 }
492}
493
494impl Optional for Type {
495 fn is_optional(&self) -> bool {
496 match self {
497 Self::Primitive(_, optional) => *optional,
498 Self::Compound(_, optional) => *optional,
499 Self::OptionalObject | Self::None => true,
500 Self::Object | Self::Union | Self::Hidden(_) | Self::Call(_) | Self::TypeNameRef(_) => {
501 false
502 }
503 }
504 }
505
506 fn optional(&self) -> Self {
507 match self {
508 Self::Primitive(ty, _) => Self::Primitive(*ty, true),
509 Self::Compound(ty, _) => Self::Compound(ty.clone(), true),
510 Self::Object => Self::OptionalObject,
511 Self::Union => Self::None,
512 Self::Call(ty) => Self::Call(ty.optional()),
513 ty => ty.clone(),
514 }
515 }
516
517 fn require(&self) -> Self {
518 match self {
519 Self::Primitive(ty, _) => Self::Primitive(*ty, false),
520 Self::Compound(ty, _) => Self::Compound(ty.clone(), false),
521 Self::OptionalObject => Self::Object,
522 Self::None => Self::Union,
523 ty => ty.clone(),
524 }
525 }
526}
527
528impl Coercible for Type {
529 fn is_coercible_to(&self, target: &Self) -> bool {
530 if self.eq(target) {
531 return true;
532 }
533
534 match (self, target) {
535 (Self::Primitive(src, src_opt), Self::Primitive(target, target_opt)) => {
536 if *src_opt && !*target_opt {
538 return false;
539 }
540
541 src.is_coercible_to(target)
542 }
543 (Self::Compound(src, src_opt), Self::Compound(target, target_opt)) => {
544 if *src_opt && !*target_opt {
546 return false;
547 }
548
549 src.is_coercible_to(target)
550 }
551
552 (Self::Object, Self::Object)
554 | (Self::Object, Self::OptionalObject)
555 | (Self::OptionalObject, Self::OptionalObject) => true,
556
557 (Self::Compound(src, false), Self::Object)
561 | (Self::Compound(src, false), Self::OptionalObject)
562 | (Self::Compound(src, _), Self::OptionalObject) => match src {
563 CompoundType::Map(src) => src
564 .key_type()
565 .is_coercible_to(&PrimitiveType::String.into()),
566 CompoundType::Custom(CustomType::Struct(_)) => true,
567 _ => false,
568 },
569
570 (Self::Object, Self::Compound(target, _))
576 | (Self::OptionalObject, Self::Compound(target, true)) => {
577 match target {
578 CompoundType::Map(target) => {
579 Type::from(PrimitiveType::String).is_coercible_to(target.key_type())
580 }
581 CompoundType::Custom(CustomType::Struct(_)) => {
582 true
584 }
585 _ => false,
586 }
587 }
588
589 (Self::Union, _) | (_, Self::Union) => true,
591
592 (Self::None, ty) if ty.is_optional() => true,
594
595 (
598 Self::Primitive(PrimitiveType::String, _),
599 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
600 )
601 | (
602 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
603 Self::Primitive(PrimitiveType::String, _),
604 ) => true,
605
606 _ => false,
608 }
609 }
610}
611
612impl From<PrimitiveType> for Type {
613 fn from(value: PrimitiveType) -> Self {
614 Self::Primitive(value, false)
615 }
616}
617
618impl From<CompoundType> for Type {
619 fn from(value: CompoundType) -> Self {
620 Self::Compound(value, false)
621 }
622}
623
624impl From<ArrayType> for Type {
625 fn from(value: ArrayType) -> Self {
626 Self::Compound(value.into(), false)
627 }
628}
629
630impl From<PairType> for Type {
631 fn from(value: PairType) -> Self {
632 Self::Compound(value.into(), false)
633 }
634}
635
636impl From<MapType> for Type {
637 fn from(value: MapType) -> Self {
638 Self::Compound(value.into(), false)
639 }
640}
641
642impl From<StructType> for Type {
643 fn from(value: StructType) -> Self {
644 Self::Compound(value.into(), false)
645 }
646}
647
648impl From<EnumType> for Type {
649 fn from(value: EnumType) -> Self {
650 Self::Compound(value.into(), false)
651 }
652}
653
654impl From<CallType> for Type {
655 fn from(value: CallType) -> Self {
656 Self::Call(value)
657 }
658}
659
660#[derive(Debug, Clone, PartialEq, Eq)]
662pub enum CustomType {
663 Struct(StructType),
665 Enum(EnumType),
667}
668
669impl CustomType {
670 pub fn name(&self) -> &str {
672 match self {
673 Self::Struct(ty) => ty.name(),
674 Self::Enum(ty) => ty.name(),
675 }
676 }
677
678 pub fn as_struct(&self) -> Option<&StructType> {
682 match self {
683 Self::Struct(ty) => Some(ty),
684 _ => None,
685 }
686 }
687
688 pub fn as_enum(&self) -> Option<&EnumType> {
692 match self {
693 Self::Enum(ty) => Some(ty),
694 _ => None,
695 }
696 }
697}
698
699impl std::fmt::Display for CustomType {
700 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
701 match self {
702 CustomType::Struct(ty) => ty.fmt(f),
703 CustomType::Enum(ty) => ty.fmt(f),
704 }
705 }
706}
707
708impl From<StructType> for CustomType {
709 fn from(value: StructType) -> Self {
710 Self::Struct(value)
711 }
712}
713
714impl From<EnumType> for CustomType {
715 fn from(value: EnumType) -> Self {
716 Self::Enum(value)
717 }
718}
719
720#[derive(Debug, Clone, PartialEq, Eq)]
722pub enum CompoundType {
723 Array(ArrayType),
725 Pair(PairType),
727 Map(MapType),
729 Custom(CustomType),
731}
732
733impl CompoundType {
734 pub fn as_array(&self) -> Option<&ArrayType> {
738 match self {
739 Self::Array(ty) => Some(ty),
740 _ => None,
741 }
742 }
743
744 pub fn as_pair(&self) -> Option<&PairType> {
748 match self {
749 Self::Pair(ty) => Some(ty),
750 _ => None,
751 }
752 }
753
754 pub fn as_map(&self) -> Option<&MapType> {
758 match self {
759 Self::Map(ty) => Some(ty),
760 _ => None,
761 }
762 }
763
764 pub fn as_struct(&self) -> Option<&StructType> {
768 match self {
769 Self::Custom(ty) => ty.as_struct(),
770 _ => None,
771 }
772 }
773
774 pub fn as_enum(&self) -> Option<&EnumType> {
778 match self {
779 Self::Custom(ty) => ty.as_enum(),
780 _ => None,
781 }
782 }
783
784 pub fn as_custom(&self) -> Option<&CustomType> {
788 match self {
789 Self::Custom(ty) => Some(ty),
790 _ => None,
791 }
792 }
793
794 fn common_type(&self, other: &Self) -> Option<CompoundType> {
799 match (self, other) {
802 (Self::Array(this), Self::Array(other)) => {
803 let element_type = this.element_type().common_type(other.element_type())?;
804 Some(ArrayType::new(element_type).into())
805 }
806 (Self::Pair(this), Self::Pair(other)) => {
807 let left_type = this.left_type().common_type(other.left_type())?;
808 let right_type = this.right_type().common_type(other.right_type())?;
809 Some(PairType::new(left_type, right_type).into())
810 }
811 (Self::Map(this), Self::Map(other)) => {
812 let key_type = this.key_type().common_type(other.key_type())?;
813 let value_type = this.value_type().common_type(other.value_type())?;
814 Some(MapType::new(key_type, value_type).into())
815 }
816 _ => None,
817 }
818 }
819}
820
821impl fmt::Display for CompoundType {
822 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
823 match self {
824 Self::Array(ty) => ty.fmt(f),
825 Self::Pair(ty) => ty.fmt(f),
826 Self::Map(ty) => ty.fmt(f),
827 Self::Custom(CustomType::Struct(ty)) => ty.fmt(f),
828 Self::Custom(CustomType::Enum(ty)) => ty.fmt(f),
829 }
830 }
831}
832
833impl Coercible for CompoundType {
834 fn is_coercible_to(&self, target: &Self) -> bool {
835 match (self, target) {
836 (Self::Array(src), Self::Array(target)) => src.is_coercible_to(target),
839
840 (Self::Pair(src), Self::Pair(target)) => src.is_coercible_to(target),
843
844 (Self::Map(src), Self::Map(target)) => src.is_coercible_to(target),
847
848 (Self::Custom(CustomType::Struct(src)), Self::Custom(CustomType::Struct(target))) => {
851 src.is_coercible_to(target)
852 }
853
854 (Self::Custom(CustomType::Enum(src)), Self::Custom(CustomType::Enum(target))) => {
856 src.is_coercible_to(target)
857 }
858
859 (Self::Map(src), Self::Custom(CustomType::Struct(target))) => {
862 if !src
863 .key_type()
864 .is_coercible_to(&PrimitiveType::String.into())
865 {
866 return false;
867 }
868
869 if !target
871 .members()
872 .values()
873 .all(|ty| src.value_type().is_coercible_to(ty))
874 {
875 return false;
876 }
877
878 true
880 }
881
882 (Self::Custom(CustomType::Struct(src)), Self::Map(target)) => {
885 if !Type::from(PrimitiveType::String).is_coercible_to(target.key_type()) {
886 return false;
887 }
888
889 if !src
891 .members()
892 .values()
893 .all(|ty| ty.is_coercible_to(target.value_type()))
894 {
895 return false;
896 }
897
898 true
899 }
900
901 _ => false,
902 }
903 }
904}
905
906impl From<ArrayType> for CompoundType {
907 fn from(value: ArrayType) -> Self {
908 Self::Array(value)
909 }
910}
911
912impl From<PairType> for CompoundType {
913 fn from(value: PairType) -> Self {
914 Self::Pair(value)
915 }
916}
917
918impl From<MapType> for CompoundType {
919 fn from(value: MapType) -> Self {
920 Self::Map(value)
921 }
922}
923
924impl From<StructType> for CompoundType {
925 fn from(value: StructType) -> Self {
926 Self::Custom(CustomType::Struct(value))
927 }
928}
929
930impl From<EnumType> for CompoundType {
931 fn from(value: EnumType) -> Self {
932 Self::Custom(CustomType::Enum(value))
933 }
934}
935
936#[derive(Debug, Clone, PartialEq, Eq)]
938struct ArrayTypeInner {
939 element_type: Type,
941 non_empty: bool,
943}
944
945#[derive(Debug, Clone, PartialEq, Eq)]
949pub struct ArrayType(Arc<ArrayTypeInner>);
950
951impl ArrayType {
952 pub fn new(element_type: impl Into<Type>) -> Self {
954 Self(Arc::new(ArrayTypeInner {
955 element_type: element_type.into(),
956 non_empty: false,
957 }))
958 }
959
960 pub fn non_empty(element_type: impl Into<Type>) -> Self {
962 Self(Arc::new(ArrayTypeInner {
963 element_type: element_type.into(),
964 non_empty: true,
965 }))
966 }
967
968 pub fn element_type(&self) -> &Type {
970 &self.0.element_type
971 }
972
973 pub fn is_non_empty(&self) -> bool {
975 self.0.non_empty
976 }
977
978 pub fn unqualified(&self) -> ArrayType {
980 if self.0.non_empty {
981 Self(Arc::new(ArrayTypeInner {
982 element_type: self.0.element_type.clone(),
983 non_empty: false,
984 }))
985 } else {
986 self.clone()
987 }
988 }
989}
990
991impl fmt::Display for ArrayType {
992 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
993 write!(f, "Array[{ty}]", ty = self.0.element_type)?;
994
995 if self.0.non_empty {
996 write!(f, "+")?;
997 }
998
999 Ok(())
1000 }
1001}
1002
1003impl Coercible for ArrayType {
1004 fn is_coercible_to(&self, target: &Self) -> bool {
1005 self.0.element_type.is_coercible_to(&target.0.element_type)
1007 }
1008}
1009
1010#[derive(Debug, Clone, PartialEq, Eq)]
1012pub struct PairType(Arc<(Type, Type)>);
1013
1014impl PairType {
1015 pub fn new(left_type: impl Into<Type>, right_type: impl Into<Type>) -> Self {
1017 Self(Arc::new((left_type.into(), right_type.into())))
1018 }
1019
1020 pub fn left_type(&self) -> &Type {
1022 &self.0.0
1023 }
1024
1025 pub fn right_type(&self) -> &Type {
1027 &self.0.1
1028 }
1029}
1030
1031impl fmt::Display for PairType {
1032 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1033 write!(
1034 f,
1035 "Pair[{left}, {right}]",
1036 left = self.left_type(),
1037 right = self.right_type()
1038 )?;
1039
1040 Ok(())
1041 }
1042}
1043
1044impl Coercible for PairType {
1045 fn is_coercible_to(&self, target: &Self) -> bool {
1046 self.left_type().is_coercible_to(target.left_type())
1047 && self.right_type().is_coercible_to(target.right_type())
1048 }
1049}
1050
1051#[derive(Debug, Clone, PartialEq, Eq)]
1053pub struct MapType(Arc<(Type, Type)>);
1054
1055impl MapType {
1056 pub fn new(key_type: impl Into<Type>, value_type: impl Into<Type>) -> Self {
1062 let key_type = key_type.into();
1063 assert!(
1064 key_type.is_union() || matches!(key_type, Type::Primitive(_, false)),
1065 "map key {key_type:#} is not a non-optional primitive"
1066 );
1067 Self(Arc::new((key_type, value_type.into())))
1068 }
1069
1070 pub fn key_type(&self) -> &Type {
1072 &self.0.0
1073 }
1074
1075 pub fn value_type(&self) -> &Type {
1077 &self.0.1
1078 }
1079}
1080
1081impl fmt::Display for MapType {
1082 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1083 write!(
1084 f,
1085 "Map[{key}, {value}]",
1086 key = self.key_type(),
1087 value = self.value_type()
1088 )?;
1089
1090 Ok(())
1091 }
1092}
1093
1094impl Coercible for MapType {
1095 fn is_coercible_to(&self, target: &Self) -> bool {
1096 self.key_type().is_coercible_to(target.key_type())
1097 && self.value_type().is_coercible_to(target.value_type())
1098 }
1099}
1100
1101#[derive(Debug, Clone, PartialEq, Eq)]
1103struct StructTypeInner {
1104 name: Arc<String>,
1110 members: IndexMap<String, Type>,
1112}
1113
1114#[derive(Debug, Clone, PartialEq, Eq)]
1118pub struct StructType(Arc<StructTypeInner>);
1119
1120impl StructType {
1121 pub fn new<N, T>(name: impl Into<String>, members: impl IntoIterator<Item = (N, T)>) -> Self
1123 where
1124 N: Into<String>,
1125 T: Into<Type>,
1126 {
1127 Self(Arc::new(StructTypeInner {
1128 name: Arc::new(name.into()),
1129 members: members
1130 .into_iter()
1131 .map(|(n, ty)| (n.into(), ty.into()))
1132 .collect(),
1133 }))
1134 }
1135
1136 pub fn name(&self) -> &Arc<String> {
1138 &self.0.name
1139 }
1140
1141 pub fn members(&self) -> &IndexMap<String, Type> {
1143 &self.0.members
1144 }
1145}
1146
1147impl fmt::Display for StructType {
1148 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1149 write!(f, "{name}", name = self.0.name)
1150 }
1151}
1152
1153impl Coercible for StructType {
1154 fn is_coercible_to(&self, target: &Self) -> bool {
1155 if self.0.members.len() != target.0.members.len() {
1156 return false;
1157 }
1158
1159 self.0.members.iter().all(|(k, v)| {
1160 target
1161 .0
1162 .members
1163 .get(k)
1164 .map(|target| v.is_coercible_to(target))
1165 .unwrap_or(false)
1166 })
1167 }
1168}
1169
1170#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
1172pub struct EnumVariantCacheKey {
1173 enum_index: usize,
1175 variant_index: usize,
1177}
1178
1179impl EnumVariantCacheKey {
1180 pub(crate) fn new(enum_index: usize, variant_index: usize) -> Self {
1182 Self {
1183 enum_index,
1184 variant_index,
1185 }
1186 }
1187}
1188
1189#[derive(Debug, Clone, PartialEq, Eq)]
1191struct EnumTypeInner {
1192 name: String,
1194 inner_value_type: Type,
1196 variants: Arc<[String]>,
1198}
1199
1200#[derive(Debug, Clone, PartialEq, Eq)]
1204pub struct EnumType(Arc<EnumTypeInner>);
1205
1206impl EnumType {
1207 pub fn new(
1213 enum_name: impl Into<String>,
1214 enum_span: Span,
1215 explicit_inner_type: Type,
1216 variants: Vec<(String, Type)>,
1217 variant_spans: &[Span],
1218 ) -> Result<Self, Diagnostic> {
1219 assert_eq!(variants.len(), variant_spans.len());
1220 let enum_name = enum_name.into();
1221 let mut results = Vec::with_capacity(variants.len());
1222
1223 for (variant_idx, (variant_name, variant_type)) in variants.iter().enumerate() {
1225 if !variant_type.is_coercible_to(&explicit_inner_type) {
1226 return Err(enum_variant_does_not_coerce_to_type(
1227 &enum_name,
1228 enum_span,
1229 variant_name,
1230 variant_spans[variant_idx],
1231 &explicit_inner_type,
1232 variant_type,
1233 ));
1234 }
1235
1236 results.push(variant_name.to_owned());
1237 }
1238
1239 Ok(Self(Arc::new(EnumTypeInner {
1240 name: enum_name,
1241 inner_value_type: explicit_inner_type,
1242 variants: results.into(),
1243 })))
1244 }
1245
1246 pub fn infer(
1254 enum_name: impl Into<String>,
1255 variants: Vec<(String, Type)>,
1256 variant_spans: &[Span],
1257 ) -> Result<Self, Diagnostic> {
1258 assert_eq!(variants.len(), variant_spans.len());
1259 let enum_name = enum_name.into();
1260
1261 let mut common_ty: Option<Type> = None;
1262 let mut names = Vec::with_capacity(variants.len());
1263 for (i, (name, variant_ty)) in variants.into_iter().enumerate() {
1264 match common_ty {
1265 Some(current_common_ty) => match current_common_ty.common_type(&variant_ty) {
1266 Some(new_common_ty) => {
1267 common_ty = Some(new_common_ty);
1268 }
1269 None => {
1270 return Err(no_common_inferred_type_for_enum(
1271 &enum_name,
1272 ¤t_common_ty,
1273 variant_spans[i - 1],
1274 &variant_ty,
1275 variant_spans[i],
1276 ));
1277 }
1278 },
1279 None => common_ty = Some(variant_ty),
1280 }
1281
1282 names.push(name);
1283 }
1284
1285 Ok(Self(Arc::new(EnumTypeInner {
1286 name: enum_name,
1287 inner_value_type: common_ty.unwrap_or(Type::Union),
1288 variants: names.into(),
1289 })))
1290 }
1291
1292 pub fn name(&self) -> &str {
1294 &self.0.name
1295 }
1296
1297 pub fn inner_value_type(&self) -> &Type {
1299 &self.0.inner_value_type
1300 }
1301
1302 pub fn variants(&self) -> &[String] {
1304 &self.0.variants
1305 }
1306}
1307
1308impl fmt::Display for EnumType {
1309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1310 write!(f, "{name}", name = self.0.name)
1311 }
1312}
1313
1314impl Coercible for EnumType {
1315 fn is_coercible_to(&self, target: &Self) -> bool {
1316 self == target
1317 }
1318}
1319
1320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1322pub enum CallKind {
1323 Task,
1325 Workflow,
1327}
1328
1329impl fmt::Display for CallKind {
1330 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1331 match self {
1332 Self::Task => write!(f, "task"),
1333 Self::Workflow => write!(f, "workflow"),
1334 }
1335 }
1336}
1337
1338#[derive(Debug, Clone, Eq)]
1340struct CallTypeInner {
1341 kind: CallKind,
1343 namespace: Option<String>,
1345 name: String,
1347 specified: Arc<HashSet<String>>,
1353 inputs: Arc<IndexMap<String, Input>>,
1357 outputs: Arc<IndexMap<String, Output>>,
1363}
1364
1365impl PartialEq for CallTypeInner {
1366 fn eq(&self, other: &Self) -> bool {
1367 std::ptr::eq(self, other)
1368 }
1369}
1370
1371#[derive(Debug, Clone, Eq)]
1375pub struct CallType(Arc<CallTypeInner>);
1376
1377impl CallType {
1378 pub(crate) fn new(
1380 kind: CallKind,
1381 name: impl Into<String>,
1382 specified: Arc<HashSet<String>>,
1383 inputs: Arc<IndexMap<String, Input>>,
1384 outputs: Arc<IndexMap<String, Output>>,
1385 ) -> Self {
1386 Self(Arc::new(CallTypeInner {
1387 kind,
1388 namespace: None,
1389 name: name.into(),
1390 specified,
1391 inputs,
1392 outputs,
1393 }))
1394 }
1395
1396 pub(crate) fn namespaced(
1399 kind: CallKind,
1400 namespace: impl Into<String>,
1401 name: impl Into<String>,
1402 specified: Arc<HashSet<String>>,
1403 inputs: Arc<IndexMap<String, Input>>,
1404 outputs: Arc<IndexMap<String, Output>>,
1405 ) -> Self {
1406 Self(Arc::new(CallTypeInner {
1407 kind,
1408 namespace: Some(namespace.into()),
1409 name: name.into(),
1410 specified,
1411 inputs,
1412 outputs,
1413 }))
1414 }
1415
1416 pub fn kind(&self) -> CallKind {
1418 self.0.kind
1419 }
1420
1421 pub fn namespace(&self) -> Option<&str> {
1425 self.0.namespace.as_deref()
1426 }
1427
1428 pub fn name(&self) -> &str {
1430 &self.0.name
1431 }
1432
1433 pub fn specified(&self) -> &HashSet<String> {
1435 &self.0.specified
1436 }
1437
1438 pub fn inputs(&self) -> &IndexMap<String, Input> {
1440 &self.0.inputs
1441 }
1442
1443 pub fn outputs(&self) -> &IndexMap<String, Output> {
1445 &self.0.outputs
1446 }
1447
1448 pub fn optional(&self) -> Self {
1450 let mut inner = self.0.as_ref().clone();
1451 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1452 *output = Output::new(output.ty().optional(), output.name_span());
1453 }
1454
1455 Self(Arc::new(inner))
1456 }
1457
1458 pub fn promote_scatter(&self) -> Self {
1460 let mut inner = self.0.as_ref().clone();
1461 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1462 *output = Output::new(output.ty().promote_scatter(), output.name_span());
1463 }
1464
1465 Self(Arc::new(inner))
1466 }
1467}
1468
1469impl Coercible for CallType {
1470 fn is_coercible_to(&self, _: &Self) -> bool {
1471 false
1473 }
1474}
1475
1476impl fmt::Display for CallType {
1477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1478 if let Some(ns) = &self.0.namespace {
1479 write!(
1480 f,
1481 "call to {kind} `{ns}.{name}`",
1482 kind = self.0.kind,
1483 name = self.0.name,
1484 )
1485 } else {
1486 write!(
1487 f,
1488 "call to {kind} `{name}`",
1489 kind = self.0.kind,
1490 name = self.0.name,
1491 )
1492 }
1493 }
1494}
1495
1496impl PartialEq for CallType {
1497 fn eq(&self, other: &Self) -> bool {
1498 Arc::ptr_eq(&self.0, &other.0)
1499 }
1500}
1501
1502#[cfg(test)]
1503mod test {
1504 use pretty_assertions::assert_eq;
1505
1506 use super::*;
1507
1508 #[test]
1509 fn primitive_type_display() {
1510 assert_eq!(PrimitiveType::Boolean.to_string(), "Boolean");
1511 assert_eq!(PrimitiveType::Integer.to_string(), "Int");
1512 assert_eq!(PrimitiveType::Float.to_string(), "Float");
1513 assert_eq!(PrimitiveType::String.to_string(), "String");
1514 assert_eq!(PrimitiveType::File.to_string(), "File");
1515 assert_eq!(PrimitiveType::Directory.to_string(), "Directory");
1516 assert_eq!(
1517 Type::from(PrimitiveType::Boolean).optional().to_string(),
1518 "Boolean?"
1519 );
1520 assert_eq!(
1521 Type::from(PrimitiveType::Integer).optional().to_string(),
1522 "Int?"
1523 );
1524 assert_eq!(
1525 Type::from(PrimitiveType::Float).optional().to_string(),
1526 "Float?"
1527 );
1528 assert_eq!(
1529 Type::from(PrimitiveType::String).optional().to_string(),
1530 "String?"
1531 );
1532 assert_eq!(
1533 Type::from(PrimitiveType::File).optional().to_string(),
1534 "File?"
1535 );
1536 assert_eq!(
1537 Type::from(PrimitiveType::Directory).optional().to_string(),
1538 "Directory?"
1539 );
1540 }
1541
1542 #[test]
1543 fn array_type_display() {
1544 assert_eq!(
1545 ArrayType::new(PrimitiveType::String).to_string(),
1546 "Array[String]"
1547 );
1548 assert_eq!(
1549 ArrayType::non_empty(PrimitiveType::String).to_string(),
1550 "Array[String]+"
1551 );
1552
1553 let ty: Type = ArrayType::new(ArrayType::new(PrimitiveType::String)).into();
1554 assert_eq!(ty.to_string(), "Array[Array[String]]");
1555
1556 let ty = Type::from(ArrayType::non_empty(
1557 Type::from(ArrayType::non_empty(
1558 Type::from(PrimitiveType::String).optional(),
1559 ))
1560 .optional(),
1561 ))
1562 .optional();
1563 assert_eq!(ty.to_string(), "Array[Array[String?]+?]+?");
1564 }
1565
1566 #[test]
1567 fn pair_type_display() {
1568 assert_eq!(
1569 PairType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1570 "Pair[String, Boolean]"
1571 );
1572
1573 let ty: Type = PairType::new(
1574 ArrayType::new(PrimitiveType::String),
1575 ArrayType::new(PrimitiveType::String),
1576 )
1577 .into();
1578 assert_eq!(ty.to_string(), "Pair[Array[String], Array[String]]");
1579
1580 let ty = Type::from(PairType::new(
1581 Type::from(ArrayType::non_empty(
1582 Type::from(PrimitiveType::File).optional(),
1583 ))
1584 .optional(),
1585 Type::from(ArrayType::non_empty(
1586 Type::from(PrimitiveType::File).optional(),
1587 ))
1588 .optional(),
1589 ))
1590 .optional();
1591 assert_eq!(ty.to_string(), "Pair[Array[File?]+?, Array[File?]+?]?");
1592 }
1593
1594 #[test]
1595 fn map_type_display() {
1596 assert_eq!(
1597 MapType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1598 "Map[String, Boolean]"
1599 );
1600
1601 let ty: Type = MapType::new(
1602 PrimitiveType::Boolean,
1603 ArrayType::new(PrimitiveType::String),
1604 )
1605 .into();
1606 assert_eq!(ty.to_string(), "Map[Boolean, Array[String]]");
1607
1608 let ty: Type = Type::from(MapType::new(
1609 PrimitiveType::String,
1610 Type::from(ArrayType::non_empty(
1611 Type::from(PrimitiveType::File).optional(),
1612 ))
1613 .optional(),
1614 ))
1615 .optional();
1616 assert_eq!(ty.to_string(), "Map[String, Array[File?]+?]?");
1617 }
1618
1619 #[test]
1620 fn struct_type_display() {
1621 assert_eq!(
1622 StructType::new("Foobar", std::iter::empty::<(String, Type)>()).to_string(),
1623 "Foobar"
1624 );
1625 }
1626
1627 #[test]
1628 fn object_type_display() {
1629 assert_eq!(Type::Object.to_string(), "Object");
1630 assert_eq!(Type::OptionalObject.to_string(), "Object?");
1631 }
1632
1633 #[test]
1634 fn union_type_display() {
1635 assert_eq!(Type::Union.to_string(), "Union");
1636 }
1637
1638 #[test]
1639 fn none_type_display() {
1640 assert_eq!(Type::None.to_string(), "None");
1641 }
1642
1643 #[test]
1644 fn primitive_type_coercion() {
1645 for ty in [
1648 Type::from(PrimitiveType::Boolean),
1649 PrimitiveType::Directory.into(),
1650 PrimitiveType::File.into(),
1651 PrimitiveType::Float.into(),
1652 PrimitiveType::Integer.into(),
1653 PrimitiveType::String.into(),
1654 ] {
1655 assert!(ty.is_coercible_to(&ty));
1656 assert!(ty.optional().is_coercible_to(&ty.optional()));
1657 assert!(ty.is_coercible_to(&ty.optional()));
1658 assert!(!ty.optional().is_coercible_to(&ty));
1659 }
1660
1661 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::File));
1663 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::Directory));
1664 assert!(PrimitiveType::Integer.is_coercible_to(&PrimitiveType::Float));
1665 assert!(PrimitiveType::File.is_coercible_to(&PrimitiveType::String));
1666 assert!(PrimitiveType::Directory.is_coercible_to(&PrimitiveType::String));
1667 assert!(!PrimitiveType::Float.is_coercible_to(&PrimitiveType::Integer));
1668 }
1669
1670 #[test]
1671 fn object_type_coercion() {
1672 assert!(Type::Object.is_coercible_to(&Type::Object));
1673 assert!(Type::Object.is_coercible_to(&Type::OptionalObject));
1674 assert!(Type::OptionalObject.is_coercible_to(&Type::OptionalObject));
1675 assert!(!Type::OptionalObject.is_coercible_to(&Type::Object));
1676
1677 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1679 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1680
1681 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1683 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1684
1685 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
1687 assert!(!Type::Object.is_coercible_to(&ty));
1688
1689 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1691 assert!(Type::Object.is_coercible_to(&ty));
1692
1693 let ty = Type::from(MapType::new(PrimitiveType::File, PrimitiveType::String)).optional();
1695 assert!(Type::Object.is_coercible_to(&ty));
1696
1697 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1699 assert!(Type::OptionalObject.is_coercible_to(&ty));
1700
1701 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1703 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1704
1705 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1707 assert!(Type::Object.is_coercible_to(&ty));
1708
1709 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1711 assert!(Type::Object.is_coercible_to(&ty));
1712
1713 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1715 assert!(Type::OptionalObject.is_coercible_to(&ty));
1716
1717 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1719 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1720 }
1721
1722 #[test]
1723 fn array_type_coercion() {
1724 assert!(
1726 ArrayType::new(PrimitiveType::String)
1727 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1728 );
1729 assert!(
1730 ArrayType::new(PrimitiveType::File)
1731 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1732 );
1733 assert!(
1734 ArrayType::new(PrimitiveType::String)
1735 .is_coercible_to(&ArrayType::new(PrimitiveType::File))
1736 );
1737
1738 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1740 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1741 assert!(type1.is_coercible_to(&type2));
1742 assert!(!type2.is_coercible_to(&type1));
1743
1744 let type1: Type = ArrayType::new(type1).into();
1746 let type2 = ArrayType::new(type2).into();
1747 assert!(type1.is_coercible_to(&type2));
1748 assert!(!type2.is_coercible_to(&type1));
1749
1750 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1752 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1753 assert!(type1.is_coercible_to(&type2));
1754 assert!(!type2.is_coercible_to(&type1));
1755
1756 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1758 let type2 = ArrayType::new(Type::from(PrimitiveType::String).optional()).into();
1759 assert!(type1.is_coercible_to(&type2));
1760 assert!(!type2.is_coercible_to(&type1));
1761
1762 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1764 let type2 = ArrayType::new(PrimitiveType::String).into();
1765 assert!(type1.is_coercible_to(&type2));
1766 assert!(type2.is_coercible_to(&type1));
1767
1768 let type1 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1770 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1771 assert!(type1.is_coercible_to(&type2));
1772 assert!(type2.is_coercible_to(&type1));
1773
1774 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1776 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1777 assert!(type1.is_coercible_to(&type2));
1778 assert!(!type2.is_coercible_to(&type1));
1779 }
1780
1781 #[test]
1782 fn pair_type_coercion() {
1783 assert!(
1785 PairType::new(PrimitiveType::String, PrimitiveType::String)
1786 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1787 );
1788 assert!(
1789 PairType::new(PrimitiveType::String, PrimitiveType::String).is_coercible_to(
1790 &PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1791 )
1792 );
1793 assert!(
1794 PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1795 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1796 );
1797
1798 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1800 let type2 = PairType::new(
1801 Type::from(PrimitiveType::File).optional(),
1802 Type::from(PrimitiveType::Directory).optional(),
1803 )
1804 .into();
1805 assert!(type1.is_coercible_to(&type2));
1806 assert!(!type2.is_coercible_to(&type1));
1807
1808 let type1: Type = PairType::new(type1.clone(), type1).into();
1810 let type2 = PairType::new(type2.clone(), type2).into();
1811 assert!(type1.is_coercible_to(&type2));
1812 assert!(!type2.is_coercible_to(&type1));
1813
1814 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1816 let type2 = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1817 assert!(type1.is_coercible_to(&type2));
1818 assert!(type2.is_coercible_to(&type1));
1819
1820 let type1 =
1822 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1823 let type2 =
1824 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1825 assert!(type1.is_coercible_to(&type2));
1826 assert!(type2.is_coercible_to(&type1));
1827
1828 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1830 let type2 =
1831 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1832 assert!(type1.is_coercible_to(&type2));
1833 assert!(!type2.is_coercible_to(&type1));
1834 }
1835
1836 #[test]
1837 fn map_type_coercion() {
1838 assert!(
1840 MapType::new(PrimitiveType::String, PrimitiveType::String)
1841 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1842 );
1843 assert!(
1844 MapType::new(PrimitiveType::String, PrimitiveType::String)
1845 .is_coercible_to(&MapType::new(PrimitiveType::File, PrimitiveType::Directory))
1846 );
1847 assert!(
1848 MapType::new(PrimitiveType::File, PrimitiveType::Directory)
1849 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1850 );
1851
1852 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1854 let type2 = MapType::new(
1855 PrimitiveType::File,
1856 Type::from(PrimitiveType::Directory).optional(),
1857 )
1858 .into();
1859 assert!(type1.is_coercible_to(&type2));
1860 assert!(!type2.is_coercible_to(&type1));
1861
1862 let type1: Type = MapType::new(PrimitiveType::String, type1).into();
1864 let type2 = MapType::new(PrimitiveType::Directory, type2).into();
1865 assert!(type1.is_coercible_to(&type2));
1866 assert!(!type2.is_coercible_to(&type1));
1867
1868 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1870 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1871 assert!(type1.is_coercible_to(&type2));
1872 assert!(type2.is_coercible_to(&type1));
1873
1874 let type1: Type =
1876 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1877 let type2: Type =
1878 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1879 assert!(type1.is_coercible_to(&type2));
1880 assert!(type2.is_coercible_to(&type1));
1881
1882 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1884 let type2 =
1885 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1886 assert!(type1.is_coercible_to(&type2));
1887 assert!(!type2.is_coercible_to(&type1));
1888
1889 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1891 let type2 = StructType::new(
1892 "Foo",
1893 [
1894 ("foo", PrimitiveType::Integer),
1895 ("bar", PrimitiveType::Integer),
1896 ("baz", PrimitiveType::Integer),
1897 ],
1898 )
1899 .into();
1900 assert!(type1.is_coercible_to(&type2));
1901
1902 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1904 let type2 = StructType::new(
1905 "Foo",
1906 [
1907 ("foo", PrimitiveType::Integer),
1908 ("bar", PrimitiveType::Integer),
1909 ("baz", PrimitiveType::Integer),
1910 ],
1911 )
1912 .into();
1913 assert!(type1.is_coercible_to(&type2));
1914
1915 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1917 let type2 = StructType::new(
1918 "Foo",
1919 [
1920 ("foo", PrimitiveType::Integer),
1921 ("bar", PrimitiveType::String),
1922 ("baz", PrimitiveType::Integer),
1923 ],
1924 )
1925 .into();
1926 assert!(!type1.is_coercible_to(&type2));
1927
1928 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
1930 let type2 = StructType::new(
1931 "Foo",
1932 [
1933 ("foo", PrimitiveType::Integer),
1934 ("bar", PrimitiveType::Integer),
1935 ("baz", PrimitiveType::Integer),
1936 ],
1937 )
1938 .into();
1939 assert!(!type1.is_coercible_to(&type2));
1940
1941 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1943 assert!(type1.is_coercible_to(&Type::Object));
1944
1945 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1947 assert!(type1.is_coercible_to(&Type::OptionalObject));
1948
1949 let type1: Type =
1951 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
1952 assert!(type1.is_coercible_to(&Type::OptionalObject));
1953
1954 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1956 assert!(type1.is_coercible_to(&Type::Object));
1957
1958 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1960 assert!(type1.is_coercible_to(&Type::OptionalObject));
1961
1962 let type1: Type =
1964 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
1965 assert!(type1.is_coercible_to(&Type::OptionalObject));
1966
1967 let type1: Type =
1969 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
1970 assert!(!type1.is_coercible_to(&Type::Object));
1971
1972 let type1: Type =
1974 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
1975 assert!(!type1.is_coercible_to(&Type::Object));
1976
1977 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
1979 assert!(!type1.is_coercible_to(&Type::Object));
1980 }
1981
1982 #[test]
1983 fn struct_type_coercion() {
1984 let type1: Type = StructType::new(
1986 "Foo",
1987 [
1988 ("foo", PrimitiveType::String),
1989 ("bar", PrimitiveType::String),
1990 ("baz", PrimitiveType::Integer),
1991 ],
1992 )
1993 .into();
1994 let type2 = StructType::new(
1995 "Foo",
1996 [
1997 ("foo", PrimitiveType::String),
1998 ("bar", PrimitiveType::String),
1999 ("baz", PrimitiveType::Integer),
2000 ],
2001 )
2002 .into();
2003 assert!(type1.is_coercible_to(&type2));
2004 assert!(type2.is_coercible_to(&type1));
2005
2006 let type1: Type = StructType::new(
2008 "Foo",
2009 [
2010 ("foo", PrimitiveType::String),
2011 ("bar", PrimitiveType::String),
2012 ("baz", PrimitiveType::Integer),
2013 ],
2014 )
2015 .into();
2016 let type2 = Type::from(StructType::new(
2017 "Foo",
2018 [
2019 ("foo", PrimitiveType::String),
2020 ("bar", PrimitiveType::String),
2021 ("baz", PrimitiveType::Integer),
2022 ],
2023 ))
2024 .optional();
2025 assert!(type1.is_coercible_to(&type2));
2026 assert!(!type2.is_coercible_to(&type1));
2027
2028 let type1: Type = Type::from(StructType::new(
2030 "Foo",
2031 [
2032 ("foo", PrimitiveType::String),
2033 ("bar", PrimitiveType::String),
2034 ("baz", PrimitiveType::Integer),
2035 ],
2036 ))
2037 .optional();
2038 let type2 = Type::from(StructType::new(
2039 "Foo",
2040 [
2041 ("foo", PrimitiveType::String),
2042 ("bar", PrimitiveType::String),
2043 ("baz", PrimitiveType::Integer),
2044 ],
2045 ))
2046 .optional();
2047 assert!(type1.is_coercible_to(&type2));
2048 assert!(type2.is_coercible_to(&type1));
2049
2050 let type1: Type = StructType::new(
2052 "Foo",
2053 [
2054 ("foo", PrimitiveType::String),
2055 ("bar", PrimitiveType::String),
2056 ("baz", PrimitiveType::Integer),
2057 ],
2058 )
2059 .into();
2060 let type2 = StructType::new(
2061 "Bar",
2062 [
2063 ("foo", PrimitiveType::File),
2064 ("bar", PrimitiveType::Directory),
2065 ("baz", PrimitiveType::Float),
2066 ],
2067 )
2068 .into();
2069 assert!(type1.is_coercible_to(&type2));
2070 assert!(!type2.is_coercible_to(&type1));
2071
2072 let type1: Type = StructType::new(
2074 "Foo",
2075 [
2076 ("foo", PrimitiveType::String),
2077 ("bar", PrimitiveType::String),
2078 ("baz", PrimitiveType::Integer),
2079 ],
2080 )
2081 .into();
2082 let type2 = StructType::new("Bar", [("baz", PrimitiveType::Float)]).into();
2083 assert!(!type1.is_coercible_to(&type2));
2084 assert!(!type2.is_coercible_to(&type1));
2085
2086 let type1: Type = StructType::new(
2088 "Foo",
2089 [
2090 ("foo", PrimitiveType::String),
2091 ("bar", PrimitiveType::String),
2092 ("baz", PrimitiveType::String),
2093 ],
2094 )
2095 .into();
2096 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2097 assert!(type1.is_coercible_to(&type2));
2098
2099 let type1: Type = StructType::new(
2101 "Foo",
2102 [
2103 ("foo", PrimitiveType::String),
2104 ("bar", PrimitiveType::String),
2105 ("baz", PrimitiveType::String),
2106 ],
2107 )
2108 .into();
2109 let type2 = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
2110 assert!(type1.is_coercible_to(&type2));
2111
2112 let type1: Type = StructType::new(
2114 "Foo",
2115 [
2116 ("foo", PrimitiveType::String),
2117 ("bar", PrimitiveType::Integer),
2118 ("baz", PrimitiveType::String),
2119 ],
2120 )
2121 .into();
2122 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2123 assert!(!type1.is_coercible_to(&type2));
2124
2125 let type1: Type = StructType::new(
2127 "Foo",
2128 [
2129 ("foo", PrimitiveType::String),
2130 ("bar", PrimitiveType::String),
2131 ("baz", PrimitiveType::String),
2132 ],
2133 )
2134 .into();
2135 let type2 = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2136 assert!(!type1.is_coercible_to(&type2));
2137
2138 assert!(type1.is_coercible_to(&Type::Object));
2140
2141 assert!(type1.is_coercible_to(&Type::OptionalObject));
2143
2144 let type1: Type =
2146 Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
2147 assert!(type1.is_coercible_to(&Type::OptionalObject));
2148
2149 assert!(!type1.is_coercible_to(&Type::Object));
2151 }
2152
2153 #[test]
2154 fn union_type_coercion() {
2155 for ty in [
2157 Type::from(PrimitiveType::Boolean),
2158 PrimitiveType::Directory.into(),
2159 PrimitiveType::File.into(),
2160 PrimitiveType::Float.into(),
2161 PrimitiveType::Integer.into(),
2162 PrimitiveType::String.into(),
2163 ] {
2164 assert!(Type::Union.is_coercible_to(&ty));
2165 assert!(Type::Union.is_coercible_to(&ty.optional()));
2166 assert!(ty.is_coercible_to(&Type::Union));
2167 }
2168
2169 for optional in [true, false] {
2170 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2172 let ty = if optional { ty.optional() } else { ty };
2173
2174 let coercible = Type::Union.is_coercible_to(&ty);
2175 assert!(coercible);
2176
2177 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2179 let ty = if optional { ty.optional() } else { ty };
2180 let coercible = Type::Union.is_coercible_to(&ty);
2181 assert!(coercible);
2182
2183 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2185 let ty = if optional { ty.optional() } else { ty };
2186 let coercible = Type::Union.is_coercible_to(&ty);
2187 assert!(coercible);
2188
2189 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2191 let ty = if optional { ty.optional() } else { ty };
2192 let coercible = Type::Union.is_coercible_to(&ty);
2193 assert!(coercible);
2194 }
2195 }
2196
2197 #[test]
2198 fn none_type_coercion() {
2199 for ty in [
2201 Type::from(PrimitiveType::Boolean),
2202 PrimitiveType::Directory.into(),
2203 PrimitiveType::File.into(),
2204 PrimitiveType::Float.into(),
2205 PrimitiveType::Integer.into(),
2206 PrimitiveType::String.into(),
2207 ] {
2208 assert!(!Type::None.is_coercible_to(&ty));
2209 assert!(Type::None.is_coercible_to(&ty.optional()));
2210 assert!(!ty.is_coercible_to(&Type::None));
2211 }
2212
2213 for optional in [true, false] {
2214 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2216 let ty = if optional { ty.optional() } else { ty };
2217 let coercible = Type::None.is_coercible_to(&ty);
2218 if optional {
2219 assert!(coercible);
2220 } else {
2221 assert!(!coercible);
2222 }
2223
2224 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2226 let ty = if optional { ty.optional() } else { ty };
2227 let coercible = Type::None.is_coercible_to(&ty);
2228 if optional {
2229 assert!(coercible);
2230 } else {
2231 assert!(!coercible);
2232 }
2233
2234 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2236 let ty = if optional { ty.optional() } else { ty };
2237 let coercible = Type::None.is_coercible_to(&ty);
2238 if optional {
2239 assert!(coercible);
2240 } else {
2241 assert!(!coercible);
2242 }
2243
2244 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2246 let ty = if optional { ty.optional() } else { ty };
2247 let coercible = Type::None.is_coercible_to(&ty);
2248 if optional {
2249 assert!(coercible);
2250 } else {
2251 assert!(!coercible);
2252 }
2253 }
2254 }
2255
2256 #[test]
2257 fn primitive_equality() {
2258 for ty in [
2259 Type::from(PrimitiveType::Boolean),
2260 PrimitiveType::Directory.into(),
2261 PrimitiveType::File.into(),
2262 PrimitiveType::Float.into(),
2263 PrimitiveType::Integer.into(),
2264 PrimitiveType::String.into(),
2265 ] {
2266 assert!(ty.eq(&ty));
2267 assert!(!ty.optional().eq(&ty));
2268 assert!(!ty.eq(&ty.optional()));
2269 assert!(ty.optional().eq(&ty.optional()));
2270 assert!(!ty.eq(&Type::Object));
2271 assert!(!ty.eq(&Type::OptionalObject));
2272 assert!(!ty.eq(&Type::Union));
2273 assert!(!ty.eq(&Type::None));
2274 }
2275 }
2276
2277 #[test]
2278 fn array_equality() {
2279 let a: Type = ArrayType::new(PrimitiveType::String).into();
2281 let b: Type = ArrayType::new(PrimitiveType::String).into();
2282 assert!(a.eq(&b));
2283 assert!(!a.optional().eq(&b));
2284 assert!(!a.eq(&b.optional()));
2285 assert!(a.optional().eq(&b.optional()));
2286
2287 let a: Type = ArrayType::new(a).into();
2289 let b: Type = ArrayType::new(b).into();
2290 assert!(a.eq(&b));
2291
2292 let a: Type = ArrayType::non_empty(a).into();
2294 let b: Type = ArrayType::non_empty(b).into();
2295 assert!(a.eq(&b));
2296
2297 let a: Type = ArrayType::new(PrimitiveType::String).into();
2299 let b: Type = ArrayType::non_empty(PrimitiveType::String).into();
2300 assert!(!a.eq(&b));
2301
2302 let a: Type = ArrayType::new(PrimitiveType::String).into();
2304 let b: Type = ArrayType::new(PrimitiveType::Integer).into();
2305 assert!(!a.eq(&b));
2306
2307 assert!(!a.eq(&Type::Object));
2308 assert!(!a.eq(&Type::OptionalObject));
2309 assert!(!a.eq(&Type::Union));
2310 assert!(!a.eq(&Type::None));
2311 }
2312
2313 #[test]
2314 fn pair_equality() {
2315 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2317 let b: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2318 assert!(a.eq(&b));
2319 assert!(!a.optional().eq(&b));
2320 assert!(!a.eq(&b.optional()));
2321 assert!(a.optional().eq(&b.optional()));
2322
2323 let a: Type = PairType::new(a.clone(), a).into();
2326 let b: Type = PairType::new(b.clone(), b).into();
2327 assert!(a.eq(&b));
2328
2329 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2331 let b: Type =
2332 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2333 assert!(!a.eq(&b));
2334
2335 assert!(!a.eq(&Type::Object));
2336 assert!(!a.eq(&Type::OptionalObject));
2337 assert!(!a.eq(&Type::Union));
2338 assert!(!a.eq(&Type::None));
2339 }
2340
2341 #[test]
2342 fn map_equality() {
2343 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2345 let b = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2346 assert!(a.eq(&b));
2347 assert!(!a.optional().eq(&b));
2348 assert!(!a.eq(&b.optional()));
2349 assert!(a.optional().eq(&b.optional()));
2350
2351 let a: Type = MapType::new(PrimitiveType::File, a).into();
2353 let b = MapType::new(PrimitiveType::File, b).into();
2354 assert!(a.eq(&b));
2355
2356 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2358 let b = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2359 assert!(!a.eq(&b));
2360
2361 assert!(!a.eq(&Type::Object));
2362 assert!(!a.eq(&Type::OptionalObject));
2363 assert!(!a.eq(&Type::Union));
2364 assert!(!a.eq(&Type::None));
2365 }
2366
2367 #[test]
2368 fn struct_equality() {
2369 let a: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2370 assert!(a.eq(&a));
2371 assert!(!a.optional().eq(&a));
2372 assert!(!a.eq(&a.optional()));
2373 assert!(a.optional().eq(&a.optional()));
2374
2375 let b: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2376 assert!(a.eq(&b));
2377 let b: Type = StructType::new("Bar", [("foo", PrimitiveType::String)]).into();
2378 assert!(!a.eq(&b));
2379 }
2380
2381 #[test]
2382 fn object_equality() {
2383 assert!(Type::Object.eq(&Type::Object));
2384 assert!(!Type::OptionalObject.eq(&Type::Object));
2385 assert!(!Type::Object.eq(&Type::OptionalObject));
2386 assert!(Type::OptionalObject.eq(&Type::OptionalObject));
2387 }
2388
2389 #[test]
2390 fn union_equality() {
2391 assert!(Type::Union.eq(&Type::Union));
2392 assert!(!Type::None.eq(&Type::Union));
2393 assert!(!Type::Union.eq(&Type::None));
2394 assert!(Type::None.eq(&Type::None));
2395 }
2396
2397 #[test]
2398 fn enum_type_new_with_explicit_type() {
2399 let status = EnumType::new(
2401 "Status",
2402 Span::new(0, 0),
2403 PrimitiveType::String.into(),
2404 vec![
2405 ("Pending".into(), PrimitiveType::String.into()),
2406 ("Running".into(), PrimitiveType::String.into()),
2407 ("Complete".into(), PrimitiveType::String.into()),
2408 ],
2409 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)][..],
2410 )
2411 .unwrap();
2412
2413 assert_eq!(status.name(), "Status");
2414 assert_eq!(
2415 status.inner_value_type(),
2416 &Type::from(PrimitiveType::String)
2417 );
2418 assert_eq!(status.variants().len(), 3);
2419 }
2420
2421 #[test]
2422 fn enum_type_new_fails_when_not_coercible() {
2423 let result = EnumType::new(
2425 "Bad",
2426 Span::new(0, 0),
2427 PrimitiveType::Integer.into(),
2428 vec![
2429 ("First".into(), PrimitiveType::String.into()),
2430 ("Second".into(), PrimitiveType::Integer.into()),
2431 ],
2432 &[Span::new(0, 0), Span::new(0, 0)][..],
2433 );
2434
2435 assert!(
2436 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot coerce variant `First` in enum `Bad` from type `String` to type `Int`")
2437 );
2438 }
2439
2440 #[test]
2441 fn enum_type_infer_finds_common_type() {
2442 let priority = EnumType::infer(
2444 "Priority",
2445 vec![
2446 ("Low".into(), PrimitiveType::Integer.into()),
2447 ("Medium".into(), PrimitiveType::Integer.into()),
2448 ("High".into(), PrimitiveType::Integer.into()),
2449 ],
2450 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)],
2451 )
2452 .unwrap();
2453
2454 assert_eq!(priority.name(), "Priority");
2455 assert_eq!(
2456 priority.inner_value_type(),
2457 &Type::from(PrimitiveType::Integer)
2458 );
2459 assert_eq!(priority.variants().len(), 3);
2460 }
2461
2462 #[test]
2463 fn enum_type_infer_coerces_int_to_float() {
2464 let mixed = EnumType::infer(
2466 "Mixed",
2467 vec![
2468 ("IntValue".into(), PrimitiveType::Integer.into()),
2469 ("FloatValue".into(), PrimitiveType::Float.into()),
2470 ],
2471 &[Span::new(0, 0), Span::new(0, 0)],
2472 )
2473 .unwrap();
2474
2475 assert_eq!(mixed.name(), "Mixed");
2476 assert_eq!(mixed.inner_value_type(), &Type::from(PrimitiveType::Float));
2477 assert_eq!(mixed.variants().len(), 2);
2478 }
2479
2480 #[test]
2481 fn enum_type_infer_fails_without_common_type() {
2482 let result = EnumType::infer(
2484 "Bad",
2485 vec![
2486 ("StringVal".into(), PrimitiveType::String.into()),
2487 ("IntVal".into(), PrimitiveType::Integer.into()),
2488 ],
2489 &[Span::new(0, 0), Span::new(0, 0)][..],
2490 );
2491
2492 assert!(
2493 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot infer a common type for enum `Bad`")
2494 );
2495 }
2496
2497 #[test]
2498 fn enum_type_empty_has_union_type() {
2499 let result = EnumType::infer("Empty", Vec::<(String, Type)>::new(), &[]);
2501
2502 let empty = result.unwrap();
2503 assert_eq!(empty.name(), "Empty");
2504 assert_eq!(empty.inner_value_type(), &Type::Union);
2505 assert_eq!(empty.variants().len(), 0);
2506 }
2507
2508 #[test]
2509 fn enum_type_display() {
2510 let enum_type = EnumType::new(
2511 "Color",
2512 Span::new(0, 0),
2513 PrimitiveType::String.into(),
2514 vec![("Red".into(), PrimitiveType::String.into())],
2515 &[Span::new(0, 0)][..],
2516 )
2517 .unwrap();
2518 assert_eq!(enum_type.to_string(), "Color");
2519 }
2520
2521 #[test]
2522 fn enum_type_not_coercible_to_other_enums() {
2523 let color = EnumType::new(
2524 "Color",
2525 Span::new(0, 0),
2526 PrimitiveType::String.into(),
2527 vec![("Red".into(), PrimitiveType::String.into())],
2528 &[Span::new(0, 0)][..],
2529 )
2530 .unwrap();
2531 let status = EnumType::new(
2532 "Status",
2533 Span::new(0, 0),
2534 PrimitiveType::String.into(),
2535 vec![("Active".into(), PrimitiveType::String.into())],
2536 &[Span::new(0, 0)][..],
2537 )
2538 .unwrap();
2539 assert!(!color.is_coercible_to(&status));
2540 }
2541}