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
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 struct RustMapper;
112
113 impl TypeMapper for RustMapper {
114 fn error_wrapper(&self) -> &str {
115 "Result"
116 }
117 }
118
119 #[test]
124 fn test_map_type_primitive_bool() {
125 assert_eq!(RustMapper.map_type(&TypeRef::Primitive(PrimitiveType::Bool)), "bool");
126 }
127
128 #[test]
129 fn test_map_type_primitive_integers() {
130 let mapper = RustMapper;
131 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U8)), "u8");
132 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U16)), "u16");
133 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U32)), "u32");
134 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::U64)), "u64");
135 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I8)), "i8");
136 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I16)), "i16");
137 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I32)), "i32");
138 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::I64)), "i64");
139 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::Usize)), "usize");
140 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::Isize)), "isize");
141 }
142
143 #[test]
144 fn test_map_type_primitive_floats() {
145 let mapper = RustMapper;
146 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::F32)), "f32");
147 assert_eq!(mapper.map_type(&TypeRef::Primitive(PrimitiveType::F64)), "f64");
148 }
149
150 #[test]
151 fn test_map_type_string_and_char() {
152 let mapper = RustMapper;
153 assert_eq!(mapper.map_type(&TypeRef::String), "String");
154 assert_eq!(mapper.map_type(&TypeRef::Char), "String");
155 }
156
157 #[test]
158 fn test_map_type_bytes() {
159 assert_eq!(RustMapper.map_type(&TypeRef::Bytes), "Vec<u8>");
160 }
161
162 #[test]
163 fn test_map_type_path() {
164 assert_eq!(RustMapper.map_type(&TypeRef::Path), "String");
165 }
166
167 #[test]
168 fn test_map_type_json() {
169 assert_eq!(RustMapper.map_type(&TypeRef::Json), "serde_json::Value");
170 }
171
172 #[test]
173 fn test_map_type_unit() {
174 assert_eq!(RustMapper.map_type(&TypeRef::Unit), "()");
175 }
176
177 #[test]
178 fn test_map_type_duration() {
179 assert_eq!(RustMapper.map_type(&TypeRef::Duration), "u64");
180 }
181
182 #[test]
183 fn test_map_type_named_identity() {
184 assert_eq!(RustMapper.map_type(&TypeRef::Named("MyConfig".to_string())), "MyConfig");
185 }
186
187 #[test]
188 fn test_map_type_optional_wraps_inner() {
189 assert_eq!(
190 RustMapper.map_type(&TypeRef::Optional(Box::new(TypeRef::Primitive(PrimitiveType::U32)))),
191 "Option<u32>"
192 );
193 }
194
195 #[test]
196 fn test_map_type_optional_nested() {
197 let ty = TypeRef::Optional(Box::new(TypeRef::Optional(Box::new(TypeRef::String))));
199 assert_eq!(RustMapper.map_type(&ty), "Option<Option<String>>");
200 }
201
202 #[test]
203 fn test_map_type_vec_wraps_inner() {
204 assert_eq!(
205 RustMapper.map_type(&TypeRef::Vec(Box::new(TypeRef::String))),
206 "Vec<String>"
207 );
208 }
209
210 #[test]
211 fn test_map_type_vec_of_named() {
212 assert_eq!(
213 RustMapper.map_type(&TypeRef::Vec(Box::new(TypeRef::Named("Item".to_string())))),
214 "Vec<Item>"
215 );
216 }
217
218 #[test]
219 fn test_map_type_map_string_to_u32() {
220 assert_eq!(
221 RustMapper.map_type(&TypeRef::Map(
222 Box::new(TypeRef::String),
223 Box::new(TypeRef::Primitive(PrimitiveType::U32))
224 )),
225 "HashMap<String, u32>"
226 );
227 }
228
229 #[test]
230 fn test_map_type_nested_vec_in_optional() {
231 let ty = TypeRef::Optional(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
232 assert_eq!(RustMapper.map_type(&ty), "Option<Vec<String>>");
233 }
234
235 #[test]
240 fn test_wrap_return_no_error_passes_through() {
241 assert_eq!(RustMapper.wrap_return("String", false), "String");
242 }
243
244 #[test]
245 fn test_wrap_return_with_error_wraps_in_error_wrapper() {
246 assert_eq!(RustMapper.wrap_return("String", true), "Result<String>");
247 }
248
249 #[test]
250 fn test_wrap_return_unit_with_error() {
251 assert_eq!(RustMapper.wrap_return("()", true), "Result<()>");
252 }
253
254 struct CustomMapper;
259
260 impl TypeMapper for CustomMapper {
261 fn json(&self) -> Cow<'static, str> {
262 Cow::Borrowed("JsValue")
263 }
264
265 fn named<'a>(&self, name: &'a str) -> Cow<'a, str> {
266 Cow::Owned(format!("Js{name}"))
267 }
268
269 fn vec(&self, inner: &str) -> String {
270 if inner.starts_with("Vec<") {
271 "JsValue".to_string()
272 } else {
273 format!("Vec<{inner}>")
274 }
275 }
276
277 fn error_wrapper(&self) -> &str {
278 "JsResult"
279 }
280 }
281
282 #[test]
283 fn test_custom_mapper_json_override() {
284 assert_eq!(CustomMapper.map_type(&TypeRef::Json), "JsValue");
285 }
286
287 #[test]
288 fn test_custom_mapper_named_override() {
289 assert_eq!(CustomMapper.map_type(&TypeRef::Named("Config".to_string())), "JsConfig");
290 }
291
292 #[test]
293 fn test_custom_mapper_nested_vec_override() {
294 let ty = TypeRef::Vec(Box::new(TypeRef::Vec(Box::new(TypeRef::String))));
296 assert_eq!(CustomMapper.map_type(&ty), "JsValue");
297 }
298
299 #[test]
300 fn test_custom_mapper_wrap_return_with_error() {
301 assert_eq!(CustomMapper.wrap_return("String", true), "JsResult<String>");
302 }
303}