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 {
327 return Self::Call(ty.promote_scatter());
328 }
329
330 Type::Compound(ArrayType::new(self.clone()).into(), false)
331 }
332
333 pub fn common_type(&self, other: &Type) -> Option<Type> {
337 if other.is_union() {
339 return Some(self.clone());
340 }
341
342 if self.is_union() {
344 return Some(other.clone());
345 }
346
347 if other.is_none() {
350 return Some(self.optional());
351 }
352
353 if self.is_none() {
356 return Some(other.optional());
357 }
358
359 if other.is_coercible_to(self) {
361 return Some(self.clone());
362 }
363
364 if self.is_coercible_to(other) {
366 return Some(other.clone());
367 }
368
369 if let (Some(this), Some(other)) = (self.as_compound(), other.as_compound())
371 && let Some(ty) = this.common_type(other)
372 {
373 return Some(Self::Compound(ty, self.is_optional()));
374 }
375
376 if let (Some(this), Some(other)) = (self.as_call(), self.as_call())
378 && this == other
379 {
380 return Some(Self::Call(this.clone()));
381 }
382
383 None
384 }
385}
386
387impl fmt::Display for Type {
388 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
389 match self {
390 Self::Primitive(ty, optional) => {
391 write!(
392 f,
393 "{prefix}{ty}{opt}{suffix}",
394 prefix = if f.alternate() { "type `" } else { "" },
395 opt = if *optional { "?" } else { "" },
396 suffix = if f.alternate() { "`" } else { "" },
397 )
398 }
399 Self::Compound(CompoundType::Custom(CustomType::Struct(ty)), optional) => {
400 write!(
401 f,
402 "{prefix}{ty}{opt}{suffix}",
403 prefix = if f.alternate() {
404 "an instance of struct `"
405 } else {
406 ""
407 },
408 opt = if *optional { "?" } else { "" },
409 suffix = if f.alternate() { "`" } else { "" },
410 )
411 }
412 Self::Compound(CompoundType::Custom(CustomType::Enum(ty)), optional) => {
413 write!(
414 f,
415 "{prefix}{ty}{opt}{suffix}",
416 prefix = if f.alternate() {
417 "an instance of enum `"
418 } else {
419 ""
420 },
421 opt = if *optional { "?" } else { "" },
422 suffix = if f.alternate() { "`" } else { "" },
423 )
424 }
425 Self::Compound(ty, optional) => {
426 write!(
427 f,
428 "{prefix}{ty}{opt}{suffix}",
429 prefix = if f.alternate() { "type `" } else { "" },
430 opt = if *optional { "?" } else { "" },
431 suffix = if f.alternate() { "`" } else { "" },
432 )
433 }
434 Self::Object => {
435 write!(
436 f,
437 "{prefix}Object{suffix}",
438 prefix = if f.alternate() { "type `" } else { "" },
439 suffix = if f.alternate() { "`" } else { "" },
440 )
441 }
442 Self::OptionalObject => {
443 write!(
444 f,
445 "{prefix}Object?{suffix}",
446 prefix = if f.alternate() { "type `" } else { "" },
447 suffix = if f.alternate() { "`" } else { "" },
448 )
449 }
450 Self::Union => {
451 write!(
452 f,
453 "{prefix}Union{suffix}",
454 prefix = if f.alternate() { "built-in type `" } else { "" },
455 suffix = if f.alternate() { "`" } else { "" },
456 )
457 }
458 Self::None => {
459 write!(
460 f,
461 "{prefix}None{suffix}",
462 prefix = if f.alternate() { "built-in type `" } else { "" },
463 suffix = if f.alternate() { "`" } else { "" },
464 )
465 }
466 Self::Hidden(ty) => {
467 write!(
468 f,
469 "{prefix}{ty}{suffix}",
470 prefix = if f.alternate() { "built-in type `" } else { "" },
471 suffix = if f.alternate() { "`" } else { "" },
472 )
473 }
474 Self::Call(ty) => ty.fmt(f),
475 Self::TypeNameRef(ty) => {
476 write!(
477 f,
478 "{prefix}{ty}{suffix}",
479 prefix = if f.alternate() { "type name `" } else { "" },
480 suffix = if f.alternate() { "`" } else { "" },
481 )
482 }
483 }
484 }
485}
486
487impl Optional for Type {
488 fn is_optional(&self) -> bool {
489 match self {
490 Self::Primitive(_, optional) => *optional,
491 Self::Compound(_, optional) => *optional,
492 Self::OptionalObject | Self::None => true,
493 Self::Object | Self::Union | Self::Hidden(_) | Self::Call(_) | Self::TypeNameRef(_) => {
494 false
495 }
496 }
497 }
498
499 fn optional(&self) -> Self {
500 match self {
501 Self::Primitive(ty, _) => Self::Primitive(*ty, true),
502 Self::Compound(ty, _) => Self::Compound(ty.clone(), true),
503 Self::Object => Self::OptionalObject,
504 Self::Union => Self::None,
505 Self::Call(ty) => Self::Call(ty.optional()),
506 ty => ty.clone(),
507 }
508 }
509
510 fn require(&self) -> Self {
511 match self {
512 Self::Primitive(ty, _) => Self::Primitive(*ty, false),
513 Self::Compound(ty, _) => Self::Compound(ty.clone(), false),
514 Self::OptionalObject => Self::Object,
515 Self::None => Self::Union,
516 ty => ty.clone(),
517 }
518 }
519}
520
521impl Coercible for Type {
522 fn is_coercible_to(&self, target: &Self) -> bool {
523 if self.eq(target) {
524 return true;
525 }
526
527 match (self, target) {
528 (Self::Primitive(src, src_opt), Self::Primitive(target, target_opt)) => {
529 if *src_opt && !*target_opt {
531 return false;
532 }
533
534 src.is_coercible_to(target)
535 }
536 (Self::Compound(src, src_opt), Self::Compound(target, target_opt)) => {
537 if *src_opt && !*target_opt {
539 return false;
540 }
541
542 src.is_coercible_to(target)
543 }
544
545 (Self::Object, Self::Object)
547 | (Self::Object, Self::OptionalObject)
548 | (Self::OptionalObject, Self::OptionalObject) => true,
549
550 (Self::Compound(src, false), Self::Object)
554 | (Self::Compound(src, false), Self::OptionalObject)
555 | (Self::Compound(src, _), Self::OptionalObject) => match src {
556 CompoundType::Map(src) => src
557 .key_type()
558 .is_coercible_to(&PrimitiveType::String.into()),
559 CompoundType::Custom(CustomType::Struct(_)) => true,
560 _ => false,
561 },
562
563 (Self::Object, Self::Compound(target, _))
569 | (Self::OptionalObject, Self::Compound(target, true)) => {
570 match target {
571 CompoundType::Map(target) => {
572 Type::from(PrimitiveType::String).is_coercible_to(target.key_type())
573 }
574 CompoundType::Custom(CustomType::Struct(_)) => {
575 true
578 }
579 _ => false,
580 }
581 }
582
583 (Self::Union, _) | (_, Self::Union) => true,
585
586 (Self::None, ty) if ty.is_optional() => true,
588
589 (
592 Self::Primitive(PrimitiveType::String, _),
593 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
594 )
595 | (
596 Self::Compound(CompoundType::Custom(CustomType::Enum(_)), _),
597 Self::Primitive(PrimitiveType::String, _),
598 ) => true,
599
600 _ => false,
602 }
603 }
604}
605
606impl From<PrimitiveType> for Type {
607 fn from(value: PrimitiveType) -> Self {
608 Self::Primitive(value, false)
609 }
610}
611
612impl From<CompoundType> for Type {
613 fn from(value: CompoundType) -> Self {
614 Self::Compound(value, false)
615 }
616}
617
618impl From<ArrayType> for Type {
619 fn from(value: ArrayType) -> Self {
620 Self::Compound(value.into(), false)
621 }
622}
623
624impl From<PairType> for Type {
625 fn from(value: PairType) -> Self {
626 Self::Compound(value.into(), false)
627 }
628}
629
630impl From<MapType> for Type {
631 fn from(value: MapType) -> Self {
632 Self::Compound(value.into(), false)
633 }
634}
635
636impl From<StructType> for Type {
637 fn from(value: StructType) -> Self {
638 Self::Compound(value.into(), false)
639 }
640}
641
642impl From<EnumType> for Type {
643 fn from(value: EnumType) -> Self {
644 Self::Compound(value.into(), false)
645 }
646}
647
648impl From<CallType> for Type {
649 fn from(value: CallType) -> Self {
650 Self::Call(value)
651 }
652}
653
654impl From<CustomType> for Type {
655 fn from(value: CustomType) -> Self {
656 Self::Compound(CompoundType::Custom(value), false)
657 }
658}
659
660impl From<TypeNameRef> for Type {
661 fn from(value: TypeNameRef) -> Self {
662 Self::TypeNameRef(value)
663 }
664}
665
666#[derive(Debug, Clone, PartialEq, Eq)]
668pub enum CustomType {
669 Struct(StructType),
671 Enum(EnumType),
673}
674
675impl CustomType {
676 pub fn name(&self) -> &str {
678 match self {
679 Self::Struct(ty) => ty.name(),
680 Self::Enum(ty) => ty.name(),
681 }
682 }
683
684 pub fn as_struct(&self) -> Option<&StructType> {
688 match self {
689 Self::Struct(ty) => Some(ty),
690 _ => None,
691 }
692 }
693
694 pub fn as_enum(&self) -> Option<&EnumType> {
698 match self {
699 Self::Enum(ty) => Some(ty),
700 _ => None,
701 }
702 }
703}
704
705impl std::fmt::Display for CustomType {
706 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
707 match self {
708 CustomType::Struct(ty) => ty.fmt(f),
709 CustomType::Enum(ty) => ty.fmt(f),
710 }
711 }
712}
713
714impl From<StructType> for CustomType {
715 fn from(value: StructType) -> Self {
716 Self::Struct(value)
717 }
718}
719
720impl From<EnumType> for CustomType {
721 fn from(value: EnumType) -> Self {
722 Self::Enum(value)
723 }
724}
725
726#[derive(Debug, Clone, PartialEq, Eq)]
728pub enum CompoundType {
729 Array(ArrayType),
731 Pair(PairType),
733 Map(MapType),
735 Custom(CustomType),
737}
738
739impl CompoundType {
740 pub fn as_array(&self) -> Option<&ArrayType> {
744 match self {
745 Self::Array(ty) => Some(ty),
746 _ => None,
747 }
748 }
749
750 pub fn as_pair(&self) -> Option<&PairType> {
754 match self {
755 Self::Pair(ty) => Some(ty),
756 _ => None,
757 }
758 }
759
760 pub fn as_map(&self) -> Option<&MapType> {
764 match self {
765 Self::Map(ty) => Some(ty),
766 _ => None,
767 }
768 }
769
770 pub fn as_struct(&self) -> Option<&StructType> {
774 match self {
775 Self::Custom(ty) => ty.as_struct(),
776 _ => None,
777 }
778 }
779
780 pub fn as_enum(&self) -> Option<&EnumType> {
784 match self {
785 Self::Custom(ty) => ty.as_enum(),
786 _ => None,
787 }
788 }
789
790 pub fn as_custom(&self) -> Option<&CustomType> {
794 match self {
795 Self::Custom(ty) => Some(ty),
796 _ => None,
797 }
798 }
799
800 fn common_type(&self, other: &Self) -> Option<CompoundType> {
805 match (self, other) {
808 (Self::Array(this), Self::Array(other)) => {
809 let element_type = this.element_type().common_type(other.element_type())?;
810 Some(ArrayType::new(element_type).into())
811 }
812 (Self::Pair(this), Self::Pair(other)) => {
813 let left_type = this.left_type().common_type(other.left_type())?;
814 let right_type = this.right_type().common_type(other.right_type())?;
815 Some(PairType::new(left_type, right_type).into())
816 }
817 (Self::Map(this), Self::Map(other)) => {
818 let key_type = this.key_type().common_type(other.key_type())?;
819 let value_type = this.value_type().common_type(other.value_type())?;
820 Some(MapType::new(key_type, value_type).into())
821 }
822 _ => None,
823 }
824 }
825}
826
827impl fmt::Display for CompoundType {
828 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
829 match self {
830 Self::Array(ty) => ty.fmt(f),
831 Self::Pair(ty) => ty.fmt(f),
832 Self::Map(ty) => ty.fmt(f),
833 Self::Custom(CustomType::Struct(ty)) => ty.fmt(f),
834 Self::Custom(CustomType::Enum(ty)) => ty.fmt(f),
835 }
836 }
837}
838
839impl Coercible for CompoundType {
840 fn is_coercible_to(&self, target: &Self) -> bool {
841 match (self, target) {
842 (Self::Array(src), Self::Array(target)) => src.is_coercible_to(target),
845
846 (Self::Pair(src), Self::Pair(target)) => src.is_coercible_to(target),
849
850 (Self::Map(src), Self::Map(target)) => src.is_coercible_to(target),
853
854 (Self::Custom(CustomType::Struct(src)), Self::Custom(CustomType::Struct(target))) => {
857 src.is_coercible_to(target)
858 }
859
860 (Self::Custom(CustomType::Enum(src)), Self::Custom(CustomType::Enum(target))) => {
862 src.is_coercible_to(target)
863 }
864
865 (Self::Map(src), Self::Custom(CustomType::Struct(target))) => {
868 if !src
869 .key_type()
870 .is_coercible_to(&PrimitiveType::String.into())
871 {
872 return false;
873 }
874
875 if !target
878 .members()
879 .values()
880 .all(|ty| src.value_type().is_coercible_to(ty))
881 {
882 return false;
883 }
884
885 true
887 }
888
889 (Self::Custom(CustomType::Struct(src)), Self::Map(target)) => {
892 if !Type::from(PrimitiveType::String).is_coercible_to(target.key_type()) {
893 return false;
894 }
895
896 if !src
898 .members()
899 .values()
900 .all(|ty| ty.is_coercible_to(target.value_type()))
901 {
902 return false;
903 }
904
905 true
906 }
907
908 _ => false,
909 }
910 }
911}
912
913impl From<ArrayType> for CompoundType {
914 fn from(value: ArrayType) -> Self {
915 Self::Array(value)
916 }
917}
918
919impl From<PairType> for CompoundType {
920 fn from(value: PairType) -> Self {
921 Self::Pair(value)
922 }
923}
924
925impl From<MapType> for CompoundType {
926 fn from(value: MapType) -> Self {
927 Self::Map(value)
928 }
929}
930
931impl From<StructType> for CompoundType {
932 fn from(value: StructType) -> Self {
933 Self::Custom(CustomType::Struct(value))
934 }
935}
936
937impl From<EnumType> for CompoundType {
938 fn from(value: EnumType) -> Self {
939 Self::Custom(CustomType::Enum(value))
940 }
941}
942
943#[derive(Debug, Clone, PartialEq, Eq)]
945struct ArrayTypeInner {
946 element_type: Type,
948 non_empty: bool,
950}
951
952#[derive(Debug, Clone, PartialEq, Eq)]
956pub struct ArrayType(Arc<ArrayTypeInner>);
957
958impl ArrayType {
959 pub fn new(element_type: impl Into<Type>) -> Self {
961 Self(Arc::new(ArrayTypeInner {
962 element_type: element_type.into(),
963 non_empty: false,
964 }))
965 }
966
967 pub fn non_empty(element_type: impl Into<Type>) -> Self {
969 Self(Arc::new(ArrayTypeInner {
970 element_type: element_type.into(),
971 non_empty: true,
972 }))
973 }
974
975 pub fn element_type(&self) -> &Type {
977 &self.0.element_type
978 }
979
980 pub fn is_non_empty(&self) -> bool {
982 self.0.non_empty
983 }
984
985 pub fn unqualified(&self) -> ArrayType {
987 if self.0.non_empty {
988 Self(Arc::new(ArrayTypeInner {
989 element_type: self.0.element_type.clone(),
990 non_empty: false,
991 }))
992 } else {
993 self.clone()
994 }
995 }
996}
997
998impl fmt::Display for ArrayType {
999 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1000 write!(f, "Array[{ty}]", ty = self.0.element_type)?;
1001
1002 if self.0.non_empty {
1003 write!(f, "+")?;
1004 }
1005
1006 Ok(())
1007 }
1008}
1009
1010impl Coercible for ArrayType {
1011 fn is_coercible_to(&self, target: &Self) -> bool {
1012 self.0.element_type.is_coercible_to(&target.0.element_type)
1015 }
1016}
1017
1018#[derive(Debug, Clone, PartialEq, Eq)]
1020pub struct PairType(Arc<(Type, Type)>);
1021
1022impl PairType {
1023 pub fn new(left_type: impl Into<Type>, right_type: impl Into<Type>) -> Self {
1025 Self(Arc::new((left_type.into(), right_type.into())))
1026 }
1027
1028 pub fn left_type(&self) -> &Type {
1030 &self.0.0
1031 }
1032
1033 pub fn right_type(&self) -> &Type {
1035 &self.0.1
1036 }
1037}
1038
1039impl fmt::Display for PairType {
1040 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1041 write!(
1042 f,
1043 "Pair[{left}, {right}]",
1044 left = self.left_type(),
1045 right = self.right_type()
1046 )?;
1047
1048 Ok(())
1049 }
1050}
1051
1052impl Coercible for PairType {
1053 fn is_coercible_to(&self, target: &Self) -> bool {
1054 self.left_type().is_coercible_to(target.left_type())
1055 && self.right_type().is_coercible_to(target.right_type())
1056 }
1057}
1058
1059#[derive(Debug, Clone, PartialEq, Eq)]
1061pub struct MapType(Arc<(Type, Type)>);
1062
1063impl MapType {
1064 pub fn new(key_type: impl Into<Type>, value_type: impl Into<Type>) -> Self {
1070 let key_type = key_type.into();
1071 assert!(
1072 key_type.is_union() || matches!(key_type, Type::Primitive(_, false)),
1073 "map key {key_type:#} is not a non-optional primitive"
1074 );
1075 Self(Arc::new((key_type, value_type.into())))
1076 }
1077
1078 pub fn key_type(&self) -> &Type {
1080 &self.0.0
1081 }
1082
1083 pub fn value_type(&self) -> &Type {
1085 &self.0.1
1086 }
1087}
1088
1089impl fmt::Display for MapType {
1090 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1091 write!(
1092 f,
1093 "Map[{key}, {value}]",
1094 key = self.key_type(),
1095 value = self.value_type()
1096 )?;
1097
1098 Ok(())
1099 }
1100}
1101
1102impl Coercible for MapType {
1103 fn is_coercible_to(&self, target: &Self) -> bool {
1104 self.key_type().is_coercible_to(target.key_type())
1105 && self.value_type().is_coercible_to(target.value_type())
1106 }
1107}
1108
1109#[derive(Debug, Clone, PartialEq, Eq)]
1111struct StructTypeInner {
1112 name: Arc<String>,
1118 members: IndexMap<String, Type>,
1120}
1121
1122#[derive(Debug, Clone, PartialEq, Eq)]
1126pub struct StructType(Arc<StructTypeInner>);
1127
1128impl StructType {
1129 pub fn new<N, T>(name: impl Into<String>, members: impl IntoIterator<Item = (N, T)>) -> Self
1131 where
1132 N: Into<String>,
1133 T: Into<Type>,
1134 {
1135 Self(Arc::new(StructTypeInner {
1136 name: Arc::new(name.into()),
1137 members: members
1138 .into_iter()
1139 .map(|(n, ty)| (n.into(), ty.into()))
1140 .collect(),
1141 }))
1142 }
1143
1144 pub fn name(&self) -> &Arc<String> {
1146 &self.0.name
1147 }
1148
1149 pub fn members(&self) -> &IndexMap<String, Type> {
1151 &self.0.members
1152 }
1153}
1154
1155impl fmt::Display for StructType {
1156 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1157 write!(f, "{name}", name = self.0.name)
1158 }
1159}
1160
1161impl Coercible for StructType {
1162 fn is_coercible_to(&self, target: &Self) -> bool {
1163 if self.0.members.len() != target.0.members.len() {
1164 return false;
1165 }
1166
1167 self.0.members.iter().all(|(k, v)| {
1168 target
1169 .0
1170 .members
1171 .get(k)
1172 .map(|target| v.is_coercible_to(target))
1173 .unwrap_or(false)
1174 })
1175 }
1176}
1177
1178#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1180pub struct EnumChoiceCacheKey {
1181 uri: Arc<Url>,
1183 enum_index: usize,
1185 choice_index: usize,
1187}
1188
1189impl EnumChoiceCacheKey {
1190 pub(crate) fn new(uri: Arc<Url>, enum_index: usize, choice_index: usize) -> Self {
1192 Self {
1193 uri,
1194 enum_index,
1195 choice_index,
1196 }
1197 }
1198}
1199
1200#[derive(Debug, Clone, PartialEq, Eq)]
1202struct EnumTypeInner {
1203 name: String,
1205 inner_value_type: Type,
1207 choices: Arc<[String]>,
1209}
1210
1211#[derive(Debug, Clone, PartialEq, Eq)]
1215pub struct EnumType(Arc<EnumTypeInner>);
1216
1217impl EnumType {
1218 pub fn new(
1224 enum_name: impl Into<String>,
1225 enum_span: Span,
1226 explicit_inner_type: Type,
1227 choices: Vec<(String, Type)>,
1228 choice_spans: &[Span],
1229 ) -> Result<Self, Diagnostic> {
1230 assert_eq!(choices.len(), choice_spans.len());
1231 let enum_name = enum_name.into();
1232 let mut results = Vec::with_capacity(choices.len());
1233
1234 for (choice_idx, (choice_name, choice_type)) in choices.iter().enumerate() {
1236 if !choice_type.is_coercible_to(&explicit_inner_type) {
1237 return Err(enum_choice_does_not_coerce_to_type(
1238 &enum_name,
1239 enum_span,
1240 choice_name,
1241 choice_spans[choice_idx],
1242 &explicit_inner_type,
1243 choice_type,
1244 ));
1245 }
1246
1247 results.push(choice_name.to_owned());
1248 }
1249
1250 Ok(Self(Arc::new(EnumTypeInner {
1251 name: enum_name,
1252 inner_value_type: explicit_inner_type,
1253 choices: results.into(),
1254 })))
1255 }
1256
1257 pub fn infer(
1265 enum_name: impl Into<String>,
1266 choices: Vec<(String, Type)>,
1267 choice_spans: &[Span],
1268 ) -> Result<Self, Diagnostic> {
1269 assert_eq!(choices.len(), choice_spans.len());
1270 let enum_name = enum_name.into();
1271
1272 let mut common_ty: Option<Type> = None;
1273 let mut names = Vec::with_capacity(choices.len());
1274 for (i, (name, choice_ty)) in choices.into_iter().enumerate() {
1275 match common_ty {
1276 Some(current_common_ty) => match current_common_ty.common_type(&choice_ty) {
1277 Some(new_common_ty) => {
1278 common_ty = Some(new_common_ty);
1279 }
1280 None => {
1281 return Err(no_common_inferred_type_for_enum(
1282 &enum_name,
1283 ¤t_common_ty,
1284 choice_spans[i - 1],
1285 &choice_ty,
1286 choice_spans[i],
1287 ));
1288 }
1289 },
1290 None => common_ty = Some(choice_ty),
1291 }
1292
1293 names.push(name);
1294 }
1295
1296 Ok(Self(Arc::new(EnumTypeInner {
1297 name: enum_name,
1298 inner_value_type: common_ty.unwrap_or(Type::Union),
1299 choices: names.into(),
1300 })))
1301 }
1302
1303 pub fn name(&self) -> &str {
1305 &self.0.name
1306 }
1307
1308 pub fn inner_value_type(&self) -> &Type {
1310 &self.0.inner_value_type
1311 }
1312
1313 pub fn choices(&self) -> &[String] {
1315 &self.0.choices
1316 }
1317}
1318
1319impl fmt::Display for EnumType {
1320 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1321 write!(f, "{name}", name = self.0.name)
1322 }
1323}
1324
1325impl Coercible for EnumType {
1326 fn is_coercible_to(&self, target: &Self) -> bool {
1327 self == target
1328 }
1329}
1330
1331#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1333pub enum CallKind {
1334 Task,
1336 Workflow,
1338}
1339
1340impl fmt::Display for CallKind {
1341 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1342 match self {
1343 Self::Task => write!(f, "task"),
1344 Self::Workflow => write!(f, "workflow"),
1345 }
1346 }
1347}
1348
1349#[derive(Debug, Clone, Eq)]
1351struct CallTypeInner {
1352 kind: CallKind,
1354 namespace: Option<String>,
1356 name: String,
1358 specified: Arc<HashSet<String>>,
1364 inputs: Arc<IndexMap<String, Input>>,
1368 outputs: Arc<IndexMap<String, Output>>,
1374}
1375
1376impl PartialEq for CallTypeInner {
1377 fn eq(&self, other: &Self) -> bool {
1378 std::ptr::eq(self, other)
1379 }
1380}
1381
1382#[derive(Debug, Clone, Eq)]
1386pub struct CallType(Arc<CallTypeInner>);
1387
1388impl CallType {
1389 pub(crate) fn new(
1391 kind: CallKind,
1392 name: impl Into<String>,
1393 specified: Arc<HashSet<String>>,
1394 inputs: Arc<IndexMap<String, Input>>,
1395 outputs: Arc<IndexMap<String, Output>>,
1396 ) -> Self {
1397 Self(Arc::new(CallTypeInner {
1398 kind,
1399 namespace: None,
1400 name: name.into(),
1401 specified,
1402 inputs,
1403 outputs,
1404 }))
1405 }
1406
1407 pub(crate) fn namespaced(
1410 kind: CallKind,
1411 namespace: impl Into<String>,
1412 name: impl Into<String>,
1413 specified: Arc<HashSet<String>>,
1414 inputs: Arc<IndexMap<String, Input>>,
1415 outputs: Arc<IndexMap<String, Output>>,
1416 ) -> Self {
1417 Self(Arc::new(CallTypeInner {
1418 kind,
1419 namespace: Some(namespace.into()),
1420 name: name.into(),
1421 specified,
1422 inputs,
1423 outputs,
1424 }))
1425 }
1426
1427 pub fn kind(&self) -> CallKind {
1429 self.0.kind
1430 }
1431
1432 pub fn namespace(&self) -> Option<&str> {
1436 self.0.namespace.as_deref()
1437 }
1438
1439 pub fn name(&self) -> &str {
1441 &self.0.name
1442 }
1443
1444 pub fn specified(&self) -> &HashSet<String> {
1446 &self.0.specified
1447 }
1448
1449 pub fn inputs(&self) -> &IndexMap<String, Input> {
1451 &self.0.inputs
1452 }
1453
1454 pub fn outputs(&self) -> &IndexMap<String, Output> {
1456 &self.0.outputs
1457 }
1458
1459 pub fn optional(&self) -> Self {
1461 let mut inner = self.0.as_ref().clone();
1462 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1463 *output = Output::new(output.ty().optional(), output.name_span());
1464 }
1465
1466 Self(Arc::new(inner))
1467 }
1468
1469 pub fn promote_scatter(&self) -> Self {
1471 let mut inner = self.0.as_ref().clone();
1472 for output in Arc::make_mut(&mut inner.outputs).values_mut() {
1473 *output = Output::new(output.ty().promote_scatter(), output.name_span());
1474 }
1475
1476 Self(Arc::new(inner))
1477 }
1478}
1479
1480impl Coercible for CallType {
1481 fn is_coercible_to(&self, _: &Self) -> bool {
1482 false
1484 }
1485}
1486
1487impl fmt::Display for CallType {
1488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1489 if let Some(ns) = &self.0.namespace {
1490 write!(
1491 f,
1492 "call to {kind} `{ns}.{name}`",
1493 kind = self.0.kind,
1494 name = self.0.name,
1495 )
1496 } else {
1497 write!(
1498 f,
1499 "call to {kind} `{name}`",
1500 kind = self.0.kind,
1501 name = self.0.name,
1502 )
1503 }
1504 }
1505}
1506
1507impl PartialEq for CallType {
1508 fn eq(&self, other: &Self) -> bool {
1509 Arc::ptr_eq(&self.0, &other.0)
1510 }
1511}
1512
1513#[derive(Debug, Clone, PartialEq, Eq)]
1515struct TypeNameRefInner {
1516 name: String,
1518 ty: CustomType,
1520}
1521
1522#[derive(Debug, Clone, PartialEq, Eq)]
1524pub struct TypeNameRef(Arc<TypeNameRefInner>);
1525
1526impl TypeNameRef {
1527 pub fn new(name: impl Into<String>, ty: CustomType) -> Self {
1529 Self(
1530 TypeNameRefInner {
1531 name: name.into(),
1532 ty,
1533 }
1534 .into(),
1535 )
1536 }
1537
1538 pub fn name(&self) -> &str {
1540 &self.0.name
1541 }
1542
1543 pub fn ty(&self) -> &CustomType {
1545 &self.0.ty
1546 }
1547
1548 pub fn as_struct(&self) -> Option<&StructType> {
1552 match &self.0.ty {
1553 CustomType::Struct(ty) => Some(ty),
1554 _ => None,
1555 }
1556 }
1557
1558 pub fn as_enum(&self) -> Option<&EnumType> {
1562 match &self.0.ty {
1563 CustomType::Enum(ty) => Some(ty),
1564 _ => None,
1565 }
1566 }
1567}
1568
1569impl std::fmt::Display for TypeNameRef {
1570 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1571 self.0.name.fmt(f)
1572 }
1573}
1574
1575#[cfg(test)]
1576mod tests {
1577 use pretty_assertions::assert_eq;
1578
1579 use super::*;
1580
1581 #[test_log::test]
1582 fn primitive_type_display() {
1583 assert_eq!(PrimitiveType::Boolean.to_string(), "Boolean");
1584 assert_eq!(PrimitiveType::Integer.to_string(), "Int");
1585 assert_eq!(PrimitiveType::Float.to_string(), "Float");
1586 assert_eq!(PrimitiveType::String.to_string(), "String");
1587 assert_eq!(PrimitiveType::File.to_string(), "File");
1588 assert_eq!(PrimitiveType::Directory.to_string(), "Directory");
1589 assert_eq!(
1590 Type::from(PrimitiveType::Boolean).optional().to_string(),
1591 "Boolean?"
1592 );
1593 assert_eq!(
1594 Type::from(PrimitiveType::Integer).optional().to_string(),
1595 "Int?"
1596 );
1597 assert_eq!(
1598 Type::from(PrimitiveType::Float).optional().to_string(),
1599 "Float?"
1600 );
1601 assert_eq!(
1602 Type::from(PrimitiveType::String).optional().to_string(),
1603 "String?"
1604 );
1605 assert_eq!(
1606 Type::from(PrimitiveType::File).optional().to_string(),
1607 "File?"
1608 );
1609 assert_eq!(
1610 Type::from(PrimitiveType::Directory).optional().to_string(),
1611 "Directory?"
1612 );
1613 }
1614
1615 #[test_log::test]
1616 fn array_type_display() {
1617 assert_eq!(
1618 ArrayType::new(PrimitiveType::String).to_string(),
1619 "Array[String]"
1620 );
1621 assert_eq!(
1622 ArrayType::non_empty(PrimitiveType::String).to_string(),
1623 "Array[String]+"
1624 );
1625
1626 let ty: Type = ArrayType::new(ArrayType::new(PrimitiveType::String)).into();
1627 assert_eq!(ty.to_string(), "Array[Array[String]]");
1628
1629 let ty = Type::from(ArrayType::non_empty(
1630 Type::from(ArrayType::non_empty(
1631 Type::from(PrimitiveType::String).optional(),
1632 ))
1633 .optional(),
1634 ))
1635 .optional();
1636 assert_eq!(ty.to_string(), "Array[Array[String?]+?]+?");
1637 }
1638
1639 #[test_log::test]
1640 fn pair_type_display() {
1641 assert_eq!(
1642 PairType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1643 "Pair[String, Boolean]"
1644 );
1645
1646 let ty: Type = PairType::new(
1647 ArrayType::new(PrimitiveType::String),
1648 ArrayType::new(PrimitiveType::String),
1649 )
1650 .into();
1651 assert_eq!(ty.to_string(), "Pair[Array[String], Array[String]]");
1652
1653 let ty = Type::from(PairType::new(
1654 Type::from(ArrayType::non_empty(
1655 Type::from(PrimitiveType::File).optional(),
1656 ))
1657 .optional(),
1658 Type::from(ArrayType::non_empty(
1659 Type::from(PrimitiveType::File).optional(),
1660 ))
1661 .optional(),
1662 ))
1663 .optional();
1664 assert_eq!(ty.to_string(), "Pair[Array[File?]+?, Array[File?]+?]?");
1665 }
1666
1667 #[test_log::test]
1668 fn map_type_display() {
1669 assert_eq!(
1670 MapType::new(PrimitiveType::String, PrimitiveType::Boolean).to_string(),
1671 "Map[String, Boolean]"
1672 );
1673
1674 let ty: Type = MapType::new(
1675 PrimitiveType::Boolean,
1676 ArrayType::new(PrimitiveType::String),
1677 )
1678 .into();
1679 assert_eq!(ty.to_string(), "Map[Boolean, Array[String]]");
1680
1681 let ty: Type = Type::from(MapType::new(
1682 PrimitiveType::String,
1683 Type::from(ArrayType::non_empty(
1684 Type::from(PrimitiveType::File).optional(),
1685 ))
1686 .optional(),
1687 ))
1688 .optional();
1689 assert_eq!(ty.to_string(), "Map[String, Array[File?]+?]?");
1690 }
1691
1692 #[test_log::test]
1693 fn struct_type_display() {
1694 assert_eq!(
1695 StructType::new("Foobar", std::iter::empty::<(String, Type)>()).to_string(),
1696 "Foobar"
1697 );
1698 }
1699
1700 #[test_log::test]
1701 fn object_type_display() {
1702 assert_eq!(Type::Object.to_string(), "Object");
1703 assert_eq!(Type::OptionalObject.to_string(), "Object?");
1704 }
1705
1706 #[test_log::test]
1707 fn union_type_display() {
1708 assert_eq!(Type::Union.to_string(), "Union");
1709 }
1710
1711 #[test_log::test]
1712 fn none_type_display() {
1713 assert_eq!(Type::None.to_string(), "None");
1714 }
1715
1716 #[test_log::test]
1717 fn primitive_type_coercion() {
1718 for ty in [
1721 Type::from(PrimitiveType::Boolean),
1722 PrimitiveType::Directory.into(),
1723 PrimitiveType::File.into(),
1724 PrimitiveType::Float.into(),
1725 PrimitiveType::Integer.into(),
1726 PrimitiveType::String.into(),
1727 ] {
1728 assert!(ty.is_coercible_to(&ty));
1729 assert!(ty.optional().is_coercible_to(&ty.optional()));
1730 assert!(ty.is_coercible_to(&ty.optional()));
1731 assert!(!ty.optional().is_coercible_to(&ty));
1732 }
1733
1734 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::File));
1736 assert!(PrimitiveType::String.is_coercible_to(&PrimitiveType::Directory));
1737 assert!(PrimitiveType::Integer.is_coercible_to(&PrimitiveType::Float));
1738 assert!(PrimitiveType::File.is_coercible_to(&PrimitiveType::String));
1739 assert!(PrimitiveType::Directory.is_coercible_to(&PrimitiveType::String));
1740 assert!(!PrimitiveType::Float.is_coercible_to(&PrimitiveType::Integer));
1741 }
1742
1743 #[test_log::test]
1744 fn object_type_coercion() {
1745 assert!(Type::Object.is_coercible_to(&Type::Object));
1746 assert!(Type::Object.is_coercible_to(&Type::OptionalObject));
1747 assert!(Type::OptionalObject.is_coercible_to(&Type::OptionalObject));
1748 assert!(!Type::OptionalObject.is_coercible_to(&Type::Object));
1749
1750 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1752 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1753
1754 let ty = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1756 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1757
1758 let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
1760 assert!(!Type::Object.is_coercible_to(&ty));
1761
1762 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1764 assert!(Type::Object.is_coercible_to(&ty));
1765
1766 let ty = Type::from(MapType::new(PrimitiveType::File, PrimitiveType::String)).optional();
1768 assert!(Type::Object.is_coercible_to(&ty));
1769
1770 let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1772 assert!(Type::OptionalObject.is_coercible_to(&ty));
1773
1774 let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1776 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1777
1778 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1780 assert!(Type::Object.is_coercible_to(&ty));
1781
1782 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1784 assert!(Type::Object.is_coercible_to(&ty));
1785
1786 let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1788 assert!(Type::OptionalObject.is_coercible_to(&ty));
1789
1790 let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1792 assert!(!Type::OptionalObject.is_coercible_to(&ty));
1793 }
1794
1795 #[test_log::test]
1796 fn array_type_coercion() {
1797 assert!(
1799 ArrayType::new(PrimitiveType::String)
1800 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1801 );
1802 assert!(
1803 ArrayType::new(PrimitiveType::File)
1804 .is_coercible_to(&ArrayType::new(PrimitiveType::String))
1805 );
1806 assert!(
1807 ArrayType::new(PrimitiveType::String)
1808 .is_coercible_to(&ArrayType::new(PrimitiveType::File))
1809 );
1810
1811 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1813 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1814 assert!(type1.is_coercible_to(&type2));
1815 assert!(!type2.is_coercible_to(&type1));
1816
1817 let type1: Type = ArrayType::new(type1).into();
1819 let type2 = ArrayType::new(type2).into();
1820 assert!(type1.is_coercible_to(&type2));
1821 assert!(!type2.is_coercible_to(&type1));
1822
1823 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1825 let type2 = ArrayType::new(Type::from(PrimitiveType::File).optional()).into();
1826 assert!(type1.is_coercible_to(&type2));
1827 assert!(!type2.is_coercible_to(&type1));
1828
1829 let type1: Type = ArrayType::non_empty(PrimitiveType::String).into();
1831 let type2 = ArrayType::new(Type::from(PrimitiveType::String).optional()).into();
1832 assert!(type1.is_coercible_to(&type2));
1833 assert!(!type2.is_coercible_to(&type1));
1834
1835 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1837 let type2 = ArrayType::new(PrimitiveType::String).into();
1838 assert!(type1.is_coercible_to(&type2));
1839 assert!(type2.is_coercible_to(&type1));
1840
1841 let type1 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1843 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1844 assert!(type1.is_coercible_to(&type2));
1845 assert!(type2.is_coercible_to(&type1));
1846
1847 let type1: Type = ArrayType::new(PrimitiveType::String).into();
1849 let type2 = Type::from(ArrayType::new(PrimitiveType::String)).optional();
1850 assert!(type1.is_coercible_to(&type2));
1851 assert!(!type2.is_coercible_to(&type1));
1852 }
1853
1854 #[test_log::test]
1855 fn pair_type_coercion() {
1856 assert!(
1858 PairType::new(PrimitiveType::String, PrimitiveType::String)
1859 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1860 );
1861 assert!(
1862 PairType::new(PrimitiveType::String, PrimitiveType::String).is_coercible_to(
1863 &PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1864 )
1865 );
1866 assert!(
1867 PairType::new(PrimitiveType::File, PrimitiveType::Directory)
1868 .is_coercible_to(&PairType::new(PrimitiveType::String, PrimitiveType::String))
1869 );
1870
1871 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1873 let type2 = PairType::new(
1874 Type::from(PrimitiveType::File).optional(),
1875 Type::from(PrimitiveType::Directory).optional(),
1876 )
1877 .into();
1878 assert!(type1.is_coercible_to(&type2));
1879 assert!(!type2.is_coercible_to(&type1));
1880
1881 let type1: Type = PairType::new(type1.clone(), type1).into();
1883 let type2 = PairType::new(type2.clone(), type2).into();
1884 assert!(type1.is_coercible_to(&type2));
1885 assert!(!type2.is_coercible_to(&type1));
1886
1887 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1889 let type2 = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1890 assert!(type1.is_coercible_to(&type2));
1891 assert!(type2.is_coercible_to(&type1));
1892
1893 let type1 =
1895 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1896 let type2 =
1897 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1898 assert!(type1.is_coercible_to(&type2));
1899 assert!(type2.is_coercible_to(&type1));
1900
1901 let type1: Type = PairType::new(PrimitiveType::String, PrimitiveType::String).into();
1903 let type2 =
1904 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1905 assert!(type1.is_coercible_to(&type2));
1906 assert!(!type2.is_coercible_to(&type1));
1907 }
1908
1909 #[test_log::test]
1910 fn map_type_coercion() {
1911 assert!(
1913 MapType::new(PrimitiveType::String, PrimitiveType::String)
1914 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1915 );
1916 assert!(
1917 MapType::new(PrimitiveType::String, PrimitiveType::String)
1918 .is_coercible_to(&MapType::new(PrimitiveType::File, PrimitiveType::Directory))
1919 );
1920 assert!(
1921 MapType::new(PrimitiveType::File, PrimitiveType::Directory)
1922 .is_coercible_to(&MapType::new(PrimitiveType::String, PrimitiveType::String))
1923 );
1924
1925 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1927 let type2 = MapType::new(
1928 PrimitiveType::File,
1929 Type::from(PrimitiveType::Directory).optional(),
1930 )
1931 .into();
1932 assert!(type1.is_coercible_to(&type2));
1933 assert!(!type2.is_coercible_to(&type1));
1934
1935 let type1: Type = MapType::new(PrimitiveType::String, type1).into();
1937 let type2 = MapType::new(PrimitiveType::Directory, type2).into();
1938 assert!(type1.is_coercible_to(&type2));
1939 assert!(!type2.is_coercible_to(&type1));
1940
1941 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1943 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1944 assert!(type1.is_coercible_to(&type2));
1945 assert!(type2.is_coercible_to(&type1));
1946
1947 let type1: Type =
1949 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1950 let type2: Type =
1951 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1952 assert!(type1.is_coercible_to(&type2));
1953 assert!(type2.is_coercible_to(&type1));
1954
1955 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1957 let type2 =
1958 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1959 assert!(type1.is_coercible_to(&type2));
1960 assert!(!type2.is_coercible_to(&type1));
1961
1962 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1964 let type2 = StructType::new(
1965 "Foo",
1966 [
1967 ("foo", PrimitiveType::Integer),
1968 ("bar", PrimitiveType::Integer),
1969 ("baz", PrimitiveType::Integer),
1970 ],
1971 )
1972 .into();
1973 assert!(type1.is_coercible_to(&type2));
1974
1975 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
1977 let type2 = StructType::new(
1978 "Foo",
1979 [
1980 ("foo", PrimitiveType::Integer),
1981 ("bar", PrimitiveType::Integer),
1982 ("baz", PrimitiveType::Integer),
1983 ],
1984 )
1985 .into();
1986 assert!(type1.is_coercible_to(&type2));
1987
1988 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
1990 let type2 = StructType::new(
1991 "Foo",
1992 [
1993 ("foo", PrimitiveType::Integer),
1994 ("bar", PrimitiveType::String),
1995 ("baz", PrimitiveType::Integer),
1996 ],
1997 )
1998 .into();
1999 assert!(!type1.is_coercible_to(&type2));
2000
2001 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
2003 let type2 = StructType::new(
2004 "Foo",
2005 [
2006 ("foo", PrimitiveType::Integer),
2007 ("bar", PrimitiveType::Integer),
2008 ("baz", PrimitiveType::Integer),
2009 ],
2010 )
2011 .into();
2012 assert!(!type1.is_coercible_to(&type2));
2013
2014 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2016 assert!(type1.is_coercible_to(&Type::Object));
2017
2018 let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2020 assert!(type1.is_coercible_to(&Type::OptionalObject));
2021
2022 let type1: Type =
2024 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2025 assert!(type1.is_coercible_to(&Type::OptionalObject));
2026
2027 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2029 assert!(type1.is_coercible_to(&Type::Object));
2030
2031 let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2033 assert!(type1.is_coercible_to(&Type::OptionalObject));
2034
2035 let type1: Type =
2037 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2038 assert!(type1.is_coercible_to(&Type::OptionalObject));
2039
2040 let type1: Type =
2042 Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2043 assert!(!type1.is_coercible_to(&Type::Object));
2044
2045 let type1: Type =
2047 Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2048 assert!(!type1.is_coercible_to(&Type::Object));
2049
2050 let type1: Type = MapType::new(PrimitiveType::Integer, PrimitiveType::Integer).into();
2052 assert!(!type1.is_coercible_to(&Type::Object));
2053 }
2054
2055 #[test_log::test]
2056 fn struct_type_coercion() {
2057 let type1: Type = StructType::new(
2059 "Foo",
2060 [
2061 ("foo", PrimitiveType::String),
2062 ("bar", PrimitiveType::String),
2063 ("baz", PrimitiveType::Integer),
2064 ],
2065 )
2066 .into();
2067 let type2 = StructType::new(
2068 "Foo",
2069 [
2070 ("foo", PrimitiveType::String),
2071 ("bar", PrimitiveType::String),
2072 ("baz", PrimitiveType::Integer),
2073 ],
2074 )
2075 .into();
2076 assert!(type1.is_coercible_to(&type2));
2077 assert!(type2.is_coercible_to(&type1));
2078
2079 let type1: Type = StructType::new(
2081 "Foo",
2082 [
2083 ("foo", PrimitiveType::String),
2084 ("bar", PrimitiveType::String),
2085 ("baz", PrimitiveType::Integer),
2086 ],
2087 )
2088 .into();
2089 let type2 = Type::from(StructType::new(
2090 "Foo",
2091 [
2092 ("foo", PrimitiveType::String),
2093 ("bar", PrimitiveType::String),
2094 ("baz", PrimitiveType::Integer),
2095 ],
2096 ))
2097 .optional();
2098 assert!(type1.is_coercible_to(&type2));
2099 assert!(!type2.is_coercible_to(&type1));
2100
2101 let type1: Type = Type::from(StructType::new(
2103 "Foo",
2104 [
2105 ("foo", PrimitiveType::String),
2106 ("bar", PrimitiveType::String),
2107 ("baz", PrimitiveType::Integer),
2108 ],
2109 ))
2110 .optional();
2111 let type2 = Type::from(StructType::new(
2112 "Foo",
2113 [
2114 ("foo", PrimitiveType::String),
2115 ("bar", PrimitiveType::String),
2116 ("baz", PrimitiveType::Integer),
2117 ],
2118 ))
2119 .optional();
2120 assert!(type1.is_coercible_to(&type2));
2121 assert!(type2.is_coercible_to(&type1));
2122
2123 let type1: Type = StructType::new(
2125 "Foo",
2126 [
2127 ("foo", PrimitiveType::String),
2128 ("bar", PrimitiveType::String),
2129 ("baz", PrimitiveType::Integer),
2130 ],
2131 )
2132 .into();
2133 let type2 = StructType::new(
2134 "Bar",
2135 [
2136 ("foo", PrimitiveType::File),
2137 ("bar", PrimitiveType::Directory),
2138 ("baz", PrimitiveType::Float),
2139 ],
2140 )
2141 .into();
2142 assert!(type1.is_coercible_to(&type2));
2143 assert!(!type2.is_coercible_to(&type1));
2144
2145 let type1: Type = StructType::new(
2147 "Foo",
2148 [
2149 ("foo", PrimitiveType::String),
2150 ("bar", PrimitiveType::String),
2151 ("baz", PrimitiveType::Integer),
2152 ],
2153 )
2154 .into();
2155 let type2 = StructType::new("Bar", [("baz", PrimitiveType::Float)]).into();
2156 assert!(!type1.is_coercible_to(&type2));
2157 assert!(!type2.is_coercible_to(&type1));
2158
2159 let type1: Type = StructType::new(
2161 "Foo",
2162 [
2163 ("foo", PrimitiveType::String),
2164 ("bar", PrimitiveType::String),
2165 ("baz", PrimitiveType::String),
2166 ],
2167 )
2168 .into();
2169 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2170 assert!(type1.is_coercible_to(&type2));
2171
2172 let type1: Type = StructType::new(
2174 "Foo",
2175 [
2176 ("foo", PrimitiveType::String),
2177 ("bar", PrimitiveType::String),
2178 ("baz", PrimitiveType::String),
2179 ],
2180 )
2181 .into();
2182 let type2 = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
2183 assert!(type1.is_coercible_to(&type2));
2184
2185 let type1: Type = StructType::new(
2187 "Foo",
2188 [
2189 ("foo", PrimitiveType::String),
2190 ("bar", PrimitiveType::Integer),
2191 ("baz", PrimitiveType::String),
2192 ],
2193 )
2194 .into();
2195 let type2 = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
2196 assert!(!type1.is_coercible_to(&type2));
2197
2198 let type1: Type = StructType::new(
2200 "Foo",
2201 [
2202 ("foo", PrimitiveType::String),
2203 ("bar", PrimitiveType::String),
2204 ("baz", PrimitiveType::String),
2205 ],
2206 )
2207 .into();
2208 let type2 = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2209 assert!(!type1.is_coercible_to(&type2));
2210
2211 assert!(type1.is_coercible_to(&Type::Object));
2213
2214 assert!(type1.is_coercible_to(&Type::OptionalObject));
2216
2217 let type1: Type =
2219 Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
2220 assert!(type1.is_coercible_to(&Type::OptionalObject));
2221
2222 assert!(!type1.is_coercible_to(&Type::Object));
2224 }
2225
2226 #[test_log::test]
2227 fn union_type_coercion() {
2228 for ty in [
2230 Type::from(PrimitiveType::Boolean),
2231 PrimitiveType::Directory.into(),
2232 PrimitiveType::File.into(),
2233 PrimitiveType::Float.into(),
2234 PrimitiveType::Integer.into(),
2235 PrimitiveType::String.into(),
2236 ] {
2237 assert!(Type::Union.is_coercible_to(&ty));
2238 assert!(Type::Union.is_coercible_to(&ty.optional()));
2239 assert!(ty.is_coercible_to(&Type::Union));
2240 }
2241
2242 for optional in [true, false] {
2243 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2245 let ty = if optional { ty.optional() } else { ty };
2246
2247 let coercible = Type::Union.is_coercible_to(&ty);
2248 assert!(coercible);
2249
2250 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2252 let ty = if optional { ty.optional() } else { ty };
2253 let coercible = Type::Union.is_coercible_to(&ty);
2254 assert!(coercible);
2255
2256 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2258 let ty = if optional { ty.optional() } else { ty };
2259 let coercible = Type::Union.is_coercible_to(&ty);
2260 assert!(coercible);
2261
2262 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2264 let ty = if optional { ty.optional() } else { ty };
2265 let coercible = Type::Union.is_coercible_to(&ty);
2266 assert!(coercible);
2267 }
2268 }
2269
2270 #[test_log::test]
2271 fn none_type_coercion() {
2272 for ty in [
2274 Type::from(PrimitiveType::Boolean),
2275 PrimitiveType::Directory.into(),
2276 PrimitiveType::File.into(),
2277 PrimitiveType::Float.into(),
2278 PrimitiveType::Integer.into(),
2279 PrimitiveType::String.into(),
2280 ] {
2281 assert!(!Type::None.is_coercible_to(&ty));
2282 assert!(Type::None.is_coercible_to(&ty.optional()));
2283 assert!(!ty.is_coercible_to(&Type::None));
2284 }
2285
2286 for optional in [true, false] {
2287 let ty: Type = ArrayType::new(PrimitiveType::String).into();
2289 let ty = if optional { ty.optional() } else { ty };
2290 let coercible = Type::None.is_coercible_to(&ty);
2291 if optional {
2292 assert!(coercible);
2293 } else {
2294 assert!(!coercible);
2295 }
2296
2297 let ty: Type = PairType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2299 let ty = if optional { ty.optional() } else { ty };
2300 let coercible = Type::None.is_coercible_to(&ty);
2301 if optional {
2302 assert!(coercible);
2303 } else {
2304 assert!(!coercible);
2305 }
2306
2307 let ty: Type = MapType::new(PrimitiveType::String, PrimitiveType::Boolean).into();
2309 let ty = if optional { ty.optional() } else { ty };
2310 let coercible = Type::None.is_coercible_to(&ty);
2311 if optional {
2312 assert!(coercible);
2313 } else {
2314 assert!(!coercible);
2315 }
2316
2317 let ty: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2319 let ty = if optional { ty.optional() } else { ty };
2320 let coercible = Type::None.is_coercible_to(&ty);
2321 if optional {
2322 assert!(coercible);
2323 } else {
2324 assert!(!coercible);
2325 }
2326 }
2327 }
2328
2329 #[test_log::test]
2330 fn primitive_equality() {
2331 for ty in [
2332 Type::from(PrimitiveType::Boolean),
2333 PrimitiveType::Directory.into(),
2334 PrimitiveType::File.into(),
2335 PrimitiveType::Float.into(),
2336 PrimitiveType::Integer.into(),
2337 PrimitiveType::String.into(),
2338 ] {
2339 assert!(ty.eq(&ty));
2340 assert!(!ty.optional().eq(&ty));
2341 assert!(!ty.eq(&ty.optional()));
2342 assert!(ty.optional().eq(&ty.optional()));
2343 assert!(!ty.eq(&Type::Object));
2344 assert!(!ty.eq(&Type::OptionalObject));
2345 assert!(!ty.eq(&Type::Union));
2346 assert!(!ty.eq(&Type::None));
2347 }
2348 }
2349
2350 #[test_log::test]
2351 fn array_equality() {
2352 let a: Type = ArrayType::new(PrimitiveType::String).into();
2354 let b: Type = ArrayType::new(PrimitiveType::String).into();
2355 assert!(a.eq(&b));
2356 assert!(!a.optional().eq(&b));
2357 assert!(!a.eq(&b.optional()));
2358 assert!(a.optional().eq(&b.optional()));
2359
2360 let a: Type = ArrayType::new(a).into();
2362 let b: Type = ArrayType::new(b).into();
2363 assert!(a.eq(&b));
2364
2365 let a: Type = ArrayType::non_empty(a).into();
2367 let b: Type = ArrayType::non_empty(b).into();
2368 assert!(a.eq(&b));
2369
2370 let a: Type = ArrayType::new(PrimitiveType::String).into();
2372 let b: Type = ArrayType::non_empty(PrimitiveType::String).into();
2373 assert!(!a.eq(&b));
2374
2375 let a: Type = ArrayType::new(PrimitiveType::String).into();
2377 let b: Type = ArrayType::new(PrimitiveType::Integer).into();
2378 assert!(!a.eq(&b));
2379
2380 assert!(!a.eq(&Type::Object));
2381 assert!(!a.eq(&Type::OptionalObject));
2382 assert!(!a.eq(&Type::Union));
2383 assert!(!a.eq(&Type::None));
2384 }
2385
2386 #[test_log::test]
2387 fn pair_equality() {
2388 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2390 let b: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2391 assert!(a.eq(&b));
2392 assert!(!a.optional().eq(&b));
2393 assert!(!a.eq(&b.optional()));
2394 assert!(a.optional().eq(&b.optional()));
2395
2396 let a: Type = PairType::new(a.clone(), a).into();
2399 let b: Type = PairType::new(b.clone(), b).into();
2400 assert!(a.eq(&b));
2401
2402 let a: Type = PairType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2404 let b: Type =
2405 Type::from(PairType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2406 assert!(!a.eq(&b));
2407
2408 assert!(!a.eq(&Type::Object));
2409 assert!(!a.eq(&Type::OptionalObject));
2410 assert!(!a.eq(&Type::Union));
2411 assert!(!a.eq(&Type::None));
2412 }
2413
2414 #[test_log::test]
2415 fn map_equality() {
2416 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2418 let b = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2419 assert!(a.eq(&b));
2420 assert!(!a.optional().eq(&b));
2421 assert!(!a.eq(&b.optional()));
2422 assert!(a.optional().eq(&b.optional()));
2423
2424 let a: Type = MapType::new(PrimitiveType::File, a).into();
2426 let b = MapType::new(PrimitiveType::File, b).into();
2427 assert!(a.eq(&b));
2428
2429 let a: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2431 let b = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
2432 assert!(!a.eq(&b));
2433
2434 assert!(!a.eq(&Type::Object));
2435 assert!(!a.eq(&Type::OptionalObject));
2436 assert!(!a.eq(&Type::Union));
2437 assert!(!a.eq(&Type::None));
2438 }
2439
2440 #[test_log::test]
2441 fn struct_equality() {
2442 let a: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2443 assert!(a.eq(&a));
2444 assert!(!a.optional().eq(&a));
2445 assert!(!a.eq(&a.optional()));
2446 assert!(a.optional().eq(&a.optional()));
2447
2448 let b: Type = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
2449 assert!(a.eq(&b));
2450 let b: Type = StructType::new("Bar", [("foo", PrimitiveType::String)]).into();
2451 assert!(!a.eq(&b));
2452 }
2453
2454 #[test_log::test]
2455 fn object_equality() {
2456 assert!(Type::Object.eq(&Type::Object));
2457 assert!(!Type::OptionalObject.eq(&Type::Object));
2458 assert!(!Type::Object.eq(&Type::OptionalObject));
2459 assert!(Type::OptionalObject.eq(&Type::OptionalObject));
2460 }
2461
2462 #[test_log::test]
2463 fn union_equality() {
2464 assert!(Type::Union.eq(&Type::Union));
2465 assert!(!Type::None.eq(&Type::Union));
2466 assert!(!Type::Union.eq(&Type::None));
2467 assert!(Type::None.eq(&Type::None));
2468 }
2469
2470 #[test_log::test]
2471 fn enum_type_new_with_explicit_type() {
2472 let status = EnumType::new(
2475 "Status",
2476 Span::new(0, 0),
2477 PrimitiveType::String.into(),
2478 vec![
2479 ("Pending".into(), PrimitiveType::String.into()),
2480 ("Running".into(), PrimitiveType::String.into()),
2481 ("Complete".into(), PrimitiveType::String.into()),
2482 ],
2483 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)][..],
2484 )
2485 .unwrap();
2486
2487 assert_eq!(status.name(), "Status");
2488 assert_eq!(
2489 status.inner_value_type(),
2490 &Type::from(PrimitiveType::String)
2491 );
2492 assert_eq!(status.choices().len(), 3);
2493 }
2494
2495 #[test_log::test]
2496 fn enum_type_new_fails_when_not_coercible() {
2497 let result = EnumType::new(
2499 "Bad",
2500 Span::new(0, 0),
2501 PrimitiveType::Integer.into(),
2502 vec![
2503 ("First".into(), PrimitiveType::String.into()),
2504 ("Second".into(), PrimitiveType::Integer.into()),
2505 ],
2506 &[Span::new(0, 0), Span::new(0, 0)][..],
2507 );
2508
2509 assert!(
2510 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot coerce choice `First` in enum `Bad` from type `String` to type `Int`")
2511 );
2512 }
2513
2514 #[test_log::test]
2515 fn enum_type_infer_finds_common_type() {
2516 let priority = EnumType::infer(
2518 "Priority",
2519 vec![
2520 ("Low".into(), PrimitiveType::Integer.into()),
2521 ("Medium".into(), PrimitiveType::Integer.into()),
2522 ("High".into(), PrimitiveType::Integer.into()),
2523 ],
2524 &[Span::new(0, 0), Span::new(0, 0), Span::new(0, 0)],
2525 )
2526 .unwrap();
2527
2528 assert_eq!(priority.name(), "Priority");
2529 assert_eq!(
2530 priority.inner_value_type(),
2531 &Type::from(PrimitiveType::Integer)
2532 );
2533 assert_eq!(priority.choices().len(), 3);
2534 }
2535
2536 #[test_log::test]
2537 fn enum_type_infer_coerces_int_to_float() {
2538 let mixed = EnumType::infer(
2540 "Mixed",
2541 vec![
2542 ("IntValue".into(), PrimitiveType::Integer.into()),
2543 ("FloatValue".into(), PrimitiveType::Float.into()),
2544 ],
2545 &[Span::new(0, 0), Span::new(0, 0)],
2546 )
2547 .unwrap();
2548
2549 assert_eq!(mixed.name(), "Mixed");
2550 assert_eq!(mixed.inner_value_type(), &Type::from(PrimitiveType::Float));
2551 assert_eq!(mixed.choices().len(), 2);
2552 }
2553
2554 #[test_log::test]
2555 fn enum_type_infer_fails_without_common_type() {
2556 let result = EnumType::infer(
2558 "Bad",
2559 vec![
2560 ("StringVal".into(), PrimitiveType::String.into()),
2561 ("IntVal".into(), PrimitiveType::Integer.into()),
2562 ],
2563 &[Span::new(0, 0), Span::new(0, 0)][..],
2564 );
2565
2566 assert!(
2567 matches!(result, Err(diagnostic) if diagnostic.message() == "cannot infer a common type for enum `Bad`")
2568 );
2569 }
2570
2571 #[test_log::test]
2572 fn enum_type_empty_has_union_type() {
2573 let result = EnumType::infer("Empty", Vec::<(String, Type)>::new(), &[]);
2575
2576 let empty = result.unwrap();
2577 assert_eq!(empty.name(), "Empty");
2578 assert_eq!(empty.inner_value_type(), &Type::Union);
2579 assert_eq!(empty.choices().len(), 0);
2580 }
2581
2582 #[test_log::test]
2583 fn enum_type_display() {
2584 let enum_type = EnumType::new(
2585 "Color",
2586 Span::new(0, 0),
2587 PrimitiveType::String.into(),
2588 vec![("Red".into(), PrimitiveType::String.into())],
2589 &[Span::new(0, 0)][..],
2590 )
2591 .unwrap();
2592 assert_eq!(enum_type.to_string(), "Color");
2593 }
2594
2595 #[test_log::test]
2596 fn enum_type_not_coercible_to_other_enums() {
2597 let color = EnumType::new(
2598 "Color",
2599 Span::new(0, 0),
2600 PrimitiveType::String.into(),
2601 vec![("Red".into(), PrimitiveType::String.into())],
2602 &[Span::new(0, 0)][..],
2603 )
2604 .unwrap();
2605 let status = EnumType::new(
2606 "Status",
2607 Span::new(0, 0),
2608 PrimitiveType::String.into(),
2609 vec![("Active".into(), PrimitiveType::String.into())],
2610 &[Span::new(0, 0)][..],
2611 )
2612 .unwrap();
2613 assert!(!color.is_coercible_to(&status));
2614 }
2615}