1use std::collections::HashSet;
4use std::fmt;
5use std::sync::Arc;
6
7use indexmap::IndexMap;
8use url::Url;
9use wdl_ast::Diagnostic;
10use wdl_ast::Span;
11
12use crate::diagnostics::enum_choice_does_not_coerce_to_type;
13use crate::diagnostics::no_common_inferred_type_for_enum;
14use crate::document::Input;
15use crate::document::Output;
16
17pub mod v1;
18
19pub fn display_types(slice: &[Type]) -> impl fmt::Display + use<'_> {
21 struct Display<'a>(&'a [Type]);
23
24 impl fmt::Display for Display<'_> {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 for (i, ty) in self.0.iter().enumerate() {
27 if i > 0 {
28 if self.0.len() == 2 {
29 write!(f, " ")?;
30 } else {
31 write!(f, ", ")?;
32 }
33
34 if i == self.0.len() - 1 {
35 write!(f, "or ")?;
36 }
37 }
38
39 write!(f, "{ty:#}")?;
40 }
41
42 Ok(())
43 }
44 }
45
46 Display(slice)
47}
48
49pub trait TypeNameResolver {
51 fn resolve(&mut self, name: &str, span: Span) -> Result<Type, Diagnostic>;
53}
54
55pub trait Optional {
57 fn is_optional(&self) -> bool;
59
60 fn optional(&self) -> Self;
62
63 fn require(&self) -> Self;
65}
66
67pub trait Coercible {
69 fn is_coercible_to(&self, target: &Self) -> bool;
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum PrimitiveType {
76 Boolean,
78 Integer,
80 Float,
82 String,
84 File,
86 Directory,
88}
89
90impl Coercible for PrimitiveType {
91 fn is_coercible_to(&self, target: &Self) -> bool {
92 if self == target {
93 return true;
94 }
95
96 match (self, target) {
97 (Self::String, Self::File) |
99 (Self::String, Self::Directory) |
101 (Self::Integer, Self::Float) |
103 (Self::File, Self::String) |
105 (Self::Directory, Self::String)
107 => true,
108
109 _ => false
111 }
112 }
113}
114
115impl fmt::Display for PrimitiveType {
116 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
117 match self {
118 Self::Boolean => write!(f, "Boolean")?,
119 Self::Integer => write!(f, "Int")?,
120 Self::Float => write!(f, "Float")?,
121 Self::String => write!(f, "String")?,
122 Self::File => write!(f, "File")?,
123 Self::Directory => write!(f, "Directory")?,
124 }
125
126 Ok(())
127 }
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum HiddenType {
136 Hints,
138 Input,
140 Output,
142 TaskPreEvaluation,
145 TaskPostEvaluation,
148 PreviousTaskData,
153}
154
155impl fmt::Display for HiddenType {
156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
157 match self {
158 Self::Hints => write!(f, "hints"),
159 Self::Input => write!(f, "input"),
160 Self::Output => write!(f, "output"),
161 Self::TaskPreEvaluation | Self::TaskPostEvaluation => write!(f, "task"),
162 Self::PreviousTaskData => write!(f, "task.previous"),
163 }
164 }
165}
166
167#[derive(Debug, Clone, PartialEq, Eq)]
169pub enum Type {
170 Primitive(PrimitiveType, bool),
174 Compound(CompoundType, bool),
178 Object,
180 OptionalObject,
182 Union,
188 None,
190 Hidden(HiddenType),
195 Call(CallType),
197 TypeNameRef(TypeNameRef),
199}
200
201const _: () = {
205 assert!(std::mem::size_of::<Type>() <= 24);
206};
207
208impl Type {
209 pub fn as_primitive(&self) -> Option<PrimitiveType> {
213 match self {
214 Self::Primitive(ty, _) => Some(*ty),
215 _ => None,
216 }
217 }
218
219 pub fn as_compound(&self) -> Option<&CompoundType> {
223 match self {
224 Self::Compound(ty, _) => Some(ty),
225 _ => None,
226 }
227 }
228
229 pub fn as_array(&self) -> Option<&ArrayType> {
233 match self {
234 Self::Compound(ty, _) => ty.as_array(),
235 _ => None,
236 }
237 }
238
239 pub fn as_pair(&self) -> Option<&PairType> {
243 match self {
244 Self::Compound(ty, _) => ty.as_pair(),
245 _ => None,
246 }
247 }
248
249 pub fn as_map(&self) -> Option<&MapType> {
253 match self {
254 Self::Compound(ty, _) => ty.as_map(),
255 _ => None,
256 }
257 }
258
259 pub fn as_struct(&self) -> Option<&StructType> {
263 match self {
264 Self::Compound(ty, _) => ty.as_struct(),
265 _ => None,
266 }
267 }
268
269 pub fn as_enum(&self) -> Option<&EnumType> {
273 match self {
274 Self::Compound(ty, _) => ty.as_enum(),
275 _ => None,
276 }
277 }
278
279 pub fn as_custom(&self) -> Option<&CustomType> {
283 match self {
284 Self::Compound(ty, _) => ty.as_custom(),
285 _ => None,
286 }
287 }
288
289 pub fn as_type_name_ref(&self) -> Option<&TypeNameRef> {
293 match self {
294 Self::TypeNameRef(ty) => Some(ty),
295 _ => None,
296 }
297 }
298
299 pub fn as_call(&self) -> Option<&CallType> {
303 match self {
304 Self::Call(ty) => Some(ty),
305 _ => None,
306 }
307 }
308
309 pub fn is_union(&self) -> bool {
311 matches!(self, Type::Union)
312 }
313
314 pub fn is_none(&self) -> bool {
316 matches!(self, Type::None)
317 }
318
319 pub fn promote_scatter(&self) -> Self {
324 if let Self::Call(ty) = self {
326 return Self::Call(ty.promote_scatter());
327 }
328
329 Type::Compound(ArrayType::new(self.clone()).into(), false)
330 }
331
332 pub fn common_type(&self, other: &Type) -> Option<Type> {
336 if other.is_union() {
338 return Some(self.clone());
339 }
340
341 if self.is_union() {
343 return Some(other.clone());
344 }
345
346 if other.is_none() {
349 return Some(self.optional());
350 }
351
352 if self.is_none() {
354 return Some(other.optional());
355 }
356
357 if other.is_coercible_to(self) {
359 return Some(self.clone());
360 }
361
362 if self.is_coercible_to(other) {
364 return Some(other.clone());
365 }
366
367 if let (Some(this), Some(other)) = (self.as_compound(), other.as_compound())
369 && let Some(ty) = this.common_type(other)
370 {
371 return Some(Self::Compound(ty, self.is_optional()));
372 }
373
374 if let (Some(this), Some(other)) = (self.as_call(), self.as_call())
376 && this == other
377 {
378 return Some(Self::Call(this.clone()));
379 }
380
381 None
382 }
383}
384
385impl fmt::Display for Type {
386 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
387 match self {
388 Self::Primitive(ty, optional) => {
389 write!(
390 f,
391 "{prefix}{ty}{opt}{suffix}",
392 prefix = if f.alternate() { "type `" } else { "" },
393 opt = if *optional { "?" } else { "" },
394 suffix = if f.alternate() { "`" } else { "" },
395 )
396 }
397 Self::Compound(CompoundType::Custom(CustomType::Struct(ty)), optional) => {
398 write!(
399 f,
400 "{prefix}{ty}{opt}{suffix}",
401 prefix = if f.alternate() {
402 "an instance of struct `"
403 } else {
404 ""
405 },
406 opt = if *optional { "?" } else { "" },
407 suffix = if f.alternate() { "`" } else { "" },
408 )
409 }
410 Self::Compound(CompoundType::Custom(CustomType::Enum(ty)), optional) => {
411 write!(
412 f,
413 "{prefix}{ty}{opt}{suffix}",
414 prefix = if f.alternate() {
415 "an instance of enum `"
416 } else {
417 ""
418 },
419 opt = if *optional { "?" } else { "" },
420 suffix = if f.alternate() { "`" } else { "" },
421 )
422 }
423 Self::Compound(ty, optional) => {
424 write!(
425 f,
426 "{prefix}{ty}{opt}{suffix}",
427 prefix = if f.alternate() { "type `" } else { "" },
428 opt = if *optional { "?" } else { "" },
429 suffix = if f.alternate() { "`" } else { "" },
430 )
431 }
432 Self::Object => {
433 write!(
434 f,
435 "{prefix}Object{suffix}",
436 prefix = if f.alternate() { "type `" } else { "" },
437 suffix = if f.alternate() { "`" } else { "" },
438 )
439 }
440 Self::OptionalObject => {
441 write!(
442 f,
443 "{prefix}Object?{suffix}",
444 prefix = if f.alternate() { "type `" } else { "" },
445 suffix = if f.alternate() { "`" } else { "" },
446 )
447 }
448 Self::Union => {
449 write!(
450 f,
451 "{prefix}Union{suffix}",
452 prefix = if f.alternate() { "built-in type `" } else { "" },
453 suffix = if f.alternate() { "`" } else { "" },
454 )
455 }
456 Self::None => {
457 write!(
458 f,
459 "{prefix}None{suffix}",
460 prefix = if f.alternate() { "built-in type `" } else { "" },
461 suffix = if f.alternate() { "`" } else { "" },
462 )
463 }
464 Self::Hidden(ty) => {
465 write!(
466 f,
467 "{prefix}{ty}{suffix}",
468 prefix = if f.alternate() { "built-in type `" } else { "" },
469 suffix = if f.alternate() { "`" } else { "" },
470 )
471 }
472 Self::Call(ty) => ty.fmt(f),
473 Self::TypeNameRef(ty) => {
474 write!(
475 f,
476 "{prefix}{ty}{suffix}",
477 prefix = if f.alternate() { "type name `" } else { "" },
478 suffix = if f.alternate() { "`" } else { "" },
479 )
480 }
481 }
482 }
483}
484
485impl Optional for Type {
486 fn is_optional(&self) -> bool {
487 match self {
488 Self::Primitive(_, optional) => *optional,
489 Self::Compound(_, optional) => *optional,
490 Self::OptionalObject | Self::None => true,
491 Self::Object | Self::Union | Self::Hidden(_) | Self::Call(_) | Self::TypeNameRef(_) => {
492 false
493 }
494 }
495 }
496
497 fn optional(&self) -> Self {
498 match self {
499 Self::Primitive(ty, _) => Self::Primitive(*ty, true),
500 Self::Compound(ty, _) => Self::Compound(ty.clone(), true),
501 Self::Object => Self::OptionalObject,
502 Self::Union => Self::None,
503 Self::Call(ty) => Self::Call(ty.optional()),
504 ty => ty.clone(),
505 }
506 }
507
508 fn require(&self) -> Self {
509 match self {
510 Self::Primitive(ty, _) => Self::Primitive(*ty, false),
511 Self::Compound(ty, _) => Self::Compound(ty.clone(), false),
512 Self::OptionalObject => Self::Object,
513 Self::None => Self::Union,
514 ty => ty.clone(),
515 }
516 }
517}
518
519impl Coercible for Type {
520 fn is_coercible_to(&self, target: &Self) -> bool {
521 if self.eq(target) {
522 return true;
523 }
524
525 match (self, target) {
526 (Self::Primitive(src, src_opt), Self::Primitive(target, target_opt)) => {
527 if *src_opt && !*target_opt {
529 return false;
530 }
531
532 src.is_coercible_to(target)
533 }
534 (Self::Compound(src, src_opt), Self::Compound(target, target_opt)) => {
535 if *src_opt && !*target_opt {
537 return false;
538 }
539
540 src.is_coercible_to(target)
541 }
542
543 (Self::Object, Self::Object)
545 | (Self::Object, Self::OptionalObject)
546 | (Self::OptionalObject, Self::OptionalObject) => true,
547
548 (Self::Compound(src, false), Self::Object)
552 | (Self::Compound(src, false), Self::OptionalObject)
553 | (Self::Compound(src, _), Self::OptionalObject) => match src {
554 CompoundType::Map(src) => src
555 .key_type()
556 .is_coercible_to(&PrimitiveType::String.into()),
557 CompoundType::Custom(CustomType::Struct(_)) => true,
558 _ => false,
559 },
560
561 (Self::Object, Self::Compound(target, _))
567 | (Self::OptionalObject, Self::Compound(target, true)) => {
568 match target {
569 CompoundType::Map(target) => {
570 Type::from(PrimitiveType::String).is_coercible_to(target.key_type())
571 }
572 CompoundType::Custom(CustomType::Struct(_)) => {
573 true
575 }
576 _ => false,
577 }
578 }
579
580 (Self::Union, _) | (_, Self::Union) => true,
582
583 (Self::None, ty) if ty.is_optional() => true,
585
586 (
589 Self::Primitive(PrimitiveType::String, _),
590 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
591 )
592 | (
593 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
594 Self::Primitive(PrimitiveType::String, _),
595 ) => true,
596
597 _ => false,
599 }
600 }
601}
602
603impl From<PrimitiveType> for Type {
604 fn from(value: PrimitiveType) -> Self {
605 Self::Primitive(value, false)
606 }
607}
608
609impl From<CompoundType> for Type {
610 fn from(value: CompoundType) -> Self {
611 Self::Compound(value, false)
612 }
613}
614
615impl From<ArrayType> for Type {
616 fn from(value: ArrayType) -> Self {
617 Self::Compound(value.into(), false)
618 }
619}
620
621impl From<PairType> for Type {
622 fn from(value: PairType) -> Self {
623 Self::Compound(value.into(), false)
624 }
625}
626
627impl From<MapType> for Type {
628 fn from(value: MapType) -> Self {
629 Self::Compound(value.into(), false)
630 }
631}
632
633impl From<StructType> for Type {
634 fn from(value: StructType) -> Self {
635 Self::Compound(value.into(), false)
636 }
637}
638
639impl From<EnumType> for Type {
640 fn from(value: EnumType) -> Self {
641 Self::Compound(value.into(), false)
642 }
643}
644
645impl From<CallType> for Type {
646 fn from(value: CallType) -> Self {
647 Self::Call(value)
648 }
649}
650
651impl From<CustomType> for Type {
652 fn from(value: CustomType) -> Self {
653 Self::Compound(CompoundType::Custom(value), false)
654 }
655}
656
657impl From<TypeNameRef> for Type {
658 fn from(value: TypeNameRef) -> Self {
659 Self::TypeNameRef(value)
660 }
661}
662
663#[derive(Debug, Clone, PartialEq, Eq)]
665pub enum CustomType {
666 Struct(StructType),
668 Enum(EnumType),
670}
671
672impl CustomType {
673 pub fn name(&self) -> &str {
675 match self {
676 Self::Struct(ty) => ty.name(),
677 Self::Enum(ty) => ty.name(),
678 }
679 }
680
681 pub fn as_struct(&self) -> Option<&StructType> {
685 match self {
686 Self::Struct(ty) => Some(ty),
687 _ => None,
688 }
689 }
690
691 pub fn as_enum(&self) -> Option<&EnumType> {
695 match self {
696 Self::Enum(ty) => Some(ty),
697 _ => None,
698 }
699 }
700}
701
702impl std::fmt::Display for CustomType {
703 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
704 match self {
705 CustomType::Struct(ty) => ty.fmt(f),
706 CustomType::Enum(ty) => ty.fmt(f),
707 }
708 }
709}
710
711impl From<StructType> for CustomType {
712 fn from(value: StructType) -> Self {
713 Self::Struct(value)
714 }
715}
716
717impl From<EnumType> for CustomType {
718 fn from(value: EnumType) -> Self {
719 Self::Enum(value)
720 }
721}
722
723#[derive(Debug, Clone, PartialEq, Eq)]
725pub enum CompoundType {
726 Array(ArrayType),
728 Pair(PairType),
730 Map(MapType),
732 Custom(CustomType),
734}
735
736impl CompoundType {
737 pub fn as_array(&self) -> Option<&ArrayType> {
741 match self {
742 Self::Array(ty) => Some(ty),
743 _ => None,
744 }
745 }
746
747 pub fn as_pair(&self) -> Option<&PairType> {
751 match self {
752 Self::Pair(ty) => Some(ty),
753 _ => None,
754 }
755 }
756
757 pub fn as_map(&self) -> Option<&MapType> {
761 match self {
762 Self::Map(ty) => Some(ty),
763 _ => None,
764 }
765 }
766
767 pub fn as_struct(&self) -> Option<&StructType> {
771 match self {
772 Self::Custom(ty) => ty.as_struct(),
773 _ => None,
774 }
775 }
776
777 pub fn as_enum(&self) -> Option<&EnumType> {
781 match self {
782 Self::Custom(ty) => ty.as_enum(),
783 _ => None,
784 }
785 }
786
787 pub fn as_custom(&self) -> Option<&CustomType> {
791 match self {
792 Self::Custom(ty) => Some(ty),
793 _ => None,
794 }
795 }
796
797 fn common_type(&self, other: &Self) -> Option<CompoundType> {
802 match (self, other) {
805 (Self::Array(this), Self::Array(other)) => {
806 let element_type = this.element_type().common_type(other.element_type())?;
807 Some(ArrayType::new(element_type).into())
808 }
809 (Self::Pair(this), Self::Pair(other)) => {
810 let left_type = this.left_type().common_type(other.left_type())?;
811 let right_type = this.right_type().common_type(other.right_type())?;
812 Some(PairType::new(left_type, right_type).into())
813 }
814 (Self::Map(this), Self::Map(other)) => {
815 let key_type = this.key_type().common_type(other.key_type())?;
816 let value_type = this.value_type().common_type(other.value_type())?;
817 Some(MapType::new(key_type, value_type).into())
818 }
819 _ => None,
820 }
821 }
822}
823
824impl fmt::Display for CompoundType {
825 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826 match self {
827 Self::Array(ty) => ty.fmt(f),
828 Self::Pair(ty) => ty.fmt(f),
829 Self::Map(ty) => ty.fmt(f),
830 Self::Custom(CustomType::Struct(ty)) => ty.fmt(f),
831 Self::Custom(CustomType::Enum(ty)) => ty.fmt(f),
832 }
833 }
834}
835
836impl Coercible for CompoundType {
837 fn is_coercible_to(&self, target: &Self) -> bool {
838 match (self, target) {
839 (Self::Array(src), Self::Array(target)) => src.is_coercible_to(target),
842
843 (Self::Pair(src), Self::Pair(target)) => src.is_coercible_to(target),
846
847 (Self::Map(src), Self::Map(target)) => src.is_coercible_to(target),
850
851 (Self::Custom(CustomType::Struct(src)), Self::Custom(CustomType::Struct(target))) => {
854 src.is_coercible_to(target)
855 }
856
857 (Self::Custom(CustomType::Enum(src)), Self::Custom(CustomType::Enum(target))) => {
859 src.is_coercible_to(target)
860 }
861
862 (Self::Map(src), Self::Custom(CustomType::Struct(target))) => {
865 if !src
866 .key_type()
867 .is_coercible_to(&PrimitiveType::String.into())
868 {
869 return false;
870 }
871
872 if !target
874 .members()
875 .values()
876 .all(|ty| src.value_type().is_coercible_to(ty))
877 {
878 return false;
879 }
880
881 true
883 }
884
885 (Self::Custom(CustomType::Struct(src)), Self::Map(target)) => {
888 if !Type::from(PrimitiveType::String).is_coercible_to(target.key_type()) {
889 return false;
890 }
891
892 if !src
894 .members()
895 .values()
896 .all(|ty| ty.is_coercible_to(target.value_type()))
897 {
898 return false;
899 }
900
901 true
902 }
903
904 _ => false,
905 }
906 }
907}
908
909impl From<ArrayType> for CompoundType {
910 fn from(value: ArrayType) -> Self {
911 Self::Array(value)
912 }
913}
914
915impl From<PairType> for CompoundType {
916 fn from(value: PairType) -> Self {
917 Self::Pair(value)
918 }
919}
920
921impl From<MapType> for CompoundType {
922 fn from(value: MapType) -> Self {
923 Self::Map(value)
924 }
925}
926
927impl From<StructType> for CompoundType {
928 fn from(value: StructType) -> Self {
929 Self::Custom(CustomType::Struct(value))
930 }
931}
932
933impl From<EnumType> for CompoundType {
934 fn from(value: EnumType) -> Self {
935 Self::Custom(CustomType::Enum(value))
936 }
937}
938
939#[derive(Debug, Clone, PartialEq, Eq)]
941struct ArrayTypeInner {
942 element_type: Type,
944 non_empty: bool,
946}
947
948#[derive(Debug, Clone, PartialEq, Eq)]
952pub struct ArrayType(Arc<ArrayTypeInner>);
953
954impl ArrayType {
955 pub fn new(element_type: impl Into<Type>) -> Self {
957 Self(Arc::new(ArrayTypeInner {
958 element_type: element_type.into(),
959 non_empty: false,
960 }))
961 }
962
963 pub fn non_empty(element_type: impl Into<Type>) -> Self {
965 Self(Arc::new(ArrayTypeInner {
966 element_type: element_type.into(),
967 non_empty: true,
968 }))
969 }
970
971 pub fn element_type(&self) -> &Type {
973 &self.0.element_type
974 }
975
976 pub fn is_non_empty(&self) -> bool {
978 self.0.non_empty
979 }
980
981 pub fn unqualified(&self) -> ArrayType {
983 if self.0.non_empty {
984 Self(Arc::new(ArrayTypeInner {
985 element_type: self.0.element_type.clone(),
986 non_empty: false,
987 }))
988 } else {
989 self.clone()
990 }
991 }
992}
993
994impl fmt::Display for ArrayType {
995 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
996 write!(f, "Array[{ty}]", ty = self.0.element_type)?;
997
998 if self.0.non_empty {
999 write!(f, "+")?;
1000 }
1001
1002 Ok(())
1003 }
1004}
1005
1006impl Coercible for ArrayType {
1007 fn is_coercible_to(&self, target: &Self) -> bool {
1008 self.0.element_type.is_coercible_to(&target.0.element_type)
1010 }
1011}
1012
1013#[derive(Debug, Clone, PartialEq, Eq)]
1015pub struct PairType(Arc<(Type, Type)>);
1016
1017impl PairType {
1018 pub fn new(left_type: impl Into<Type>, right_type: impl Into<Type>) -> Self {
1020 Self(Arc::new((left_type.into(), right_type.into())))
1021 }
1022
1023 pub fn left_type(&self) -> &Type {
1025 &self.0.0
1026 }
1027
1028 pub fn right_type(&self) -> &Type {
1030 &self.0.1
1031 }
1032}
1033
1034impl fmt::Display for PairType {
1035 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1036 write!(
1037 f,
1038 "Pair[{left}, {right}]",
1039 left = self.left_type(),
1040 right = self.right_type()
1041 )?;
1042
1043 Ok(())
1044 }
1045}
1046
1047impl Coercible for PairType {
1048 fn is_coercible_to(&self, target: &Self) -> bool {
1049 self.left_type().is_coercible_to(target.left_type())
1050 && self.right_type().is_coercible_to(target.right_type())
1051 }
1052}
1053
1054#[derive(Debug, Clone, PartialEq, Eq)]
1056pub struct MapType(Arc<(Type, Type)>);
1057
1058impl MapType {
1059 pub fn new(key_type: impl Into<Type>, value_type: impl Into<Type>) -> Self {
1065 let key_type = key_type.into();
1066 assert!(
1067 key_type.is_union() || matches!(key_type, Type::Primitive(_, false)),
1068 "map key {key_type:#} is not a non-optional primitive"
1069 );
1070 Self(Arc::new((key_type, value_type.into())))
1071 }
1072
1073 pub fn key_type(&self) -> &Type {
1075 &self.0.0
1076 }
1077
1078 pub fn value_type(&self) -> &Type {
1080 &self.0.1
1081 }
1082}
1083
1084impl fmt::Display for MapType {
1085 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1086 write!(
1087 f,
1088 "Map[{key}, {value}]",
1089 key = self.key_type(),
1090 value = self.value_type()
1091 )?;
1092
1093 Ok(())
1094 }
1095}
1096
1097impl Coercible for MapType {
1098 fn is_coercible_to(&self, target: &Self) -> bool {
1099 self.key_type().is_coercible_to(target.key_type())
1100 && self.value_type().is_coercible_to(target.value_type())
1101 }
1102}
1103
1104#[derive(Debug, Clone, PartialEq, Eq)]
1106struct StructTypeInner {
1107 name: Arc<String>,
1113 members: IndexMap<String, Type>,
1115}
1116
1117#[derive(Debug, Clone, PartialEq, Eq)]
1121pub struct StructType(Arc<StructTypeInner>);
1122
1123impl StructType {
1124 pub fn new<N, T>(name: impl Into<String>, members: impl IntoIterator<Item = (N, T)>) -> Self
1126 where
1127 N: Into<String>,
1128 T: Into<Type>,
1129 {
1130 Self(Arc::new(StructTypeInner {
1131 name: Arc::new(name.into()),
1132 members: members
1133 .into_iter()
1134 .map(|(n, ty)| (n.into(), ty.into()))
1135 .collect(),
1136 }))
1137 }
1138
1139 pub fn name(&self) -> &Arc<String> {
1141 &self.0.name
1142 }
1143
1144 pub fn members(&self) -> &IndexMap<String, Type> {
1146 &self.0.members
1147 }
1148}
1149
1150impl fmt::Display for StructType {
1151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1152 write!(f, "{name}", name = self.0.name)
1153 }
1154}
1155
1156impl Coercible for StructType {
1157 fn is_coercible_to(&self, target: &Self) -> bool {
1158 if self.0.members.len() != target.0.members.len() {
1159 return false;
1160 }
1161
1162 self.0.members.iter().all(|(k, v)| {
1163 target
1164 .0
1165 .members
1166 .get(k)
1167 .map(|target| v.is_coercible_to(target))
1168 .unwrap_or(false)
1169 })
1170 }
1171}
1172
1173#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1175pub struct EnumChoiceCacheKey {
1176 uri: Arc<Url>,
1178 enum_index: usize,
1180 choice_index: usize,
1182}
1183
1184impl EnumChoiceCacheKey {
1185 pub(crate) fn new(uri: Arc<Url>, enum_index: usize, choice_index: usize) -> Self {
1187 Self {
1188 uri,
1189 enum_index,
1190 choice_index,
1191 }
1192 }
1193}
1194
1195#[derive(Debug, Clone, PartialEq, Eq)]
1197struct EnumTypeInner {
1198 name: String,
1200 inner_value_type: Type,
1202 choices: Arc<[String]>,
1204}
1205
1206#[derive(Debug, Clone, PartialEq, Eq)]
1210pub struct EnumType(Arc<EnumTypeInner>);
1211
1212impl EnumType {
1213 pub fn new(
1219 enum_name: impl Into<String>,
1220 enum_span: Span,
1221 explicit_inner_type: Type,
1222 choices: Vec<(String, Type)>,
1223 choice_spans: &[Span],
1224 ) -> Result<Self, Diagnostic> {
1225 assert_eq!(choices.len(), choice_spans.len());
1226 let enum_name = enum_name.into();
1227 let mut results = Vec::with_capacity(choices.len());
1228
1229 for (choice_idx, (choice_name, choice_type)) in choices.iter().enumerate() {
1231 if !choice_type.is_coercible_to(&explicit_inner_type) {
1232 return Err(enum_choice_does_not_coerce_to_type(
1233 &enum_name,
1234 enum_span,
1235 choice_name,
1236 choice_spans[choice_idx],
1237 &explicit_inner_type,
1238 choice_type,
1239 ));
1240 }
1241
1242 results.push(choice_name.to_owned());
1243 }
1244
1245 Ok(Self(Arc::new(EnumTypeInner {
1246 name: enum_name,
1247 inner_value_type: explicit_inner_type,
1248 choices: results.into(),
1249 })))
1250 }
1251
1252 pub fn infer(
1260 enum_name: impl Into<String>,
1261 choices: Vec<(String, Type)>,
1262 choice_spans: &[Span],
1263 ) -> Result<Self, Diagnostic> {
1264 assert_eq!(choices.len(), choice_spans.len());
1265 let enum_name = enum_name.into();
1266
1267 let mut common_ty: Option<Type> = None;
1268 let mut names = Vec::with_capacity(choices.len());
1269 for (i, (name, choice_ty)) in choices.into_iter().enumerate() {
1270 match common_ty {
1271 Some(current_common_ty) => match current_common_ty.common_type(&choice_ty) {
1272 Some(new_common_ty) => {
1273 common_ty = Some(new_common_ty);
1274 }
1275 None => {
1276 return Err(no_common_inferred_type_for_enum(
1277 &enum_name,
1278 ¤t_common_ty,
1279 choice_spans[i - 1],
1280 &choice_ty,
1281 choice_spans[i],
1282 ));
1283 }
1284 },
1285 None => common_ty = Some(choice_ty),
1286 }
1287
1288 names.push(name);
1289 }
1290
1291 Ok(Self(Arc::new(EnumTypeInner {
1292 name: enum_name,
1293 inner_value_type: common_ty.unwrap_or(Type::Union),
1294 choices: names.into(),
1295 })))
1296 }
1297
1298 pub fn name(&self) -> &str {
1300 &self.0.name
1301 }
1302
1303 pub fn inner_value_type(&self) -> &Type {
1305 &self.0.inner_value_type
1306 }
1307
1308 pub fn choices(&self) -> &[String] {
1310 &self.0.choices
1311 }
1312}
1313
1314impl fmt::Display for EnumType {
1315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1316 write!(f, "{name}", name = self.0.name)
1317 }
1318}
1319
1320impl Coercible for EnumType {
1321 fn is_coercible_to(&self, target: &Self) -> bool {
1322 self == target
1323 }
1324}
1325
1326#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1328pub enum CallKind {
1329 Task,
1331 Workflow,
1333}
1334
1335impl fmt::Display for CallKind {
1336 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1337 match self {
1338 Self::Task => write!(f, "task"),
1339 Self::Workflow => write!(f, "workflow"),
1340 }
1341 }
1342}
1343
1344#[derive(Debug, Clone, Eq)]
1346struct CallTypeInner {
1347 kind: CallKind,
1349 namespace: Option<String>,
1351 name: String,
1353 specified: Arc<HashSet<String>>,
1359 inputs: Arc<IndexMap<String, Input>>,
1363 outputs: Arc<IndexMap<String, Output>>,
1369}
1370
1371impl PartialEq for CallTypeInner {
1372 fn eq(&self, other: &Self) -> bool {
1373 std::ptr::eq(self, other)
1374 }
1375}
1376
1377#[derive(Debug, Clone, Eq)]
1381pub struct CallType(Arc<CallTypeInner>);
1382
1383impl CallType {
1384 pub(crate) fn new(
1386 kind: CallKind,
1387 name: impl Into<String>,
1388 specified: Arc<HashSet<String>>,
1389 inputs: Arc<IndexMap<String, Input>>,
1390 outputs: Arc<IndexMap<String, Output>>,
1391 ) -> Self {
1392 Self(Arc::new(CallTypeInner {
1393 kind,
1394 namespace: None,
1395 name: name.into(),
1396 specified,
1397 inputs,
1398 outputs,
1399 }))
1400 }
1401
1402 pub(crate) fn namespaced(
1405 kind: CallKind,
1406 namespace: impl Into<String>,
1407 name: impl Into<String>,
1408 specified: Arc<HashSet<String>>,
1409 inputs: Arc<IndexMap<String, Input>>,
1410 outputs: Arc<IndexMap<String, Output>>,
1411 ) -> Self {
1412 Self(Arc::new(CallTypeInner {
1413 kind,
1414 namespace: Some(namespace.into()),
1415 name: name.into(),
1416 specified,
1417 inputs,
1418 outputs,
1419 }))
1420 }
1421
1422 pub fn kind(&self) -> CallKind {
1424 self.0.kind
1425 }
1426
1427 pub fn namespace(&self) -> Option<&str> {
1431 self.0.namespace.as_deref()
1432 }
1433
1434 pub fn name(&self) -> &str {
1436 &self.0.name
1437 }
1438
1439 pub fn specified(&self) -> &HashSet<String> {
1441 &self.0.specified
1442 }
1443
1444 pub fn inputs(&self) -> &IndexMap<String, Input> {
1446 &self.0.inputs
1447 }
1448
1449 pub fn outputs(&self) -> &IndexMap<String, Output> {
1451 &self.0.outputs
1452 }
1453
1454 pub fn optional(&self) -> Self {
1456 let mut inner = self.0.as_ref().clone();
1457 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1458 *output = Output::new(output.ty().optional(), output.name_span());
1459 }
1460
1461 Self(Arc::new(inner))
1462 }
1463
1464 pub fn promote_scatter(&self) -> Self {
1466 let mut inner = self.0.as_ref().clone();
1467 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1468 *output = Output::new(output.ty().promote_scatter(), output.name_span());
1469 }
1470
1471 Self(Arc::new(inner))
1472 }
1473}
1474
1475impl Coercible for CallType {
1476 fn is_coercible_to(&self, _: &Self) -> bool {
1477 false
1479 }
1480}
1481
1482impl fmt::Display for CallType {
1483 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1484 if let Some(ns) = &self.0.namespace {
1485 write!(
1486 f,
1487 "call to {kind} `{ns}.{name}`",
1488 kind = self.0.kind,
1489 name = self.0.name,
1490 )
1491 } else {
1492 write!(
1493 f,
1494 "call to {kind} `{name}`",
1495 kind = self.0.kind,
1496 name = self.0.name,
1497 )
1498 }
1499 }
1500}
1501
1502impl PartialEq for CallType {
1503 fn eq(&self, other: &Self) -> bool {
1504 Arc::ptr_eq(&self.0, &other.0)
1505 }
1506}
1507
1508#[derive(Debug, Clone, PartialEq, Eq)]
1510struct TypeNameRefInner {
1511 name: String,
1513 ty: CustomType,
1515}
1516
1517#[derive(Debug, Clone, PartialEq, Eq)]
1519pub struct TypeNameRef(Arc<TypeNameRefInner>);
1520
1521impl TypeNameRef {
1522 pub fn new(name: impl Into<String>, ty: CustomType) -> Self {
1524 Self(
1525 TypeNameRefInner {
1526 name: name.into(),
1527 ty,
1528 }
1529 .into(),
1530 )
1531 }
1532
1533 pub fn name(&self) -> &str {
1535 &self.0.name
1536 }
1537
1538 pub fn ty(&self) -> &CustomType {
1540 &self.0.ty
1541 }
1542
1543 pub fn as_struct(&self) -> Option<&StructType> {
1547 match &self.0.ty {
1548 CustomType::Struct(ty) => Some(ty),
1549 _ => None,
1550 }
1551 }
1552
1553 pub fn as_enum(&self) -> Option<&EnumType> {
1557 match &self.0.ty {
1558 CustomType::Enum(ty) => Some(ty),
1559 _ => None,
1560 }
1561 }
1562}
1563
1564impl std::fmt::Display for TypeNameRef {
1565 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1566 self.0.name.fmt(f)
1567 }
1568}
1569
1570#[cfg(test)]
1571mod test {
1572 use pretty_assertions::assert_eq;
1573
1574 use super::*;
1575
1576 #[test_log::test]
1577 fn primitive_type_display() {
1578 assert_eq!(PrimitiveType::Boolean.to_string(), "Boolean");
1579 assert_eq!(PrimitiveType::Integer.to_string(), "Int");
1580 assert_eq!(PrimitiveType::Float.to_string(), "Float");
1581 assert_eq!(PrimitiveType::String.to_string(), "String");
1582 assert_eq!(PrimitiveType::File.to_string(), "File");
1583 assert_eq!(PrimitiveType::Directory.to_string(), "Directory");
1584 assert_eq!(
1585 Type::from(PrimitiveType::Boolean).optional().to_string(),
1586 "Boolean?"
1587 );
1588 assert_eq!(
1589 Type::from(PrimitiveType::Integer).optional().to_string(),
1590 "Int?"
1591 );
1592 assert_eq!(
1593 Type::from(PrimitiveType::Float).optional().to_string(),
1594 "Float?"
1595 );
1596 assert_eq!(
1597 Type::from(PrimitiveType::String).optional().to_string(),
1598 "String?"
1599 );
1600 assert_eq!(
1601 Type::from(PrimitiveType::File).optional().to_string(),
1602 "File?"
1603 );
1604 assert_eq!(
1605 Type::from(PrimitiveType::Directory).optional().to_string(),
1606 "Directory?"
1607 );
1608 }
1609
1610 #[test_log::test]
1611 fn array_type_display() {
1612 assert_eq!(
1613 ArrayType::new(PrimitiveType::String).to_string(),
1614 "Array[String]"
1615 );
1616 assert_eq!(
1617 ArrayType::non_empty(PrimitiveType::String).to_string(),
1618 "Array[String]+"
1619 );
1620
1621 let ty: Type = ArrayType::new(ArrayType::new(PrimitiveType::String)).into();
1622 assert_eq!(ty.to_string(), "Array[Array[String]]");
1623
1624 let ty = Type::from(ArrayType::non_empty(
1625 Type::from(ArrayType::non_empty(
1626 Type::from(PrimitiveType::String).optional(),
1627 ))
1628 .optional(),
1629 ))
1630 .optional();
1631 assert_eq!(ty.to_string(), "Array[Array[String?]+?]+?");
1632 }
1633
1634 #[test_log::test]
1635 fn pair_type_display() {
1636 assert_eq!(
1637 PairType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1638 "Pair[String, Boolean]"
1639 );
1640
1641 let ty: Type = PairType::new(
1642 ArrayType::new(PrimitiveType::String),
1643 ArrayType::new(PrimitiveType::String),
1644 )
1645 .into();
1646 assert_eq!(ty.to_string(), "Pair[Array[String], Array[String]]");
1647
1648 let ty = Type::from(PairType::new(
1649 Type::from(ArrayType::non_empty(
1650 Type::from(PrimitiveType::File).optional(),
1651 ))
1652 .optional(),
1653 Type::from(ArrayType::non_empty(
1654 Type::from(PrimitiveType::File).optional(),
1655 ))
1656 .optional(),
1657 ))
1658 .optional();
1659 assert_eq!(ty.to_string(), "Pair[Array[File?]+?, Array[File?]+?]?");
1660 }
1661
1662 #[test_log::test]
1663 fn map_type_display() {
1664 assert_eq!(
1665 MapType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1666 "Map[String, Boolean]"
1667 );
1668
1669 let ty: Type = MapType::new(
1670 PrimitiveType::Boolean,
1671 ArrayType::new(PrimitiveType::String),
1672 )
1673 .into();
1674 assert_eq!(ty.to_string(), "Map[Boolean, Array[String]]");
1675
1676 let ty: Type = Type::from(MapType::new(
1677 PrimitiveType::String,
1678 Type::from(ArrayType::non_empty(
1679 Type::from(PrimitiveType::File).optional(),
1680 ))
1681 .optional(),
1682 ))
1683 .optional();
1684 assert_eq!(ty.to_string(), "Map[String, Array[File?]+?]?");
1685 }
1686
1687 #[test_log::test]
1688 fn struct_type_display() {
1689 assert_eq!(
1690 StructType::new("Foobar", std::iter::empty::<(String, Type)>()).to_string(),
1691 "Foobar"
1692 );
1693 }
1694
1695 #[test_log::test]
1696 fn object_type_display() {
1697 assert_eq!(Type::Object.to_string(), "Object");
1698 assert_eq!(Type::OptionalObject.to_string(), "Object?");
1699 }
1700
1701 #[test_log::test]
1702 fn union_type_display() {
1703 assert_eq!(Type::Union.to_string(), "Union");
1704 }
1705
1706 #[test_log::test]
1707 fn none_type_display() {
1708 assert_eq!(Type::None.to_string(), "None");
1709 }
1710
1711 #[test_log::test]
1712 fn primitive_type_coercion() {
1713 for ty in [
1716 Type::from(PrimitiveType::Boolean),
1717 PrimitiveType::Directory.into(),
1718 PrimitiveType::File.into(),
1719 PrimitiveType::Float.into(),
1720 PrimitiveType::Integer.into(),
1721 PrimitiveType::String.into(),
1722 ] {
1723 assert!(ty.is_coercible_to(&ty));
1724 assert!(ty.optional().is_coercible_to(&ty.optional()));
1725 assert!(ty.is_coercible_to(&ty.optional()));
1726 assert!(!ty.optional().is_coercible_to(&ty));
1727 }
1728
1729 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::File));
1731 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::Directory));
1732 assert!(PrimitiveType::Integer.is_coercible_to(&PrimitiveType::Float));
1733 assert!(PrimitiveType::File.is_coercible_to(&PrimitiveType::String));
1734 assert!(PrimitiveType::Directory.is_coercible_to(&PrimitiveType::String));
1735 assert!(!PrimitiveType::Float.is_coercible_to(&PrimitiveType::Integer));
1736 }
1737
1738 #[test_log::test]
1739 fn object_type_coercion() {
1740 assert!(Type::Object.is_coercible_to(&Type::Object));
1741 assert!(Type::Object.is_coercible_to(&Type::OptionalObject));
1742 assert!(Type::OptionalObject.is_coercible_to(&Type::OptionalObject));
1743 assert!(!Type::OptionalObject.is_coercible_to(&Type::Object));
1744
1745 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1747 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1748
1749 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1751 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1752
1753 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
1755 assert!(!Type::Object.is_coercible_to(&ty));
1756
1757 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1759 assert!(Type::Object.is_coercible_to(&ty));
1760
1761 let ty = Type::from(MapType::new(PrimitiveType::File, PrimitiveType::String)).optional();
1763 assert!(Type::Object.is_coercible_to(&ty));
1764
1765 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1767 assert!(Type::OptionalObject.is_coercible_to(&ty));
1768
1769 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1771 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1772
1773 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1775 assert!(Type::Object.is_coercible_to(&ty));
1776
1777 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1779 assert!(Type::Object.is_coercible_to(&ty));
1780
1781 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1783 assert!(Type::OptionalObject.is_coercible_to(&ty));
1784
1785 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1787 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1788 }
1789
1790 #[test_log::test]
1791 fn array_type_coercion() {
1792 assert!(
1794 ArrayType::new(PrimitiveType::String)
1795 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1796 );
1797 assert!(
1798 ArrayType::new(PrimitiveType::File)
1799 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1800 );
1801 assert!(
1802 ArrayType::new(PrimitiveType::String)
1803 .is_coercible_to(&ArrayType::new(PrimitiveType::File))
1804 );
1805
1806 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1808 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1809 assert!(type1.is_coercible_to(&type2));
1810 assert!(!type2.is_coercible_to(&type1));
1811
1812 let type1: Type = ArrayType::new(type1).into();
1814 let type2 = ArrayType::new(type2).into();
1815 assert!(type1.is_coercible_to(&type2));
1816 assert!(!type2.is_coercible_to(&type1));
1817
1818 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1820 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1821 assert!(type1.is_coercible_to(&type2));
1822 assert!(!type2.is_coercible_to(&type1));
1823
1824 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1826 let type2 = ArrayType::new(Type::from(PrimitiveType::String).optional()).into();
1827 assert!(type1.is_coercible_to(&type2));
1828 assert!(!type2.is_coercible_to(&type1));
1829
1830 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1832 let type2 = ArrayType::new(PrimitiveType::String).into();
1833 assert!(type1.is_coercible_to(&type2));
1834 assert!(type2.is_coercible_to(&type1));
1835
1836 let type1 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1838 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1839 assert!(type1.is_coercible_to(&type2));
1840 assert!(type2.is_coercible_to(&type1));
1841
1842 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1844 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1845 assert!(type1.is_coercible_to(&type2));
1846 assert!(!type2.is_coercible_to(&type1));
1847 }
1848
1849 #[test_log::test]
1850 fn pair_type_coercion() {
1851 assert!(
1853 PairType::new(PrimitiveType::String, PrimitiveType::String)
1854 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1855 );
1856 assert!(
1857 PairType::new(PrimitiveType::String, PrimitiveType::String).is_coercible_to(
1858 &PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1859 )
1860 );
1861 assert!(
1862 PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1863 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1864 );
1865
1866 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1868 let type2 = PairType::new(
1869 Type::from(PrimitiveType::File).optional(),
1870 Type::from(PrimitiveType::Directory).optional(),
1871 )
1872 .into();
1873 assert!(type1.is_coercible_to(&type2));
1874 assert!(!type2.is_coercible_to(&type1));
1875
1876 let type1: Type = PairType::new(type1.clone(), type1).into();
1878 let type2 = PairType::new(type2.clone(), type2).into();
1879 assert!(type1.is_coercible_to(&type2));
1880 assert!(!type2.is_coercible_to(&type1));
1881
1882 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1884 let type2 = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1885 assert!(type1.is_coercible_to(&type2));
1886 assert!(type2.is_coercible_to(&type1));
1887
1888 let type1 =
1890 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1891 let type2 =
1892 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1893 assert!(type1.is_coercible_to(&type2));
1894 assert!(type2.is_coercible_to(&type1));
1895
1896 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1898 let type2 =
1899 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1900 assert!(type1.is_coercible_to(&type2));
1901 assert!(!type2.is_coercible_to(&type1));
1902 }
1903
1904 #[test_log::test]
1905 fn map_type_coercion() {
1906 assert!(
1908 MapType::new(PrimitiveType::String, PrimitiveType::String)
1909 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1910 );
1911 assert!(
1912 MapType::new(PrimitiveType::String, PrimitiveType::String)
1913 .is_coercible_to(&MapType::new(PrimitiveType::File, PrimitiveType::Directory))
1914 );
1915 assert!(
1916 MapType::new(PrimitiveType::File, PrimitiveType::Directory)
1917 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1918 );
1919
1920 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1922 let type2 = MapType::new(
1923 PrimitiveType::File,
1924 Type::from(PrimitiveType::Directory).optional(),
1925 )
1926 .into();
1927 assert!(type1.is_coercible_to(&type2));
1928 assert!(!type2.is_coercible_to(&type1));
1929
1930 let type1: Type = MapType::new(PrimitiveType::String, type1).into();
1932 let type2 = MapType::new(PrimitiveType::Directory, type2).into();
1933 assert!(type1.is_coercible_to(&type2));
1934 assert!(!type2.is_coercible_to(&type1));
1935
1936 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1938 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1939 assert!(type1.is_coercible_to(&type2));
1940 assert!(type2.is_coercible_to(&type1));
1941
1942 let type1: Type =
1944 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1945 let type2: Type =
1946 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1947 assert!(type1.is_coercible_to(&type2));
1948 assert!(type2.is_coercible_to(&type1));
1949
1950 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1952 let type2 =
1953 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1954 assert!(type1.is_coercible_to(&type2));
1955 assert!(!type2.is_coercible_to(&type1));
1956
1957 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1959 let type2 = StructType::new(
1960 "Foo",
1961 [
1962 ("foo", PrimitiveType::Integer),
1963 ("bar", PrimitiveType::Integer),
1964 ("baz", PrimitiveType::Integer),
1965 ],
1966 )
1967 .into();
1968 assert!(type1.is_coercible_to(&type2));
1969
1970 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1972 let type2 = StructType::new(
1973 "Foo",
1974 [
1975 ("foo", PrimitiveType::Integer),
1976 ("bar", PrimitiveType::Integer),
1977 ("baz", PrimitiveType::Integer),
1978 ],
1979 )
1980 .into();
1981 assert!(type1.is_coercible_to(&type2));
1982
1983 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1985 let type2 = StructType::new(
1986 "Foo",
1987 [
1988 ("foo", PrimitiveType::Integer),
1989 ("bar", PrimitiveType::String),
1990 ("baz", PrimitiveType::Integer),
1991 ],
1992 )
1993 .into();
1994 assert!(!type1.is_coercible_to(&type2));
1995
1996 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
1998 let type2 = StructType::new(
1999 "Foo",
2000 [
2001 ("foo", PrimitiveType::Integer),
2002 ("bar", PrimitiveType::Integer),
2003 ("baz", PrimitiveType::Integer),
2004 ],
2005 )
2006 .into();
2007 assert!(!type1.is_coercible_to(&type2));
2008
2009 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2011 assert!(type1.is_coercible_to(&Type::Object));
2012
2013 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2015 assert!(type1.is_coercible_to(&Type::OptionalObject));
2016
2017 let type1: Type =
2019 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2020 assert!(type1.is_coercible_to(&Type::OptionalObject));
2021
2022 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2024 assert!(type1.is_coercible_to(&Type::Object));
2025
2026 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2028 assert!(type1.is_coercible_to(&Type::OptionalObject));
2029
2030 let type1: Type =
2032 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2033 assert!(type1.is_coercible_to(&Type::OptionalObject));
2034
2035 let type1: Type =
2037 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2038 assert!(!type1.is_coercible_to(&Type::Object));
2039
2040 let type1: Type =
2042 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2043 assert!(!type1.is_coercible_to(&Type::Object));
2044
2045 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
2047 assert!(!type1.is_coercible_to(&Type::Object));
2048 }
2049
2050 #[test_log::test]
2051 fn struct_type_coercion() {
2052 let type1: Type = StructType::new(
2054 "Foo",
2055 [
2056 ("foo", PrimitiveType::String),
2057 ("bar", PrimitiveType::String),
2058 ("baz", PrimitiveType::Integer),
2059 ],
2060 )
2061 .into();
2062 let type2 = StructType::new(
2063 "Foo",
2064 [
2065 ("foo", PrimitiveType::String),
2066 ("bar", PrimitiveType::String),
2067 ("baz", PrimitiveType::Integer),
2068 ],
2069 )
2070 .into();
2071 assert!(type1.is_coercible_to(&type2));
2072 assert!(type2.is_coercible_to(&type1));
2073
2074 let type1: Type = StructType::new(
2076 "Foo",
2077 [
2078 ("foo", PrimitiveType::String),
2079 ("bar", PrimitiveType::String),
2080 ("baz", PrimitiveType::Integer),
2081 ],
2082 )
2083 .into();
2084 let type2 = Type::from(StructType::new(
2085 "Foo",
2086 [
2087 ("foo", PrimitiveType::String),
2088 ("bar", PrimitiveType::String),
2089 ("baz", PrimitiveType::Integer),
2090 ],
2091 ))
2092 .optional();
2093 assert!(type1.is_coercible_to(&type2));
2094 assert!(!type2.is_coercible_to(&type1));
2095
2096 let type1: Type = Type::from(StructType::new(
2098 "Foo",
2099 [
2100 ("foo", PrimitiveType::String),
2101 ("bar", PrimitiveType::String),
2102 ("baz", PrimitiveType::Integer),
2103 ],
2104 ))
2105 .optional();
2106 let type2 = Type::from(StructType::new(
2107 "Foo",
2108 [
2109 ("foo", PrimitiveType::String),
2110 ("bar", PrimitiveType::String),
2111 ("baz", PrimitiveType::Integer),
2112 ],
2113 ))
2114 .optional();
2115 assert!(type1.is_coercible_to(&type2));
2116 assert!(type2.is_coercible_to(&type1));
2117
2118 let type1: Type = StructType::new(
2120 "Foo",
2121 [
2122 ("foo", PrimitiveType::String),
2123 ("bar", PrimitiveType::String),
2124 ("baz", PrimitiveType::Integer),
2125 ],
2126 )
2127 .into();
2128 let type2 = StructType::new(
2129 "Bar",
2130 [
2131 ("foo", PrimitiveType::File),
2132 ("bar", PrimitiveType::Directory),
2133 ("baz", PrimitiveType::Float),
2134 ],
2135 )
2136 .into();
2137 assert!(type1.is_coercible_to(&type2));
2138 assert!(!type2.is_coercible_to(&type1));
2139
2140 let type1: Type = StructType::new(
2142 "Foo",
2143 [
2144 ("foo", PrimitiveType::String),
2145 ("bar", PrimitiveType::String),
2146 ("baz", PrimitiveType::Integer),
2147 ],
2148 )
2149 .into();
2150 let type2 = StructType::new("Bar", [("baz", PrimitiveType::Float)]).into();
2151 assert!(!type1.is_coercible_to(&type2));
2152 assert!(!type2.is_coercible_to(&type1));
2153
2154 let type1: Type = StructType::new(
2156 "Foo",
2157 [
2158 ("foo", PrimitiveType::String),
2159 ("bar", PrimitiveType::String),
2160 ("baz", PrimitiveType::String),
2161 ],
2162 )
2163 .into();
2164 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2165 assert!(type1.is_coercible_to(&type2));
2166
2167 let type1: Type = StructType::new(
2169 "Foo",
2170 [
2171 ("foo", PrimitiveType::String),
2172 ("bar", PrimitiveType::String),
2173 ("baz", PrimitiveType::String),
2174 ],
2175 )
2176 .into();
2177 let type2 = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
2178 assert!(type1.is_coercible_to(&type2));
2179
2180 let type1: Type = StructType::new(
2182 "Foo",
2183 [
2184 ("foo", PrimitiveType::String),
2185 ("bar", PrimitiveType::Integer),
2186 ("baz", PrimitiveType::String),
2187 ],
2188 )
2189 .into();
2190 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2191 assert!(!type1.is_coercible_to(&type2));
2192
2193 let type1: Type = StructType::new(
2195 "Foo",
2196 [
2197 ("foo", PrimitiveType::String),
2198 ("bar", PrimitiveType::String),
2199 ("baz", PrimitiveType::String),
2200 ],
2201 )
2202 .into();
2203 let type2 = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2204 assert!(!type1.is_coercible_to(&type2));
2205
2206 assert!(type1.is_coercible_to(&Type::Object));
2208
2209 assert!(type1.is_coercible_to(&Type::OptionalObject));
2211
2212 let type1: Type =
2214 Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
2215 assert!(type1.is_coercible_to(&Type::OptionalObject));
2216
2217 assert!(!type1.is_coercible_to(&Type::Object));
2219 }
2220
2221 #[test_log::test]
2222 fn union_type_coercion() {
2223 for ty in [
2225 Type::from(PrimitiveType::Boolean),
2226 PrimitiveType::Directory.into(),
2227 PrimitiveType::File.into(),
2228 PrimitiveType::Float.into(),
2229 PrimitiveType::Integer.into(),
2230 PrimitiveType::String.into(),
2231 ] {
2232 assert!(Type::Union.is_coercible_to(&ty));
2233 assert!(Type::Union.is_coercible_to(&ty.optional()));
2234 assert!(ty.is_coercible_to(&Type::Union));
2235 }
2236
2237 for optional in [true, false] {
2238 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2240 let ty = if optional { ty.optional() } else { ty };
2241
2242 let coercible = Type::Union.is_coercible_to(&ty);
2243 assert!(coercible);
2244
2245 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2247 let ty = if optional { ty.optional() } else { ty };
2248 let coercible = Type::Union.is_coercible_to(&ty);
2249 assert!(coercible);
2250
2251 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2253 let ty = if optional { ty.optional() } else { ty };
2254 let coercible = Type::Union.is_coercible_to(&ty);
2255 assert!(coercible);
2256
2257 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2259 let ty = if optional { ty.optional() } else { ty };
2260 let coercible = Type::Union.is_coercible_to(&ty);
2261 assert!(coercible);
2262 }
2263 }
2264
2265 #[test_log::test]
2266 fn none_type_coercion() {
2267 for ty in [
2269 Type::from(PrimitiveType::Boolean),
2270 PrimitiveType::Directory.into(),
2271 PrimitiveType::File.into(),
2272 PrimitiveType::Float.into(),
2273 PrimitiveType::Integer.into(),
2274 PrimitiveType::String.into(),
2275 ] {
2276 assert!(!Type::None.is_coercible_to(&ty));
2277 assert!(Type::None.is_coercible_to(&ty.optional()));
2278 assert!(!ty.is_coercible_to(&Type::None));
2279 }
2280
2281 for optional in [true, false] {
2282 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2284 let ty = if optional { ty.optional() } else { ty };
2285 let coercible = Type::None.is_coercible_to(&ty);
2286 if optional {
2287 assert!(coercible);
2288 } else {
2289 assert!(!coercible);
2290 }
2291
2292 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2294 let ty = if optional { ty.optional() } else { ty };
2295 let coercible = Type::None.is_coercible_to(&ty);
2296 if optional {
2297 assert!(coercible);
2298 } else {
2299 assert!(!coercible);
2300 }
2301
2302 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2304 let ty = if optional { ty.optional() } else { ty };
2305 let coercible = Type::None.is_coercible_to(&ty);
2306 if optional {
2307 assert!(coercible);
2308 } else {
2309 assert!(!coercible);
2310 }
2311
2312 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2314 let ty = if optional { ty.optional() } else { ty };
2315 let coercible = Type::None.is_coercible_to(&ty);
2316 if optional {
2317 assert!(coercible);
2318 } else {
2319 assert!(!coercible);
2320 }
2321 }
2322 }
2323
2324 #[test_log::test]
2325 fn primitive_equality() {
2326 for ty in [
2327 Type::from(PrimitiveType::Boolean),
2328 PrimitiveType::Directory.into(),
2329 PrimitiveType::File.into(),
2330 PrimitiveType::Float.into(),
2331 PrimitiveType::Integer.into(),
2332 PrimitiveType::String.into(),
2333 ] {
2334 assert!(ty.eq(&ty));
2335 assert!(!ty.optional().eq(&ty));
2336 assert!(!ty.eq(&ty.optional()));
2337 assert!(ty.optional().eq(&ty.optional()));
2338 assert!(!ty.eq(&Type::Object));
2339 assert!(!ty.eq(&Type::OptionalObject));
2340 assert!(!ty.eq(&Type::Union));
2341 assert!(!ty.eq(&Type::None));
2342 }
2343 }
2344
2345 #[test_log::test]
2346 fn array_equality() {
2347 let a: Type = ArrayType::new(PrimitiveType::String).into();
2349 let b: Type = ArrayType::new(PrimitiveType::String).into();
2350 assert!(a.eq(&b));
2351 assert!(!a.optional().eq(&b));
2352 assert!(!a.eq(&b.optional()));
2353 assert!(a.optional().eq(&b.optional()));
2354
2355 let a: Type = ArrayType::new(a).into();
2357 let b: Type = ArrayType::new(b).into();
2358 assert!(a.eq(&b));
2359
2360 let a: Type = ArrayType::non_empty(a).into();
2362 let b: Type = ArrayType::non_empty(b).into();
2363 assert!(a.eq(&b));
2364
2365 let a: Type = ArrayType::new(PrimitiveType::String).into();
2367 let b: Type = ArrayType::non_empty(PrimitiveType::String).into();
2368 assert!(!a.eq(&b));
2369
2370 let a: Type = ArrayType::new(PrimitiveType::String).into();
2372 let b: Type = ArrayType::new(PrimitiveType::Integer).into();
2373 assert!(!a.eq(&b));
2374
2375 assert!(!a.eq(&Type::Object));
2376 assert!(!a.eq(&Type::OptionalObject));
2377 assert!(!a.eq(&Type::Union));
2378 assert!(!a.eq(&Type::None));
2379 }
2380
2381 #[test_log::test]
2382 fn pair_equality() {
2383 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2385 let b: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2386 assert!(a.eq(&b));
2387 assert!(!a.optional().eq(&b));
2388 assert!(!a.eq(&b.optional()));
2389 assert!(a.optional().eq(&b.optional()));
2390
2391 let a: Type = PairType::new(a.clone(), a).into();
2394 let b: Type = PairType::new(b.clone(), b).into();
2395 assert!(a.eq(&b));
2396
2397 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2399 let b: Type =
2400 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2401 assert!(!a.eq(&b));
2402
2403 assert!(!a.eq(&Type::Object));
2404 assert!(!a.eq(&Type::OptionalObject));
2405 assert!(!a.eq(&Type::Union));
2406 assert!(!a.eq(&Type::None));
2407 }
2408
2409 #[test_log::test]
2410 fn map_equality() {
2411 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2413 let b = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2414 assert!(a.eq(&b));
2415 assert!(!a.optional().eq(&b));
2416 assert!(!a.eq(&b.optional()));
2417 assert!(a.optional().eq(&b.optional()));
2418
2419 let a: Type = MapType::new(PrimitiveType::File, a).into();
2421 let b = MapType::new(PrimitiveType::File, b).into();
2422 assert!(a.eq(&b));
2423
2424 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2426 let b = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2427 assert!(!a.eq(&b));
2428
2429 assert!(!a.eq(&Type::Object));
2430 assert!(!a.eq(&Type::OptionalObject));
2431 assert!(!a.eq(&Type::Union));
2432 assert!(!a.eq(&Type::None));
2433 }
2434
2435 #[test_log::test]
2436 fn struct_equality() {
2437 let a: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2438 assert!(a.eq(&a));
2439 assert!(!a.optional().eq(&a));
2440 assert!(!a.eq(&a.optional()));
2441 assert!(a.optional().eq(&a.optional()));
2442
2443 let b: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2444 assert!(a.eq(&b));
2445 let b: Type = StructType::new("Bar", [("foo", PrimitiveType::String)]).into();
2446 assert!(!a.eq(&b));
2447 }
2448
2449 #[test_log::test]
2450 fn object_equality() {
2451 assert!(Type::Object.eq(&Type::Object));
2452 assert!(!Type::OptionalObject.eq(&Type::Object));
2453 assert!(!Type::Object.eq(&Type::OptionalObject));
2454 assert!(Type::OptionalObject.eq(&Type::OptionalObject));
2455 }
2456
2457 #[test_log::test]
2458 fn union_equality() {
2459 assert!(Type::Union.eq(&Type::Union));
2460 assert!(!Type::None.eq(&Type::Union));
2461 assert!(!Type::Union.eq(&Type::None));
2462 assert!(Type::None.eq(&Type::None));
2463 }
2464
2465 #[test_log::test]
2466 fn enum_type_new_with_explicit_type() {
2467 let status = EnumType::new(
2469 "Status",
2470 Span::new(0, 0),
2471 PrimitiveType::String.into(),
2472 vec![
2473 ("Pending".into(), PrimitiveType::String.into()),
2474 ("Running".into(), PrimitiveType::String.into()),
2475 ("Complete".into(), PrimitiveType::String.into()),
2476 ],
2477 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)][..],
2478 )
2479 .unwrap();
2480
2481 assert_eq!(status.name(), "Status");
2482 assert_eq!(
2483 status.inner_value_type(),
2484 &Type::from(PrimitiveType::String)
2485 );
2486 assert_eq!(status.choices().len(), 3);
2487 }
2488
2489 #[test_log::test]
2490 fn enum_type_new_fails_when_not_coercible() {
2491 let result = EnumType::new(
2493 "Bad",
2494 Span::new(0, 0),
2495 PrimitiveType::Integer.into(),
2496 vec![
2497 ("First".into(), PrimitiveType::String.into()),
2498 ("Second".into(), PrimitiveType::Integer.into()),
2499 ],
2500 &[Span::new(0, 0), Span::new(0, 0)][..],
2501 );
2502
2503 assert!(
2504 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot coerce choice `First` in enum `Bad` from type `String` to type `Int`")
2505 );
2506 }
2507
2508 #[test_log::test]
2509 fn enum_type_infer_finds_common_type() {
2510 let priority = EnumType::infer(
2512 "Priority",
2513 vec![
2514 ("Low".into(), PrimitiveType::Integer.into()),
2515 ("Medium".into(), PrimitiveType::Integer.into()),
2516 ("High".into(), PrimitiveType::Integer.into()),
2517 ],
2518 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)],
2519 )
2520 .unwrap();
2521
2522 assert_eq!(priority.name(), "Priority");
2523 assert_eq!(
2524 priority.inner_value_type(),
2525 &Type::from(PrimitiveType::Integer)
2526 );
2527 assert_eq!(priority.choices().len(), 3);
2528 }
2529
2530 #[test_log::test]
2531 fn enum_type_infer_coerces_int_to_float() {
2532 let mixed = EnumType::infer(
2534 "Mixed",
2535 vec![
2536 ("IntValue".into(), PrimitiveType::Integer.into()),
2537 ("FloatValue".into(), PrimitiveType::Float.into()),
2538 ],
2539 &[Span::new(0, 0), Span::new(0, 0)],
2540 )
2541 .unwrap();
2542
2543 assert_eq!(mixed.name(), "Mixed");
2544 assert_eq!(mixed.inner_value_type(), &Type::from(PrimitiveType::Float));
2545 assert_eq!(mixed.choices().len(), 2);
2546 }
2547
2548 #[test_log::test]
2549 fn enum_type_infer_fails_without_common_type() {
2550 let result = EnumType::infer(
2552 "Bad",
2553 vec![
2554 ("StringVal".into(), PrimitiveType::String.into()),
2555 ("IntVal".into(), PrimitiveType::Integer.into()),
2556 ],
2557 &[Span::new(0, 0), Span::new(0, 0)][..],
2558 );
2559
2560 assert!(
2561 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot infer a common type for enum `Bad`")
2562 );
2563 }
2564
2565 #[test_log::test]
2566 fn enum_type_empty_has_union_type() {
2567 let result = EnumType::infer("Empty", Vec::<(String, Type)>::new(), &[]);
2569
2570 let empty = result.unwrap();
2571 assert_eq!(empty.name(), "Empty");
2572 assert_eq!(empty.inner_value_type(), &Type::Union);
2573 assert_eq!(empty.choices().len(), 0);
2574 }
2575
2576 #[test_log::test]
2577 fn enum_type_display() {
2578 let enum_type = EnumType::new(
2579 "Color",
2580 Span::new(0, 0),
2581 PrimitiveType::String.into(),
2582 vec![("Red".into(), PrimitiveType::String.into())],
2583 &[Span::new(0, 0)][..],
2584 )
2585 .unwrap();
2586 assert_eq!(enum_type.to_string(), "Color");
2587 }
2588
2589 #[test_log::test]
2590 fn enum_type_not_coercible_to_other_enums() {
2591 let color = EnumType::new(
2592 "Color",
2593 Span::new(0, 0),
2594 PrimitiveType::String.into(),
2595 vec![("Red".into(), PrimitiveType::String.into())],
2596 &[Span::new(0, 0)][..],
2597 )
2598 .unwrap();
2599 let status = EnumType::new(
2600 "Status",
2601 Span::new(0, 0),
2602 PrimitiveType::String.into(),
2603 vec![("Active".into(), PrimitiveType::String.into())],
2604 &[Span::new(0, 0)][..],
2605 )
2606 .unwrap();
2607 assert!(!color.is_coercible_to(&status));
2608 }
2609}