1use serde::{Deserialize, Serialize};
13use std::fmt;
14
15use crate::error::AdapterError;
16
17#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
25pub struct ParametricType {
26 pub constructor: String,
28 pub parameters: Vec<TypeParameter>,
30}
31
32#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
34pub enum TypeParameter {
35 Concrete(String),
37 Parametric(Box<ParametricType>),
39}
40
41impl ParametricType {
42 pub fn new(constructor: impl Into<String>, parameters: Vec<TypeParameter>) -> Self {
44 ParametricType {
45 constructor: constructor.into(),
46 parameters,
47 }
48 }
49
50 pub fn list(inner: TypeParameter) -> Self {
52 ParametricType::new("List", vec![inner])
53 }
54
55 pub fn option(inner: TypeParameter) -> Self {
57 ParametricType::new("Option", vec![inner])
58 }
59
60 pub fn pair(first: TypeParameter, second: TypeParameter) -> Self {
62 ParametricType::new("Pair", vec![first, second])
63 }
64
65 pub fn map(key: TypeParameter, value: TypeParameter) -> Self {
67 ParametricType::new("Map", vec![key, value])
68 }
69
70 pub fn validate(&self) -> Result<(), AdapterError> {
77 if self.constructor.is_empty() {
78 return Err(AdapterError::InvalidParametricType(
79 "Constructor name cannot be empty".to_string(),
80 ));
81 }
82
83 let expected_arity = match self.constructor.as_str() {
85 "List" | "Option" | "Set" => 1,
86 "Pair" | "Map" | "Either" => 2,
87 _ => return Ok(()), };
89
90 if self.parameters.len() != expected_arity {
91 return Err(AdapterError::InvalidParametricType(format!(
92 "Constructor '{}' expects {} parameters, found {}",
93 self.constructor,
94 expected_arity,
95 self.parameters.len()
96 )));
97 }
98
99 for param in &self.parameters {
101 if let TypeParameter::Parametric(nested) = param {
102 nested.validate()?;
103 }
104 }
105
106 Ok(())
107 }
108
109 pub fn arity(&self) -> usize {
111 self.parameters.len()
112 }
113
114 pub fn is_monomorphic(&self) -> bool {
116 self.parameters.is_empty()
117 }
118
119 pub fn contains_parameter(&self, name: &str) -> bool {
121 self.parameters.iter().any(|param| match param {
122 TypeParameter::Concrete(n) => n == name,
123 TypeParameter::Parametric(nested) => nested.contains_parameter(name),
124 })
125 }
126
127 pub fn substitute(&self, from: &str, to: &TypeParameter) -> ParametricType {
132 let new_params = self
133 .parameters
134 .iter()
135 .map(|param| match param {
136 TypeParameter::Concrete(name) if name == from => to.clone(),
137 TypeParameter::Concrete(_) => param.clone(),
138 TypeParameter::Parametric(nested) => {
139 TypeParameter::Parametric(Box::new(nested.substitute(from, to)))
140 }
141 })
142 .collect();
143
144 ParametricType {
145 constructor: self.constructor.clone(),
146 parameters: new_params,
147 }
148 }
149}
150
151impl TypeParameter {
152 pub fn concrete(name: impl Into<String>) -> Self {
154 TypeParameter::Concrete(name.into())
155 }
156
157 pub fn parametric(ptype: ParametricType) -> Self {
159 TypeParameter::Parametric(Box::new(ptype))
160 }
161
162 pub fn as_concrete(&self) -> Option<&str> {
164 match self {
165 TypeParameter::Concrete(name) => Some(name),
166 TypeParameter::Parametric(_) => None,
167 }
168 }
169
170 pub fn as_parametric(&self) -> Option<&ParametricType> {
172 match self {
173 TypeParameter::Concrete(_) => None,
174 TypeParameter::Parametric(ptype) => Some(ptype),
175 }
176 }
177}
178
179impl fmt::Display for ParametricType {
180 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
181 write!(f, "{}", self.constructor)?;
182 if !self.parameters.is_empty() {
183 write!(f, "<")?;
184 for (i, param) in self.parameters.iter().enumerate() {
185 if i > 0 {
186 write!(f, ", ")?;
187 }
188 write!(f, "{}", param)?;
189 }
190 write!(f, ">")?;
191 }
192 Ok(())
193 }
194}
195
196impl fmt::Display for TypeParameter {
197 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
198 match self {
199 TypeParameter::Concrete(name) => write!(f, "{}", name),
200 TypeParameter::Parametric(ptype) => write!(f, "{}", ptype),
201 }
202 }
203}
204
205#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
212pub struct TypeBound {
213 pub param_name: String,
215 pub constraint: BoundConstraint,
217}
218
219#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
221pub enum BoundConstraint {
222 Subtype(String),
224 Trait(String),
226 Comparable,
228 Numeric,
230}
231
232impl TypeBound {
233 pub fn new(param_name: impl Into<String>, constraint: BoundConstraint) -> Self {
235 TypeBound {
236 param_name: param_name.into(),
237 constraint,
238 }
239 }
240
241 pub fn subtype(param_name: impl Into<String>, supertype: impl Into<String>) -> Self {
243 TypeBound::new(param_name, BoundConstraint::Subtype(supertype.into()))
244 }
245
246 pub fn trait_bound(param_name: impl Into<String>, trait_name: impl Into<String>) -> Self {
248 TypeBound::new(param_name, BoundConstraint::Trait(trait_name.into()))
249 }
250
251 pub fn comparable(param_name: impl Into<String>) -> Self {
253 TypeBound::new(param_name, BoundConstraint::Comparable)
254 }
255
256 pub fn numeric(param_name: impl Into<String>) -> Self {
258 TypeBound::new(param_name, BoundConstraint::Numeric)
259 }
260}
261
262impl fmt::Display for TypeBound {
263 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
264 write!(f, "{}: ", self.param_name)?;
265 match &self.constraint {
266 BoundConstraint::Subtype(s) => write!(f, "Subtype({})", s),
267 BoundConstraint::Trait(t) => write!(f, "{}", t),
268 BoundConstraint::Comparable => write!(f, "Comparable"),
269 BoundConstraint::Numeric => write!(f, "Numeric"),
270 }
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_parametric_type_list() {
280 let list_person = ParametricType::list(TypeParameter::concrete("Person"));
281 assert_eq!(list_person.constructor, "List");
282 assert_eq!(list_person.arity(), 1);
283 assert_eq!(list_person.to_string(), "List<Person>");
284 }
285
286 #[test]
287 fn test_parametric_type_option() {
288 let opt_city = ParametricType::option(TypeParameter::concrete("City"));
289 assert_eq!(opt_city.constructor, "Option");
290 assert_eq!(opt_city.arity(), 1);
291 assert_eq!(opt_city.to_string(), "Option<City>");
292 }
293
294 #[test]
295 fn test_parametric_type_pair() {
296 let pair = ParametricType::pair(
297 TypeParameter::concrete("Person"),
298 TypeParameter::concrete("City"),
299 );
300 assert_eq!(pair.constructor, "Pair");
301 assert_eq!(pair.arity(), 2);
302 assert_eq!(pair.to_string(), "Pair<Person, City>");
303 }
304
305 #[test]
306 fn test_parametric_type_map() {
307 let map = ParametricType::map(
308 TypeParameter::concrete("String"),
309 TypeParameter::concrete("Int"),
310 );
311 assert_eq!(map.constructor, "Map");
312 assert_eq!(map.arity(), 2);
313 assert_eq!(map.to_string(), "Map<String, Int>");
314 }
315
316 #[test]
317 fn test_nested_parametric_type() {
318 let list_option_person = ParametricType::list(TypeParameter::parametric(
319 ParametricType::option(TypeParameter::concrete("Person")),
320 ));
321 assert_eq!(list_option_person.to_string(), "List<Option<Person>>");
322 assert!(list_option_person.contains_parameter("Person"));
323 }
324
325 #[test]
326 fn test_parametric_type_validation() {
327 let valid = ParametricType::list(TypeParameter::concrete("Person"));
328 assert!(valid.validate().is_ok());
329
330 let invalid = ParametricType::new(
331 "List",
332 vec![TypeParameter::concrete("A"), TypeParameter::concrete("B")],
333 );
334 assert!(invalid.validate().is_err());
335 }
336
337 #[test]
338 fn test_parametric_type_substitution() {
339 let list_t = ParametricType::list(TypeParameter::concrete("T"));
340 let list_person = list_t.substitute("T", &TypeParameter::concrete("Person"));
341 assert_eq!(list_person.to_string(), "List<Person>");
342 }
343
344 #[test]
345 fn test_type_bounds() {
346 let bound = TypeBound::subtype("T", "Person");
347 assert_eq!(bound.param_name, "T");
348 assert_eq!(bound.to_string(), "T: Subtype(Person)");
349
350 let comparable = TypeBound::comparable("T");
351 assert_eq!(comparable.to_string(), "T: Comparable");
352
353 let numeric = TypeBound::numeric("N");
354 assert_eq!(numeric.to_string(), "N: Numeric");
355 }
356}