1pub fn rust_type_to_assura(rust_type: &str) -> String {
23 let trimmed = rust_type.trim();
24
25 if let Some(inner) = trimmed.strip_prefix('&') {
27 let inner = inner.trim_start();
28 if let Some(inner) = inner.strip_prefix("mut ") {
29 return rust_type_to_assura(inner.trim());
30 }
31 if inner == "str" {
32 return "String".to_string();
33 }
34 if inner == "[u8]" {
35 return "Bytes".to_string();
36 }
37 if let Some(slice_inner) = inner.strip_prefix('[')
39 && let Some(slice_inner) = slice_inner.strip_suffix(']')
40 {
41 let mapped = rust_type_to_assura(slice_inner.trim());
42 return format!("List<{mapped}>");
43 }
44 return rust_type_to_assura(inner);
45 }
46
47 if trimmed.starts_with('(') && trimmed.ends_with(')') {
49 let inner = &trimmed[1..trimmed.len() - 1];
50 if inner.is_empty() {
51 return "Unit".to_string();
52 }
53 let parts = split_type_args(inner);
54 let mapped: Vec<String> = parts.iter().map(|p| rust_type_to_assura(p)).collect();
55 return format!("({})", mapped.join(", "));
56 }
57
58 if !trimmed.contains('<') {
60 return map_simple_rust_type(trimmed).to_string();
61 }
62
63 if let Some((base, args)) = split_generic(trimmed) {
65 let base_trimmed = base.trim();
66 let type_args = split_type_args(args);
67
68 match base_trimmed {
69 "Vec" => {
71 if type_args.len() == 1 && type_args[0].trim() == "u8" {
72 "Bytes".to_string()
73 } else if type_args.len() == 1 {
74 let inner = rust_type_to_assura(type_args[0].trim());
75 format!("List<{inner}>")
76 } else {
77 format!("Vec<{}>", map_type_arg_list(&type_args))
78 }
79 }
80
81 "Option" => {
83 if type_args.len() == 1 {
84 let inner = rust_type_to_assura(type_args[0].trim());
85 format!("{inner}?")
86 } else {
87 format!("Option<{}>", map_type_arg_list(&type_args))
88 }
89 }
90
91 "Result" => {
93 format!("Result<{}>", map_type_arg_list(&type_args))
94 }
95
96 "HashMap" | "BTreeMap" | "std::collections::HashMap" | "std::collections::BTreeMap" => {
98 format!("Map<{}>", map_type_arg_list(&type_args))
99 }
100
101 "HashSet" | "BTreeSet" | "std::collections::HashSet" | "std::collections::BTreeSet" => {
103 if type_args.len() == 1 {
104 let inner = rust_type_to_assura(type_args[0].trim());
105 format!("Set<{inner}>")
106 } else {
107 format!("Set<{}>", map_type_arg_list(&type_args))
108 }
109 }
110
111 "Box" | "Arc" | "Rc" | "Cow" | "std::sync::Arc" | "std::rc::Rc"
113 | "std::borrow::Cow" => {
114 if type_args.len() == 1 {
115 rust_type_to_assura(type_args[0].trim())
116 } else {
117 format!("{base_trimmed}<{}>", map_type_arg_list(&type_args))
118 }
119 }
120
121 _ => {
123 let mapped_base = map_simple_rust_type(base_trimmed);
124 format!("{mapped_base}<{}>", map_type_arg_list(&type_args))
125 }
126 }
127 } else {
128 map_simple_rust_type(trimmed).to_string()
129 }
130}
131
132fn map_simple_rust_type(ty: &str) -> &str {
134 match ty {
135 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" => "Int",
137 "u8" | "u16" | "u32" | "u64" | "u128" | "usize" => "Nat",
139 "f32" | "f64" => "Float",
141 "bool" => "Bool",
143 "String" | "str" => "String",
145 "()" => "Unit",
147 "!" | "Infallible" | "std::convert::Infallible" => "Never",
149 "Bytes" | "bytes::Bytes" => "Bytes",
151 _ => ty,
153 }
154}
155
156fn split_generic(ty: &str) -> Option<(&str, &str)> {
158 let open = ty.find('<')?;
159 let close = ty.rfind('>')?;
160 if close <= open {
161 return None;
162 }
163 Some((&ty[..open], &ty[open + 1..close]))
164}
165
166fn split_type_args(args: &str) -> Vec<&str> {
168 let mut result = Vec::new();
169 let mut depth = 0i32;
170 let mut paren_depth = 0i32;
171 let mut start = 0;
172
173 for (i, ch) in args.char_indices() {
174 match ch {
175 '<' => depth += 1,
176 '>' if depth > 0 => depth -= 1,
177 '(' => paren_depth += 1,
178 ')' if paren_depth > 0 => paren_depth -= 1,
179 ',' if depth == 0 && paren_depth == 0 => {
180 result.push(&args[start..i]);
181 start = i + 1;
182 }
183 _ => {}
184 }
185 }
186 if start < args.len() {
187 result.push(&args[start..]);
188 }
189 result
190}
191
192fn map_type_arg_list(args: &[&str]) -> String {
194 args.iter()
195 .map(|a| rust_type_to_assura(a.trim()))
196 .collect::<Vec<_>>()
197 .join(", ")
198}
199
200pub fn rust_type_to_assura_lenient(rust_type: &str) -> String {
207 let ty = rust_type.trim();
208 if ty.starts_with("impl ") || ty.starts_with("dyn ") || ty == "Self" {
210 return "Unknown".to_string();
211 }
212 if ty.starts_with("Pin<") || ty.starts_with("PhantomData") {
213 return "Unknown".to_string();
214 }
215 let cleaned = if ty.starts_with("&'") {
217 if let Some(space_idx) = ty[1..].find(' ') {
218 format!("&{}", &ty[2 + space_idx..])
219 } else {
220 ty.to_string()
221 }
222 } else {
223 ty.to_string()
224 };
225 let mapped = rust_type_to_assura(&cleaned);
226 sanitize_mapped_type(&mapped)
227}
228
229fn sanitize_mapped_type(ty: &str) -> String {
231 let ty = ty.trim();
232 if ty.is_empty() {
233 return "Unknown".to_string();
234 }
235 if let Some(inner) = ty.strip_suffix('?') {
237 let s = sanitize_mapped_type(inner);
238 return format!("{s}?");
239 }
240 if ty.starts_with('(') && ty.ends_with(')') {
242 let inner = &ty[1..ty.len() - 1];
243 if inner.is_empty() {
244 return "Unit".to_string();
245 }
246 let parts = split_type_args(inner);
247 let sanitized: Vec<String> = parts
248 .iter()
249 .map(|p| sanitize_mapped_type(p.trim()))
250 .collect();
251 return format!("({})", sanitized.join(", "));
252 }
253 if let Some((base, args)) = split_generic(ty) {
255 let base = base.trim();
256 match base {
257 "List" | "Map" | "Set" | "Result" => {
258 let parts = split_type_args(args);
259 let sanitized: Vec<String> = parts
260 .iter()
261 .map(|p| sanitize_mapped_type(p.trim()))
262 .collect();
263 return format!("{base}<{}>", sanitized.join(", "));
264 }
265 _ => return "Unknown".to_string(),
266 }
267 }
268 match ty {
270 "Int" | "Nat" | "Float" | "Bool" | "String" | "Bytes" | "Unit" | "Never" | "Unknown" => {
271 ty.to_string()
272 }
273 _ => "Unknown".to_string(),
274 }
275}
276
277#[cfg(test)]
282mod tests {
283 use super::*;
284
285 #[test]
288 fn signed_integers_map_to_int() {
289 for ty in &["i8", "i16", "i32", "i64", "i128", "isize"] {
290 assert_eq!(rust_type_to_assura(ty), "Int", "failed for {ty}");
291 }
292 }
293
294 #[test]
295 fn unsigned_integers_map_to_nat() {
296 for ty in &["u8", "u16", "u32", "u64", "u128", "usize"] {
297 assert_eq!(rust_type_to_assura(ty), "Nat", "failed for {ty}");
298 }
299 }
300
301 #[test]
302 fn floats_map_to_float() {
303 assert_eq!(rust_type_to_assura("f32"), "Float");
304 assert_eq!(rust_type_to_assura("f64"), "Float");
305 }
306
307 #[test]
308 fn bool_maps_to_bool() {
309 assert_eq!(rust_type_to_assura("bool"), "Bool");
310 }
311
312 #[test]
313 fn string_types() {
314 assert_eq!(rust_type_to_assura("String"), "String");
315 assert_eq!(rust_type_to_assura("&str"), "String");
316 }
317
318 #[test]
319 fn unit_and_never() {
320 assert_eq!(rust_type_to_assura("()"), "Unit");
321 assert_eq!(rust_type_to_assura("!"), "Never");
322 assert_eq!(rust_type_to_assura("Infallible"), "Never");
323 }
324
325 #[test]
328 fn vec_u8_maps_to_bytes() {
329 assert_eq!(rust_type_to_assura("Vec<u8>"), "Bytes");
330 }
331
332 #[test]
333 fn vec_maps_to_list() {
334 assert_eq!(rust_type_to_assura("Vec<i64>"), "List<Int>");
335 assert_eq!(rust_type_to_assura("Vec<String>"), "List<String>");
336 }
337
338 #[test]
339 fn map_types() {
340 assert_eq!(
341 rust_type_to_assura("HashMap<String, i64>"),
342 "Map<String, Int>"
343 );
344 assert_eq!(
345 rust_type_to_assura("BTreeMap<String, u64>"),
346 "Map<String, Nat>"
347 );
348 }
349
350 #[test]
351 fn set_types() {
352 assert_eq!(rust_type_to_assura("HashSet<i64>"), "Set<Int>");
353 assert_eq!(rust_type_to_assura("BTreeSet<String>"), "Set<String>");
354 }
355
356 #[test]
359 fn option_maps_to_nullable() {
360 assert_eq!(rust_type_to_assura("Option<i64>"), "Int?");
361 assert_eq!(rust_type_to_assura("Option<String>"), "String?");
362 }
363
364 #[test]
367 fn references_are_erased() {
368 assert_eq!(rust_type_to_assura("&i64"), "Int");
369 assert_eq!(rust_type_to_assura("&mut i64"), "Int");
370 assert_eq!(rust_type_to_assura("&[u8]"), "Bytes");
371 assert_eq!(rust_type_to_assura("&[i64]"), "List<Int>");
372 }
373
374 #[test]
377 fn smart_pointers_are_erased() {
378 assert_eq!(rust_type_to_assura("Box<i64>"), "Int");
379 assert_eq!(rust_type_to_assura("Arc<String>"), "String");
380 assert_eq!(rust_type_to_assura("Rc<Vec<i64>>"), "List<Int>");
381 }
382
383 #[test]
386 fn nested_generics() {
387 assert_eq!(rust_type_to_assura("Vec<Option<i64>>"), "List<Int?>");
388 assert_eq!(
389 rust_type_to_assura("Vec<Option<BTreeMap<String, i64>>>"),
390 "List<Map<String, Int>?>"
391 );
392 }
393
394 #[test]
397 fn tuple_types() {
398 assert_eq!(rust_type_to_assura("(i64, u64)"), "(Int, Nat)");
399 assert_eq!(
400 rust_type_to_assura("(String, bool, f64)"),
401 "(String, Bool, Float)"
402 );
403 }
404
405 #[test]
408 fn unknown_types_pass_through() {
409 assert_eq!(rust_type_to_assura("MyCustomStruct"), "MyCustomStruct");
410 assert_eq!(rust_type_to_assura("MyGeneric<i64>"), "MyGeneric<Int>");
411 }
412
413 #[test]
416 fn result_type() {
417 assert_eq!(
418 rust_type_to_assura("Result<i64, String>"),
419 "Result<Int, String>"
420 );
421 }
422
423 #[test]
426 fn lenient_primitives_pass_through() {
427 assert_eq!(rust_type_to_assura_lenient("i64"), "Int");
428 assert_eq!(rust_type_to_assura_lenient("u32"), "Nat");
429 assert_eq!(rust_type_to_assura_lenient("bool"), "Bool");
430 assert_eq!(rust_type_to_assura_lenient("String"), "String");
431 assert_eq!(rust_type_to_assura_lenient("f64"), "Float");
432 }
433
434 #[test]
435 fn lenient_unknown_types_become_unknown() {
436 assert_eq!(rust_type_to_assura_lenient("Config"), "Unknown");
437 assert_eq!(rust_type_to_assura_lenient("PathBuf"), "Unknown");
438 assert_eq!(rust_type_to_assura_lenient("MyStruct"), "Unknown");
439 }
440
441 #[test]
442 fn lenient_unknown_generics_become_unknown() {
443 assert_eq!(rust_type_to_assura_lenient("MyGeneric<i64>"), "Unknown");
444 assert_eq!(rust_type_to_assura_lenient("Foo<Bar, Baz>"), "Unknown");
445 }
446
447 #[test]
448 fn lenient_known_generics_preserve_structure() {
449 assert_eq!(rust_type_to_assura_lenient("Vec<i64>"), "List<Int>");
450 assert_eq!(
451 rust_type_to_assura_lenient("HashMap<String, i64>"),
452 "Map<String, Int>"
453 );
454 assert_eq!(rust_type_to_assura_lenient("Option<bool>"), "Bool?");
455 }
456
457 #[test]
458 fn lenient_nested_unknown_in_known_generic() {
459 assert_eq!(rust_type_to_assura_lenient("Vec<Config>"), "List<Unknown>");
460 assert_eq!(
461 rust_type_to_assura_lenient("Result<i64, MyError>"),
462 "Result<Int, Unknown>"
463 );
464 }
465
466 #[test]
467 fn lenient_impl_dyn_self() {
468 assert_eq!(rust_type_to_assura_lenient("impl Iterator"), "Unknown");
469 assert_eq!(rust_type_to_assura_lenient("dyn Trait"), "Unknown");
470 assert_eq!(rust_type_to_assura_lenient("Self"), "Unknown");
471 }
472
473 #[test]
474 fn lenient_lifetime_references() {
475 assert_eq!(rust_type_to_assura_lenient("&'a str"), "String");
476 assert_eq!(rust_type_to_assura_lenient("&'_ i64"), "Int");
477 }
478
479 #[test]
480 fn lenient_pin_phantomdata() {
481 assert_eq!(
482 rust_type_to_assura_lenient("Pin<Box<dyn Future>>"),
483 "Unknown"
484 );
485 assert_eq!(rust_type_to_assura_lenient("PhantomData<T>"), "Unknown");
486 }
487
488 #[test]
489 fn lenient_wrapper_erasure_then_sanitize() {
490 assert_eq!(rust_type_to_assura_lenient("Box<i64>"), "Int");
491 assert_eq!(rust_type_to_assura_lenient("Arc<String>"), "String");
492 assert_eq!(rust_type_to_assura_lenient("Arc<Config>"), "Unknown");
493 }
494}