1use decy_analyzer::tagged_union_analysis::TaggedUnionInfo;
116use decy_hir::HirType;
117
118pub struct EnumGenerator;
120
121impl EnumGenerator {
122 pub fn new() -> Self {
124 Self
125 }
126
127 pub fn generate_enum(&self, info: &TaggedUnionInfo) -> String {
137 let mut result = String::new();
138
139 result.push_str("#[derive(Debug, Clone, PartialEq)]\n");
141
142 result.push_str(&format!("pub enum {} {{\n", info.struct_name));
144
145 for (idx, variant) in info.variants.iter().enumerate() {
147 let variant_name = Self::capitalize_variant_name(&variant.name, &variant.payload_type);
148 let variant_type = Self::map_hir_type_to_rust(&variant.payload_type);
149
150 if matches!(variant.payload_type, HirType::Void) {
152 result.push_str(&format!(" {},\n", variant_name));
153 } else {
154 result.push_str(&format!(" {}({})", variant_name, variant_type));
155 if idx < info.variants.len() - 1 {
156 result.push_str(",\n");
157 } else {
158 result.push('\n');
159 }
160 }
161 }
162
163 result.push('}');
164 result
165 }
166
167 fn capitalize_variant_name(name: &str, payload_type: &HirType) -> String {
172 if name.len() <= 2 {
174 return Self::type_based_variant_name(payload_type);
175 }
176
177 let parts: Vec<String> = name
179 .split('_')
180 .filter(|s| !s.is_empty())
181 .map(|part| {
182 let mut chars = part.chars();
183 match chars.next() {
184 None => String::new(),
185 Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
186 }
187 })
188 .collect();
189
190 if parts.is_empty() {
191 Self::type_based_variant_name(payload_type)
193 } else {
194 parts.join("")
195 }
196 }
197
198 fn type_based_variant_name(payload_type: &HirType) -> String {
200 match payload_type {
201 HirType::Void => "None".to_string(),
202 HirType::Bool => "Bool".to_string(),
203 HirType::Int => "Int".to_string(),
204 HirType::UnsignedInt => "UInt".to_string(), HirType::Float => "Float".to_string(),
206 HirType::Double => "Double".to_string(),
207 HirType::Char => "Char".to_string(),
208 HirType::SignedChar => "SignedChar".to_string(), HirType::Pointer(inner) if matches!(**inner, HirType::Char) => "String".to_string(),
210 HirType::Pointer(inner) if matches!(**inner, HirType::Void) => "Pointer".to_string(),
211 HirType::Pointer(_) => "Pointer".to_string(),
212 HirType::Box(_) => "Boxed".to_string(),
213 HirType::Vec(_) => "Vec".to_string(),
214 HirType::Option(_) => "Option".to_string(),
215 HirType::Reference { .. } => "Ref".to_string(),
216 HirType::Struct(name) => name.clone(),
217 HirType::Enum(name) => name.clone(),
218 HirType::Union(_) => "Union".to_string(),
219 HirType::Array { .. } => "Array".to_string(),
220 HirType::FunctionPointer { .. } => "Function".to_string(),
221 HirType::StringLiteral | HirType::OwnedString | HirType::StringReference => {
222 "String".to_string()
223 }
224 HirType::TypeAlias(name) => name.clone(),
226 }
227 }
228
229 fn map_hir_type_to_rust(hir_type: &HirType) -> String {
231 match hir_type {
232 HirType::Void => "()".to_string(),
233 HirType::Bool => "bool".to_string(),
234 HirType::Int => "i32".to_string(),
235 HirType::UnsignedInt => "u32".to_string(), HirType::Float => "f32".to_string(),
237 HirType::Double => "f64".to_string(),
238 HirType::Char => "u8".to_string(),
239 HirType::SignedChar => "i8".to_string(), HirType::Pointer(inner) => {
241 if matches!(**inner, HirType::Char) {
243 "String".to_string()
244 } else if matches!(**inner, HirType::Void) {
245 "*mut ()".to_string()
246 } else {
247 format!("*mut {}", Self::map_hir_type_to_rust(inner))
248 }
249 }
250 HirType::Box(inner) => format!("Box<{}>", Self::map_hir_type_to_rust(inner)),
251 HirType::Vec(inner) => format!("Vec<{}>", Self::map_hir_type_to_rust(inner)),
252 HirType::Option(inner) => format!("Option<{}>", Self::map_hir_type_to_rust(inner)),
253 HirType::Reference { inner, mutable } => {
254 if *mutable {
255 format!("&mut {}", Self::map_hir_type_to_rust(inner))
256 } else {
257 format!("&{}", Self::map_hir_type_to_rust(inner))
258 }
259 }
260 HirType::Struct(name) => name.clone(),
261 HirType::Enum(name) => name.clone(),
262 HirType::Union(_) => "/* Union */".to_string(),
263 HirType::Array { element_type, size } => {
264 if let Some(n) = size {
265 format!("[{}; {}]", Self::map_hir_type_to_rust(element_type), n)
266 } else {
267 format!("[{}]", Self::map_hir_type_to_rust(element_type))
268 }
269 }
270 HirType::FunctionPointer { param_types, return_type } => {
271 let params: Vec<String> =
272 param_types.iter().map(Self::map_hir_type_to_rust).collect();
273 let params_str = params.join(", ");
274 if matches!(**return_type, HirType::Void) {
275 format!("fn({})", params_str)
276 } else {
277 format!("fn({}) -> {}", params_str, Self::map_hir_type_to_rust(return_type))
278 }
279 }
280 HirType::StringLiteral => "&str".to_string(),
281 HirType::OwnedString => "String".to_string(),
282 HirType::StringReference => "&str".to_string(),
283 HirType::TypeAlias(name) => name.clone(),
285 }
286 }
287}
288
289impl Default for EnumGenerator {
290 fn default() -> Self {
291 Self::new()
292 }
293}
294
295#[cfg(test)]
296mod tests {
297 use super::*;
298 use decy_analyzer::tagged_union_analysis::{TaggedUnionInfo, VariantInfo};
299
300 #[test]
305 fn test_enum_generator_new() {
306 let gen = EnumGenerator::new();
307 assert!(std::mem::size_of_val(&gen) == 0);
309 }
310
311 #[test]
312 fn test_enum_generator_default() {
313 let gen: EnumGenerator = Default::default();
314 assert!(std::mem::size_of_val(&gen) == 0);
315 }
316
317 #[test]
322 fn test_generate_enum_single_variant() {
323 let gen = EnumGenerator::new();
324 let info = TaggedUnionInfo {
325 struct_name: "Value".to_string(),
326 tag_field_name: "tag".to_string(),
327 union_field_name: "data".to_string(),
328 variants: vec![VariantInfo {
329 name: "int_value".to_string(),
330 payload_type: HirType::Int,
331 }],
332 };
333
334 let result = gen.generate_enum(&info);
335 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
336 assert!(result.contains("pub enum Value"));
337 assert!(result.contains("IntValue(i32)"));
338 }
339
340 #[test]
341 fn test_generate_enum_multiple_variants() {
342 let gen = EnumGenerator::new();
343 let info = TaggedUnionInfo {
344 struct_name: "Value".to_string(),
345 tag_field_name: "tag".to_string(),
346 union_field_name: "data".to_string(),
347 variants: vec![
348 VariantInfo { name: "int_value".to_string(), payload_type: HirType::Int },
349 VariantInfo { name: "float_value".to_string(), payload_type: HirType::Float },
350 VariantInfo { name: "double_value".to_string(), payload_type: HirType::Double },
351 ],
352 };
353
354 let result = gen.generate_enum(&info);
355 assert!(result.contains("IntValue(i32),"));
356 assert!(result.contains("FloatValue(f32),"));
357 assert!(result.contains("DoubleValue(f64)"));
358 }
359
360 #[test]
361 fn test_generate_enum_void_variant() {
362 let gen = EnumGenerator::new();
363 let info = TaggedUnionInfo {
364 struct_name: "Option".to_string(),
365 tag_field_name: "tag".to_string(),
366 union_field_name: "data".to_string(),
367 variants: vec![
368 VariantInfo { name: "none".to_string(), payload_type: HirType::Void },
369 VariantInfo { name: "some_int".to_string(), payload_type: HirType::Int },
370 ],
371 };
372
373 let result = gen.generate_enum(&info);
374 assert!(result.contains("None,"));
376 assert!(result.contains("SomeInt(i32)"));
377 }
378
379 #[test]
380 fn test_generate_enum_last_variant_no_trailing_comma() {
381 let gen = EnumGenerator::new();
382 let info = TaggedUnionInfo {
383 struct_name: "Test".to_string(),
384 tag_field_name: "tag".to_string(),
385 union_field_name: "data".to_string(),
386 variants: vec![VariantInfo { name: "value".to_string(), payload_type: HirType::Int }],
387 };
388
389 let result = gen.generate_enum(&info);
390 assert!(result.contains("Value(i32)\n}"));
392 }
393
394 #[test]
399 fn test_capitalize_variant_name_snake_case() {
400 let result = EnumGenerator::capitalize_variant_name("int_value", &HirType::Int);
401 assert_eq!(result, "IntValue");
402 }
403
404 #[test]
405 fn test_capitalize_variant_name_short_name_uses_type() {
406 let result = EnumGenerator::capitalize_variant_name("i", &HirType::Int);
408 assert_eq!(result, "Int");
409
410 let result = EnumGenerator::capitalize_variant_name("f", &HirType::Float);
411 assert_eq!(result, "Float");
412
413 let result = EnumGenerator::capitalize_variant_name("d", &HirType::Double);
414 assert_eq!(result, "Double");
415 }
416
417 #[test]
418 fn test_capitalize_variant_name_two_char_name() {
419 let result = EnumGenerator::capitalize_variant_name("id", &HirType::Int);
420 assert_eq!(result, "Int"); }
422
423 #[test]
424 fn test_capitalize_variant_name_three_char_name() {
425 let result = EnumGenerator::capitalize_variant_name("val", &HirType::Int);
426 assert_eq!(result, "Val"); }
428
429 #[test]
430 fn test_capitalize_variant_name_empty_parts_fallback() {
431 let result = EnumGenerator::capitalize_variant_name("___", &HirType::Int);
433 assert_eq!(result, "Int");
434 }
435
436 #[test]
437 fn test_capitalize_variant_name_single_word() {
438 let result = EnumGenerator::capitalize_variant_name("value", &HirType::Int);
439 assert_eq!(result, "Value");
440 }
441
442 #[test]
443 fn test_capitalize_variant_name_multiple_underscores() {
444 let result = EnumGenerator::capitalize_variant_name("my__long__name", &HirType::Int);
445 assert_eq!(result, "MyLongName");
446 }
447
448 #[test]
453 fn test_type_based_variant_name_primitives() {
454 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Void), "None");
455 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Int), "Int");
456 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::UnsignedInt), "UInt");
457 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Float), "Float");
458 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Double), "Double");
459 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::Char), "Char");
460 }
461
462 #[test]
463 fn test_type_based_variant_name_char_pointer_is_string() {
464 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
465 assert_eq!(EnumGenerator::type_based_variant_name(&char_ptr), "String");
466 }
467
468 #[test]
469 fn test_type_based_variant_name_void_pointer() {
470 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
471 assert_eq!(EnumGenerator::type_based_variant_name(&void_ptr), "Pointer");
472 }
473
474 #[test]
475 fn test_type_based_variant_name_other_pointer() {
476 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
477 assert_eq!(EnumGenerator::type_based_variant_name(&int_ptr), "Pointer");
478 }
479
480 #[test]
481 fn test_type_based_variant_name_box() {
482 let boxed = HirType::Box(Box::new(HirType::Int));
483 assert_eq!(EnumGenerator::type_based_variant_name(&boxed), "Boxed");
484 }
485
486 #[test]
487 fn test_type_based_variant_name_vec() {
488 let vec = HirType::Vec(Box::new(HirType::Int));
489 assert_eq!(EnumGenerator::type_based_variant_name(&vec), "Vec");
490 }
491
492 #[test]
493 fn test_type_based_variant_name_option() {
494 let opt = HirType::Option(Box::new(HirType::Int));
495 assert_eq!(EnumGenerator::type_based_variant_name(&opt), "Option");
496 }
497
498 #[test]
499 fn test_type_based_variant_name_reference() {
500 let ref_type = HirType::Reference { inner: Box::new(HirType::Int), mutable: false };
501 assert_eq!(EnumGenerator::type_based_variant_name(&ref_type), "Ref");
502 }
503
504 #[test]
505 fn test_type_based_variant_name_struct() {
506 let struct_type = HirType::Struct("MyStruct".to_string());
507 assert_eq!(EnumGenerator::type_based_variant_name(&struct_type), "MyStruct");
508 }
509
510 #[test]
511 fn test_type_based_variant_name_enum() {
512 let enum_type = HirType::Enum("MyEnum".to_string());
513 assert_eq!(EnumGenerator::type_based_variant_name(&enum_type), "MyEnum");
514 }
515
516 #[test]
517 fn test_type_based_variant_name_union() {
518 let union_type = HirType::Union(vec![]);
519 assert_eq!(EnumGenerator::type_based_variant_name(&union_type), "Union");
520 }
521
522 #[test]
523 fn test_type_based_variant_name_array() {
524 let array = HirType::Array { element_type: Box::new(HirType::Int), size: Some(10) };
525 assert_eq!(EnumGenerator::type_based_variant_name(&array), "Array");
526 }
527
528 #[test]
529 fn test_type_based_variant_name_function_pointer() {
530 let fn_ptr = HirType::FunctionPointer {
531 param_types: vec![HirType::Int],
532 return_type: Box::new(HirType::Void),
533 };
534 assert_eq!(EnumGenerator::type_based_variant_name(&fn_ptr), "Function");
535 }
536
537 #[test]
538 fn test_type_based_variant_name_string_types() {
539 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::StringLiteral), "String");
540 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::OwnedString), "String");
541 assert_eq!(EnumGenerator::type_based_variant_name(&HirType::StringReference), "String");
542 }
543
544 #[test]
545 fn test_type_based_variant_name_type_alias() {
546 let alias = HirType::TypeAlias("size_t".to_string());
547 assert_eq!(EnumGenerator::type_based_variant_name(&alias), "size_t");
548 }
549
550 #[test]
555 fn test_map_hir_type_primitives() {
556 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Void), "()");
557 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Int), "i32");
558 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::UnsignedInt), "u32");
559 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Float), "f32");
560 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Double), "f64");
561 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::Char), "u8");
562 }
563
564 #[test]
565 fn test_map_hir_type_char_pointer_to_string() {
566 let char_ptr = HirType::Pointer(Box::new(HirType::Char));
567 assert_eq!(EnumGenerator::map_hir_type_to_rust(&char_ptr), "String");
568 }
569
570 #[test]
571 fn test_map_hir_type_void_pointer() {
572 let void_ptr = HirType::Pointer(Box::new(HirType::Void));
573 assert_eq!(EnumGenerator::map_hir_type_to_rust(&void_ptr), "*mut ()");
574 }
575
576 #[test]
577 fn test_map_hir_type_other_pointer() {
578 let int_ptr = HirType::Pointer(Box::new(HirType::Int));
579 assert_eq!(EnumGenerator::map_hir_type_to_rust(&int_ptr), "*mut i32");
580 }
581
582 #[test]
583 fn test_map_hir_type_box() {
584 let boxed = HirType::Box(Box::new(HirType::Int));
585 assert_eq!(EnumGenerator::map_hir_type_to_rust(&boxed), "Box<i32>");
586 }
587
588 #[test]
589 fn test_map_hir_type_vec() {
590 let vec = HirType::Vec(Box::new(HirType::Float));
591 assert_eq!(EnumGenerator::map_hir_type_to_rust(&vec), "Vec<f32>");
592 }
593
594 #[test]
595 fn test_map_hir_type_option() {
596 let opt = HirType::Option(Box::new(HirType::Double));
597 assert_eq!(EnumGenerator::map_hir_type_to_rust(&opt), "Option<f64>");
598 }
599
600 #[test]
601 fn test_map_hir_type_immutable_reference() {
602 let ref_type = HirType::Reference { inner: Box::new(HirType::Int), mutable: false };
603 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&i32");
604 }
605
606 #[test]
607 fn test_map_hir_type_mutable_reference() {
608 let ref_type = HirType::Reference { inner: Box::new(HirType::Int), mutable: true };
609 assert_eq!(EnumGenerator::map_hir_type_to_rust(&ref_type), "&mut i32");
610 }
611
612 #[test]
613 fn test_map_hir_type_struct() {
614 let struct_type = HirType::Struct("Point".to_string());
615 assert_eq!(EnumGenerator::map_hir_type_to_rust(&struct_type), "Point");
616 }
617
618 #[test]
619 fn test_map_hir_type_enum() {
620 let enum_type = HirType::Enum("Color".to_string());
621 assert_eq!(EnumGenerator::map_hir_type_to_rust(&enum_type), "Color");
622 }
623
624 #[test]
625 fn test_map_hir_type_union() {
626 let union_type = HirType::Union(vec![]);
627 assert_eq!(EnumGenerator::map_hir_type_to_rust(&union_type), "/* Union */");
628 }
629
630 #[test]
631 fn test_map_hir_type_array_with_size() {
632 let array = HirType::Array { element_type: Box::new(HirType::Int), size: Some(10) };
633 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32; 10]");
634 }
635
636 #[test]
637 fn test_map_hir_type_array_without_size() {
638 let array = HirType::Array { element_type: Box::new(HirType::Int), size: None };
639 assert_eq!(EnumGenerator::map_hir_type_to_rust(&array), "[i32]");
640 }
641
642 #[test]
643 fn test_map_hir_type_function_pointer_void_return() {
644 let fn_ptr = HirType::FunctionPointer {
645 param_types: vec![HirType::Int, HirType::Float],
646 return_type: Box::new(HirType::Void),
647 };
648 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn(i32, f32)");
649 }
650
651 #[test]
652 fn test_map_hir_type_function_pointer_with_return() {
653 let fn_ptr = HirType::FunctionPointer {
654 param_types: vec![HirType::Int],
655 return_type: Box::new(HirType::Int),
656 };
657 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn(i32) -> i32");
658 }
659
660 #[test]
661 fn test_map_hir_type_function_pointer_no_params() {
662 let fn_ptr =
663 HirType::FunctionPointer { param_types: vec![], return_type: Box::new(HirType::Int) };
664 assert_eq!(EnumGenerator::map_hir_type_to_rust(&fn_ptr), "fn() -> i32");
665 }
666
667 #[test]
668 fn test_map_hir_type_string_types() {
669 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::StringLiteral), "&str");
670 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::OwnedString), "String");
671 assert_eq!(EnumGenerator::map_hir_type_to_rust(&HirType::StringReference), "&str");
672 }
673
674 #[test]
675 fn test_map_hir_type_type_alias() {
676 let alias = HirType::TypeAlias("size_t".to_string());
677 assert_eq!(EnumGenerator::map_hir_type_to_rust(&alias), "size_t");
678 }
679
680 #[test]
685 fn test_generate_enum_complete_value_type() {
686 let gen = EnumGenerator::new();
687 let info = TaggedUnionInfo {
688 struct_name: "Value".to_string(),
689 tag_field_name: "type".to_string(),
690 union_field_name: "data".to_string(),
691 variants: vec![
692 VariantInfo {
693 name: "i".to_string(), payload_type: HirType::Int,
695 },
696 VariantInfo {
697 name: "f".to_string(), payload_type: HirType::Float,
699 },
700 VariantInfo {
701 name: "s".to_string(), payload_type: HirType::Pointer(Box::new(HirType::Char)),
703 },
704 ],
705 };
706
707 let result = gen.generate_enum(&info);
708
709 assert!(result.contains("#[derive(Debug, Clone, PartialEq)]"));
711
712 assert!(result.contains("pub enum Value {"));
714
715 assert!(result.contains("Int(i32)"));
717 assert!(result.contains("Float(f32)"));
718 assert!(result.contains("String(String)")); }
720
721 #[test]
722 fn test_generate_enum_with_nested_types() {
723 let gen = EnumGenerator::new();
724 let info = TaggedUnionInfo {
725 struct_name: "Container".to_string(),
726 tag_field_name: "kind".to_string(),
727 union_field_name: "data".to_string(),
728 variants: vec![
729 VariantInfo {
730 name: "boxed_int".to_string(),
731 payload_type: HirType::Box(Box::new(HirType::Int)),
732 },
733 VariantInfo {
734 name: "vec_float".to_string(),
735 payload_type: HirType::Vec(Box::new(HirType::Float)),
736 },
737 ],
738 };
739
740 let result = gen.generate_enum(&info);
741 assert!(result.contains("BoxedInt(Box<i32>)"));
742 assert!(result.contains("VecFloat(Vec<f32>)"));
743 }
744}