1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
9pub enum TypeSig {
10 Any,
12 Null,
14 Bool,
16 Int,
18 Float,
20 String,
22 Bytes,
24 List(Box<TypeSig>),
26 Map(Box<TypeSig>),
28 Named(std::string::String),
30}
31
32impl TypeSig {
33 pub fn is_compatible_with(&self, other: &TypeSig) -> bool {
43 match (self, other) {
44 (TypeSig::Any, _) | (_, TypeSig::Any) => true,
45 (TypeSig::List(a), TypeSig::List(b)) => a.is_compatible_with(b),
46 (TypeSig::Map(a), TypeSig::Map(b)) => a.is_compatible_with(b),
47 (TypeSig::Named(a), TypeSig::Named(b)) => a == b,
48 _ => self == other,
49 }
50 }
51}
52
53impl std::fmt::Display for TypeSig {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 TypeSig::Any => write!(f, "Any"),
57 TypeSig::Null => write!(f, "Null"),
58 TypeSig::Bool => write!(f, "Bool"),
59 TypeSig::Int => write!(f, "Int"),
60 TypeSig::Float => write!(f, "Float"),
61 TypeSig::String => write!(f, "String"),
62 TypeSig::Bytes => write!(f, "Bytes"),
63 TypeSig::List(inner) => write!(f, "List<{}>", inner),
64 TypeSig::Map(inner) => write!(f, "Map<{}>", inner),
65 TypeSig::Named(name) => write!(f, "{}", name),
66 }
67 }
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
76pub enum LayerTag {
77 Core,
79 Domain,
81 Integration,
83 Custom(std::string::String),
85}
86
87impl std::fmt::Display for LayerTag {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 match self {
90 LayerTag::Core => write!(f, "Core"),
91 LayerTag::Domain => write!(f, "Domain"),
92 LayerTag::Integration => write!(f, "Integration"),
93 LayerTag::Custom(s) => write!(f, "Custom({})", s),
94 }
95 }
96}
97
98#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn any_is_compatible_with_everything() {
108 assert!(TypeSig::Any.is_compatible_with(&TypeSig::Int));
109 assert!(TypeSig::Int.is_compatible_with(&TypeSig::Any));
110 assert!(TypeSig::Any.is_compatible_with(&TypeSig::Any));
111 }
112
113 #[test]
114 fn exact_match_compatible() {
115 assert!(TypeSig::Int.is_compatible_with(&TypeSig::Int));
116 assert!(TypeSig::Bool.is_compatible_with(&TypeSig::Bool));
117 }
118
119 #[test]
120 fn mismatched_primitives_incompatible() {
121 assert!(!TypeSig::Int.is_compatible_with(&TypeSig::Float));
122 assert!(!TypeSig::Bool.is_compatible_with(&TypeSig::String));
123 }
124
125 #[test]
126 fn named_compatible_same() {
127 assert!(TypeSig::Named("Foo".into()).is_compatible_with(&TypeSig::Named("Foo".into())));
128 }
129
130 #[test]
131 fn named_incompatible_different() {
132 assert!(!TypeSig::Named("Foo".into()).is_compatible_with(&TypeSig::Named("Bar".into())));
133 }
134
135 #[test]
136 fn list_compatibility_recursive() {
137 let list_int = TypeSig::List(Box::new(TypeSig::Int));
138 let list_any = TypeSig::List(Box::new(TypeSig::Any));
139 assert!(list_int.is_compatible_with(&list_any));
140 assert!(!list_int.is_compatible_with(&TypeSig::List(Box::new(TypeSig::Bool))));
141 }
142}