1use alef_core::ir::{PrimitiveType, TypeRef};
2use std::borrow::Cow;
3
4pub trait TypeMapper {
7 fn primitive(&self, prim: &PrimitiveType) -> Cow<'static, str> {
9 Cow::Borrowed(match prim {
10 PrimitiveType::Bool => "bool",
11 PrimitiveType::U8 => "u8",
12 PrimitiveType::U16 => "u16",
13 PrimitiveType::U32 => "u32",
14 PrimitiveType::U64 => "u64",
15 PrimitiveType::I8 => "i8",
16 PrimitiveType::I16 => "i16",
17 PrimitiveType::I32 => "i32",
18 PrimitiveType::I64 => "i64",
19 PrimitiveType::F32 => "f32",
20 PrimitiveType::F64 => "f64",
21 PrimitiveType::Usize => "usize",
22 PrimitiveType::Isize => "isize",
23 })
24 }
25
26 fn string(&self) -> Cow<'static, str> {
28 Cow::Borrowed("String")
29 }
30
31 fn bytes(&self) -> Cow<'static, str> {
33 Cow::Borrowed("Vec<u8>")
34 }
35
36 fn path(&self) -> Cow<'static, str> {
38 Cow::Borrowed("String")
39 }
40
41 fn json(&self) -> Cow<'static, str> {
43 Cow::Borrowed("serde_json::Value")
44 }
45
46 fn unit(&self) -> Cow<'static, str> {
48 Cow::Borrowed("()")
49 }
50
51 fn duration(&self) -> Cow<'static, str> {
53 Cow::Borrowed("u64")
54 }
55
56 fn optional(&self, inner: &str) -> String {
58 format!("Option<{inner}>")
59 }
60
61 fn vec(&self, inner: &str) -> String {
63 format!("Vec<{inner}>")
64 }
65
66 fn map(&self, key: &str, value: &str) -> String {
68 format!("HashMap<{key}, {value}>")
69 }
70
71 fn named<'a>(&self, name: &'a str) -> Cow<'a, str> {
73 Cow::Borrowed(name)
74 }
75
76 fn map_type(&self, ty: &TypeRef) -> String {
78 match ty {
79 TypeRef::Primitive(p) => self.primitive(p).into_owned(),
80 TypeRef::String | TypeRef::Char => self.string().into_owned(),
81 TypeRef::Bytes => self.bytes().into_owned(),
82 TypeRef::Path => self.path().into_owned(),
83 TypeRef::Json => self.json().into_owned(),
84 TypeRef::Unit => self.unit().into_owned(),
85 TypeRef::Optional(inner) => self.optional(&self.map_type(inner)),
86 TypeRef::Vec(inner) => self.vec(&self.map_type(inner)),
87 TypeRef::Map(k, v) => self.map(&self.map_type(k), &self.map_type(v)),
88 TypeRef::Named(name) => self.named(name).into_owned(),
89 TypeRef::Duration => self.duration().into_owned(),
90 }
91 }
92
93 fn error_wrapper(&self) -> &str;
95
96 fn wrap_return(&self, base: &str, has_error: bool) -> String {
98 if has_error {
99 format!("{}<{base}>", self.error_wrapper())
100 } else {
101 base.to_string()
102 }
103 }
104}
105
106pub struct IdentityMapper;
109
110impl TypeMapper for IdentityMapper {
111 fn error_wrapper(&self) -> &str {
112 "Result"
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 struct RustMapper;
122
123 impl TypeMapper for RustMapper {
124 fn error_wrapper(&self) -> &str {
125 "Result"
126 }
127 }
128
129 #[test]
134 fn test_map_type_primitive_bool() {
135 assert_eq!(RustMapper.map_type(&TypeRef::Primitive(PrimitiveType::Bool)), "bool");
136 }
137
138 #[test]
139 fn test_map_type_primitive_integers() {
140 let mapper = RustMapper;
141 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U8)), "u8");
142 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U16)), "u16");
143 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U32)), "u32");
144 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U64)), "u64");
145 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I8)), "i8");
146 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I16)), "i16");
147 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I32)), "i32");
148 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I64)), "i64");
149 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::Usize)), "usize");
150 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::Isize)), "isize");
151 }
152
153 #[test]
154 fn test_map_type_primitive_floats() {
155 let mapper = RustMapper;
156 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::F32)), "f32");
157 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::F64)), "f64");
158 }
159
160 #[test]
161 fn test_map_type_string_and_char() {
162 let mapper = RustMapper;
163 assert_eq!(mapper.map_type(&TypeRef::String), "String");
164 assert_eq!(mapper.map_type(&TypeRef::Char), "String");
165 }
166
167 #[test]
168 fn test_map_type_bytes() {
169 assert_eq!(RustMapper.map_type(&TypeRef::Bytes), "Vec<u8>");
170 }
171
172 #[test]
173 fn test_map_type_path() {
174 assert_eq!(RustMapper.map_type(&TypeRef::Path), "String");
175 }
176
177 #[test]
178 fn test_map_type_json() {
179 assert_eq!(RustMapper.map_type(&TypeRef::Json), "serde_json::Value");
180 }
181
182 #[test]
183 fn test_map_type_unit() {
184 assert_eq!(RustMapper.map_type(&TypeRef::Unit), "()");
185 }
186
187 #[test]
188 fn test_map_type_duration() {
189 assert_eq!(RustMapper.map_type(&TypeRef::Duration), "u64");
190 }
191
192 #[test]
193 fn test_map_type_named_identity() {
194 assert_eq!(RustMapper.map_type(&TypeRef::Named("MyConfig".to_string())), "MyConfig");
195 }
196
197 #[test]
198 fn test_map_type_optional_wraps_inner() {
199 assert_eq!(
200 RustMapper.map_type(&TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U32)))),
201 "Option<u32>"
202 );
203 }
204
205 #[test]
206 fn test_map_type_optional_nested() {
207 let ty = TypeRef::Optional(Box::new(TypeRef::Optional(Box::new(TypeRef::String))));
209 assert_eq!(RustMapper.map_type(&ty), "Option<Option<String>>");
210 }
211
212 #[test]
213 fn test_map_type_vec_wraps_inner() {
214 assert_eq!(
215 RustMapper.map_type(&TypeRef::Vec(Box::new(TypeRef::String))),
216 "Vec<String>"
217 );
218 }
219
220 #[test]
221 fn test_map_type_vec_of_named() {
222 assert_eq!(
223 RustMapper.map_type(&TypeRef::Vec(Box::new(TypeRef::Named("Item".to_string())))),
224 "Vec<Item>"
225 );
226 }
227
228 #[test]
229 fn test_map_type_map_string_to_u32() {
230 assert_eq!(
231 RustMapper.map_type(&TypeRef::Map(
232 Box::new(TypeRef::String),
233 Box::new(TypeRef::Primitive(PrimitiveType::U32))
234 )),
235 "HashMap<String, u32>"
236 );
237 }
238
239 #[test]
240 fn test_map_type_nested_vec_in_optional() {
241 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
242 assert_eq!(RustMapper.map_type(&ty), "Option<Vec<String>>");
243 }
244
245 #[test]
250 fn test_wrap_return_no_error_passes_through() {
251 assert_eq!(RustMapper.wrap_return("String", false), "String");
252 }
253
254 #[test]
255 fn test_wrap_return_with_error_wraps_in_error_wrapper() {
256 assert_eq!(RustMapper.wrap_return("String", true), "Result<String>");
257 }
258
259 #[test]
260 fn test_wrap_return_unit_with_error() {
261 assert_eq!(RustMapper.wrap_return("()", true), "Result<()>");
262 }
263
264 struct CustomMapper;
269
270 impl TypeMapper for CustomMapper {
271 fn json(&self) -> Cow<'static, str> {
272 Cow::Borrowed("JsValue")
273 }
274
275 fn named<'a>(&self, name: &'a str) -> Cow<'a, str> {
276 Cow::Owned(format!("Js{name}"))
277 }
278
279 fn vec(&self, inner: &str) -> String {
280 if inner.starts_with("Vec<") {
281 "JsValue".to_string()
282 } else {
283 format!("Vec<{inner}>")
284 }
285 }
286
287 fn error_wrapper(&self) -> &str {
288 "JsResult"
289 }
290 }
291
292 #[test]
293 fn test_custom_mapper_json_override() {
294 assert_eq!(CustomMapper.map_type(&TypeRef::Json), "JsValue");
295 }
296
297 #[test]
298 fn test_custom_mapper_named_override() {
299 assert_eq!(CustomMapper.map_type(&TypeRef::Named("Config".to_string())), "JsConfig");
300 }
301
302 #[test]
303 fn test_custom_mapper_nested_vec_override() {
304 let ty = TypeRef::Vec(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
306 assert_eq!(CustomMapper.map_type(&ty), "JsValue");
307 }
308
309 #[test]
310 fn test_custom_mapper_wrap_return_with_error() {
311 assert_eq!(CustomMapper.wrap_return("String", true), "JsResult<String>");
312 }
313}