Skip to main content

wdl_analysis/
types.rs

1//! Representation of the WDL type system.
2
3use 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
19/// Used to display a slice of types.
20pub fn display_types(slice: &[Type]) -> impl fmt::Display + use<'_> {
21    /// Used to display a slice of types.
22    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
49/// A trait implemented on type name resolvers.
50pub trait TypeNameResolver {
51    /// Resolves the given type name to a type.
52    fn resolve(&mut self, name: &str, span: Span) -> Result<Type, Diagnostic>;
53}
54
55/// A trait implemented on types that may be optional.
56pub trait Optional {
57    /// Determines if the type is optional.
58    fn is_optional(&self) -> bool;
59
60    /// Makes the type optional if it isn't already optional.
61    fn optional(&self) -> Self;
62
63    /// Makes the type required if it isn't already required.
64    fn require(&self) -> Self;
65}
66
67/// A trait implemented on types that are coercible to other types.
68pub trait Coercible {
69    /// Determines if the type is coercible to the target type.
70    fn is_coercible_to(&self, target: &Self) -> bool;
71}
72
73/// Represents a primitive WDL type.
74#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
75pub enum PrimitiveType {
76    /// The type is a `Boolean`.
77    Boolean,
78    /// The type is an `Int`.
79    Integer,
80    /// The type is a `Float`.
81    Float,
82    /// The type is a `String`.
83    String,
84    /// The type is a `File`.
85    File,
86    /// The type is a `Directory`.
87    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            // String -> File
98            (Self::String, Self::File) |
99            // String -> Directory
100            (Self::String, Self::Directory) |
101            // Int -> Float
102            (Self::Integer, Self::Float) |
103            // File -> String
104            (Self::File, Self::String) |
105            // Directory -> String
106            (Self::Directory, Self::String)
107            => true,
108
109            // Not coercible
110            _ => 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/// Represents a hidden type in WDL.
131///
132/// Hidden types are special types used internally for type checking but
133/// are not directly expressible in WDL source code.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum HiddenType {
136    /// A hidden type for `hints` that is available in task hints sections.
137    Hints,
138    /// A hidden type for `input` that is available in task hints sections.
139    Input,
140    /// A hidden type for `output` that is available in task hints sections.
141    Output,
142    /// A hidden type for `task` that is available in requirements,
143    /// hints, and runtime sections before constraint evaluation.
144    TaskPreEvaluation,
145    /// A hidden type for `task` that is available in command and output
146    /// sections after constraint evaluation.
147    TaskPostEvaluation,
148    /// A hidden type for `task.previous` that contains the previous
149    /// attempt's computed requirements. Available in WDL 1.3+ in both
150    /// pre-evaluation (requirements, hints, runtime) and post-evaluation
151    /// (command, output) contexts.
152    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/// Represents a WDL type.
168#[derive(Debug, Clone, PartialEq, Eq)]
169pub enum Type {
170    /// The type is a primitive type.
171    ///
172    /// The second field is whether or not the primitive type is optional.
173    Primitive(PrimitiveType, bool),
174    /// The type is a compound type.
175    ///
176    /// The second field is whether or not the compound type is optional.
177    Compound(CompoundType, bool),
178    /// The type is `Object`.
179    Object,
180    /// The type is `Object?`.
181    OptionalObject,
182    /// A special hidden type for a value that may have any one of several
183    /// concrete types.
184    ///
185    /// This variant is also used to convey an "indeterminate" type; an
186    /// indeterminate type may result from a previous type error.
187    Union,
188    /// A special type that behaves like an optional `Union`.
189    None,
190    /// A special hidden type that is not directly expressible in WDL source.
191    ///
192    /// Hidden types are used for type checking special values like `task`,
193    /// `task.previous`, `hints`, `input`, and `output`.
194    Hidden(HiddenType),
195    /// The type is a call output.
196    Call(CallType),
197    /// A reference to a custom type name (struct or enum).
198    TypeNameRef(TypeNameRef),
199}
200
201// NOTE: `Type` was optimized to `24` bytes as part of the type representation
202// shrinking effort. Any attempts to raise this limit should be carefully
203// considered from a performance perspective.
204const _: () = {
205    assert!(std::mem::size_of::<Type>() <= 24);
206};
207
208impl Type {
209    /// Casts the type to a primitive type.
210    ///
211    /// Returns `None` if the type is not primitive.
212    pub fn as_primitive(&self) -> Option<PrimitiveType> {
213        match self {
214            Self::Primitive(ty, _) => Some(*ty),
215            _ => None,
216        }
217    }
218
219    /// Casts the type to a compound type.
220    ///
221    /// Returns `None` if the type is not a compound type.
222    pub fn as_compound(&self) -> Option<&CompoundType> {
223        match self {
224            Self::Compound(ty, _) => Some(ty),
225            _ => None,
226        }
227    }
228
229    /// Converts the type to an array type.
230    ///
231    /// Returns `None` if the type is not an array type.
232    pub fn as_array(&self) -> Option<&ArrayType> {
233        match self {
234            Self::Compound(ty, _) => ty.as_array(),
235            _ => None,
236        }
237    }
238
239    /// Converts the type to a pair type.
240    ///
241    /// Returns `None` if the type is not a pair type.
242    pub fn as_pair(&self) -> Option<&PairType> {
243        match self {
244            Self::Compound(ty, _) => ty.as_pair(),
245            _ => None,
246        }
247    }
248
249    /// Converts the type to a map type.
250    ///
251    /// Returns `None` if the type is not a map type.
252    pub fn as_map(&self) -> Option<&MapType> {
253        match self {
254            Self::Compound(ty, _) => ty.as_map(),
255            _ => None,
256        }
257    }
258
259    /// Converts the type to a struct type.
260    ///
261    /// Returns `None` if the type is not a struct type.
262    pub fn as_struct(&self) -> Option<&StructType> {
263        match self {
264            Self::Compound(ty, _) => ty.as_struct(),
265            _ => None,
266        }
267    }
268
269    /// Converts the type to an enum type.
270    ///
271    /// Returns `None` if the type is not an enum type.
272    pub fn as_enum(&self) -> Option<&EnumType> {
273        match self {
274            Self::Compound(ty, _) => ty.as_enum(),
275            _ => None,
276        }
277    }
278
279    /// Converts the type to a custom type.
280    ///
281    /// Returns `None` if the type is not a custom type.
282    pub fn as_custom(&self) -> Option<&CustomType> {
283        match self {
284            Self::Compound(ty, _) => ty.as_custom(),
285            _ => None,
286        }
287    }
288
289    /// Converts the type to a type name reference.
290    ///
291    /// Returns `None` if the type is not a type name reference.
292    pub fn as_type_name_ref(&self) -> Option<&TypeNameRef> {
293        match self {
294            Self::TypeNameRef(ty) => Some(ty),
295            _ => None,
296        }
297    }
298
299    /// Converts the type to a call type
300    ///
301    /// Returns `None` if the type if not a call type.
302    pub fn as_call(&self) -> Option<&CallType> {
303        match self {
304            Self::Call(ty) => Some(ty),
305            _ => None,
306        }
307    }
308
309    /// Determines if the type is `Union`.
310    pub fn is_union(&self) -> bool {
311        matches!(self, Type::Union)
312    }
313
314    /// Determines if the type is `None`.
315    pub fn is_none(&self) -> bool {
316        matches!(self, Type::None)
317    }
318
319    /// Promotes a type from a scatter statement into the parent scope.
320    ///
321    /// For most types, this wraps them in an array. For call types, this
322    /// promotes each output type into an array.
323    pub fn promote_scatter(&self) -> Self {
324        // For calls, the outputs of the call are promoted instead of the call
325        // itself
326        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    /// Calculates a common type between this type and the given type.
334    ///
335    /// Returns `None` if the types have no common type.
336    pub fn common_type(&self, other: &Type) -> Option<Type> {
337        // If the other type is union, then the common type would be this type
338        if other.is_union() {
339            return Some(self.clone());
340        }
341
342        // If this type is union, then the common type would be the other type
343        if self.is_union() {
344            return Some(other.clone());
345        }
346
347        // If the other type is `None`, then the common type would be an
348        // optional this type
349        if other.is_none() {
350            return Some(self.optional());
351        }
352
353        // If this type is `None`, then the common type would be an optional
354        // other type
355        if self.is_none() {
356            return Some(other.optional());
357        }
358
359        // Check for the other type being coercible to this type
360        if other.is_coercible_to(self) {
361            return Some(self.clone());
362        }
363
364        // Check for this type being coercible to the other type
365        if self.is_coercible_to(other) {
366            return Some(other.clone());
367        }
368
369        // Check for a compound type that might have a common type within it
370        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        // Check for a call type to have a common type with itself
377        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                // An optional type cannot coerce into a required type
530                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                // An optional type cannot coerce into a required type
538                if *src_opt && !*target_opt {
539                    return false;
540                }
541
542                src.is_coercible_to(target)
543            }
544
545            // Object -> Object, Object -> Object?, Object? -> Object?
546            (Self::Object, Self::Object)
547            | (Self::Object, Self::OptionalObject)
548            | (Self::OptionalObject, Self::OptionalObject) => true,
549
550            // Map[X, Y] -> Object, Map[X, Y] -> Object?, Map[X, Y]? -> Object? where: X -> String
551            //
552            // Struct -> Object, Struct -> Object?, Struct? -> Object?
553            (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            // Object -> Map[X, Y], Object -> Map[X, Y]?, Object? -> Map[X, Y]? where: String -> X
564            // and all object members are coercible to Y
565            //
566            // Object -> Struct, Object -> Struct?, Object? -> Struct? where: object keys match
567            // struct member names and object values are coercible to struct member types
568            (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                        // Note: checking object keys and values is a runtime
576                        // constraint
577                        true
578                    }
579                    _ => false,
580                }
581            }
582
583            // Union is always coercible to the target (and vice versa)
584            (Self::Union, _) | (_, Self::Union) => true,
585
586            // None is coercible to an optional type
587            (Self::None, ty) if ty.is_optional() => true,
588
589            // String -> Enum
590            // Enum -> String
591            (
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            // Not coercible
601            _ => 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/// Represents a custom type (struct or enum).
667#[derive(Debug, Clone, PartialEq, Eq)]
668pub enum CustomType {
669    /// The type is a struct (e.g. `Foo`).
670    Struct(StructType),
671    /// The type is an enum.
672    Enum(EnumType),
673}
674
675impl CustomType {
676    /// Gets the name of the custom type.
677    pub fn name(&self) -> &str {
678        match self {
679            Self::Struct(ty) => ty.name(),
680            Self::Enum(ty) => ty.name(),
681        }
682    }
683
684    /// Converts the custom type to a struct type.
685    ///
686    /// Returns `None` if the custom type is not a struct.
687    pub fn as_struct(&self) -> Option<&StructType> {
688        match self {
689            Self::Struct(ty) => Some(ty),
690            _ => None,
691        }
692    }
693
694    /// Converts the custom type to an enum type.
695    ///
696    /// Returns `None` if the custom type is not an enum.
697    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/// Represents a compound type definition.
727#[derive(Debug, Clone, PartialEq, Eq)]
728pub enum CompoundType {
729    /// The type is an `Array`.
730    Array(ArrayType),
731    /// The type is a `Pair`.
732    Pair(PairType),
733    /// The type is a `Map`.
734    Map(MapType),
735    /// The type is a custom type (a struct or enum).
736    Custom(CustomType),
737}
738
739impl CompoundType {
740    /// Converts the compound type to an array type.
741    ///
742    /// Returns `None` if the compound type is not an array type.
743    pub fn as_array(&self) -> Option<&ArrayType> {
744        match self {
745            Self::Array(ty) => Some(ty),
746            _ => None,
747        }
748    }
749
750    /// Converts the compound type to a pair type.
751    ///
752    /// Returns `None` if the compound type is not a pair type.
753    pub fn as_pair(&self) -> Option<&PairType> {
754        match self {
755            Self::Pair(ty) => Some(ty),
756            _ => None,
757        }
758    }
759
760    /// Converts the compound type to a map type.
761    ///
762    /// Returns `None` if the compound type is not a map type.
763    pub fn as_map(&self) -> Option<&MapType> {
764        match self {
765            Self::Map(ty) => Some(ty),
766            _ => None,
767        }
768    }
769
770    /// Converts the compound type to a struct type.
771    ///
772    /// Returns `None` if the compound type is not a struct type.
773    pub fn as_struct(&self) -> Option<&StructType> {
774        match self {
775            Self::Custom(ty) => ty.as_struct(),
776            _ => None,
777        }
778    }
779
780    /// Converts the compound type to an enum type.
781    ///
782    /// Returns `None` if the compound type is not an enum type.
783    pub fn as_enum(&self) -> Option<&EnumType> {
784        match self {
785            Self::Custom(ty) => ty.as_enum(),
786            _ => None,
787        }
788    }
789
790    /// Converts the compound type to a custom type.
791    ///
792    /// Returns `None` if the compound type is not a custom type.
793    pub fn as_custom(&self) -> Option<&CustomType> {
794        match self {
795            Self::Custom(ty) => Some(ty),
796            _ => None,
797        }
798    }
799
800    /// Calculates a common type between two compound types.
801    ///
802    /// This method does not attempt coercion; it only attempts to find common
803    /// inner types for the same outer type.
804    fn common_type(&self, other: &Self) -> Option<CompoundType> {
805        // Check to see if the types are both `Array`, `Pair`, or `Map`; if so,
806        // attempt to find a common type for their inner types
807        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            // Array[X] -> Array[Y], Array[X] -> Array[Y]?, Array[X]? -> Array[Y]?, Array[X]+ ->
843            // Array[Y] where: X -> Y
844            (Self::Array(src), Self::Array(target)) => src.is_coercible_to(target),
845
846            // Pair[W, X] -> Pair[Y, Z], Pair[W, X] -> Pair[Y, Z]?, Pair[W, X]? -> Pair[Y, Z]?
847            // where: W -> Y and X -> Z
848            (Self::Pair(src), Self::Pair(target)) => src.is_coercible_to(target),
849
850            // Map[W, X] -> Map[Y, Z], Map[W, X] -> Map[Y, Z]?, Map[W, X]? -> Map[Y, Z]? where: W ->
851            // Y and X -> Z
852            (Self::Map(src), Self::Map(target)) => src.is_coercible_to(target),
853
854            // Struct -> Struct, Struct -> Struct?, Struct? -> Struct? where: all member names match
855            // and all member types coerce
856            (Self::Custom(CustomType::Struct(src)), Self::Custom(CustomType::Struct(target))) => {
857                src.is_coercible_to(target)
858            }
859
860            // Enum -> Enum, Enum -> Enum?, Enum? -> Enum? where: same enum type
861            (Self::Custom(CustomType::Enum(src)), Self::Custom(CustomType::Enum(target))) => {
862                src.is_coercible_to(target)
863            }
864
865            // Map[X, Y] -> Struct, Map[X, Y] -> Struct?, Map[X, Y]? -> Struct? where: X -> String,
866            // keys match member names, and Y -> member type
867            (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                // Ensure the value type is coercible to every struct member
876                // type
877                if !target
878                    .members()
879                    .values()
880                    .all(|ty| src.value_type().is_coercible_to(ty))
881                {
882                    return false;
883                }
884
885                // Note: checking map keys is a runtime value constraint
886                true
887            }
888
889            // Struct -> Map[X, Y], Struct -> Map[X, Y]?, Struct? -> Map[X, Y]? where: String -> X
890            // and member types -> Y
891            (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                // Ensure all the struct members are coercible to the value type
897                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/// The inner representation of an array type.
944#[derive(Debug, Clone, PartialEq, Eq)]
945struct ArrayTypeInner {
946    /// The element type of the array.
947    element_type: Type,
948    /// Whether or not the array type is non-empty.
949    non_empty: bool,
950}
951
952/// Represents the type of an `Array`.
953///
954/// Array types are cheap to clone.
955#[derive(Debug, Clone, PartialEq, Eq)]
956pub struct ArrayType(Arc<ArrayTypeInner>);
957
958impl ArrayType {
959    /// Constructs a new array type.
960    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    /// Constructs a new non-empty array type.
968    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    /// Gets the array's element type.
976    pub fn element_type(&self) -> &Type {
977        &self.0.element_type
978    }
979
980    /// Determines if the array type is non-empty.
981    pub fn is_non_empty(&self) -> bool {
982        self.0.non_empty
983    }
984
985    /// Returns a new array type with the non-empty (`+`) qualifier removed.
986    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        // Note: non-empty constraints are enforced at runtime and are not
1013        // checked here.
1014        self.0.element_type.is_coercible_to(&target.0.element_type)
1015    }
1016}
1017
1018/// Represents the type of a `Pair`.
1019#[derive(Debug, Clone, PartialEq, Eq)]
1020pub struct PairType(Arc<(Type, Type)>);
1021
1022impl PairType {
1023    /// Constructs a new pair type.
1024    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    /// Gets the pairs's left type.
1029    pub fn left_type(&self) -> &Type {
1030        &self.0.0
1031    }
1032
1033    /// Gets the pairs's right type.
1034    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/// Represents the type of a `Map`.
1060#[derive(Debug, Clone, PartialEq, Eq)]
1061pub struct MapType(Arc<(Type, Type)>);
1062
1063impl MapType {
1064    /// Constructs a new map type.
1065    ///
1066    /// # Panics
1067    ///
1068    /// Panics if the given key type is not a required primitive type.
1069    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    /// Gets the maps's key type.
1079    pub fn key_type(&self) -> &Type {
1080        &self.0.0
1081    }
1082
1083    /// Gets the maps's value type.
1084    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/// The inner representation of a struct type.
1110#[derive(Debug, Clone, PartialEq, Eq)]
1111struct StructTypeInner {
1112    /// The name of the struct.
1113    ///
1114    /// Arc-wrapped because it is shared with the engine's `Struct` value
1115    /// type when constructing struct values, avoiding allocation of a
1116    /// second copy of the name string.
1117    name: Arc<String>,
1118    /// The members of the struct.
1119    members: IndexMap<String, Type>,
1120}
1121
1122/// Represents the type of a struct.
1123///
1124/// Struct types are cheap to clone.
1125#[derive(Debug, Clone, PartialEq, Eq)]
1126pub struct StructType(Arc<StructTypeInner>);
1127
1128impl StructType {
1129    /// Constructs a new struct type definition.
1130    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    /// Gets the name of the struct.
1145    pub fn name(&self) -> &Arc<String> {
1146        &self.0.name
1147    }
1148
1149    /// Gets the members of the struct.
1150    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/// Cache key for enum choice values.
1179#[derive(Debug, Clone, PartialEq, Eq, Hash)]
1180pub struct EnumChoiceCacheKey {
1181    /// The URI of the document containing the enum.
1182    uri: Arc<Url>,
1183    /// The index of the enum in the document.
1184    enum_index: usize,
1185    /// The index of the choice within the enum.
1186    choice_index: usize,
1187}
1188
1189impl EnumChoiceCacheKey {
1190    /// Constructs a new enum choice cache key.
1191    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/// The inner representation of an enum type.
1201#[derive(Debug, Clone, PartialEq, Eq)]
1202struct EnumTypeInner {
1203    /// The name of the enum.
1204    name: String,
1205    /// The common coerced type computed from all choice values.
1206    inner_value_type: Type,
1207    /// The choices.
1208    choices: Arc<[String]>,
1209}
1210
1211/// Represents the type of an enum.
1212///
1213/// Enum types are cheap to clone.
1214#[derive(Debug, Clone, PartialEq, Eq)]
1215pub struct EnumType(Arc<EnumTypeInner>);
1216
1217impl EnumType {
1218    /// Constructs a new enum type with a known coerced type.
1219    ///
1220    /// Validates that all choice types are coercible to the provided type.
1221    ///
1222    /// Returns an error if any choice cannot be coerced.
1223    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        // Validate that all choice types are coercible to the value type.
1235        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    /// Attempts to create a new enum type by computing the common inner type
1258    /// through coercion.
1259    ///
1260    /// Finds the common inner type among all choice types. If the enum has no
1261    /// choices, the coerced inner type is [`Type::Union`].
1262    ///
1263    /// Returns an error if no common type can be found among the choices.
1264    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                            &current_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    /// Gets the name of the enum.
1304    pub fn name(&self) -> &str {
1305        &self.0.name
1306    }
1307
1308    /// Gets the inner value type that all choices coerce to.
1309    pub fn inner_value_type(&self) -> &Type {
1310        &self.0.inner_value_type
1311    }
1312
1313    /// Gets the choices.
1314    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/// The kind of call for a call type.
1332#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
1333pub enum CallKind {
1334    /// The call is to a task.
1335    Task,
1336    /// The call is to a workflow.
1337    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/// The inner representation of a call type.
1350#[derive(Debug, Clone, Eq)]
1351struct CallTypeInner {
1352    /// The call kind.
1353    kind: CallKind,
1354    /// The namespace of the call.
1355    namespace: Option<String>,
1356    /// The name of the task or workflow that was called.
1357    name: String,
1358    /// The set of specified inputs in the call.
1359    ///
1360    /// Arc-wrapped because [`CallType::optional`] and
1361    /// [`CallType::promote_scatter`] clone the inner to produce modified
1362    /// copies.
1363    specified: Arc<HashSet<String>>,
1364    /// The input types to the call.
1365    ///
1366    /// Arc-wrapped for the same reason as `specified`.
1367    inputs: Arc<IndexMap<String, Input>>,
1368    /// The output types from the call.
1369    ///
1370    /// Arc-wrapped for the same reason as `specified`, and additionally
1371    /// because [`CallType::optional`] and [`CallType::promote_scatter`]
1372    /// use [`Arc::make_mut`] for copy-on-write modification of outputs.
1373    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/// Represents the type of a call.
1383///
1384/// Call types are cheap to clone.
1385#[derive(Debug, Clone, Eq)]
1386pub struct CallType(Arc<CallTypeInner>);
1387
1388impl CallType {
1389    /// Constructs a new call type given the task or workflow name being called.
1390    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    /// Constructs a new call type given namespace and the task or workflow name
1408    /// being called.
1409    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    /// Gets the kind of the call.
1428    pub fn kind(&self) -> CallKind {
1429        self.0.kind
1430    }
1431
1432    /// Gets the namespace of the call target.
1433    ///
1434    /// Returns `None` if the call is local to the current document.
1435    pub fn namespace(&self) -> Option<&str> {
1436        self.0.namespace.as_deref()
1437    }
1438
1439    /// Gets the name of the call target.
1440    pub fn name(&self) -> &str {
1441        &self.0.name
1442    }
1443
1444    /// Gets the set of inputs specified in the call.
1445    pub fn specified(&self) -> &HashSet<String> {
1446        &self.0.specified
1447    }
1448
1449    /// Gets the inputs of the called workflow or task.
1450    pub fn inputs(&self) -> &IndexMap<String, Input> {
1451        &self.0.inputs
1452    }
1453
1454    /// Gets the outputs of the called workflow or task.
1455    pub fn outputs(&self) -> &IndexMap<String, Output> {
1456        &self.0.outputs
1457    }
1458
1459    /// Makes all outputs of the call type optional.
1460    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    /// Promotes the call type into a scatter statement.
1470    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        // Calls are not coercible to other types
1483        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/// The inner type for [`TypeNameRef`].
1514#[derive(Debug, Clone, PartialEq, Eq)]
1515struct TypeNameRefInner {
1516    /// The name used to refer to the type.
1517    name: String,
1518    /// The custom type that was referred to.
1519    ty: CustomType,
1520}
1521
1522/// Represents a reference to a custom type (struct or enum).
1523#[derive(Debug, Clone, PartialEq, Eq)]
1524pub struct TypeNameRef(Arc<TypeNameRefInner>);
1525
1526impl TypeNameRef {
1527    /// Constructs a new [`TypeNameRef`].
1528    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    /// Gets the name used to reference the type.
1539    pub fn name(&self) -> &str {
1540        &self.0.name
1541    }
1542
1543    /// Gets the referenced custom type.
1544    pub fn ty(&self) -> &CustomType {
1545        &self.0.ty
1546    }
1547
1548    /// Converts the referenced custom type to a struct type.
1549    ///
1550    /// Returns `None` if the referenced custom type is not a struct.
1551    pub fn as_struct(&self) -> Option<&StructType> {
1552        match &self.0.ty {
1553            CustomType::Struct(ty) => Some(ty),
1554            _ => None,
1555        }
1556    }
1557
1558    /// Converts the referenced custom type to an enum type.
1559    ///
1560    /// Returns `None` if the referenced custom type is not an enum.
1561    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        // All types should be coercible to self, and required should coerce to
1719        // optional (but not vice versa)
1720        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        // Check the valid coercions
1735        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        // Object? -> Map[String, X]
1751        let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1752        assert!(!Type::OptionalObject.is_coercible_to(&ty));
1753
1754        // Object? -> Map[File, X]
1755        let ty = MapType::new(PrimitiveType::File, PrimitiveType::String).into();
1756        assert!(!Type::OptionalObject.is_coercible_to(&ty));
1757
1758        // Object -> Map[Int, X] (key not coercible from string)
1759        let ty = MapType::new(PrimitiveType::Integer, PrimitiveType::String).into();
1760        assert!(!Type::Object.is_coercible_to(&ty));
1761
1762        // Object -> Map[String, X]?
1763        let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1764        assert!(Type::Object.is_coercible_to(&ty));
1765
1766        // Object -> Map[File, X]?
1767        let ty = Type::from(MapType::new(PrimitiveType::File, PrimitiveType::String)).optional();
1768        assert!(Type::Object.is_coercible_to(&ty));
1769
1770        // Object? -> Map[String, X]?
1771        let ty = Type::from(MapType::new(PrimitiveType::String, PrimitiveType::String)).optional();
1772        assert!(Type::OptionalObject.is_coercible_to(&ty));
1773
1774        // Object? -> Map[String, X]
1775        let ty = MapType::new(PrimitiveType::String, PrimitiveType::String).into();
1776        assert!(!Type::OptionalObject.is_coercible_to(&ty));
1777
1778        // Object -> Struct
1779        let ty = StructType::new("Foo", [("foo", PrimitiveType::String)]).into();
1780        assert!(Type::Object.is_coercible_to(&ty));
1781
1782        // Object -> Struct?
1783        let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1784        assert!(Type::Object.is_coercible_to(&ty));
1785
1786        // Object? -> Struct?
1787        let ty = Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
1788        assert!(Type::OptionalObject.is_coercible_to(&ty));
1789
1790        // Object? -> Struct
1791        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        // Array[X] -> Array[Y]
1798        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        // Array[X] -> Array[Y?]
1812        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        // Array[Array[X]] -> Array[Array[Y]]
1818        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        // Array[X]+ -> Array[Y]
1824        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        // Array[X]+ -> Array[X?]
1830        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        // Array[X] -> Array[X]
1836        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        // Array[X]? -> Array[X]?
1842        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        // Array[X] -> Array[X]?
1848        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        // Pair[W, X] -> Pair[Y, Z]
1857        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        // Pair[W, X] -> Pair[Y?, Z?]
1872        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        // Pair[Pair[W, X], Pair[W, X]] -> Pair[Pair[Y, Z], Pair[Y, Z]]
1882        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        // Pair[W, X] -> Pair[W, X]
1888        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        // Pair[W, X]? -> Pair[W, X]?
1894        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        // Pair[W, X] -> Pair[W, X]?
1902        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        // Map[W, X] -> Map[Y, Z]
1912        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        // Map[W, X] -> Map[Y, Z?]
1926        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        // Map[P, Map[W, X]] -> Map[Q, Map[Y, Z]]
1936        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        // Map[W, X] -> Map[W, X]
1942        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        // Map[W, X]? -> Map[W, X]?
1948        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        // Map[W, X] -> Map[W, X]?
1956        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        // Map[String, Int] -> Struct
1963        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        // Map[File, Int] -> Struct
1976        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        // Map[String, Int] -> Struct (mismatched fields)
1989        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        // Map[Int, Int] -> Struct
2002        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        // Map[String, Int] -> Object
2015        let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2016        assert!(type1.is_coercible_to(&Type::Object));
2017
2018        // Map[String, Int] -> Object?
2019        let type1: Type = MapType::new(PrimitiveType::String, PrimitiveType::Integer).into();
2020        assert!(type1.is_coercible_to(&Type::OptionalObject));
2021
2022        // Map[String, Int]? -> Object?
2023        let type1: Type =
2024            Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2025        assert!(type1.is_coercible_to(&Type::OptionalObject));
2026
2027        // Map[File, Int] -> Object
2028        let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2029        assert!(type1.is_coercible_to(&Type::Object));
2030
2031        // Map[File, Int] -> Object?
2032        let type1: Type = MapType::new(PrimitiveType::File, PrimitiveType::Integer).into();
2033        assert!(type1.is_coercible_to(&Type::OptionalObject));
2034
2035        // Map[File, Int]? -> Object?
2036        let type1: Type =
2037            Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2038        assert!(type1.is_coercible_to(&Type::OptionalObject));
2039
2040        // Map[String, Int]? -> Object
2041        let type1: Type =
2042            Type::from(MapType::new(PrimitiveType::String, PrimitiveType::Integer)).optional();
2043        assert!(!type1.is_coercible_to(&Type::Object));
2044
2045        // Map[File, Int]? -> Object
2046        let type1: Type =
2047            Type::from(MapType::new(PrimitiveType::File, PrimitiveType::Integer)).optional();
2048        assert!(!type1.is_coercible_to(&Type::Object));
2049
2050        // Map[Integer, Int] -> Object
2051        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        // S -> S (identical)
2058        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        // S -> S?
2080        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        // S? -> S?
2102        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        // S -> S (coercible fields)
2124        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        // S -> S (mismatched fields)
2146        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        // Struct -> Map[String, String]
2160        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        // Struct -> Map[File, String]
2173        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        // Struct -> Map[String, X] (mismatched types)
2186        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        // Struct -> Map[Int, String] (key not coercible from String)
2199        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        // Struct -> Object
2212        assert!(type1.is_coercible_to(&Type::Object));
2213
2214        // Struct -> Object?
2215        assert!(type1.is_coercible_to(&Type::OptionalObject));
2216
2217        // Struct? -> Object?
2218        let type1: Type =
2219            Type::from(StructType::new("Foo", [("foo", PrimitiveType::String)])).optional();
2220        assert!(type1.is_coercible_to(&Type::OptionalObject));
2221
2222        // Struct? -> Object
2223        assert!(!type1.is_coercible_to(&Type::Object));
2224    }
2225
2226    #[test_log::test]
2227    fn union_type_coercion() {
2228        // Union -> anything (ok)
2229        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            // Union -> Array[X], Union -> Array[X]?
2244            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            // Union -> Pair[X, Y], Union -> Pair[X, Y]?
2251            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            // Union -> Map[X, Y], Union -> Map[X, Y]?
2257            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            // Union -> Struct, Union -> Struct?
2263            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        // None -> optional type (ok)
2273        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            // None -> Array[X], None -> Array[X]?
2288            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            // None -> Pair[X, Y], None -> Pair[X, Y]?
2298            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            // None -> Map[X, Y], None -> Map[X, Y]?
2308            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            // None -> Struct, None -> Struct?
2318            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        // Array[String] == Array[String]
2353        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        // Array[Array[String]] == Array[Array[String]
2361        let a: Type = ArrayType::new(a).into();
2362        let b: Type = ArrayType::new(b).into();
2363        assert!(a.eq(&b));
2364
2365        // Array[Array[Array[String]]]+ == Array[Array[Array[String]]+
2366        let a: Type = ArrayType::non_empty(a).into();
2367        let b: Type = ArrayType::non_empty(b).into();
2368        assert!(a.eq(&b));
2369
2370        // Array[String] != Array[String]+
2371        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        // Array[String] != Array[Int]
2376        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        // Pair[String, Int] == Pair[String, Int]
2389        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        // Pair[Pair[String, Int], Pair[String, Int]] == Pair[Pair[String, Int],
2397        // Pair[String, Int]]
2398        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        // Pair[String, Int] != Pair[String, Int]?
2403        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        // Map[String, Int] == Map[String, Int]
2417        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        // Map[File, Map[String, Int]] == Map[File, Map[String, Int]]
2425        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        // Map[String, Int] != Map[Int, String]
2430        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        // Create enum with explicit `String` type, all choices coerce to
2473        // `String`.
2474        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        // Try to create enum with `Int` type but `String` choices.
2498        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        // All `Int` choices should infer `Int` type.
2517        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        // Mix of `Int` and `Float` should coerce to `Float`.
2539        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        // `String` and `Int` have no common type.
2557        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        // Empty enum should have `Union` type.
2574        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}