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 let ty = ty.rsplit("::").next().unwrap_or(ty).trim();
136 match ty {
137 "i8" | "i16" | "i32" | "i64" | "i128" | "isize" => "Int",
139 "u8" | "u16" | "u32" | "u64" | "u128" | "usize" => "Nat",
141 "NonZeroU8" | "NonZeroU16" | "NonZeroU32" | "NonZeroU64" | "NonZeroU128"
143 | "NonZeroUsize" => "Nat",
144 "NonZeroI8" | "NonZeroI16" | "NonZeroI32" | "NonZeroI64" | "NonZeroI128"
145 | "NonZeroIsize" => "Int",
146 "f32" | "f64" => "Float",
148 "bool" => "Bool",
150 "String" | "str" => "String",
152 "()" => "Unit",
154 "!" | "Infallible" | "std::convert::Infallible" => "Never",
156 "Bytes" | "bytes::Bytes" => "Bytes",
158 _ => ty,
160 }
161}
162
163fn split_generic(ty: &str) -> Option<(&str, &str)> {
165 let open = ty.find('<')?;
166 let close = ty.rfind('>')?;
167 if close <= open {
168 return None;
169 }
170 Some((&ty[..open], &ty[open + 1..close]))
171}
172
173fn split_type_args(args: &str) -> Vec<&str> {
175 let mut result = Vec::new();
176 let mut depth = 0i32;
177 let mut paren_depth = 0i32;
178 let mut start = 0;
179
180 for (i, ch) in args.char_indices() {
181 match ch {
182 '<' => depth += 1,
183 '>' if depth > 0 => depth -= 1,
184 '(' => paren_depth += 1,
185 ')' if paren_depth > 0 => paren_depth -= 1,
186 ',' if depth == 0 && paren_depth == 0 => {
187 result.push(&args[start..i]);
188 start = i + 1;
189 }
190 _ => {}
191 }
192 }
193 if start < args.len() {
194 result.push(&args[start..]);
195 }
196 result
197}
198
199fn map_type_arg_list(args: &[&str]) -> String {
201 args.iter()
202 .map(|a| rust_type_to_assura(a.trim()))
203 .collect::<Vec<_>>()
204 .join(", ")
205}
206
207pub fn rust_type_to_assura_lenient(rust_type: &str) -> String {
214 let ty = rust_type.trim();
215 if ty.starts_with("impl ") || ty.starts_with("dyn ") || ty == "Self" {
217 return "Unknown".to_string();
218 }
219 if ty.starts_with("Pin<") || ty.starts_with("PhantomData") {
220 return "Unknown".to_string();
221 }
222 let cleaned = if ty.starts_with("&'") {
224 if let Some(space_idx) = ty[1..].find(' ') {
225 format!("&{}", &ty[2 + space_idx..])
226 } else {
227 ty.to_string()
228 }
229 } else {
230 ty.to_string()
231 };
232 let mapped = rust_type_to_assura(&cleaned);
233 sanitize_mapped_type(&mapped)
234}
235
236fn sanitize_mapped_type(ty: &str) -> String {
238 let ty = ty.trim();
239 if ty.is_empty() {
240 return "Unknown".to_string();
241 }
242 if let Some(inner) = ty.strip_suffix('?') {
244 let s = sanitize_mapped_type(inner);
245 return format!("{s}?");
246 }
247 if ty.starts_with('(') && ty.ends_with(')') {
249 let inner = &ty[1..ty.len() - 1];
250 if inner.is_empty() {
251 return "Unit".to_string();
252 }
253 let parts = split_type_args(inner);
254 let sanitized: Vec<String> = parts
255 .iter()
256 .map(|p| sanitize_mapped_type(p.trim()))
257 .collect();
258 return format!("({})", sanitized.join(", "));
259 }
260 if let Some((base, args)) = split_generic(ty) {
262 let base = base.trim();
263 match base {
264 "List" | "Map" | "Set" | "Result" => {
265 let parts = split_type_args(args);
266 let sanitized: Vec<String> = parts
267 .iter()
268 .map(|p| sanitize_mapped_type(p.trim()))
269 .collect();
270 return format!("{base}<{}>", sanitized.join(", "));
271 }
272 _ => return "Unknown".to_string(),
273 }
274 }
275 match ty {
277 "Int" | "Nat" | "Float" | "Bool" | "String" | "Bytes" | "Unit" | "Never" | "Unknown" => {
278 ty.to_string()
279 }
280 _ => "Unknown".to_string(),
281 }
282}
283
284#[cfg(test)]
289mod tests {
290 use super::*;
291
292 #[test]
295 fn signed_integers_map_to_int() {
296 for ty in &["i8", "i16", "i32", "i64", "i128", "isize"] {
297 assert_eq!(rust_type_to_assura(ty), "Int", "failed for {ty}");
298 }
299 }
300
301 #[test]
302 fn unsigned_integers_map_to_nat() {
303 for ty in &["u8", "u16", "u32", "u64", "u128", "usize"] {
304 assert_eq!(rust_type_to_assura(ty), "Nat", "failed for {ty}");
305 }
306 }
307
308 #[test]
309 fn nonzero_integers_map_to_int_or_nat() {
310 for ty in &[
311 "NonZeroU8",
312 "NonZeroU16",
313 "NonZeroU32",
314 "NonZeroU64",
315 "NonZeroUsize",
316 ] {
317 assert_eq!(rust_type_to_assura(ty), "Nat", "failed for {ty}");
318 }
319 for ty in &[
320 "NonZeroI8",
321 "NonZeroI16",
322 "NonZeroI32",
323 "NonZeroI64",
324 "NonZeroIsize",
325 ] {
326 assert_eq!(rust_type_to_assura(ty), "Int", "failed for {ty}");
327 }
328 assert_eq!(rust_type_to_assura("std::num::NonZeroU8"), "Nat");
329 }
330
331 #[test]
332 fn floats_map_to_float() {
333 assert_eq!(rust_type_to_assura("f32"), "Float");
334 assert_eq!(rust_type_to_assura("f64"), "Float");
335 }
336
337 #[test]
338 fn bool_maps_to_bool() {
339 assert_eq!(rust_type_to_assura("bool"), "Bool");
340 }
341
342 #[test]
343 fn string_types() {
344 assert_eq!(rust_type_to_assura("String"), "String");
345 assert_eq!(rust_type_to_assura("&str"), "String");
346 }
347
348 #[test]
349 fn unit_and_never() {
350 assert_eq!(rust_type_to_assura("()"), "Unit");
351 assert_eq!(rust_type_to_assura("!"), "Never");
352 assert_eq!(rust_type_to_assura("Infallible"), "Never");
353 }
354
355 #[test]
358 fn vec_u8_maps_to_bytes() {
359 assert_eq!(rust_type_to_assura("Vec<u8>"), "Bytes");
360 }
361
362 #[test]
363 fn vec_maps_to_list() {
364 assert_eq!(rust_type_to_assura("Vec<i64>"), "List<Int>");
365 assert_eq!(rust_type_to_assura("Vec<String>"), "List<String>");
366 }
367
368 #[test]
369 fn map_types() {
370 assert_eq!(
371 rust_type_to_assura("HashMap<String, i64>"),
372 "Map<String, Int>"
373 );
374 assert_eq!(
375 rust_type_to_assura("BTreeMap<String, u64>"),
376 "Map<String, Nat>"
377 );
378 }
379
380 #[test]
381 fn set_types() {
382 assert_eq!(rust_type_to_assura("HashSet<i64>"), "Set<Int>");
383 assert_eq!(rust_type_to_assura("BTreeSet<String>"), "Set<String>");
384 }
385
386 #[test]
389 fn option_maps_to_nullable() {
390 assert_eq!(rust_type_to_assura("Option<i64>"), "Int?");
391 assert_eq!(rust_type_to_assura("Option<String>"), "String?");
392 }
393
394 #[test]
397 fn references_are_erased() {
398 assert_eq!(rust_type_to_assura("&i64"), "Int");
399 assert_eq!(rust_type_to_assura("&mut i64"), "Int");
400 assert_eq!(rust_type_to_assura("&[u8]"), "Bytes");
401 assert_eq!(rust_type_to_assura("&[i64]"), "List<Int>");
402 }
403
404 #[test]
407 fn smart_pointers_are_erased() {
408 assert_eq!(rust_type_to_assura("Box<i64>"), "Int");
409 assert_eq!(rust_type_to_assura("Arc<String>"), "String");
410 assert_eq!(rust_type_to_assura("Rc<Vec<i64>>"), "List<Int>");
411 }
412
413 #[test]
416 fn nested_generics() {
417 assert_eq!(rust_type_to_assura("Vec<Option<i64>>"), "List<Int?>");
418 assert_eq!(
419 rust_type_to_assura("Vec<Option<BTreeMap<String, i64>>>"),
420 "List<Map<String, Int>?>"
421 );
422 }
423
424 #[test]
427 fn tuple_types() {
428 assert_eq!(rust_type_to_assura("(i64, u64)"), "(Int, Nat)");
429 assert_eq!(
430 rust_type_to_assura("(String, bool, f64)"),
431 "(String, Bool, Float)"
432 );
433 }
434
435 #[test]
438 fn unknown_types_pass_through() {
439 assert_eq!(rust_type_to_assura("MyCustomStruct"), "MyCustomStruct");
440 assert_eq!(rust_type_to_assura("MyGeneric<i64>"), "MyGeneric<Int>");
441 }
442
443 #[test]
446 fn result_type() {
447 assert_eq!(
448 rust_type_to_assura("Result<i64, String>"),
449 "Result<Int, String>"
450 );
451 }
452
453 #[test]
456 fn lenient_primitives_pass_through() {
457 assert_eq!(rust_type_to_assura_lenient("i64"), "Int");
458 assert_eq!(rust_type_to_assura_lenient("u32"), "Nat");
459 assert_eq!(rust_type_to_assura_lenient("bool"), "Bool");
460 assert_eq!(rust_type_to_assura_lenient("String"), "String");
461 assert_eq!(rust_type_to_assura_lenient("f64"), "Float");
462 }
463
464 #[test]
465 fn lenient_unknown_types_become_unknown() {
466 assert_eq!(rust_type_to_assura_lenient("Config"), "Unknown");
467 assert_eq!(rust_type_to_assura_lenient("PathBuf"), "Unknown");
468 assert_eq!(rust_type_to_assura_lenient("MyStruct"), "Unknown");
469 }
470
471 #[test]
472 fn lenient_unknown_generics_become_unknown() {
473 assert_eq!(rust_type_to_assura_lenient("MyGeneric<i64>"), "Unknown");
474 assert_eq!(rust_type_to_assura_lenient("Foo<Bar, Baz>"), "Unknown");
475 }
476
477 #[test]
478 fn lenient_known_generics_preserve_structure() {
479 assert_eq!(rust_type_to_assura_lenient("Vec<i64>"), "List<Int>");
480 assert_eq!(
481 rust_type_to_assura_lenient("HashMap<String, i64>"),
482 "Map<String, Int>"
483 );
484 assert_eq!(rust_type_to_assura_lenient("Option<bool>"), "Bool?");
485 }
486
487 #[test]
488 fn lenient_nested_unknown_in_known_generic() {
489 assert_eq!(rust_type_to_assura_lenient("Vec<Config>"), "List<Unknown>");
490 assert_eq!(
491 rust_type_to_assura_lenient("Result<i64, MyError>"),
492 "Result<Int, Unknown>"
493 );
494 }
495
496 #[test]
497 fn lenient_impl_dyn_self() {
498 assert_eq!(rust_type_to_assura_lenient("impl Iterator"), "Unknown");
499 assert_eq!(rust_type_to_assura_lenient("dyn Trait"), "Unknown");
500 assert_eq!(rust_type_to_assura_lenient("Self"), "Unknown");
501 }
502
503 #[test]
504 fn lenient_lifetime_references() {
505 assert_eq!(rust_type_to_assura_lenient("&'a str"), "String");
506 assert_eq!(rust_type_to_assura_lenient("&'_ i64"), "Int");
507 }
508
509 #[test]
510 fn lenient_pin_phantomdata() {
511 assert_eq!(
512 rust_type_to_assura_lenient("Pin<Box<dyn Future>>"),
513 "Unknown"
514 );
515 assert_eq!(rust_type_to_assura_lenient("PhantomData<T>"), "Unknown");
516 }
517
518 #[test]
519 fn lenient_wrapper_erasure_then_sanitize() {
520 assert_eq!(rust_type_to_assura_lenient("Box<i64>"), "Int");
521 assert_eq!(rust_type_to_assura_lenient("Arc<String>"), "String");
522 assert_eq!(rust_type_to_assura_lenient("Arc<Config>"), "Unknown");
523 }
524}